Merge remote-tracking branch 'citadel/master' into mobility_flags
This commit is contained in:
@@ -1,314 +1,314 @@
|
||||
/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()
|
||||
SEND_SIGNAL(src, COMSIG_COMPONENT_REGISTER_PARENT) //CITADEL EDIT
|
||||
|
||||
/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()
|
||||
SEND_SIGNAL(src, COMSIG_COMPONENT_UNREGISTER_PARENT) //CITADEL EDIT
|
||||
|
||||
/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)
|
||||
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)
|
||||
RETURN_TYPE(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
|
||||
/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()
|
||||
SEND_SIGNAL(src, COMSIG_COMPONENT_REGISTER_PARENT) //CITADEL EDIT
|
||||
|
||||
/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()
|
||||
SEND_SIGNAL(src, COMSIG_COMPONENT_UNREGISTER_PARENT) //CITADEL EDIT
|
||||
|
||||
/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)
|
||||
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)
|
||||
RETURN_TYPE(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
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
/datum/component/jousting
|
||||
var/current_direction = NONE
|
||||
var/max_tile_charge = 5
|
||||
var/min_tile_charge = 2 //tiles before this code gets into effect.
|
||||
var/current_tile_charge = 0
|
||||
var/movement_reset_tolerance = 2 //deciseconds
|
||||
var/unmounted_damage_boost_per_tile = 0
|
||||
var/unmounted_knockdown_chance_per_tile = 0
|
||||
var/unmounted_knockdown_time = 0
|
||||
var/mounted_damage_boost_per_tile = 2
|
||||
var/mounted_knockdown_chance_per_tile = 20
|
||||
var/mounted_knockdown_time = 20
|
||||
var/requires_mob_riding = TRUE //whether this only works if the attacker is riding a mob, rather than anything they can buckle to.
|
||||
var/requires_mount = TRUE //kinda defeats the point of jousting if you're not mounted but whatever.
|
||||
var/mob/current_holder
|
||||
var/current_timerid
|
||||
|
||||
/datum/component/jousting/Initialize()
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
|
||||
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
|
||||
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/on_attack)
|
||||
|
||||
/datum/component/jousting/proc/on_equip(datum/source, mob/user, slot)
|
||||
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/mob_move, TRUE)
|
||||
current_holder = user
|
||||
|
||||
/datum/component/jousting/proc/on_drop(datum/source, mob/user)
|
||||
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
|
||||
current_holder = null
|
||||
current_direction = NONE
|
||||
current_tile_charge = 0
|
||||
|
||||
/datum/component/jousting/proc/on_attack(datum/source, mob/living/target, mob/user)
|
||||
if(user != current_holder)
|
||||
return
|
||||
var/current = current_tile_charge
|
||||
var/obj/item/I = parent
|
||||
var/target_buckled = target.buckled ? TRUE : FALSE //we don't need the reference of what they're buckled to, just whether they are.
|
||||
if((requires_mount && ((requires_mob_riding && !ismob(user.buckled)) || (!user.buckled))) || !current_direction || (current_tile_charge < min_tile_charge))
|
||||
return
|
||||
var/turf/target_turf = get_step(user, current_direction)
|
||||
if(target in range(1, target_turf))
|
||||
var/knockdown_chance = (target_buckled? mounted_knockdown_chance_per_tile : unmounted_knockdown_chance_per_tile) * current
|
||||
var/knockdown_time = (target_buckled? mounted_knockdown_time : unmounted_knockdown_time)
|
||||
var/damage = (target_buckled? mounted_damage_boost_per_tile : unmounted_damage_boost_per_tile) * current
|
||||
var/sharp = I.get_sharpness()
|
||||
var/msg
|
||||
if(damage)
|
||||
msg += "[user] [sharp? "impales" : "slams into"] [target] [sharp? "on" : "with"] their [parent]"
|
||||
target.apply_damage(damage, BRUTE, user.zone_selected, 0)
|
||||
if(prob(knockdown_chance))
|
||||
msg += " and knocks [target] [target_buckled? "off of [target.buckled]" : "down"]"
|
||||
if(target_buckled)
|
||||
target.buckled.unbuckle_mob(target)
|
||||
target.DefaultCombatKnockdown(knockdown_time)
|
||||
if(length(msg))
|
||||
user.visible_message("<span class='danger'>[msg]!</span>")
|
||||
|
||||
/datum/component/jousting/proc/mob_move(datum/source, newloc, dir)
|
||||
if(!current_holder || (requires_mount && ((requires_mob_riding && !ismob(current_holder.buckled)) || (!current_holder.buckled))))
|
||||
return
|
||||
if(dir != current_direction)
|
||||
current_tile_charge = 0
|
||||
current_direction = dir
|
||||
if(current_tile_charge < max_tile_charge)
|
||||
current_tile_charge++
|
||||
if(current_timerid)
|
||||
deltimer(current_timerid)
|
||||
current_timerid = addtimer(CALLBACK(src, .proc/reset_charge), movement_reset_tolerance, TIMER_STOPPABLE)
|
||||
|
||||
/datum/component/jousting/proc/reset_charge()
|
||||
current_tile_charge = 0
|
||||
/datum/component/jousting
|
||||
var/current_direction = NONE
|
||||
var/max_tile_charge = 5
|
||||
var/min_tile_charge = 2 //tiles before this code gets into effect.
|
||||
var/current_tile_charge = 0
|
||||
var/movement_reset_tolerance = 2 //deciseconds
|
||||
var/unmounted_damage_boost_per_tile = 0
|
||||
var/unmounted_knockdown_chance_per_tile = 0
|
||||
var/unmounted_knockdown_time = 0
|
||||
var/mounted_damage_boost_per_tile = 2
|
||||
var/mounted_knockdown_chance_per_tile = 20
|
||||
var/mounted_knockdown_time = 20
|
||||
var/requires_mob_riding = TRUE //whether this only works if the attacker is riding a mob, rather than anything they can buckle to.
|
||||
var/requires_mount = TRUE //kinda defeats the point of jousting if you're not mounted but whatever.
|
||||
var/mob/current_holder
|
||||
var/current_timerid
|
||||
|
||||
/datum/component/jousting/Initialize()
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
|
||||
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
|
||||
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/on_attack)
|
||||
|
||||
/datum/component/jousting/proc/on_equip(datum/source, mob/user, slot)
|
||||
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/mob_move, TRUE)
|
||||
current_holder = user
|
||||
|
||||
/datum/component/jousting/proc/on_drop(datum/source, mob/user)
|
||||
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
|
||||
current_holder = null
|
||||
current_direction = NONE
|
||||
current_tile_charge = 0
|
||||
|
||||
/datum/component/jousting/proc/on_attack(datum/source, mob/living/target, mob/user)
|
||||
if(user != current_holder)
|
||||
return
|
||||
var/current = current_tile_charge
|
||||
var/obj/item/I = parent
|
||||
var/target_buckled = target.buckled ? TRUE : FALSE //we don't need the reference of what they're buckled to, just whether they are.
|
||||
if((requires_mount && ((requires_mob_riding && !ismob(user.buckled)) || (!user.buckled))) || !current_direction || (current_tile_charge < min_tile_charge))
|
||||
return
|
||||
var/turf/target_turf = get_step(user, current_direction)
|
||||
if(target in range(1, target_turf))
|
||||
var/knockdown_chance = (target_buckled? mounted_knockdown_chance_per_tile : unmounted_knockdown_chance_per_tile) * current
|
||||
var/knockdown_time = (target_buckled? mounted_knockdown_time : unmounted_knockdown_time)
|
||||
var/damage = (target_buckled? mounted_damage_boost_per_tile : unmounted_damage_boost_per_tile) * current
|
||||
var/sharp = I.get_sharpness()
|
||||
var/msg
|
||||
if(damage)
|
||||
msg += "[user] [sharp? "impales" : "slams into"] [target] [sharp? "on" : "with"] their [parent]"
|
||||
target.apply_damage(damage, BRUTE, user.zone_selected, 0)
|
||||
if(prob(knockdown_chance))
|
||||
msg += " and knocks [target] [target_buckled? "off of [target.buckled]" : "down"]"
|
||||
if(target_buckled)
|
||||
target.buckled.unbuckle_mob(target)
|
||||
target.DefaultCombatKnockdown(knockdown_time)
|
||||
if(length(msg))
|
||||
user.visible_message("<span class='danger'>[msg]!</span>")
|
||||
|
||||
/datum/component/jousting/proc/mob_move(datum/source, newloc, dir)
|
||||
if(!current_holder || (requires_mount && ((requires_mob_riding && !ismob(current_holder.buckled)) || (!current_holder.buckled))))
|
||||
return
|
||||
if(dir != current_direction)
|
||||
current_tile_charge = 0
|
||||
current_direction = dir
|
||||
if(current_tile_charge < max_tile_charge)
|
||||
current_tile_charge++
|
||||
if(current_timerid)
|
||||
deltimer(current_timerid)
|
||||
current_timerid = addtimer(CALLBACK(src, .proc/reset_charge), movement_reset_tolerance, TIMER_STOPPABLE)
|
||||
|
||||
/datum/component/jousting/proc/reset_charge()
|
||||
current_tile_charge = 0
|
||||
|
||||
@@ -1,239 +1,239 @@
|
||||
#define LOCKON_AIMING_MAX_CURSOR_RADIUS 7
|
||||
#define LOCKON_IGNORE_RESULT "ignore_my_result"
|
||||
#define LOCKON_RANGING_BREAK_CHECK if(current_ranging_id != this_id){return LOCKON_IGNORE_RESULT}
|
||||
|
||||
/datum/component/lockon_aiming
|
||||
dupe_mode = COMPONENT_DUPE_ALLOWED
|
||||
var/lock_icon = 'icons/mob/cameramob.dmi'
|
||||
var/lock_icon_state = "marker"
|
||||
var/mutable_appearance/lock_appearance
|
||||
var/list/image/lock_images
|
||||
var/list/target_typecache
|
||||
var/list/immune_weakrefs //list(weakref = TRUE)
|
||||
var/mob_stat_check = TRUE //if a potential target is a mob make sure it's conscious!
|
||||
var/lock_amount = 1
|
||||
var/lock_cursor_range = 5
|
||||
var/list/locked_weakrefs
|
||||
var/update_disabled = FALSE
|
||||
var/current_ranging_id = 0
|
||||
var/list/last_location
|
||||
var/datum/callback/on_lock
|
||||
var/datum/callback/can_target_callback
|
||||
|
||||
/datum/component/lockon_aiming/Initialize(range, list/typecache, amount, list/immune, datum/callback/when_locked, icon, icon_state, datum/callback/target_callback)
|
||||
if(!ismob(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(target_callback)
|
||||
can_target_callback = target_callback
|
||||
else
|
||||
can_target_callback = CALLBACK(src, .proc/can_target)
|
||||
if(range)
|
||||
lock_cursor_range = range
|
||||
if(typecache)
|
||||
target_typecache = typecache
|
||||
if(amount)
|
||||
lock_amount = amount
|
||||
immune_weakrefs = list(WEAKREF(parent) = TRUE) //Manually take this out if you want..
|
||||
if(immune)
|
||||
for(var/i in immune)
|
||||
if(isweakref(i))
|
||||
immune_weakrefs[i] = TRUE
|
||||
else if(isatom(i))
|
||||
immune_weakrefs[WEAKREF(i)] = TRUE
|
||||
if(when_locked)
|
||||
on_lock = when_locked
|
||||
if(icon)
|
||||
lock_icon = icon
|
||||
if(icon_state)
|
||||
lock_icon_state = icon_state
|
||||
generate_lock_visuals()
|
||||
var/mob/M = parent
|
||||
LAZYOR(M.mousemove_intercept_objects, src)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/datum/component/lockon_aiming/Destroy()
|
||||
var/mob/M = parent
|
||||
clear_visuals()
|
||||
LAZYREMOVE(M.mousemove_intercept_objects, src)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
return ..()
|
||||
|
||||
/datum/component/lockon_aiming/proc/show_visuals()
|
||||
LAZYINITLIST(lock_images)
|
||||
var/mob/M = parent
|
||||
if(!M.client)
|
||||
return
|
||||
for(var/i in locked_weakrefs)
|
||||
var/datum/weakref/R = i
|
||||
var/atom/A = R.resolve()
|
||||
if(!A)
|
||||
continue //It'll be cleared by processing.
|
||||
var/image/I = new
|
||||
I.appearance = lock_appearance
|
||||
I.loc = A
|
||||
M.client.images |= I
|
||||
lock_images |= I
|
||||
|
||||
/datum/component/lockon_aiming/proc/clear_visuals()
|
||||
var/mob/M = parent
|
||||
if(!M.client)
|
||||
return
|
||||
if(!lock_images)
|
||||
return
|
||||
for(var/i in lock_images)
|
||||
M.client.images -= i
|
||||
qdel(i)
|
||||
lock_images.Cut()
|
||||
|
||||
/datum/component/lockon_aiming/proc/refresh_visuals()
|
||||
clear_visuals()
|
||||
show_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/generate_lock_visuals()
|
||||
lock_appearance = mutable_appearance(icon = lock_icon, icon_state = lock_icon_state, layer = FLOAT_LAYER)
|
||||
|
||||
/datum/component/lockon_aiming/proc/unlock_all(refresh_vis = TRUE)
|
||||
LAZYCLEARLIST(locked_weakrefs)
|
||||
if(refresh_vis)
|
||||
refresh_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/unlock(atom/A, refresh_vis = TRUE)
|
||||
if(!A.weak_reference)
|
||||
return
|
||||
LAZYREMOVE(locked_weakrefs, A.weak_reference)
|
||||
if(refresh_vis)
|
||||
refresh_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/lock(atom/A, refresh_vis = TRUE)
|
||||
LAZYOR(locked_weakrefs, WEAKREF(A))
|
||||
if(refresh_vis)
|
||||
refresh_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/add_immune_atom(atom/A)
|
||||
var/datum/weakref/R = WEAKREF(A)
|
||||
if(immune_weakrefs && (immune_weakrefs[R]))
|
||||
return
|
||||
LAZYSET(immune_weakrefs, R, TRUE)
|
||||
|
||||
/datum/component/lockon_aiming/proc/remove_immune_atom(atom/A)
|
||||
if(!A.weak_reference || !immune_weakrefs) //if A doesn't have a weakref how did it get on the immunity list?
|
||||
return
|
||||
LAZYREMOVE(immune_weakrefs, A.weak_reference)
|
||||
|
||||
/datum/component/lockon_aiming/onMouseMove(object,location,control,params)
|
||||
var/mob/M = parent
|
||||
if(!istype(M) || !M.client)
|
||||
return
|
||||
var/datum/position/P = mouse_absolute_datum_map_position_from_client(M.client)
|
||||
if(!P)
|
||||
return
|
||||
var/turf/T = P.return_turf()
|
||||
LAZYINITLIST(last_location)
|
||||
if(length(last_location) == 3 && last_location[1] == T.x && last_location[2] == T.y && last_location[3] == T.z)
|
||||
return //Same turf, don't bother.
|
||||
if(last_location)
|
||||
last_location.Cut()
|
||||
else
|
||||
last_location = list()
|
||||
last_location.len = 3
|
||||
last_location[1] = T.x
|
||||
last_location[2] = T.y
|
||||
last_location[3] = T.z
|
||||
autolock()
|
||||
|
||||
/datum/component/lockon_aiming/process()
|
||||
if(update_disabled)
|
||||
return
|
||||
if(!last_location)
|
||||
return
|
||||
var/changed = FALSE
|
||||
for(var/i in locked_weakrefs)
|
||||
var/datum/weakref/R = i
|
||||
if(istype(R))
|
||||
var/atom/thing = R.resolve()
|
||||
if(!istype(thing) || (get_dist(thing, locate(last_location[1], last_location[2], last_location[3])) > lock_cursor_range))
|
||||
unlock(R)
|
||||
changed = TRUE
|
||||
else
|
||||
unlock(R)
|
||||
changed = TRUE
|
||||
if(changed)
|
||||
autolock()
|
||||
|
||||
/datum/component/lockon_aiming/proc/autolock()
|
||||
var/mob/M = parent
|
||||
if(!M.client)
|
||||
return FALSE
|
||||
var/datum/position/current = mouse_absolute_datum_map_position_from_client(M.client)
|
||||
var/turf/target = current.return_turf()
|
||||
var/list/atom/targets = get_nearest(target, target_typecache, lock_amount, lock_cursor_range)
|
||||
if(targets == LOCKON_IGNORE_RESULT)
|
||||
return
|
||||
unlock_all(FALSE)
|
||||
for(var/i in targets)
|
||||
if(immune_weakrefs[WEAKREF(i)])
|
||||
continue
|
||||
lock(i, FALSE)
|
||||
refresh_visuals()
|
||||
on_lock.Invoke(locked_weakrefs)
|
||||
|
||||
/datum/component/lockon_aiming/proc/can_target(atom/A)
|
||||
var/mob/M = A
|
||||
return is_type_in_typecache(A, target_typecache) && !(ismob(A) && mob_stat_check && M.stat != CONSCIOUS) && !immune_weakrefs[WEAKREF(A)]
|
||||
|
||||
/datum/component/lockon_aiming/proc/get_nearest(turf/T, list/typecache, amount, range)
|
||||
current_ranging_id++
|
||||
var/this_id = current_ranging_id
|
||||
var/list/L = list()
|
||||
var/turf/center = get_turf(T)
|
||||
if(amount < 1 || range < 0 || !istype(center) || !islist(typecache))
|
||||
return
|
||||
if(range == 0)
|
||||
return typecache_filter_list(T.contents + T, typecache)
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/cd = 0
|
||||
while(cd <= range)
|
||||
x = center.x - cd + 1
|
||||
y = center.y + cd
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
for(x in x to center.x + cd)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
y = center.y + cd - 1
|
||||
x = center.x + cd
|
||||
for(y in center.y - cd to y)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
y = center.y - cd
|
||||
x = center.x + cd - 1
|
||||
for(x in center.x - cd to x)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
y = center.y - cd + 1
|
||||
x = center.x - cd
|
||||
for(y in y to center.y + cd)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
cd++
|
||||
CHECK_TICK
|
||||
#define LOCKON_AIMING_MAX_CURSOR_RADIUS 7
|
||||
#define LOCKON_IGNORE_RESULT "ignore_my_result"
|
||||
#define LOCKON_RANGING_BREAK_CHECK if(current_ranging_id != this_id){return LOCKON_IGNORE_RESULT}
|
||||
|
||||
/datum/component/lockon_aiming
|
||||
dupe_mode = COMPONENT_DUPE_ALLOWED
|
||||
var/lock_icon = 'icons/mob/cameramob.dmi'
|
||||
var/lock_icon_state = "marker"
|
||||
var/mutable_appearance/lock_appearance
|
||||
var/list/image/lock_images
|
||||
var/list/target_typecache
|
||||
var/list/immune_weakrefs //list(weakref = TRUE)
|
||||
var/mob_stat_check = TRUE //if a potential target is a mob make sure it's conscious!
|
||||
var/lock_amount = 1
|
||||
var/lock_cursor_range = 5
|
||||
var/list/locked_weakrefs
|
||||
var/update_disabled = FALSE
|
||||
var/current_ranging_id = 0
|
||||
var/list/last_location
|
||||
var/datum/callback/on_lock
|
||||
var/datum/callback/can_target_callback
|
||||
|
||||
/datum/component/lockon_aiming/Initialize(range, list/typecache, amount, list/immune, datum/callback/when_locked, icon, icon_state, datum/callback/target_callback)
|
||||
if(!ismob(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(target_callback)
|
||||
can_target_callback = target_callback
|
||||
else
|
||||
can_target_callback = CALLBACK(src, .proc/can_target)
|
||||
if(range)
|
||||
lock_cursor_range = range
|
||||
if(typecache)
|
||||
target_typecache = typecache
|
||||
if(amount)
|
||||
lock_amount = amount
|
||||
immune_weakrefs = list(WEAKREF(parent) = TRUE) //Manually take this out if you want..
|
||||
if(immune)
|
||||
for(var/i in immune)
|
||||
if(isweakref(i))
|
||||
immune_weakrefs[i] = TRUE
|
||||
else if(isatom(i))
|
||||
immune_weakrefs[WEAKREF(i)] = TRUE
|
||||
if(when_locked)
|
||||
on_lock = when_locked
|
||||
if(icon)
|
||||
lock_icon = icon
|
||||
if(icon_state)
|
||||
lock_icon_state = icon_state
|
||||
generate_lock_visuals()
|
||||
var/mob/M = parent
|
||||
LAZYOR(M.mousemove_intercept_objects, src)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/datum/component/lockon_aiming/Destroy()
|
||||
var/mob/M = parent
|
||||
clear_visuals()
|
||||
LAZYREMOVE(M.mousemove_intercept_objects, src)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
return ..()
|
||||
|
||||
/datum/component/lockon_aiming/proc/show_visuals()
|
||||
LAZYINITLIST(lock_images)
|
||||
var/mob/M = parent
|
||||
if(!M.client)
|
||||
return
|
||||
for(var/i in locked_weakrefs)
|
||||
var/datum/weakref/R = i
|
||||
var/atom/A = R.resolve()
|
||||
if(!A)
|
||||
continue //It'll be cleared by processing.
|
||||
var/image/I = new
|
||||
I.appearance = lock_appearance
|
||||
I.loc = A
|
||||
M.client.images |= I
|
||||
lock_images |= I
|
||||
|
||||
/datum/component/lockon_aiming/proc/clear_visuals()
|
||||
var/mob/M = parent
|
||||
if(!M.client)
|
||||
return
|
||||
if(!lock_images)
|
||||
return
|
||||
for(var/i in lock_images)
|
||||
M.client.images -= i
|
||||
qdel(i)
|
||||
lock_images.Cut()
|
||||
|
||||
/datum/component/lockon_aiming/proc/refresh_visuals()
|
||||
clear_visuals()
|
||||
show_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/generate_lock_visuals()
|
||||
lock_appearance = mutable_appearance(icon = lock_icon, icon_state = lock_icon_state, layer = FLOAT_LAYER)
|
||||
|
||||
/datum/component/lockon_aiming/proc/unlock_all(refresh_vis = TRUE)
|
||||
LAZYCLEARLIST(locked_weakrefs)
|
||||
if(refresh_vis)
|
||||
refresh_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/unlock(atom/A, refresh_vis = TRUE)
|
||||
if(!A.weak_reference)
|
||||
return
|
||||
LAZYREMOVE(locked_weakrefs, A.weak_reference)
|
||||
if(refresh_vis)
|
||||
refresh_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/lock(atom/A, refresh_vis = TRUE)
|
||||
LAZYOR(locked_weakrefs, WEAKREF(A))
|
||||
if(refresh_vis)
|
||||
refresh_visuals()
|
||||
|
||||
/datum/component/lockon_aiming/proc/add_immune_atom(atom/A)
|
||||
var/datum/weakref/R = WEAKREF(A)
|
||||
if(immune_weakrefs && (immune_weakrefs[R]))
|
||||
return
|
||||
LAZYSET(immune_weakrefs, R, TRUE)
|
||||
|
||||
/datum/component/lockon_aiming/proc/remove_immune_atom(atom/A)
|
||||
if(!A.weak_reference || !immune_weakrefs) //if A doesn't have a weakref how did it get on the immunity list?
|
||||
return
|
||||
LAZYREMOVE(immune_weakrefs, A.weak_reference)
|
||||
|
||||
/datum/component/lockon_aiming/onMouseMove(object,location,control,params)
|
||||
var/mob/M = parent
|
||||
if(!istype(M) || !M.client)
|
||||
return
|
||||
var/datum/position/P = mouse_absolute_datum_map_position_from_client(M.client)
|
||||
if(!P)
|
||||
return
|
||||
var/turf/T = P.return_turf()
|
||||
LAZYINITLIST(last_location)
|
||||
if(length(last_location) == 3 && last_location[1] == T.x && last_location[2] == T.y && last_location[3] == T.z)
|
||||
return //Same turf, don't bother.
|
||||
if(last_location)
|
||||
last_location.Cut()
|
||||
else
|
||||
last_location = list()
|
||||
last_location.len = 3
|
||||
last_location[1] = T.x
|
||||
last_location[2] = T.y
|
||||
last_location[3] = T.z
|
||||
autolock()
|
||||
|
||||
/datum/component/lockon_aiming/process()
|
||||
if(update_disabled)
|
||||
return
|
||||
if(!last_location)
|
||||
return
|
||||
var/changed = FALSE
|
||||
for(var/i in locked_weakrefs)
|
||||
var/datum/weakref/R = i
|
||||
if(istype(R))
|
||||
var/atom/thing = R.resolve()
|
||||
if(!istype(thing) || (get_dist(thing, locate(last_location[1], last_location[2], last_location[3])) > lock_cursor_range))
|
||||
unlock(R)
|
||||
changed = TRUE
|
||||
else
|
||||
unlock(R)
|
||||
changed = TRUE
|
||||
if(changed)
|
||||
autolock()
|
||||
|
||||
/datum/component/lockon_aiming/proc/autolock()
|
||||
var/mob/M = parent
|
||||
if(!M.client)
|
||||
return FALSE
|
||||
var/datum/position/current = mouse_absolute_datum_map_position_from_client(M.client)
|
||||
var/turf/target = current.return_turf()
|
||||
var/list/atom/targets = get_nearest(target, target_typecache, lock_amount, lock_cursor_range)
|
||||
if(targets == LOCKON_IGNORE_RESULT)
|
||||
return
|
||||
unlock_all(FALSE)
|
||||
for(var/i in targets)
|
||||
if(immune_weakrefs[WEAKREF(i)])
|
||||
continue
|
||||
lock(i, FALSE)
|
||||
refresh_visuals()
|
||||
on_lock.Invoke(locked_weakrefs)
|
||||
|
||||
/datum/component/lockon_aiming/proc/can_target(atom/A)
|
||||
var/mob/M = A
|
||||
return is_type_in_typecache(A, target_typecache) && !(ismob(A) && mob_stat_check && M.stat != CONSCIOUS) && !immune_weakrefs[WEAKREF(A)]
|
||||
|
||||
/datum/component/lockon_aiming/proc/get_nearest(turf/T, list/typecache, amount, range)
|
||||
current_ranging_id++
|
||||
var/this_id = current_ranging_id
|
||||
var/list/L = list()
|
||||
var/turf/center = get_turf(T)
|
||||
if(amount < 1 || range < 0 || !istype(center) || !islist(typecache))
|
||||
return
|
||||
if(range == 0)
|
||||
return typecache_filter_list(T.contents + T, typecache)
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/cd = 0
|
||||
while(cd <= range)
|
||||
x = center.x - cd + 1
|
||||
y = center.y + cd
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
for(x in x to center.x + cd)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
y = center.y + cd - 1
|
||||
x = center.x + cd
|
||||
for(y in center.y - cd to y)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
y = center.y - cd
|
||||
x = center.x + cd - 1
|
||||
for(x in center.x - cd to x)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
y = center.y - cd + 1
|
||||
x = center.x - cd
|
||||
for(y in y to center.y + cd)
|
||||
T = locate(x, y, center.z)
|
||||
if(T)
|
||||
L |= special_list_filter(T.contents, can_target_callback)
|
||||
if(L.len >= amount)
|
||||
L.Cut(amount+1)
|
||||
return L
|
||||
LOCKON_RANGING_BREAK_CHECK
|
||||
cd++
|
||||
CHECK_TICK
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
//Thing meant for allowing datums and objects to access a NTnet network datum.
|
||||
/datum/proc/ntnet_receive(datum/netdata/data)
|
||||
return
|
||||
|
||||
/datum/proc/ntnet_receive_broadcast(datum/netdata/data)
|
||||
return
|
||||
|
||||
/datum/proc/ntnet_send(datum/netdata/data, netid)
|
||||
var/datum/component/ntnet_interface/NIC = GetComponent(/datum/component/ntnet_interface)
|
||||
if(!NIC)
|
||||
return FALSE
|
||||
return NIC.__network_send(data, netid)
|
||||
|
||||
/datum/component/ntnet_interface
|
||||
var/hardware_id //text. this is the true ID. do not change this. stuff like ID forgery can be done manually.
|
||||
var/network_name = "" //text
|
||||
var/list/networks_connected_by_id = list() //id = datum/ntnet
|
||||
var/differentiate_broadcast = TRUE //If false, broadcasts go to ntnet_receive. NOT RECOMMENDED.
|
||||
|
||||
/datum/component/ntnet_interface/Initialize(force_name = "NTNet Device", autoconnect_station_network = TRUE) //Don't force ID unless you know what you're doing!
|
||||
hardware_id = "[SSnetworks.get_next_HID()]"
|
||||
network_name = force_name
|
||||
if(!SSnetworks.register_interface(src))
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
CRASH("Unable to register NTNet interface. Interface deleted.")
|
||||
if(autoconnect_station_network)
|
||||
register_connection(SSnetworks.station_network)
|
||||
|
||||
/datum/component/ntnet_interface/Destroy()
|
||||
unregister_all_connections()
|
||||
SSnetworks.unregister_interface(src)
|
||||
return ..()
|
||||
|
||||
/datum/component/ntnet_interface/proc/__network_receive(datum/netdata/data) //Do not directly proccall!
|
||||
SEND_SIGNAL(parent, COMSIG_COMPONENT_NTNET_RECEIVE, data)
|
||||
if(differentiate_broadcast && data.broadcast)
|
||||
parent.ntnet_receive_broadcast(data)
|
||||
else
|
||||
parent.ntnet_receive(data)
|
||||
|
||||
/datum/component/ntnet_interface/proc/__network_send(datum/netdata/data, netid) //Do not directly proccall!
|
||||
// Process data before sending it
|
||||
data.pre_send(src)
|
||||
|
||||
if(netid)
|
||||
if(networks_connected_by_id[netid])
|
||||
var/datum/ntnet/net = networks_connected_by_id[netid]
|
||||
return net.process_data_transmit(src, data)
|
||||
return FALSE
|
||||
for(var/i in networks_connected_by_id)
|
||||
var/datum/ntnet/net = networks_connected_by_id[i]
|
||||
net.process_data_transmit(src, data)
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/register_connection(datum/ntnet/net)
|
||||
if(net.interface_connect(src))
|
||||
networks_connected_by_id[net.network_id] = net
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/unregister_all_connections()
|
||||
for(var/i in networks_connected_by_id)
|
||||
unregister_connection(networks_connected_by_id[i])
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/unregister_connection(datum/ntnet/net)
|
||||
net.interface_disconnect(src)
|
||||
networks_connected_by_id -= net.network_id
|
||||
return TRUE
|
||||
//Thing meant for allowing datums and objects to access a NTnet network datum.
|
||||
/datum/proc/ntnet_receive(datum/netdata/data)
|
||||
return
|
||||
|
||||
/datum/proc/ntnet_receive_broadcast(datum/netdata/data)
|
||||
return
|
||||
|
||||
/datum/proc/ntnet_send(datum/netdata/data, netid)
|
||||
var/datum/component/ntnet_interface/NIC = GetComponent(/datum/component/ntnet_interface)
|
||||
if(!NIC)
|
||||
return FALSE
|
||||
return NIC.__network_send(data, netid)
|
||||
|
||||
/datum/component/ntnet_interface
|
||||
var/hardware_id //text. this is the true ID. do not change this. stuff like ID forgery can be done manually.
|
||||
var/network_name = "" //text
|
||||
var/list/networks_connected_by_id = list() //id = datum/ntnet
|
||||
var/differentiate_broadcast = TRUE //If false, broadcasts go to ntnet_receive. NOT RECOMMENDED.
|
||||
|
||||
/datum/component/ntnet_interface/Initialize(force_name = "NTNet Device", autoconnect_station_network = TRUE) //Don't force ID unless you know what you're doing!
|
||||
hardware_id = "[SSnetworks.get_next_HID()]"
|
||||
network_name = force_name
|
||||
if(!SSnetworks.register_interface(src))
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
CRASH("Unable to register NTNet interface. Interface deleted.")
|
||||
if(autoconnect_station_network)
|
||||
register_connection(SSnetworks.station_network)
|
||||
|
||||
/datum/component/ntnet_interface/Destroy()
|
||||
unregister_all_connections()
|
||||
SSnetworks.unregister_interface(src)
|
||||
return ..()
|
||||
|
||||
/datum/component/ntnet_interface/proc/__network_receive(datum/netdata/data) //Do not directly proccall!
|
||||
SEND_SIGNAL(parent, COMSIG_COMPONENT_NTNET_RECEIVE, data)
|
||||
if(differentiate_broadcast && data.broadcast)
|
||||
parent.ntnet_receive_broadcast(data)
|
||||
else
|
||||
parent.ntnet_receive(data)
|
||||
|
||||
/datum/component/ntnet_interface/proc/__network_send(datum/netdata/data, netid) //Do not directly proccall!
|
||||
// Process data before sending it
|
||||
data.pre_send(src)
|
||||
|
||||
if(netid)
|
||||
if(networks_connected_by_id[netid])
|
||||
var/datum/ntnet/net = networks_connected_by_id[netid]
|
||||
return net.process_data_transmit(src, data)
|
||||
return FALSE
|
||||
for(var/i in networks_connected_by_id)
|
||||
var/datum/ntnet/net = networks_connected_by_id[i]
|
||||
net.process_data_transmit(src, data)
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/register_connection(datum/ntnet/net)
|
||||
if(net.interface_connect(src))
|
||||
networks_connected_by_id[net.network_id] = net
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/unregister_all_connections()
|
||||
for(var/i in networks_connected_by_id)
|
||||
unregister_connection(networks_connected_by_id[i])
|
||||
return TRUE
|
||||
|
||||
/datum/component/ntnet_interface/proc/unregister_connection(datum/ntnet/net)
|
||||
net.interface_disconnect(src)
|
||||
networks_connected_by_id -= net.network_id
|
||||
return TRUE
|
||||
|
||||
+359
-359
@@ -1,359 +1,359 @@
|
||||
/datum/component/riding
|
||||
var/last_vehicle_move = 0 //used for move delays
|
||||
var/last_move_diagonal = FALSE
|
||||
var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower
|
||||
var/keytype
|
||||
|
||||
var/slowed = FALSE
|
||||
var/slowvalue = 1
|
||||
|
||||
var/list/riding_offsets = list() //position_of_user = list(dir = list(px, py)), or RIDING_OFFSET_ALL for a generic one.
|
||||
var/list/directional_vehicle_layers = list() //["[DIRECTION]"] = layer. Don't set it for a direction for default, set a direction to null for no change.
|
||||
var/list/directional_vehicle_offsets = list() //same as above but instead of layer you have a list(px, py)
|
||||
var/list/allowed_turf_typecache
|
||||
var/list/forbid_turf_typecache //allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
|
||||
var/allow_one_away_from_valid_turf = TRUE //allow moving one tile away from a valid turf but not more.
|
||||
var/override_allow_spacemove = FALSE
|
||||
var/drive_verb = "drive"
|
||||
var/ride_check_rider_incapacitated = FALSE
|
||||
var/ride_check_rider_restrained = FALSE
|
||||
var/ride_check_ridden_incapacitated = FALSE
|
||||
var/list/offhands = list() // keyed list containing all the current riding offsets associated by mob
|
||||
|
||||
/datum/component/riding/Initialize()
|
||||
if(!ismovableatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, .proc/vehicle_mob_buckle)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_UNBUCKLE, .proc/vehicle_mob_unbuckle)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE)
|
||||
restore_position(M)
|
||||
unequip_buckle_inhands(M)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
|
||||
handle_vehicle_offsets()
|
||||
|
||||
/datum/component/riding/proc/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER)
|
||||
. = defaults["[AM.dir]"]
|
||||
if(directional_vehicle_layers["[AM.dir]"])
|
||||
. = directional_vehicle_layers["[AM.dir]"]
|
||||
if(isnull(.)) //you can set it to null to not change it.
|
||||
. = AM.layer
|
||||
AM.layer = .
|
||||
|
||||
/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
|
||||
directional_vehicle_layers["[dir]"] = layer
|
||||
|
||||
/datum/component/riding/proc/vehicle_moved(datum/source)
|
||||
var/atom/movable/AM = parent
|
||||
for(var/i in AM.buckled_mobs)
|
||||
ride_check(i)
|
||||
handle_vehicle_offsets()
|
||||
handle_vehicle_layer()
|
||||
|
||||
/datum/component/riding/proc/ride_check(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
var/mob/AMM = AM
|
||||
if((ride_check_rider_restrained && M.restrained(TRUE)) || (ride_check_rider_incapacitated && M.incapacitated(FALSE, TRUE)) || (ride_check_ridden_incapacitated && istype(AMM) && AMM.incapacitated(FALSE, TRUE)))
|
||||
AM.visible_message("<span class='warning'>[M] falls off of [AM]!</span>")
|
||||
AM.unbuckle_mob(M)
|
||||
return TRUE
|
||||
|
||||
/datum/component/riding/proc/force_dismount(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(M)
|
||||
|
||||
/datum/component/riding/proc/handle_vehicle_offsets()
|
||||
var/atom/movable/AM = parent
|
||||
var/AM_dir = "[AM.dir]"
|
||||
var/passindex = 0
|
||||
if(AM.has_buckled_mobs())
|
||||
for(var/m in AM.buckled_mobs)
|
||||
passindex++
|
||||
var/mob/living/buckled_mob = m
|
||||
var/list/offsets = get_offsets(passindex)
|
||||
var/rider_dir = get_rider_dir(passindex)
|
||||
buckled_mob.setDir(rider_dir)
|
||||
dir_loop:
|
||||
for(var/offsetdir in offsets)
|
||||
if(offsetdir == AM_dir)
|
||||
var/list/diroffsets = offsets[offsetdir]
|
||||
buckled_mob.pixel_x = diroffsets[1]
|
||||
if(diroffsets.len >= 2)
|
||||
buckled_mob.pixel_y = diroffsets[2]
|
||||
if(diroffsets.len == 3)
|
||||
buckled_mob.layer = diroffsets[3]
|
||||
break dir_loop
|
||||
var/list/static/default_vehicle_pixel_offsets = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
|
||||
var/px = default_vehicle_pixel_offsets[AM_dir]
|
||||
var/py = default_vehicle_pixel_offsets[AM_dir]
|
||||
if(directional_vehicle_offsets[AM_dir])
|
||||
if(isnull(directional_vehicle_offsets[AM_dir]))
|
||||
px = AM.pixel_x
|
||||
py = AM.pixel_y
|
||||
else
|
||||
px = directional_vehicle_offsets[AM_dir][1]
|
||||
py = directional_vehicle_offsets[AM_dir][2]
|
||||
AM.pixel_x = px
|
||||
AM.pixel_y = py
|
||||
|
||||
/datum/component/riding/proc/set_vehicle_dir_offsets(dir, x, y)
|
||||
directional_vehicle_offsets["[dir]"] = list(x, y)
|
||||
|
||||
//Override this to set your vehicle's various pixel offsets
|
||||
/datum/component/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer)
|
||||
. = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
|
||||
if(riding_offsets["[pass_index]"])
|
||||
. = riding_offsets["[pass_index]"]
|
||||
else if(riding_offsets["[RIDING_OFFSET_ALL]"])
|
||||
. = riding_offsets["[RIDING_OFFSET_ALL]"]
|
||||
|
||||
/datum/component/riding/proc/set_riding_offsets(index, list/offsets)
|
||||
if(!islist(offsets))
|
||||
return FALSE
|
||||
riding_offsets["[index]"] = offsets
|
||||
|
||||
//Override this to set the passengers/riders dir based on which passenger they are.
|
||||
//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc.
|
||||
/datum/component/riding/proc/get_rider_dir(pass_index)
|
||||
var/atom/movable/AM = parent
|
||||
return AM.dir
|
||||
|
||||
//KEYS
|
||||
/datum/component/riding/proc/keycheck(mob/user)
|
||||
return !keytype || user.is_holding_item_of_type(keytype)
|
||||
|
||||
//BUCKLE HOOKS
|
||||
/datum/component/riding/proc/restore_position(mob/living/buckled_mob)
|
||||
if(buckled_mob)
|
||||
buckled_mob.pixel_x = 0
|
||||
buckled_mob.pixel_y = 0
|
||||
if(buckled_mob.client)
|
||||
buckled_mob.client.change_view(CONFIG_GET(string/default_view))
|
||||
|
||||
//MOVEMENT
|
||||
/datum/component/riding/proc/turf_check(turf/next, turf/current)
|
||||
if(allowed_turf_typecache && !allowed_turf_typecache[next.type])
|
||||
return (allow_one_away_from_valid_turf && allowed_turf_typecache[current.type])
|
||||
else if(forbid_turf_typecache && forbid_turf_typecache[next.type])
|
||||
return (allow_one_away_from_valid_turf && !forbid_turf_typecache[current.type])
|
||||
return TRUE
|
||||
|
||||
/datum/component/riding/proc/handle_ride(mob/user, direction)
|
||||
var/atom/movable/AM = parent
|
||||
if(user.incapacitated())
|
||||
Unbuckle(user)
|
||||
return
|
||||
|
||||
if(world.time < last_vehicle_move + ((last_move_diagonal? 2 : 1) * vehicle_move_delay))
|
||||
return
|
||||
last_vehicle_move = world.time
|
||||
|
||||
if(keycheck(user))
|
||||
var/turf/next = get_step(AM, direction)
|
||||
var/turf/current = get_turf(AM)
|
||||
if(!istype(next) || !istype(current))
|
||||
return //not happening.
|
||||
if(!turf_check(next, current))
|
||||
to_chat(user, "Your \the [AM] can not go onto [next]!")
|
||||
return
|
||||
if(!Process_Spacemove(direction) || !isturf(AM.loc))
|
||||
return
|
||||
step(AM, direction)
|
||||
|
||||
if((direction & (direction - 1)) && (AM.loc == next)) //moved diagonally
|
||||
last_move_diagonal = TRUE
|
||||
else
|
||||
last_move_diagonal = FALSE
|
||||
|
||||
handle_vehicle_layer()
|
||||
handle_vehicle_offsets()
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You'll need the keys in one of your hands to [drive_verb] [AM].</span>")
|
||||
|
||||
/datum/component/riding/proc/Unbuckle(atom/movable/M)
|
||||
addtimer(CALLBACK(parent, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE)
|
||||
|
||||
/datum/component/riding/proc/Process_Spacemove(direction)
|
||||
var/atom/movable/AM = parent
|
||||
return override_allow_spacemove || AM.has_gravity()
|
||||
|
||||
/datum/component/riding/proc/account_limbs(mob/living/M)
|
||||
if(M.get_num_legs() < 2 && !slowed)
|
||||
vehicle_move_delay = vehicle_move_delay + slowvalue
|
||||
slowed = TRUE
|
||||
else if(slowed)
|
||||
vehicle_move_delay = vehicle_move_delay - slowvalue
|
||||
slowed = FALSE
|
||||
|
||||
///////Yes, I said humans. No, this won't end well...//////////
|
||||
/datum/component/riding/human
|
||||
|
||||
/datum/component/riding/human/Initialize()
|
||||
. = ..()
|
||||
RegisterSignal(parent, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
|
||||
|
||||
/datum/component/riding/human/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE)
|
||||
var/mob/living/carbon/human/H = parent
|
||||
H.remove_movespeed_modifier(MOVESPEED_ID_HUMAN_CARRYING)
|
||||
. = ..()
|
||||
|
||||
/datum/component/riding/human/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
|
||||
. = ..()
|
||||
var/mob/living/carbon/human/H = parent
|
||||
H.add_movespeed_modifier(MOVESPEED_ID_HUMAN_CARRYING, multiplicative_slowdown = HUMAN_CARRY_SLOWDOWN)
|
||||
|
||||
/datum/component/riding/human/proc/on_host_unarmed_melee(atom/target)
|
||||
var/mob/living/carbon/human/H = parent
|
||||
if(H.a_intent == INTENT_DISARM && (target in H.buckled_mobs))
|
||||
force_dismount(target)
|
||||
|
||||
/datum/component/riding/human/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.buckled_mobs && AM.buckled_mobs.len)
|
||||
for(var/mob/M in AM.buckled_mobs) //ensure proper layering of piggyback and carry, sometimes weird offsets get applied
|
||||
M.layer = MOB_LAYER
|
||||
if(!AM.buckle_lying)
|
||||
if(AM.dir == SOUTH)
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
if(AM.dir == NORTH)
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = MOB_LAYER
|
||||
|
||||
/datum/component/riding/human/get_offsets(pass_index)
|
||||
var/mob/living/carbon/human/H = parent
|
||||
if(H.buckle_lying)
|
||||
return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(0, 6), TEXT_WEST = list(0, 6))
|
||||
else
|
||||
return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(-6, 4), TEXT_WEST = list( 6, 4))
|
||||
|
||||
|
||||
/datum/component/riding/human/force_dismount(mob/living/user)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(user)
|
||||
user.DefaultCombatKnockdown(60)
|
||||
user.visible_message("<span class='warning'>[AM] pushes [user] off of [AM.p_them()]!</span>")
|
||||
|
||||
/datum/component/riding/cyborg
|
||||
|
||||
/datum/component/riding/cyborg/ride_check(mob/user)
|
||||
var/atom/movable/AM = parent
|
||||
if(user.incapacitated())
|
||||
var/kick = TRUE
|
||||
if(iscyborg(AM))
|
||||
var/mob/living/silicon/robot/R = AM
|
||||
if(R.module && R.module.ride_allow_incapacitated)
|
||||
kick = FALSE
|
||||
if(kick)
|
||||
to_chat(user, "<span class='userdanger'>You fall off of [AM]!</span>")
|
||||
Unbuckle(user)
|
||||
return
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/carbonuser = user
|
||||
if(!carbonuser.get_num_arms())
|
||||
Unbuckle(user)
|
||||
to_chat(user, "<span class='userdanger'>You can't grab onto [AM] with no hands!</span>")
|
||||
return
|
||||
|
||||
/datum/component/riding/cyborg/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.buckled_mobs && AM.buckled_mobs.len)
|
||||
if(AM.dir == SOUTH)
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
AM.layer = MOB_LAYER
|
||||
|
||||
/datum/component/riding/cyborg/get_offsets(pass_index) // list(dir = x, y, layer)
|
||||
return list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-6, 3), TEXT_WEST = list( 6, 3))
|
||||
|
||||
/datum/component/riding/cyborg/handle_vehicle_offsets()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.has_buckled_mobs())
|
||||
for(var/mob/living/M in AM.buckled_mobs)
|
||||
M.setDir(AM.dir)
|
||||
if(iscyborg(AM))
|
||||
var/mob/living/silicon/robot/R = AM
|
||||
if(istype(R.module))
|
||||
M.pixel_x = R.module.ride_offset_x[dir2text(AM.dir)]
|
||||
M.pixel_y = R.module.ride_offset_y[dir2text(AM.dir)]
|
||||
else
|
||||
..()
|
||||
|
||||
/datum/component/riding/cyborg/force_dismount(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(M)
|
||||
var/turf/target = get_edge_target_turf(AM, AM.dir)
|
||||
var/turf/targetm = get_step(get_turf(AM), AM.dir)
|
||||
M.Move(targetm)
|
||||
M.visible_message("<span class='warning'>[M] is thrown clear of [AM]!</span>")
|
||||
M.throw_at(target, 14, 5, AM)
|
||||
M.DefaultCombatKnockdown(60)
|
||||
|
||||
/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1, mob/living/riding_target_override)
|
||||
var/list/equipped
|
||||
var/mob/living/L = riding_target_override ? riding_target_override : user
|
||||
for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--)
|
||||
var/obj/item/riding_offhand/inhand = new
|
||||
inhand.rider = L
|
||||
inhand.parent = parent
|
||||
if(!user.put_in_hands(inhand, TRUE))
|
||||
qdel(inhand) // it isn't going to be added to offhands anyway
|
||||
break
|
||||
LAZYADD(equipped, inhand)
|
||||
var/amount_equipped = LAZYLEN(equipped)
|
||||
if(amount_equipped)
|
||||
LAZYADD(offhands[L], equipped)
|
||||
if(amount_equipped >= amount_required)
|
||||
return TRUE
|
||||
unequip_buckle_inhands(L)
|
||||
return FALSE
|
||||
|
||||
/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user)
|
||||
for(var/a in offhands[user])
|
||||
LAZYREMOVE(offhands[user], a)
|
||||
if(a) //edge cases null entries
|
||||
var/obj/item/riding_offhand/O = a
|
||||
if(O.parent != parent)
|
||||
CRASH("RIDING OFFHAND ON WRONG MOB")
|
||||
else if(!O.selfdeleting)
|
||||
qdel(O)
|
||||
return TRUE
|
||||
|
||||
/obj/item/riding_offhand
|
||||
name = "offhand"
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "offhand"
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
item_flags = ABSTRACT | DROPDEL | NOBLUDGEON
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
var/mob/living/carbon/rider
|
||||
var/mob/living/parent
|
||||
var/selfdeleting = FALSE
|
||||
|
||||
/obj/item/riding_offhand/dropped()
|
||||
selfdeleting = TRUE
|
||||
. = ..()
|
||||
|
||||
/obj/item/riding_offhand/equipped()
|
||||
if(loc != rider && loc != parent)
|
||||
selfdeleting = TRUE
|
||||
qdel(src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/riding_offhand/Destroy()
|
||||
var/atom/movable/AM = parent
|
||||
if(selfdeleting)
|
||||
if(rider in AM.buckled_mobs)
|
||||
AM.unbuckle_mob(rider)
|
||||
. = ..()
|
||||
/datum/component/riding
|
||||
var/last_vehicle_move = 0 //used for move delays
|
||||
var/last_move_diagonal = FALSE
|
||||
var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower
|
||||
var/keytype
|
||||
|
||||
var/slowed = FALSE
|
||||
var/slowvalue = 1
|
||||
|
||||
var/list/riding_offsets = list() //position_of_user = list(dir = list(px, py)), or RIDING_OFFSET_ALL for a generic one.
|
||||
var/list/directional_vehicle_layers = list() //["[DIRECTION]"] = layer. Don't set it for a direction for default, set a direction to null for no change.
|
||||
var/list/directional_vehicle_offsets = list() //same as above but instead of layer you have a list(px, py)
|
||||
var/list/allowed_turf_typecache
|
||||
var/list/forbid_turf_typecache //allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
|
||||
var/allow_one_away_from_valid_turf = TRUE //allow moving one tile away from a valid turf but not more.
|
||||
var/override_allow_spacemove = FALSE
|
||||
var/drive_verb = "drive"
|
||||
var/ride_check_rider_incapacitated = FALSE
|
||||
var/ride_check_rider_restrained = FALSE
|
||||
var/ride_check_ridden_incapacitated = FALSE
|
||||
var/list/offhands = list() // keyed list containing all the current riding offsets associated by mob
|
||||
|
||||
/datum/component/riding/Initialize()
|
||||
if(!ismovableatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, .proc/vehicle_mob_buckle)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_UNBUCKLE, .proc/vehicle_mob_unbuckle)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE)
|
||||
restore_position(M)
|
||||
unequip_buckle_inhands(M)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
|
||||
handle_vehicle_offsets()
|
||||
|
||||
/datum/component/riding/proc/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER)
|
||||
. = defaults["[AM.dir]"]
|
||||
if(directional_vehicle_layers["[AM.dir]"])
|
||||
. = directional_vehicle_layers["[AM.dir]"]
|
||||
if(isnull(.)) //you can set it to null to not change it.
|
||||
. = AM.layer
|
||||
AM.layer = .
|
||||
|
||||
/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
|
||||
directional_vehicle_layers["[dir]"] = layer
|
||||
|
||||
/datum/component/riding/proc/vehicle_moved(datum/source)
|
||||
var/atom/movable/AM = parent
|
||||
for(var/i in AM.buckled_mobs)
|
||||
ride_check(i)
|
||||
handle_vehicle_offsets()
|
||||
handle_vehicle_layer()
|
||||
|
||||
/datum/component/riding/proc/ride_check(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
var/mob/AMM = AM
|
||||
if((ride_check_rider_restrained && M.restrained(TRUE)) || (ride_check_rider_incapacitated && M.incapacitated(FALSE, TRUE)) || (ride_check_ridden_incapacitated && istype(AMM) && AMM.incapacitated(FALSE, TRUE)))
|
||||
AM.visible_message("<span class='warning'>[M] falls off of [AM]!</span>")
|
||||
AM.unbuckle_mob(M)
|
||||
return TRUE
|
||||
|
||||
/datum/component/riding/proc/force_dismount(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(M)
|
||||
|
||||
/datum/component/riding/proc/handle_vehicle_offsets()
|
||||
var/atom/movable/AM = parent
|
||||
var/AM_dir = "[AM.dir]"
|
||||
var/passindex = 0
|
||||
if(AM.has_buckled_mobs())
|
||||
for(var/m in AM.buckled_mobs)
|
||||
passindex++
|
||||
var/mob/living/buckled_mob = m
|
||||
var/list/offsets = get_offsets(passindex)
|
||||
var/rider_dir = get_rider_dir(passindex)
|
||||
buckled_mob.setDir(rider_dir)
|
||||
dir_loop:
|
||||
for(var/offsetdir in offsets)
|
||||
if(offsetdir == AM_dir)
|
||||
var/list/diroffsets = offsets[offsetdir]
|
||||
buckled_mob.pixel_x = diroffsets[1]
|
||||
if(diroffsets.len >= 2)
|
||||
buckled_mob.pixel_y = diroffsets[2]
|
||||
if(diroffsets.len == 3)
|
||||
buckled_mob.layer = diroffsets[3]
|
||||
break dir_loop
|
||||
var/list/static/default_vehicle_pixel_offsets = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
|
||||
var/px = default_vehicle_pixel_offsets[AM_dir]
|
||||
var/py = default_vehicle_pixel_offsets[AM_dir]
|
||||
if(directional_vehicle_offsets[AM_dir])
|
||||
if(isnull(directional_vehicle_offsets[AM_dir]))
|
||||
px = AM.pixel_x
|
||||
py = AM.pixel_y
|
||||
else
|
||||
px = directional_vehicle_offsets[AM_dir][1]
|
||||
py = directional_vehicle_offsets[AM_dir][2]
|
||||
AM.pixel_x = px
|
||||
AM.pixel_y = py
|
||||
|
||||
/datum/component/riding/proc/set_vehicle_dir_offsets(dir, x, y)
|
||||
directional_vehicle_offsets["[dir]"] = list(x, y)
|
||||
|
||||
//Override this to set your vehicle's various pixel offsets
|
||||
/datum/component/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer)
|
||||
. = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
|
||||
if(riding_offsets["[pass_index]"])
|
||||
. = riding_offsets["[pass_index]"]
|
||||
else if(riding_offsets["[RIDING_OFFSET_ALL]"])
|
||||
. = riding_offsets["[RIDING_OFFSET_ALL]"]
|
||||
|
||||
/datum/component/riding/proc/set_riding_offsets(index, list/offsets)
|
||||
if(!islist(offsets))
|
||||
return FALSE
|
||||
riding_offsets["[index]"] = offsets
|
||||
|
||||
//Override this to set the passengers/riders dir based on which passenger they are.
|
||||
//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc.
|
||||
/datum/component/riding/proc/get_rider_dir(pass_index)
|
||||
var/atom/movable/AM = parent
|
||||
return AM.dir
|
||||
|
||||
//KEYS
|
||||
/datum/component/riding/proc/keycheck(mob/user)
|
||||
return !keytype || user.is_holding_item_of_type(keytype)
|
||||
|
||||
//BUCKLE HOOKS
|
||||
/datum/component/riding/proc/restore_position(mob/living/buckled_mob)
|
||||
if(buckled_mob)
|
||||
buckled_mob.pixel_x = 0
|
||||
buckled_mob.pixel_y = 0
|
||||
if(buckled_mob.client)
|
||||
buckled_mob.client.change_view(CONFIG_GET(string/default_view))
|
||||
|
||||
//MOVEMENT
|
||||
/datum/component/riding/proc/turf_check(turf/next, turf/current)
|
||||
if(allowed_turf_typecache && !allowed_turf_typecache[next.type])
|
||||
return (allow_one_away_from_valid_turf && allowed_turf_typecache[current.type])
|
||||
else if(forbid_turf_typecache && forbid_turf_typecache[next.type])
|
||||
return (allow_one_away_from_valid_turf && !forbid_turf_typecache[current.type])
|
||||
return TRUE
|
||||
|
||||
/datum/component/riding/proc/handle_ride(mob/user, direction)
|
||||
var/atom/movable/AM = parent
|
||||
if(user.incapacitated())
|
||||
Unbuckle(user)
|
||||
return
|
||||
|
||||
if(world.time < last_vehicle_move + ((last_move_diagonal? 2 : 1) * vehicle_move_delay))
|
||||
return
|
||||
last_vehicle_move = world.time
|
||||
|
||||
if(keycheck(user))
|
||||
var/turf/next = get_step(AM, direction)
|
||||
var/turf/current = get_turf(AM)
|
||||
if(!istype(next) || !istype(current))
|
||||
return //not happening.
|
||||
if(!turf_check(next, current))
|
||||
to_chat(user, "Your \the [AM] can not go onto [next]!")
|
||||
return
|
||||
if(!Process_Spacemove(direction) || !isturf(AM.loc))
|
||||
return
|
||||
step(AM, direction)
|
||||
|
||||
if((direction & (direction - 1)) && (AM.loc == next)) //moved diagonally
|
||||
last_move_diagonal = TRUE
|
||||
else
|
||||
last_move_diagonal = FALSE
|
||||
|
||||
handle_vehicle_layer()
|
||||
handle_vehicle_offsets()
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You'll need the keys in one of your hands to [drive_verb] [AM].</span>")
|
||||
|
||||
/datum/component/riding/proc/Unbuckle(atom/movable/M)
|
||||
addtimer(CALLBACK(parent, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE)
|
||||
|
||||
/datum/component/riding/proc/Process_Spacemove(direction)
|
||||
var/atom/movable/AM = parent
|
||||
return override_allow_spacemove || AM.has_gravity()
|
||||
|
||||
/datum/component/riding/proc/account_limbs(mob/living/M)
|
||||
if(M.get_num_legs() < 2 && !slowed)
|
||||
vehicle_move_delay = vehicle_move_delay + slowvalue
|
||||
slowed = TRUE
|
||||
else if(slowed)
|
||||
vehicle_move_delay = vehicle_move_delay - slowvalue
|
||||
slowed = FALSE
|
||||
|
||||
///////Yes, I said humans. No, this won't end well...//////////
|
||||
/datum/component/riding/human
|
||||
|
||||
/datum/component/riding/human/Initialize()
|
||||
. = ..()
|
||||
RegisterSignal(parent, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
|
||||
|
||||
/datum/component/riding/human/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE)
|
||||
var/mob/living/carbon/human/H = parent
|
||||
H.remove_movespeed_modifier(MOVESPEED_ID_HUMAN_CARRYING)
|
||||
. = ..()
|
||||
|
||||
/datum/component/riding/human/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
|
||||
. = ..()
|
||||
var/mob/living/carbon/human/H = parent
|
||||
H.add_movespeed_modifier(MOVESPEED_ID_HUMAN_CARRYING, multiplicative_slowdown = HUMAN_CARRY_SLOWDOWN)
|
||||
|
||||
/datum/component/riding/human/proc/on_host_unarmed_melee(atom/target)
|
||||
var/mob/living/carbon/human/H = parent
|
||||
if(H.a_intent == INTENT_DISARM && (target in H.buckled_mobs))
|
||||
force_dismount(target)
|
||||
|
||||
/datum/component/riding/human/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.buckled_mobs && AM.buckled_mobs.len)
|
||||
for(var/mob/M in AM.buckled_mobs) //ensure proper layering of piggyback and carry, sometimes weird offsets get applied
|
||||
M.layer = MOB_LAYER
|
||||
if(!AM.buckle_lying)
|
||||
if(AM.dir == SOUTH)
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
if(AM.dir == NORTH)
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = MOB_LAYER
|
||||
|
||||
/datum/component/riding/human/get_offsets(pass_index)
|
||||
var/mob/living/carbon/human/H = parent
|
||||
if(H.buckle_lying)
|
||||
return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(0, 6), TEXT_WEST = list(0, 6))
|
||||
else
|
||||
return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(-6, 4), TEXT_WEST = list( 6, 4))
|
||||
|
||||
|
||||
/datum/component/riding/human/force_dismount(mob/living/user)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(user)
|
||||
user.DefaultCombatKnockdown(60)
|
||||
user.visible_message("<span class='warning'>[AM] pushes [user] off of [AM.p_them()]!</span>")
|
||||
|
||||
/datum/component/riding/cyborg
|
||||
|
||||
/datum/component/riding/cyborg/ride_check(mob/user)
|
||||
var/atom/movable/AM = parent
|
||||
if(user.incapacitated())
|
||||
var/kick = TRUE
|
||||
if(iscyborg(AM))
|
||||
var/mob/living/silicon/robot/R = AM
|
||||
if(R.module && R.module.ride_allow_incapacitated)
|
||||
kick = FALSE
|
||||
if(kick)
|
||||
to_chat(user, "<span class='userdanger'>You fall off of [AM]!</span>")
|
||||
Unbuckle(user)
|
||||
return
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/carbonuser = user
|
||||
if(!carbonuser.get_num_arms())
|
||||
Unbuckle(user)
|
||||
to_chat(user, "<span class='userdanger'>You can't grab onto [AM] with no hands!</span>")
|
||||
return
|
||||
|
||||
/datum/component/riding/cyborg/handle_vehicle_layer()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.buckled_mobs && AM.buckled_mobs.len)
|
||||
if(AM.dir == SOUTH)
|
||||
AM.layer = ABOVE_MOB_LAYER
|
||||
else
|
||||
AM.layer = OBJ_LAYER
|
||||
else
|
||||
AM.layer = MOB_LAYER
|
||||
|
||||
/datum/component/riding/cyborg/get_offsets(pass_index) // list(dir = x, y, layer)
|
||||
return list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-6, 3), TEXT_WEST = list( 6, 3))
|
||||
|
||||
/datum/component/riding/cyborg/handle_vehicle_offsets()
|
||||
var/atom/movable/AM = parent
|
||||
if(AM.has_buckled_mobs())
|
||||
for(var/mob/living/M in AM.buckled_mobs)
|
||||
M.setDir(AM.dir)
|
||||
if(iscyborg(AM))
|
||||
var/mob/living/silicon/robot/R = AM
|
||||
if(istype(R.module))
|
||||
M.pixel_x = R.module.ride_offset_x[dir2text(AM.dir)]
|
||||
M.pixel_y = R.module.ride_offset_y[dir2text(AM.dir)]
|
||||
else
|
||||
..()
|
||||
|
||||
/datum/component/riding/cyborg/force_dismount(mob/living/M)
|
||||
var/atom/movable/AM = parent
|
||||
AM.unbuckle_mob(M)
|
||||
var/turf/target = get_edge_target_turf(AM, AM.dir)
|
||||
var/turf/targetm = get_step(get_turf(AM), AM.dir)
|
||||
M.Move(targetm)
|
||||
M.visible_message("<span class='warning'>[M] is thrown clear of [AM]!</span>")
|
||||
M.throw_at(target, 14, 5, AM)
|
||||
M.DefaultCombatKnockdown(60)
|
||||
|
||||
/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1, mob/living/riding_target_override)
|
||||
var/list/equipped
|
||||
var/mob/living/L = riding_target_override ? riding_target_override : user
|
||||
for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--)
|
||||
var/obj/item/riding_offhand/inhand = new
|
||||
inhand.rider = L
|
||||
inhand.parent = parent
|
||||
if(!user.put_in_hands(inhand, TRUE))
|
||||
qdel(inhand) // it isn't going to be added to offhands anyway
|
||||
break
|
||||
LAZYADD(equipped, inhand)
|
||||
var/amount_equipped = LAZYLEN(equipped)
|
||||
if(amount_equipped)
|
||||
LAZYADD(offhands[L], equipped)
|
||||
if(amount_equipped >= amount_required)
|
||||
return TRUE
|
||||
unequip_buckle_inhands(L)
|
||||
return FALSE
|
||||
|
||||
/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user)
|
||||
for(var/a in offhands[user])
|
||||
LAZYREMOVE(offhands[user], a)
|
||||
if(a) //edge cases null entries
|
||||
var/obj/item/riding_offhand/O = a
|
||||
if(O.parent != parent)
|
||||
CRASH("RIDING OFFHAND ON WRONG MOB")
|
||||
else if(!O.selfdeleting)
|
||||
qdel(O)
|
||||
return TRUE
|
||||
|
||||
/obj/item/riding_offhand
|
||||
name = "offhand"
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "offhand"
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
item_flags = ABSTRACT | DROPDEL | NOBLUDGEON
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
var/mob/living/carbon/rider
|
||||
var/mob/living/parent
|
||||
var/selfdeleting = FALSE
|
||||
|
||||
/obj/item/riding_offhand/dropped()
|
||||
selfdeleting = TRUE
|
||||
. = ..()
|
||||
|
||||
/obj/item/riding_offhand/equipped()
|
||||
if(loc != rider && loc != parent)
|
||||
selfdeleting = TRUE
|
||||
qdel(src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/riding_offhand/Destroy()
|
||||
var/atom/movable/AM = parent
|
||||
if(selfdeleting)
|
||||
if(rider in AM.buckled_mobs)
|
||||
AM.unbuckle_mob(rider)
|
||||
. = ..()
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/datum/component/slippery
|
||||
var/intensity
|
||||
var/lube_flags
|
||||
var/datum/callback/callback
|
||||
|
||||
/datum/component/slippery/Initialize(_intensity, _lube_flags = NONE, datum/callback/_callback)
|
||||
intensity = max(_intensity, 0)
|
||||
lube_flags = _lube_flags
|
||||
callback = _callback
|
||||
RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED), .proc/Slip)
|
||||
|
||||
/datum/component/slippery/proc/Slip(datum/source, atom/movable/AM)
|
||||
var/mob/victim = AM
|
||||
if(istype(victim) && !victim.is_flying() && victim.slip(intensity, parent, lube_flags) && callback)
|
||||
callback.Invoke(victim)
|
||||
/datum/component/slippery
|
||||
var/intensity
|
||||
var/lube_flags
|
||||
var/datum/callback/callback
|
||||
|
||||
/datum/component/slippery/Initialize(_intensity, _lube_flags = NONE, datum/callback/_callback)
|
||||
intensity = max(_intensity, 0)
|
||||
lube_flags = _lube_flags
|
||||
callback = _callback
|
||||
RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED), .proc/Slip)
|
||||
|
||||
/datum/component/slippery/proc/Slip(datum/source, atom/movable/AM)
|
||||
var/mob/victim = AM
|
||||
if(istype(victim) && !victim.is_flying() && victim.slip(intensity, parent, lube_flags) && callback)
|
||||
callback.Invoke(victim)
|
||||
|
||||
@@ -1,208 +1,208 @@
|
||||
|
||||
// External storage-related logic:
|
||||
// /mob/proc/ClickOn() in /_onclick/click.dm - clicking items in storages
|
||||
// /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement
|
||||
|
||||
/datum/component/storage/concrete
|
||||
can_transfer = TRUE
|
||||
var/drop_all_on_deconstruct = TRUE
|
||||
var/drop_all_on_destroy = FALSE
|
||||
var/drop_all_on_break = FALSE
|
||||
var/unlock_on_break = FALSE
|
||||
var/transfer_contents_on_component_transfer = FALSE
|
||||
var/list/datum/component/storage/slaves = list()
|
||||
|
||||
var/list/_contents_limbo // Where objects go to live mid transfer
|
||||
var/list/_user_limbo // The last users before the component started moving
|
||||
|
||||
/datum/component/storage/concrete/Initialize()
|
||||
. = ..()
|
||||
RegisterSignal(parent, COMSIG_ATOM_CONTENTS_DEL, .proc/on_contents_del)
|
||||
RegisterSignal(parent, COMSIG_OBJ_DECONSTRUCT, .proc/on_deconstruct)
|
||||
RegisterSignal(parent, COMSIG_OBJ_BREAK, .proc/on_break)
|
||||
|
||||
/datum/component/storage/concrete/Destroy()
|
||||
var/atom/real_location = real_location()
|
||||
for(var/atom/_A in real_location)
|
||||
_A.mouse_opacity = initial(_A.mouse_opacity)
|
||||
if(drop_all_on_destroy)
|
||||
do_quick_empty()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
slave.change_master(null)
|
||||
QDEL_LIST(_contents_limbo)
|
||||
_user_limbo = null
|
||||
return ..()
|
||||
|
||||
/datum/component/storage/concrete/master()
|
||||
return src
|
||||
|
||||
/datum/component/storage/concrete/real_location()
|
||||
return parent
|
||||
|
||||
/datum/component/storage/concrete/PreTransfer()
|
||||
if(is_using)
|
||||
_user_limbo = is_using.Copy()
|
||||
close_all()
|
||||
if(transfer_contents_on_component_transfer)
|
||||
_contents_limbo = list()
|
||||
for(var/atom/movable/AM in parent)
|
||||
_contents_limbo += AM
|
||||
AM.moveToNullspace()
|
||||
|
||||
/datum/component/storage/concrete/PostTransfer()
|
||||
if(!isatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(transfer_contents_on_component_transfer)
|
||||
for(var/i in _contents_limbo)
|
||||
var/atom/movable/AM = i
|
||||
AM.forceMove(parent)
|
||||
_contents_limbo = null
|
||||
if(_user_limbo)
|
||||
for(var/i in _user_limbo)
|
||||
show_to(i)
|
||||
_user_limbo = null
|
||||
|
||||
/datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE)
|
||||
. = TRUE
|
||||
var/atom/real_location = real_location()
|
||||
if(I.loc != real_location)
|
||||
I.forceMove(real_location)
|
||||
refresh_mob_views()
|
||||
|
||||
/datum/component/storage/concrete/refresh_mob_views()
|
||||
. = ..()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
slave.refresh_mob_views()
|
||||
|
||||
/datum/component/storage/concrete/emp_act(datum/source, severity)
|
||||
if(emp_shielded)
|
||||
return
|
||||
var/atom/real_location = real_location()
|
||||
for(var/i in real_location)
|
||||
var/atom/A = i
|
||||
A.emp_act(severity)
|
||||
|
||||
/datum/component/storage/concrete/proc/on_slave_link(datum/component/storage/S)
|
||||
if(S == src)
|
||||
return FALSE
|
||||
slaves += S
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/proc/on_slave_unlink(datum/component/storage/S)
|
||||
slaves -= S
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/concrete/proc/on_contents_del(datum/source, atom/A)
|
||||
var/atom/real_location = parent
|
||||
if(A in real_location)
|
||||
usr = null
|
||||
remove_from_storage(A, null)
|
||||
|
||||
/datum/component/storage/concrete/proc/on_deconstruct(datum/source, disassembled)
|
||||
if(drop_all_on_deconstruct)
|
||||
do_quick_empty()
|
||||
|
||||
/datum/component/storage/concrete/proc/on_break(datum/source, damage_flag)
|
||||
if(drop_all_on_break)
|
||||
do_quick_empty()
|
||||
if(unlock_on_break)
|
||||
set_locked(source, FALSE)
|
||||
|
||||
/datum/component/storage/concrete/can_see_contents()
|
||||
. = ..()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
. |= slave.can_see_contents()
|
||||
|
||||
//Resets screen loc and other vars of something being removed from storage.
|
||||
/datum/component/storage/concrete/_removal_reset(atom/movable/thing)
|
||||
thing.layer = initial(thing.layer)
|
||||
thing.plane = initial(thing.plane)
|
||||
thing.mouse_opacity = initial(thing.mouse_opacity)
|
||||
if(thing.maptext)
|
||||
thing.maptext = ""
|
||||
|
||||
/datum/component/storage/concrete/remove_from_storage(atom/movable/AM, atom/new_location)
|
||||
//Cache this as it should be reusable down the bottom, will not apply if anyone adds a sleep to dropped
|
||||
//or moving objects, things that should never happen
|
||||
var/atom/parent = src.parent
|
||||
var/list/seeing_mobs = can_see_contents()
|
||||
for(var/mob/M in seeing_mobs)
|
||||
M.client.screen -= AM
|
||||
if(ismob(parent.loc) && isitem(AM))
|
||||
var/obj/item/I = AM
|
||||
var/mob/M = parent.loc
|
||||
I.dropped(M)
|
||||
if(new_location)
|
||||
//Reset the items values
|
||||
_removal_reset(AM)
|
||||
AM.forceMove(new_location)
|
||||
//We don't want to call this if the item is being destroyed
|
||||
AM.on_exit_storage(src)
|
||||
else
|
||||
//Being destroyed, just move to nullspace now (so it's not in contents for the icon update)
|
||||
AM.moveToNullspace()
|
||||
refresh_mob_views()
|
||||
if(isobj(parent))
|
||||
var/obj/O = parent
|
||||
O.update_icon()
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/proc/slave_can_insert_object(datum/component/storage/slave, obj/item/I, stop_messages = FALSE, mob/M)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/proc/handle_item_insertion_from_slave(datum/component/storage/slave, obj/item/I, prevent_warning = FALSE, M)
|
||||
. = handle_item_insertion(I, prevent_warning, M, slave)
|
||||
if(. && !prevent_warning)
|
||||
slave.mob_item_insertion_feedback(usr, M, I)
|
||||
|
||||
/datum/component/storage/concrete/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote) //Remote is null or the slave datum
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
var/atom/parent = src.parent
|
||||
var/moved = FALSE
|
||||
if(!istype(I))
|
||||
return FALSE
|
||||
if(M)
|
||||
if(!M.temporarilyRemoveItemFromInventory(I))
|
||||
return FALSE
|
||||
else
|
||||
moved = TRUE //At this point if the proc fails we need to manually move the object back to the turf/mob/whatever.
|
||||
if(I.pulledby)
|
||||
I.pulledby.stop_pulling()
|
||||
if(silent)
|
||||
prevent_warning = TRUE
|
||||
if(!_insert_physical_item(I))
|
||||
if(moved)
|
||||
if(M)
|
||||
if(!M.put_in_active_hand(I))
|
||||
I.forceMove(parent.drop_location())
|
||||
else
|
||||
I.forceMove(parent.drop_location())
|
||||
return FALSE
|
||||
I.on_enter_storage(master)
|
||||
refresh_mob_views()
|
||||
I.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt
|
||||
if(M)
|
||||
if(M.client && M.active_storage != src)
|
||||
M.client.screen -= I
|
||||
if(M.observers && M.observers.len)
|
||||
for(var/i in M.observers)
|
||||
var/mob/dead/observe = i
|
||||
if(observe.client && observe.active_storage != src)
|
||||
observe.client.screen -= I
|
||||
if(!remote)
|
||||
parent.add_fingerprint(M)
|
||||
if(!prevent_warning)
|
||||
mob_item_insertion_feedback(usr, M, I)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/update_icon()
|
||||
if(isobj(parent))
|
||||
var/obj/O = parent
|
||||
O.update_icon()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
slave.update_icon()
|
||||
|
||||
// External storage-related logic:
|
||||
// /mob/proc/ClickOn() in /_onclick/click.dm - clicking items in storages
|
||||
// /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement
|
||||
|
||||
/datum/component/storage/concrete
|
||||
can_transfer = TRUE
|
||||
var/drop_all_on_deconstruct = TRUE
|
||||
var/drop_all_on_destroy = FALSE
|
||||
var/drop_all_on_break = FALSE
|
||||
var/unlock_on_break = FALSE
|
||||
var/transfer_contents_on_component_transfer = FALSE
|
||||
var/list/datum/component/storage/slaves = list()
|
||||
|
||||
var/list/_contents_limbo // Where objects go to live mid transfer
|
||||
var/list/_user_limbo // The last users before the component started moving
|
||||
|
||||
/datum/component/storage/concrete/Initialize()
|
||||
. = ..()
|
||||
RegisterSignal(parent, COMSIG_ATOM_CONTENTS_DEL, .proc/on_contents_del)
|
||||
RegisterSignal(parent, COMSIG_OBJ_DECONSTRUCT, .proc/on_deconstruct)
|
||||
RegisterSignal(parent, COMSIG_OBJ_BREAK, .proc/on_break)
|
||||
|
||||
/datum/component/storage/concrete/Destroy()
|
||||
var/atom/real_location = real_location()
|
||||
for(var/atom/_A in real_location)
|
||||
_A.mouse_opacity = initial(_A.mouse_opacity)
|
||||
if(drop_all_on_destroy)
|
||||
do_quick_empty()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
slave.change_master(null)
|
||||
QDEL_LIST(_contents_limbo)
|
||||
_user_limbo = null
|
||||
return ..()
|
||||
|
||||
/datum/component/storage/concrete/master()
|
||||
return src
|
||||
|
||||
/datum/component/storage/concrete/real_location()
|
||||
return parent
|
||||
|
||||
/datum/component/storage/concrete/PreTransfer()
|
||||
if(is_using)
|
||||
_user_limbo = is_using.Copy()
|
||||
close_all()
|
||||
if(transfer_contents_on_component_transfer)
|
||||
_contents_limbo = list()
|
||||
for(var/atom/movable/AM in parent)
|
||||
_contents_limbo += AM
|
||||
AM.moveToNullspace()
|
||||
|
||||
/datum/component/storage/concrete/PostTransfer()
|
||||
if(!isatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(transfer_contents_on_component_transfer)
|
||||
for(var/i in _contents_limbo)
|
||||
var/atom/movable/AM = i
|
||||
AM.forceMove(parent)
|
||||
_contents_limbo = null
|
||||
if(_user_limbo)
|
||||
for(var/i in _user_limbo)
|
||||
show_to(i)
|
||||
_user_limbo = null
|
||||
|
||||
/datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE)
|
||||
. = TRUE
|
||||
var/atom/real_location = real_location()
|
||||
if(I.loc != real_location && real_location)
|
||||
I.forceMove(real_location)
|
||||
refresh_mob_views()
|
||||
|
||||
/datum/component/storage/concrete/refresh_mob_views()
|
||||
. = ..()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
slave.refresh_mob_views()
|
||||
|
||||
/datum/component/storage/concrete/emp_act(datum/source, severity)
|
||||
if(emp_shielded)
|
||||
return
|
||||
var/atom/real_location = real_location()
|
||||
for(var/i in real_location)
|
||||
var/atom/A = i
|
||||
A.emp_act(severity)
|
||||
|
||||
/datum/component/storage/concrete/proc/on_slave_link(datum/component/storage/S)
|
||||
if(S == src)
|
||||
return FALSE
|
||||
slaves += S
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/proc/on_slave_unlink(datum/component/storage/S)
|
||||
slaves -= S
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/concrete/proc/on_contents_del(datum/source, atom/A)
|
||||
var/atom/real_location = parent
|
||||
if(A in real_location)
|
||||
usr = null
|
||||
remove_from_storage(A, null)
|
||||
|
||||
/datum/component/storage/concrete/proc/on_deconstruct(datum/source, disassembled)
|
||||
if(drop_all_on_deconstruct)
|
||||
do_quick_empty()
|
||||
|
||||
/datum/component/storage/concrete/proc/on_break(datum/source, damage_flag)
|
||||
if(drop_all_on_break)
|
||||
do_quick_empty()
|
||||
if(unlock_on_break)
|
||||
set_locked(source, FALSE)
|
||||
|
||||
/datum/component/storage/concrete/can_see_contents()
|
||||
. = ..()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
. |= slave.can_see_contents()
|
||||
|
||||
//Resets screen loc and other vars of something being removed from storage.
|
||||
/datum/component/storage/concrete/_removal_reset(atom/movable/thing)
|
||||
thing.layer = initial(thing.layer)
|
||||
thing.plane = initial(thing.plane)
|
||||
thing.mouse_opacity = initial(thing.mouse_opacity)
|
||||
if(thing.maptext)
|
||||
thing.maptext = ""
|
||||
|
||||
/datum/component/storage/concrete/remove_from_storage(atom/movable/AM, atom/new_location)
|
||||
//Cache this as it should be reusable down the bottom, will not apply if anyone adds a sleep to dropped
|
||||
//or moving objects, things that should never happen
|
||||
var/atom/parent = src.parent
|
||||
var/list/seeing_mobs = can_see_contents()
|
||||
for(var/mob/M in seeing_mobs)
|
||||
M.client.screen -= AM
|
||||
if(ismob(parent.loc) && isitem(AM))
|
||||
var/obj/item/I = AM
|
||||
var/mob/M = parent.loc
|
||||
I.dropped(M)
|
||||
if(new_location)
|
||||
//Reset the items values
|
||||
_removal_reset(AM)
|
||||
AM.forceMove(new_location)
|
||||
//We don't want to call this if the item is being destroyed
|
||||
AM.on_exit_storage(src)
|
||||
else
|
||||
//Being destroyed, just move to nullspace now (so it's not in contents for the icon update)
|
||||
AM.moveToNullspace()
|
||||
refresh_mob_views()
|
||||
if(isobj(parent))
|
||||
var/obj/O = parent
|
||||
O.update_icon()
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/proc/slave_can_insert_object(datum/component/storage/slave, obj/item/I, stop_messages = FALSE, mob/M)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/proc/handle_item_insertion_from_slave(datum/component/storage/slave, obj/item/I, prevent_warning = FALSE, M)
|
||||
. = handle_item_insertion(I, prevent_warning, M, slave)
|
||||
if(. && !prevent_warning)
|
||||
slave.mob_item_insertion_feedback(usr, M, I)
|
||||
|
||||
/datum/component/storage/concrete/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote) //Remote is null or the slave datum
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
var/atom/parent = src.parent
|
||||
var/moved = FALSE
|
||||
if(!istype(I))
|
||||
return FALSE
|
||||
if(M)
|
||||
if(!M.temporarilyRemoveItemFromInventory(I))
|
||||
return FALSE
|
||||
else
|
||||
moved = TRUE //At this point if the proc fails we need to manually move the object back to the turf/mob/whatever.
|
||||
if(I.pulledby)
|
||||
I.pulledby.stop_pulling()
|
||||
if(silent)
|
||||
prevent_warning = TRUE
|
||||
if(!_insert_physical_item(I))
|
||||
if(moved)
|
||||
if(M)
|
||||
if(!M.put_in_active_hand(I))
|
||||
I.forceMove(parent.drop_location())
|
||||
else
|
||||
I.forceMove(parent.drop_location())
|
||||
return FALSE
|
||||
I.on_enter_storage(master)
|
||||
refresh_mob_views()
|
||||
I.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt
|
||||
if(M)
|
||||
if(M.client && M.active_storage != src)
|
||||
M.client.screen -= I
|
||||
if(M.observers && M.observers.len)
|
||||
for(var/i in M.observers)
|
||||
var/mob/dead/observe = i
|
||||
if(observe.client && observe.active_storage != src)
|
||||
observe.client.screen -= I
|
||||
if(!remote)
|
||||
parent.add_fingerprint(M)
|
||||
if(!prevent_warning)
|
||||
mob_item_insertion_feedback(usr, M, I)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/update_icon()
|
||||
if(isobj(parent))
|
||||
var/obj/O = parent
|
||||
O.update_icon()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
slave.update_icon()
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
/datum/component/storage/concrete/bluespace/bag_of_holding/handle_item_insertion(obj/item/W, prevent_warning = FALSE, mob/living/user)
|
||||
var/atom/A = parent
|
||||
if(A == W) //don't put yourself into yourself.
|
||||
return
|
||||
var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(W.GetAllContents(), typecacheof(/obj/item/storage/backpack/holding))
|
||||
matching -= A
|
||||
if(istype(W, /obj/item/storage/backpack/holding) || matching.len)
|
||||
var/safety = alert(user, "Doing this will have extremely dire consequences for the station and its crew. Be sure you know what you're doing.", "Put in [A.name]?", "Abort", "Proceed")
|
||||
if(safety != "Proceed" || QDELETED(A) || QDELETED(W) || QDELETED(user) || !user.canUseTopic(A, BE_CLOSE, iscarbon(user)))
|
||||
return
|
||||
var/turf/loccheck = get_turf(A)
|
||||
if(is_reebe(loccheck.z))
|
||||
user.visible_message("<span class='warning'>An unseen force knocks [user] to the ground!</span>", "<span class='big_brass'>\"I think not!\"</span>")
|
||||
user.DefaultCombatKnockdown(60)
|
||||
return
|
||||
if(istype(loccheck.loc, /area/fabric_of_reality))
|
||||
to_chat(user, "<span class='danger'>You can't do that here!</span>")
|
||||
to_chat(user, "<span class='danger'>The Bluespace interfaces of the two devices catastrophically malfunction!</span>")
|
||||
qdel(W)
|
||||
playsound(loccheck,'sound/effects/supermatter.ogg', 200, 1)
|
||||
user.gib(TRUE, TRUE, TRUE)
|
||||
for(var/turf/T in range(6,loccheck))
|
||||
if(istype(T, /turf/open/space/transit))
|
||||
continue
|
||||
for(var/mob/living/M in T)
|
||||
if(M.movement_type & FLYING)
|
||||
M.visible_message("<span class='danger'>The bluespace collapse crushes the air towards it, pulling [M] towards the ground...</span>")
|
||||
M.DefaultCombatKnockdown(5, TRUE, TRUE) //Overrides stun absorbs.
|
||||
T.TerraformTurf(/turf/open/chasm/magic, /turf/open/chasm/magic)
|
||||
for (var/obj/structure/ladder/unbreakable/binary/ladder in GLOB.ladders)
|
||||
ladder.ActivateAlmonds()
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] detonated a bag of holding at [ADMIN_VERBOSEJMP(loccheck)].")
|
||||
log_game("[key_name(user)] detonated a bag of holding at [loc_name(loccheck)].")
|
||||
qdel(A)
|
||||
return
|
||||
. = ..()
|
||||
/datum/component/storage/concrete/bluespace/bag_of_holding/handle_item_insertion(obj/item/W, prevent_warning = FALSE, mob/living/user)
|
||||
var/atom/A = parent
|
||||
if(A == W) //don't put yourself into yourself.
|
||||
return
|
||||
var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(W.GetAllContents(), typecacheof(/obj/item/storage/backpack/holding))
|
||||
matching -= A
|
||||
if(istype(W, /obj/item/storage/backpack/holding) || matching.len)
|
||||
var/safety = alert(user, "Doing this will have extremely dire consequences for the station and its crew. Be sure you know what you're doing.", "Put in [A.name]?", "Abort", "Proceed")
|
||||
if(safety != "Proceed" || QDELETED(A) || QDELETED(W) || QDELETED(user) || !user.canUseTopic(A, BE_CLOSE, iscarbon(user)))
|
||||
return
|
||||
var/turf/loccheck = get_turf(A)
|
||||
if(is_reebe(loccheck.z))
|
||||
user.visible_message("<span class='warning'>An unseen force knocks [user] to the ground!</span>", "<span class='big_brass'>\"I think not!\"</span>")
|
||||
user.DefaultCombatKnockdown(60)
|
||||
return
|
||||
if(istype(loccheck.loc, /area/fabric_of_reality))
|
||||
to_chat(user, "<span class='danger'>You can't do that here!</span>")
|
||||
to_chat(user, "<span class='danger'>The Bluespace interfaces of the two devices catastrophically malfunction!</span>")
|
||||
qdel(W)
|
||||
playsound(loccheck,'sound/effects/supermatter.ogg', 200, 1)
|
||||
user.gib(TRUE, TRUE, TRUE)
|
||||
for(var/turf/T in range(6,loccheck))
|
||||
if(istype(T, /turf/open/space/transit))
|
||||
continue
|
||||
for(var/mob/living/M in T)
|
||||
if(M.movement_type & FLYING)
|
||||
M.visible_message("<span class='danger'>The bluespace collapse crushes the air towards it, pulling [M] towards the ground...</span>")
|
||||
M.DefaultCombatKnockdown(5, TRUE, TRUE) //Overrides stun absorbs.
|
||||
T.TerraformTurf(/turf/open/chasm/magic, /turf/open/chasm/magic)
|
||||
for (var/obj/structure/ladder/unbreakable/binary/ladder in GLOB.ladders)
|
||||
ladder.ActivateAlmonds()
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] detonated a bag of holding at [ADMIN_VERBOSEJMP(loccheck)].")
|
||||
log_game("[key_name(user)] detonated a bag of holding at [loc_name(loccheck)].")
|
||||
qdel(A)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/datum/component/storage/concrete/implant
|
||||
max_w_class = WEIGHT_CLASS_NORMAL
|
||||
max_combined_w_class = 6
|
||||
max_items = 2
|
||||
drop_all_on_destroy = TRUE
|
||||
drop_all_on_deconstruct = TRUE
|
||||
silent = TRUE
|
||||
allow_big_nesting = TRUE
|
||||
|
||||
/datum/component/storage/concrete/implant/Initialize()
|
||||
. = ..()
|
||||
cant_hold = typecacheof(list(/obj/item/disk/nuclear))
|
||||
|
||||
/datum/component/storage/concrete/implant/InheritComponent(datum/component/storage/concrete/implant/I, original)
|
||||
if(!istype(I))
|
||||
return ..()
|
||||
max_combined_w_class += I.max_combined_w_class
|
||||
max_items += I.max_items
|
||||
/datum/component/storage/concrete/implant
|
||||
max_w_class = WEIGHT_CLASS_NORMAL
|
||||
max_combined_w_class = 6
|
||||
max_items = 2
|
||||
drop_all_on_destroy = TRUE
|
||||
drop_all_on_deconstruct = TRUE
|
||||
silent = TRUE
|
||||
allow_big_nesting = TRUE
|
||||
|
||||
/datum/component/storage/concrete/implant/Initialize()
|
||||
. = ..()
|
||||
cant_hold = typecacheof(list(/obj/item/disk/nuclear))
|
||||
|
||||
/datum/component/storage/concrete/implant/InheritComponent(datum/component/storage/concrete/implant/I, original)
|
||||
if(!istype(I))
|
||||
return ..()
|
||||
max_combined_w_class += I.max_combined_w_class
|
||||
max_items += I.max_items
|
||||
|
||||
@@ -87,3 +87,12 @@
|
||||
/datum/component/storage/concrete/pockets/pocketprotector/real_location()
|
||||
// if the component is reparented to a jumpsuit, the items still go in the protector
|
||||
return original_parent
|
||||
|
||||
/datum/component/storage/concrete/pockets/small/rushelmet
|
||||
max_items = 1
|
||||
quickdraw = TRUE
|
||||
|
||||
/datum/component/storage/concrete/pockets/small/rushelmet/Initialize()
|
||||
. = ..()
|
||||
can_hold = typecacheof(list(/obj/item/reagent_containers/glass/bottle,
|
||||
/obj/item/ammo_box/a762))
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
/datum/component/storage/concrete/rped
|
||||
collection_mode = COLLECT_EVERYTHING
|
||||
allow_quick_gather = TRUE
|
||||
allow_quick_empty = TRUE
|
||||
click_gather = TRUE
|
||||
max_w_class = WEIGHT_CLASS_NORMAL
|
||||
max_combined_w_class = 100
|
||||
max_items = 100
|
||||
display_numerical_stacking = TRUE
|
||||
|
||||
/datum/component/storage/concrete/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
|
||||
. = ..()
|
||||
if(!I.get_part_rating())
|
||||
if (!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[parent] only accepts machine parts!</span>")
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/concrete/bluespace/rped
|
||||
collection_mode = COLLECT_EVERYTHING
|
||||
allow_quick_gather = TRUE
|
||||
allow_quick_empty = TRUE
|
||||
click_gather = TRUE
|
||||
max_w_class = WEIGHT_CLASS_BULKY // can fit vending refills
|
||||
max_combined_w_class = 800
|
||||
max_items = 350
|
||||
display_numerical_stacking = TRUE
|
||||
|
||||
/datum/component/storage/concrete/bluespace/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
|
||||
. = ..()
|
||||
if(!I.get_part_rating())
|
||||
if (!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[parent] only accepts machine parts!</span>")
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/concrete/cyborg/rped
|
||||
max_items = 150
|
||||
/datum/component/storage/concrete/rped
|
||||
collection_mode = COLLECT_EVERYTHING
|
||||
allow_quick_gather = TRUE
|
||||
allow_quick_empty = TRUE
|
||||
click_gather = TRUE
|
||||
max_w_class = WEIGHT_CLASS_NORMAL
|
||||
max_combined_w_class = 100
|
||||
max_items = 100
|
||||
display_numerical_stacking = TRUE
|
||||
|
||||
/datum/component/storage/concrete/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
|
||||
. = ..()
|
||||
if(!I.get_part_rating())
|
||||
if (!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[parent] only accepts machine parts!</span>")
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/concrete/bluespace/rped
|
||||
collection_mode = COLLECT_EVERYTHING
|
||||
allow_quick_gather = TRUE
|
||||
allow_quick_empty = TRUE
|
||||
click_gather = TRUE
|
||||
max_w_class = WEIGHT_CLASS_BULKY // can fit vending refills
|
||||
max_combined_w_class = 800
|
||||
max_items = 350
|
||||
display_numerical_stacking = TRUE
|
||||
|
||||
/datum/component/storage/concrete/bluespace/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
|
||||
. = ..()
|
||||
if(!I.get_part_rating())
|
||||
if (!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[parent] only accepts machine parts!</span>")
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/concrete/cyborg/rped
|
||||
max_items = 150
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+211
-211
@@ -1,211 +1,211 @@
|
||||
/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
|
||||
if(TURF_WET_SUPERLUBE)
|
||||
intensity = 120
|
||||
lube_flags = SLIDE | GALOSHES_DONT_HELP | SLIP_WHEN_CRAWLING
|
||||
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, TURF_WET_SUPERLUBE)
|
||||
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
|
||||
if(TURF_WET_SUPERLUBE)
|
||||
intensity = 120
|
||||
lube_flags = SLIDE | GALOSHES_DONT_HELP | SLIP_WHEN_CRAWLING
|
||||
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, TURF_WET_SUPERLUBE)
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user