DCS Update + Footstep element (#17076)

* DCS Update + Footstep element

* Steppy

* Revert "Steppy"

This reverts commit fab0590fb7.

* Reapply "Steppy"

This reverts commit 1f7ad8f8f7.

* Ough

* Oops! Wrong map.

* lil stuff

* Update

* Only loud

* Missed one

* NO MORE SILENT

* ...parenthesis.
This commit is contained in:
Guti
2025-02-08 18:19:59 -05:00
committed by GitHub
parent d1b65c3635
commit a081d48590
79 changed files with 1695 additions and 579 deletions
@@ -0,0 +1,55 @@
# Template file for your new component
See _component.dm for detailed explanations
```dm
/datum/component/mycomponent
//can_transfer = TRUE // Must have PostTransfer
//dupe_mode = COMPONENT_DUPE_ALLOWED // code/__DEFINES/dcs/flags.dm
var/myvar
/datum/component/mycomponent/Initialize(myargone, myargtwo)
if(myargone)
myvar = myargone
if(myargtwo)
send_to_playing_players(myargtwo)
/datum/component/mycomponent/RegisterWithParent()
RegisterSignal(parent, COMSIG_NOT_REAL, PROC_REF(signalproc)) // RegisterSignal can take a signal name by itself,
RegisterSignal(parent, list(COMSIG_NOT_REAL_EITHER, COMSIG_ALMOST_REAL), PROC_REF(otherproc)) // or a list of them to assign to the same proc
/datum/component/mycomponent/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_NOT_REAL) // UnregisterSignal has similar behavior
UnregisterSignal(parent, list( // But you can just include all registered signals in one call
COMSIG_NOT_REAL,
COMSIG_NOT_REAL_EITHER,
COMSIG_ALMOST_REAL,
))
/datum/component/mycomponent/proc/signalproc(datum/source)
SIGNAL_HANDLER
send_to_playing_players("[source] signaled [src]!")
/*
/datum/component/mycomponent/InheritComponent(datum/component/mycomponent/old, i_am_original, list/arguments)
myvar = old.myvar
if(i_am_original)
send_to_playing_players("No parent should have to bury their child")
*/
/*
/datum/component/mycomponent/PreTransfer()
send_to_playing_players("Goodbye [parent], I'm getting adopted")
/datum/component/mycomponent/PostTransfer()
send_to_playing_players("Hello my new parent, [parent]! It's nice to meet you!")
*/
/*
/datum/component/mycomponent/CheckDupeComponent(datum/mycomponent/new, myargone, myargtwo)
if(myargone == myvar)
return TRUE
*/
```
+2 -2
View File
@@ -4,6 +4,6 @@
Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SendSignal()` call. Now every component that want's to can also know about this happening.
See [this thread](https://tgstation13.org/phpBB/viewtopic.php?f=5&t=22674) for an introduction to the system as a whole.
### [HackMD page for an introduction to the system as a whole.](https://hackmd.io/@tgstation/SignalsComponentsElements)
### See/Define signals and their arguments in [__DEFINES\dcs\signals.dm](../../__DEFINES/dcs/signals.dm)
### See/Define signals and their arguments in [__DEFINES\components.dm](../../__DEFINES/components.dm)
+283 -327
View File
@@ -1,14 +1,14 @@
/**
* # Component
*
* The component datum
*
* A component should be a single standalone unit
* of functionality, that works by receiving signals from it's parent
* object to provide some single functionality (i.e a slippery component)
* that makes the object it's attached to cause people to slip over.
* Useful when you want shared behaviour independent of type inheritance
*/
* # Component
*
* The component datum
*
* A component should be a single standalone unit
* of functionality, that works by receiving signals from it's parent
* object to provide some single functionality (i.e a slippery component)
* that makes the object it's attached to cause people to slip over.
* Useful when you want shared behaviour independent of type inheritance
*/
/datum/component
/**
* Defines how duplicate existing components are handled when added to a datum
@@ -38,14 +38,17 @@
*/
var/can_transfer = FALSE
/// A lazy list of the sources for this component
var/list/sources
/**
* Create a new component.
*
* Additional arguments are passed to [Initialize()][/datum/component/proc/Initialize]
*
* Arguments:
* * datum/P the parent datum this component reacts to signals from
*/
* Create a new component.
*
* Additional arguments are passed to [Initialize()][/datum/component/proc/Initialize]
*
* Arguments:
* * datum/P the parent datum this component reacts to signals from
*/
/datum/component/New(list/raw_args)
parent = raw_args[1]
var/list/arguments = raw_args.Copy(2)
@@ -57,50 +60,50 @@
_JoinParent(parent)
/**
* Called during component creation with the same arguments as in new excluding parent.
*
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*/
* Called during component creation with the same arguments as in new excluding parent.
*
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*/
/datum/component/proc/Initialize(...)
return
/**
* Properly removes the component from `parent` and cleans up references
*
* Arguments:
* * force - makes it not check for and remove the component from the parent
* * silent - deletes the component without sending a [COMSIG_COMPONENT_REMOVING] signal
*/
/datum/component/Destroy(force=FALSE, silent=FALSE)
if(!force && parent)
* Properly removes the component from `parent` and cleans up references
*
* Arguments:
* * force - makes it not check for and remove the component from the parent
*/
/datum/component/Destroy(force = FALSE)
if(!parent)
return ..()
if(!force)
_RemoveFromParent()
if(!silent)
SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
parent = null
return ..()
/**
* Internal proc to handle behaviour of components when joining a parent
*/
* Internal proc to handle behaviour of components when joining a parent
*/
/datum/component/proc/_JoinParent()
var/datum/P = parent
//lazy init the parent's dc list
var/list/dc = P.datum_components
var/list/dc = P._datum_components
if(!dc)
P.datum_components = dc = list()
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
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
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]
@@ -110,191 +113,126 @@
break
if(!inserted)
components_of_type += src
else //indirect match, back of the line with ya
else //indirect match, back of the line with ya
components_of_type += src
else //only component of this type, no list
else //only component of this type, no list
dc[I] = src
RegisterWithParent()
/**
* Internal proc to handle behaviour when being removed from a parent
*/
* Internal proc to handle behaviour when being removed from a parent
*/
/datum/component/proc/_RemoveFromParent()
var/datum/P = parent
var/list/dc = P.datum_components
var/datum/parent = src.parent
var/list/parents_components = parent._datum_components
for(var/I in _GetInverseTypeList())
var/list/components_of_type = dc[I]
if(length(components_of_type)) //
var/list/components_of_type = parents_components[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
if(subtracted.len == 1) //only 1 guy left
parents_components[I] = subtracted[1] //make him special
else
dc[I] = subtracted
else //just us
dc -= I
if(!dc.len)
P.datum_components = null
parents_components[I] = subtracted
else //just us
parents_components -= I
if(!parents_components.len)
parent._datum_components = null
UnregisterFromParent()
/**
* Register the component with the parent object
*
* Use this proc to register with your parent object
*
* Overridable proc that's called when added to a new parent
*/
* Register the component with the parent object
*
* Use this proc to register with your parent object
*
* Overridable proc that's called when added to a new parent
*/
/datum/component/proc/RegisterWithParent()
return
/**
* Unregister from our parent object
*
* Use this proc to unregister from your parent object
*
* Overridable proc that's called when removed from a parent
* *
*/
* Unregister from our parent object
*
* Use this proc to unregister from your parent object
*
* Overridable proc that's called when removed from a parent
* *
*/
/datum/component/proc/UnregisterFromParent()
return
/**
* Register to listen for a signal from the passed in target
*
* This sets up a listening relationship such that when the target object emits a signal
* the source datum this proc is called upon, will recieve a callback to the given proctype
* Return values from procs registered must be a bitfield
*
* Arguments:
* * datum/target The target to listen for signals from
* * sig_type_or_types Either a string signal name, or a list of signal names (strings)
* * proctype The proc to call back when the signal is emitted
* * override If a previous registration exists you must explicitly set this
*/
/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])
var/trace_msg = "[sig_type] overridden. Use override = TRUE to suppress this warning."
if(isatom(target))
var/atom/A = target
trace_msg += " [A.x],[A.y],[A.z]"
stack_trace(trace_msg)
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
* Called when the component has a new source registered.
* Return COMPONENT_INCOMPATIBLE to signal that the source is incompatible and should not be added
*/
/datum/component/proc/on_source_add(source, ...)
SHOULD_CALL_PARENT(TRUE)
if(dupe_mode != COMPONENT_DUPE_SOURCES)
return COMPONENT_INCOMPATIBLE
LAZYOR(sources, source)
/**
* Stop listening to a given signal from target
*
* Breaks the relationship between target and source datum, removing the callback when the signal fires
*
* Doesn't care if a registration exists or not
*
* Arguments:
* * datum/target Datum to stop listening to signals from
* * sig_type_or_types Signal string key or list of signal keys to stop listening to specifically
*/
/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)
if(!signal_procs[target][sig])
continue
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
* Called when the component has a source removed.
* You probably want to call parent after you do your logic because at the end of this we qdel if we have no sources remaining!
*/
/datum/component/proc/on_source_remove(source)
SHOULD_CALL_PARENT(TRUE)
if(dupe_mode != COMPONENT_DUPE_SOURCES)
CRASH("Component '[type]' does not use sources but is trying to remove a source")
LAZYREMOVE(sources, source)
if(!LAZYLEN(sources))
qdel(src)
/**
* Called on a component when a component of the same type was added to the same parent
*
* See [/datum/component/var/dupe_mode]
*
* `C`'s type will always be the same of the called component
*/
* Called on a component when a component of the same type was added to the same parent
*
* See [/datum/component/var/dupe_mode]
*
* `C`'s type will always be the same of the called component
*/
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return
/**
* Called on a component when a component of the same type was added to the same parent with [COMPONENT_DUPE_SELECTIVE]
*
* See [/datum/component/var/dupe_mode]
*
* `C`'s type will always be the same of the called component
*
* return TRUE if you are absorbing the component, otherwise FALSE if you are fine having it exist as a duplicate component
*/
* Called on a component when a component of the same type was added to the same parent with [COMPONENT_DUPE_SELECTIVE]
*
* See [/datum/component/var/dupe_mode]
*
* `C`'s type will always be the same of the called component
*
* return TRUE if you are absorbing the component, otherwise FALSE if you are fine having it exist as a duplicate component
*/
/datum/component/proc/CheckDupeComponent(datum/component/C, ...)
return
/**
* Callback Just before this component is transferred
*
* Use this to do any special cleanup you might need to do before being deregged from an object
*/
* Callback Just before this component is transferred
*
* Use this to do any special cleanup you might need to do before being deregged from an object
*/
/datum/component/proc/PreTransfer()
return
/**
* Callback Just after a component is transferred
*
* Use this to do any special setup you need to do after being moved to a new object
*
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*/
* Callback Just after a component is transferred
*
* Use this to do any special setup you need to do after being moved to a new object
*
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*/
/datum/component/proc/PostTransfer()
return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
/**
* Internal proc to create a list of our type and all parent types
*/
* Internal proc to create a list of our type and all parent types
*/
/datum/component/proc/_GetInverseTypeList(our_type = type)
//we can do this one simple trick
. = list(our_type)
@@ -304,42 +242,20 @@
. += current_type
current_type = type2parent(current_type)
/**
* Internal proc to handle most all of the signaling procedure
*
* Will runtime if used on datums with an empty component list
*
* Use the [SEND_SIGNAL] define instead
*/
/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/datum/C as anything in target)
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
/**
* Return any component assigned to this datum of the given type
*
* This will throw an error if it's possible to have more than one component of that type on the parent
*
* Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to
*/
* Return any component assigned to this datum of the given type
*
* This will throw an error if it's possible to have more than one component of that type on the parent
*
* Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to
*/
/datum/proc/GetComponent(datum/component/c_type)
RETURN_TYPE(c_type)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
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
var/list/dc = _datum_components
if(!dc)
return null
. = dc[c_type]
@@ -348,18 +264,18 @@
// The type arg is casted so initial works, you shouldn't be passing a real instance into this
/**
* Return any component assigned to this datum of the exact given type
*
* This will throw an error if it's possible to have more than one component of that type on the parent
*
* Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to
*/
* Return any component assigned to this datum of the exact given type
*
* This will throw an error if it's possible to have more than one component of that type on the parent
*
* Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to
*/
/datum/proc/GetExactComponent(datum/component/c_type)
RETURN_TYPE(c_type)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
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
var/list/dc = _datum_components
if(!dc)
return null
var/datum/component/C = dc[c_type]
@@ -371,109 +287,148 @@
return null
/**
* Get all components of a given type that are attached to this datum
*
* Arguments:
* * c_type The component type path
*/
* Get all components of a given type that are attached to this datum
*
* Arguments:
* * c_type The component type path
*/
/datum/proc/GetComponents(c_type)
var/list/dc = datum_components
if(!dc)
return null
. = dc[c_type]
if(!length(.))
return list(.)
var/list/components = _datum_components?[c_type]
if(!components)
return list()
return islist(components) ? components : list(components)
/**
* Creates an instance of `new_type` in the datum and attaches to it as parent
*
* Sends the [COMSIG_COMPONENT_ADDED] signal to the datum
*
* Returns the component that was created. Or the old component in a dupe situation where [COMPONENT_DUPE_UNIQUE] was set
*
* If this tries to add a component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
*
* Properly handles duplicate situations based on the `dupe_mode` var
*/
/datum/proc/_AddComponent(list/raw_args)
var/new_type = raw_args[1]
var/datum/component/nt = new_type
var/dm = initial(nt.dupe_mode)
var/dt = initial(nt.dupe_type)
* Creates an instance of `new_type` in the datum and attaches to it as parent
*
* Sends the [COMSIG_COMPONENT_ADDED] signal to the datum
*
* Returns the component that was created. Or the old component in a dupe situation where [COMPONENT_DUPE_UNIQUE] was set
*
* If this tries to add a component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
*
* Properly handles duplicate situations based on the `dupe_mode` var
*/
/datum/proc/_AddComponent(list/raw_args, source)
var/original_type = raw_args[1]
var/datum/component/component_type = original_type
var/datum/component/old_comp
var/datum/component/new_comp
if(QDELING(src))
CRASH("Attempted to add a new component of type \[[component_type]\] to a qdeleting parent of type \[[type]\]!")
if(ispath(nt))
if(nt == /datum/component)
CRASH("[nt] attempted instantiation!")
else
new_comp = nt
nt = new_comp.type
var/datum/component/new_component
if(!ispath(component_type, /datum/component))
if(!istype(component_type, /datum/component))
CRASH("Attempted to instantiate \[[component_type]\] as a component added to parent of type \[[type]\]!")
else
new_component = component_type
component_type = new_component.type
else if(component_type == /datum/component)
CRASH("[component_type] attempted instantiation!")
var/dupe_mode = initial(component_type.dupe_mode)
var/dupe_type = initial(component_type.dupe_type)
var/uses_sources = (dupe_mode == COMPONENT_DUPE_SOURCES)
if(uses_sources && !source)
CRASH("Attempted to add a sourced component of type '[component_type]' to '[type]' without a source!")
else if(!uses_sources && source)
CRASH("Attempted to add a normal component of type '[component_type]' to '[type]' with a source!")
var/datum/component/old_component
raw_args[1] = src
if(dm != COMPONENT_DUPE_ALLOWED)
if(!dt)
old_comp = GetExactComponent(nt)
if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES)
if(!dupe_type)
old_component = GetExactComponent(component_type)
else
old_comp = GetComponent(dt)
if(old_comp)
switch(dm)
old_component = GetComponent(dupe_type)
if(old_component)
switch(dupe_mode)
if(COMPONENT_DUPE_UNIQUE)
if(!new_comp)
new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
old_comp.InheritComponent(new_comp, TRUE)
QDEL_NULL(new_comp)
if(!new_component)
new_component = new component_type(raw_args)
if(!QDELETED(new_component))
old_component.InheritComponent(new_component, TRUE)
QDEL_NULL(new_component)
if(COMPONENT_DUPE_HIGHLANDER)
if(!new_comp)
new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
new_comp.InheritComponent(old_comp, FALSE)
QDEL_NULL(old_comp)
if(!new_component)
new_component = new component_type(raw_args)
if(!QDELETED(new_component))
new_component.InheritComponent(old_component, FALSE)
QDEL_NULL(old_component)
if(COMPONENT_DUPE_UNIQUE_PASSARGS)
if(!new_comp)
if(!new_component)
var/list/arguments = raw_args.Copy(2)
arguments.Insert(1, null, TRUE)
old_comp.InheritComponent(arglist(arguments))
old_component.InheritComponent(arglist(arguments))
else
old_comp.InheritComponent(new_comp, TRUE)
if(COMPONENT_DUPE_SELECTIVE)
var/list/arguments = raw_args.Copy()
arguments[1] = new_comp
var/make_new_component = TRUE
for(var/datum/component/C as anything in GetComponents(new_type))
if(C.CheckDupeComponent(arglist(arguments)))
make_new_component = FALSE
QDEL_NULL(new_comp)
break
if(!new_comp && make_new_component)
new_comp = new nt(raw_args)
else if(!new_comp)
new_comp = new nt(raw_args) // There's a valid dupe mode but there's no old component, act like normal
else if(!new_comp)
new_comp = new nt(raw_args) // Dupes are allowed, act like normal
old_component.InheritComponent(new_component, TRUE)
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
if(COMPONENT_DUPE_SOURCES)
if(source in old_component.sources)
return old_component // source already registered, no work to do
if(old_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE)
stack_trace("incompatible source added to a [old_component.type]. Args: [json_encode(raw_args)]")
return null
else if(!new_component)
new_component = new component_type(raw_args) // There's a valid dupe mode but there's no old component, act like normal
else if(dupe_mode == COMPONENT_DUPE_SELECTIVE)
var/list/arguments = raw_args.Copy()
arguments[1] = new_component
var/make_new_component = TRUE
for(var/datum/component/existing_component as anything in GetComponents(original_type))
if(existing_component.CheckDupeComponent(arglist(arguments)))
make_new_component = FALSE
QDEL_NULL(new_component)
break
if(!new_component && make_new_component)
new_component = new component_type(raw_args)
else if(dupe_mode == COMPONENT_DUPE_SOURCES)
new_component = new component_type(raw_args)
if(new_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE)
stack_trace("incompatible source added to a [new_component.type]. Args: [json_encode(raw_args)]")
return null
else if(!new_component)
new_component = new component_type(raw_args) // Dupes are allowed, act like normal
if(!old_component && !QDELETED(new_component)) // Nothing related to duplicate components happened and the new component is healthy
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_component)
return new_component
return old_component
/**
* Get existing component of type, or create it and return a reference to it
*
* Use this if the item needs to exist at the time of this call, but may not have been created before now
*
* Arguments:
* * component_type The typepath of the component to create or return
* * ... additional arguments to be passed when creating the component if it does not exist
*/
/datum/proc/LoadComponent(component_type, ...)
. = GetComponent(component_type)
if(!.)
return _AddComponent(args)
* Removes a component source from this datum
*/
/datum/proc/RemoveComponentSource(source, datum/component/component_type)
if(ispath(component_type))
component_type = GetExactComponent(component_type)
if(!component_type)
return
component_type.on_source_remove(source)
/**
* Get existing component of type, or create it and return a reference to it
*
* Use this if the item needs to exist at the time of this call, but may not have been created before now
*
* Arguments:
* * component_type The typepath of the component to create or return
* * ... additional arguments to be passed when creating the component if it does not exist
*/
/datum/proc/_LoadComponent(list/arguments)
. = GetComponent(arguments[1])
if(!.)
return _AddComponent(arguments)
/**
* Removes the component from parent, ends up with a null parent
* Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does
@@ -488,13 +443,13 @@
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
/**
* Transfer this component to another parent
*
* Component is taken from source datum
*
* Arguments:
* * datum/component/target Target datum to transfer to
*/
* Transfer this component to another parent
*
* Component is taken from source datum
*
* Arguments:
* * datum/component/target Target datum to transfer to
*/
/datum/proc/TakeComponent(datum/component/target)
if(!target || target.parent == src)
return
@@ -512,29 +467,30 @@
target._JoinParent()
/**
* Transfer all components to target
*
* All components from source datum are taken
*
* Arguments:
* * /datum/target the target to move the components to
*/
* Transfer all components to target
*
* All components from source datum are taken
*
* Arguments:
* * /datum/target the target to move the components to
*/
/datum/proc/TransferComponents(datum/target)
var/list/dc = datum_components
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)
for(var/component_key in dc)
var/component_or_list = dc[component_key]
if(islist(component_or_list))
for(var/datum/component/I in component_or_list)
if(I.can_transfer)
target.TakeComponent(I)
else
var/datum/component/C = component_or_list
if(C.can_transfer)
target.TakeComponent(C)
/**
* Return the object that is the host of any UI's that this component has
*/
* Return the object that is the host of any UI's that this component has
*/
/datum/component/tgui_host()
return parent
+1 -1
View File
@@ -60,7 +60,7 @@
mat_amt = 0
materials[mat_ref] += mat_amt
/datum/component/material_container/Destroy(force, silent)
/datum/component/material_container/Destroy(force)
materials = null
allowed_materials = null
if(insertion_check)
+44 -31
View File
@@ -19,7 +19,7 @@
/// Open uis owned by this datum
/// Lazy, since this case is semi rare
var/list/open_uis
var/list/open_tguis // FIXME: open_uis
/// Active timers with this datum as the target
var/list/_active_timers
@@ -29,20 +29,22 @@
/**
* Components attached to this datum
*
* Lazy associated list in the structure of `type:component/list of components`
* Lazy associated list in the structure of `type -> component/list of components`
*/
var/list/datum_components
var/list/_datum_components
/**
* Any datum registered to receive signals from this datum is in this list
*
* Lazy associated list in the structure of `signal:registree/list of registrees`
* Lazy associated list in the structure of `signal -> registree/list of registrees`
*/
var/list/comp_lookup
var/list/list/signal_procs // List of lists
var/signal_enabled = FALSE
var/list/_listen_lookup
/// Lazy associated list in the structure of `target -> list(signal -> proctype)` that are run when the datum receives that signal
var/list/list/_signal_procs
/// Datum level flags
var/datum_flags = NONE
var/trigger_uid
var/status_traits
/// A weak reference to another datum
var/datum/weakref/weak_reference
@@ -91,6 +93,7 @@
tag = null
weak_reference = null //ensure prompt GCing of weakref.
//clear timers
if(_active_timers)
var/list/timers = _active_timers
_active_timers = null
@@ -99,34 +102,26 @@
continue
qdel(timer)
//BEGIN: ECS SHIT
signal_enabled = FALSE
#ifdef REFERENCE_TRACKING
#ifdef REFERENCE_TRACKING_DEBUG
found_refs = null
#endif
#endif
var/list/dc = datum_components
//BEGIN: ECS SHIT
var/list/dc = _datum_components
if(dc)
var/all_components = dc[/datum/component]
if(length(all_components))
for(var/datum/component/C as anything in all_components)
qdel(C, FALSE, TRUE)
else
var/datum/component/C = all_components
qdel(C, FALSE, TRUE)
for(var/component_key in dc)
var/component_or_list = dc[component_key]
if(islist(component_or_list))
for(var/datum/component/component as anything in component_or_list)
qdel(component, FALSE)
else
var/datum/component/C = component_or_list
qdel(C, FALSE)
dc.Cut()
var/list/lookup = comp_lookup
if(lookup)
for(var/sig in lookup)
var/list/comps = lookup[sig]
if(length(comps))
for(var/datum/component/comp as anything in comps)
comp.UnregisterSignal(src, sig)
else
var/datum/component/comp = comps
comp.UnregisterSignal(src, sig)
comp_lookup = lookup = null
for(var/target in signal_procs)
UnregisterSignal(target, signal_procs[target])
_clear_signal_refs()
//END: ECS SHIT
SStgui.close_uis(src)
@@ -140,6 +135,24 @@
return QDEL_HINT_QUEUE
///Only override this if you know what you're doing. You do not know what you're doing
///This is a threat
/datum/proc/_clear_signal_refs()
var/list/lookup = _listen_lookup
if(lookup)
for(var/sig in lookup)
var/list/comps = lookup[sig]
if(length(comps))
for(var/datum/component/comp as anything in comps)
comp.UnregisterSignal(src, sig)
else
var/datum/component/comp = comps
comp.UnregisterSignal(src, sig)
_listen_lookup = lookup = null
for(var/target in _signal_procs)
UnregisterSignal(target, _signal_procs[target])
/**
* Callback called by a timer to end an associative-list-indexed cooldown.
*
+25
View File
@@ -0,0 +1,25 @@
# Template file for your new element
See _element.dm for detailed explanations
```dm
/datum/element/myelement
element_flags = ELEMENT_BESPOKE | ELEMENT_COMPLEX_DETACH | ELEMENT_DETACH_ON_HOST_DESTROY | ELEMENT_NOTAREALFLAG // code/__DEFINES/dcs/flags.dm
//argument_hash_start_idx = 2 // Use with ELEMENT_BESPOKE
var/list/myvar = list()
/datum/element/myelement/Attach(datum/target)
if(!ismovable(target))
return COMPONENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_MOVED, myproc)
to_chat(target, "Hey, you're in your element.")
/datum/element/myelement/Detach(datum/source)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
to_chat(source, "You feel way out of your element.")
/datum/element/myelement/proc/myproc(datum/source)
SIGNAL_HANDLER
playsound(source, 'sound/effects/gong.ogg', 50, TRUE)
```
+43 -7
View File
@@ -10,11 +10,14 @@
/**
* The index of the first attach argument to consider for duplicate elements
*
* All arguments from this index onwards (1 based) are hashed into the key to determine
* if this is a new unique element or one already exists
*
* Is only used when flags contains [ELEMENT_BESPOKE]
*
* This is infinity so you must explicitly set this
*/
var/id_arg_index = INFINITY
var/argument_hash_start_idx = INFINITY
/// Activates the functionality defined by the element on the given target datum
/datum/element/proc/Attach(datum/target)
@@ -22,19 +25,19 @@
if(type == /datum/element)
return ELEMENT_INCOMPATIBLE
SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src)
if(element_flags & ELEMENT_DETACH)
if(element_flags & ELEMENT_DETACH_ON_HOST_DESTROY)
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(OnTargetDelete), override = TRUE)
/datum/element/proc/OnTargetDelete(datum/source, force)
/datum/element/proc/OnTargetDelete(datum/source)
SIGNAL_HANDLER
Detach(source)
/// Deactivates the functionality defines by the element on the given datum
/datum/element/proc/Detach(datum/source, ...)
SIGNAL_HANDLER
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
SHOULD_CALL_PARENT(TRUE)
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
/datum/element/Destroy(force)
@@ -48,20 +51,53 @@
/// Finds the singleton for the element type given and attaches it to src
/datum/proc/_AddElement(list/arguments)
if(QDELING(src))
CRASH("We just tried to add an element to a qdeleted datum, something is fucked")
var/datum/element/element_type = arguments[1]
stack_trace("We just tried to add the element [element_type] to a qdeleted datum, something is fucked")
return
var/datum/element/ele = SSdcs.GetElement(arguments)
if(!ele) // We couldn't fetch the element, likely because it was not an element.
return // the crash message has already been sent
arguments[1] = src
if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE)
CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]")
CRASH("Incompatible element [ele.type] was assigned to a [type]! args: [json_encode(args)]")
/**
* Finds the singleton for the element type given and detaches it from src
* You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE]
*/
/datum/proc/_RemoveElement(list/arguments)
var/datum/element/ele = SSdcs.GetElement(arguments)
var/datum/element/ele = SSdcs.GetElement(arguments, FALSE)
if(!ele) // We couldn't fetch the element, likely because it didn't exist.
return
if(ele.element_flags & ELEMENT_COMPLEX_DETACH)
arguments[1] = src
ele.Detach(arglist(arguments))
else
ele.Detach(src)
/**
* Used to manage (typically non_bespoke) elements with multiple sources through traits
* so we don't have to make them a components again.
* The element will be later removed once all trait sources are gone, there's no need of a
* "RemoveElementTrait" counterpart.
*/
/datum/proc/AddElementTrait(trait, source, datum/element/eletype, ...)
if(!ispath(eletype, /datum/element))
CRASH("AddElementTrait called, but [eletype] is not of a /datum/element path")
ADD_TRAIT(src, trait, source)
if(HAS_TRAIT_NOT_FROM(src, trait, source))
return
var/list/arguments = list(eletype)
/// 3 is the length of fixed args of this proc, any further one is passed down to AddElement.
if(length(args) > 3)
arguments += args.Copy(4)
/// We actually pass down a copy of the arguments since it's manipulated by the end of the proc.
_AddElement(arguments.Copy())
var/datum/ele = SSdcs.GetElement(arguments)
ele.RegisterSignal(src, SIGNAL_REMOVETRAIT(trait), TYPE_PROC_REF(/datum/element, _detach_on_trait_removed))
/datum/element/proc/_detach_on_trait_removed(datum/source, trait)
SIGNAL_HANDLER
Detach(source)
UnregisterSignal(source, SIGNAL_REMOVETRAIT(trait))
+2 -2
View File
@@ -2,8 +2,8 @@
* Simple conflict checking for getting number of conflicting things on someone with the same ID.
*/
/datum/element/conflict_checking
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH
id_arg_index = 1
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY
argument_hash_start_idx = 1
/// we don't need to KNOW who has us, only our ID.
var/id
+187
View File
@@ -0,0 +1,187 @@
#define SHOULD_DISABLE_FOOTSTEPS(source)
///Footstep element. Plays footsteps at parents location when it is appropriate.
/datum/element/footstep
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE
argument_hash_start_idx = 2
///A list containing living mobs and the number of steps they have taken since the last time their footsteps were played.
var/list/steps_for_living = list()
///volume determines the extra volume of the footstep. This is multiplied by the base volume, should there be one.
var/volume
///e_range stands for extra range - aka how far the sound can be heard. This is added to the base value and ignored if there isn't a base value.
var/e_range
///footstep_type is a define which determines what kind of sounds should get chosen.
var/footstep_type
///This can be a list OR a soundfile OR null. Determines whatever sound gets played.
var/footstep_sounds
///Whether or not to add variation to the sounds played
var/sound_vary = FALSE
/datum/element/footstep/Attach(datum/target, footstep_type = FOOTSTEP_MOB_BAREFOOT, volume = 0.1, e_range = -8, sound_vary = FALSE)
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
src.volume = volume
src.e_range = e_range
src.footstep_type = footstep_type
src.sound_vary = sound_vary
if(ishuman(target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(play_humanstep))
steps_for_living[target] = 0
return
footstep_sounds = check_footstep_type(footstep_type)
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(play_simplestep))
steps_for_living[target] = 0
/datum/element/footstep/proc/check_footstep_type(footstep_type)
var/footstep_ret
switch(footstep_type)
if(FOOTSTEP_MOB_TESHARI)
footstep_ret = GLOB.lightclawfootstep
if(FOOTSTEP_MOB_CLAW)
footstep_ret = GLOB.clawfootstep
if(FOOTSTEP_MOB_HEAVY)
footstep_ret = GLOB.heavyfootstep
if(FOOTSTEP_MOB_SHOE)
footstep_ret = GLOB.footstep
if(FOOTSTEP_MOB_SLIME)
footstep_ret = 'sound/effects/footstep/slime1.ogg'
if(FOOTSTEP_MOB_SLITHER)
footstep_ret = 'sound/effects/footstep/crawl1.ogg'
else
footstep_ret = GLOB.barefootstep
return footstep_ret
/datum/element/footstep/Detach(atom/movable/source)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
steps_for_living -= source
return ..()
///Prepares a footstep for living mobs. Determines if it should get played. Returns the turf it should get played on. Note that it is always a /turf/simulated
/datum/element/footstep/proc/prepare_step(mob/living/source)
var/turf/simulated/turf = get_turf(source)
if(!istype(turf))
return
if(source.is_incorporeal())
return
if(source.buckled || source.throwing || source.movement_type & (source.is_ventcrawling | source.flying))
return
if(source.lying) //play crawling sound if we're lying
if(turf.footstep)
playsound(turf, 'sound/effects/footstep/crawl1.ogg', 15 * volume, falloff = 1, vary = sound_vary)
return
if(iscarbon(source))
var/mob/living/carbon/carbon_source = source
if(!carbon_source.get_organ(BP_L_LEG) && !carbon_source.get_organ(BP_R_LEG))
return
if(carbon_source.m_intent == I_WALK)
return// stealth
steps_for_living[source] += 1
var/steps = steps_for_living[source]
if(steps >= 6)
steps_for_living[source] = 0
steps = 0
if(steps % 2)
return
if(steps != 0 && !get_gravity(source)) // don't need to step as often when you hop around
return
. = list(
FOOTSTEP_MOB_SHOE = turf.footstep,
FOOTSTEP_MOB_BAREFOOT = turf.barefootstep,
FOOTSTEP_MOB_HEAVY = turf.heavyfootstep,
FOOTSTEP_MOB_CLAW = turf.clawfootstep,
STEP_SOUND_PRIORITY = STEP_SOUND_NO_PRIORITY
)
var/overriden = SEND_SIGNAL(turf, COMSIG_TURF_PREPARE_STEP_SOUND, .) & FOOTSTEP_OVERRIDEN
//The turf has no footstep sound (e.g. open space) and none of the objects on that turf (e.g. catwalks) overrides it
if(!overriden && isnull(turf.footstep))
return null
return .
/datum/element/footstep/proc/play_simplestep(mob/living/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
var/volume_multiplier = 0.3
if(!isturf(source.loc))
return
var/list/prepared_steps = prepare_step(source)
if(isnull(prepared_steps))
return
if(isfile(footstep_sounds) || istext(footstep_sounds))
playsound(source.loc, footstep_sounds, volume * volume_multiplier, falloff = 1, vary = sound_vary)
return
var/turf_footstep = prepared_steps[footstep_type]
if(isnull(turf_footstep) || !footstep_sounds[turf_footstep])
return
playsound(source.loc, pick(footstep_sounds[turf_footstep][1]), footstep_sounds[turf_footstep][2] * volume, TRUE, footstep_sounds[turf_footstep][3] + e_range, falloff = 1, vary = sound_vary)
/datum/element/footstep/proc/play_humanstep(mob/living/carbon/human/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
var/volume_multiplier = 0.3
var/range_adjustment = 0
var/list/prepared_steps = prepare_step(source)
if(isnull(prepared_steps))
return
//cache for sanic speed (lists are references anyways)
var/footstep_sounds = GLOB.footstep
if( source.shoes || ( source.wear_suit && (source.wear_suit.body_parts_covered & FEET) ) )
// we are wearing shoes
var/shoestep_type = prepared_steps[FOOTSTEP_MOB_SHOE]
if(!isnull(shoestep_type) && footstep_sounds[shoestep_type]) // shoestep type can be null
playsound(source.loc, pick(footstep_sounds[shoestep_type][1]),
footstep_sounds[shoestep_type][2] * volume * volume_multiplier,
TRUE,
footstep_sounds[shoestep_type][3] + e_range + range_adjustment, falloff = 1, vary = sound_vary)
else
// we are barefoot
if(source.species.special_step_sounds)
playsound(source.loc, pick(source.species.special_step_sounds), volume, TRUE, falloff = 1, vary = sound_vary)
else if (istype(source.species, /datum/species/shapeshifter/promethean))
playsound(source.loc, 'sound/effects/footstep/slime1.ogg', volume, TRUE, falloff = 1)
else if (source.custom_footstep == FOOTSTEP_MOB_SLITHER)
playsound(source.loc, 'sound/effects/footstep/crawl1.ogg', 15 * volume, falloff = 1, vary = sound_vary)
else
var/barefoot_type = prepared_steps[FOOTSTEP_MOB_BAREFOOT]
var/bare_footstep_sounds
if(source.custom_footstep != FOOTSTEP_MOB_HUMAN)
bare_footstep_sounds = check_footstep_type(source.custom_footstep)
else
bare_footstep_sounds = GLOB.barefootstep
if(!isnull(barefoot_type) && bare_footstep_sounds[barefoot_type]) // barefoot_type can be null
playsound(source.loc, pick(bare_footstep_sounds[barefoot_type][1]),
bare_footstep_sounds[barefoot_type][2] * volume * volume_multiplier,
TRUE,
bare_footstep_sounds[barefoot_type][3] + e_range + range_adjustment, falloff = 1, vary = sound_vary)
///Prepares a footstep for machine walking
/datum/element/footstep/proc/play_simplestep_machine(atom/movable/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
var/turf/simulated/source_loc = get_turf(source)
if(!istype(source_loc))
return
playsound(source_loc, footstep_sounds, 50, falloff = 1, vary = sound_vary)
#undef SHOULD_DISABLE_FOOTSTEPS
+81
View File
@@ -0,0 +1,81 @@
///When attached, the footstep sound played by the footstep element will be replaced by this one's
/datum/element/footstep_override
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH_ON_HOST_DESTROY
argument_hash_start_idx = 2
///The sound played for movables with claw step sound type.
var/clawfootstep
///The sound played for movables with barefoot step sound type.
var/barefootstep
///The sound played for movables with heavy step sound type.
var/heavyfootstep
///The sound played for movables with shoed step sound type.
var/footstep
///The priority this element has in relation to other elements of the same type attached to other movables on the same turf.
var/priority
/**
* A list of turfs occupied by the movables this element is attached to.
* Needed so it stops listening the turf's signals ONLY when it has no movable with the element.
*/
var/list/occupied_turfs = list()
/datum/element/footstep_override/Attach(atom/movable/target, clawfootstep = FOOTSTEP_HARD_CLAW, barefootstep = FOOTSTEP_HARD_BAREFOOT, heavyfootstep = FOOTSTEP_GENERIC_HEAVY, footstep = FOOTSTEP_FLOOR, priority = STEP_SOUND_NO_PRIORITY)
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
src.clawfootstep = clawfootstep
src.barefootstep = barefootstep
src.heavyfootstep = heavyfootstep
src.footstep = footstep
src.priority = priority
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
if(isturf(target.loc))
occupy_turf(target, target.loc)
/datum/element/footstep_override/Detach(atom/movable/source)
if(isturf(source.loc))
vacate_turf(source, source.loc)
return ..()
/datum/element/footstep_override/proc/on_moved(atom/movable/source, atom/oldloc)
SIGNAL_HANDLER
if(isturf(oldloc))
vacate_turf(source, oldloc)
if(isturf(source.loc))
occupy_turf(source, source.loc)
/**
* Adds the movable to the list of movables with the element occupying the turf.
* If the turf was not on the list of occupied turfs before, a signal will be registered
* to it.
*/
/datum/element/footstep_override/proc/occupy_turf(atom/movable/movable, turf/location)
if(occupied_turfs[location])
occupied_turfs[location] |= movable
return
occupied_turfs[location] = list(movable)
RegisterSignal(location, COMSIG_TURF_PREPARE_STEP_SOUND, PROC_REF(prepare_steps))
/**
* Removes the movable from the list of movables with the element occupying the turf.
* If the turf is no longer occupied, it'll be removed from the list, and the signal
* unregistered from it
*/
/datum/element/footstep_override/proc/vacate_turf(atom/movable/movable, turf/location)
LAZYREMOVE(occupied_turfs[location], movable)
if(!occupied_turfs[location])
occupied_turfs -= location
UnregisterSignal(location, COMSIG_TURF_PREPARE_STEP_SOUND)
///Changes the sound types to be played if the element priority is higher than the one in the steps list.
/datum/element/footstep_override/proc/prepare_steps(turf/source, list/steps)
SIGNAL_HANDLER
if(steps[STEP_SOUND_PRIORITY] > priority)
return
steps[FOOTSTEP_MOB_SHOE] = footstep
steps[FOOTSTEP_MOB_BAREFOOT] = barefootstep
steps[FOOTSTEP_MOB_HEAVY] = heavyfootstep
steps[FOOTSTEP_MOB_CLAW] = clawfootstep
steps[STEP_SOUND_PRIORITY] = priority
return FOOTSTEP_OVERRIDEN
+1 -1
View File
@@ -2,7 +2,7 @@
* Attached to movable atoms with opacity. Listens to them move and updates their old and new turf loc's opacity accordingly.
*/
/datum/element/light_blocking
element_flags = ELEMENT_DETACH
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY
/datum/element/light_blocking/Attach(datum/target)
+94
View File
@@ -0,0 +1,94 @@
/datum/element/slosh
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE
var/step_count
var/vore_organs_reagents
var/vore_footstep_volume
var/vore_footstep_chance
/datum/element/slosh/Attach(datum/target)
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(handle_sloshstep), override = TRUE)
step_count = 0
vore_organs_reagents = list()
vore_footstep_volume = 0
vore_footstep_chance = 0
return
/datum/element/slosh/Detach(datum/source)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
step_count -= source
return ..()
/datum/element/slosh/proc/handle_sloshstep(mob/living/source)
SIGNAL_HANDLER
if(ishuman(source))
var/mob/living/carbon/human/source_human = source
if(source_human.m_intent == "walk" && step_count++ % 20 == 0)
return
if(source_human.m_intent == "run" && step_count++ % 2 != 0)
return
choose_vorefootstep(source)
if(issilicon(source))
if(step_count++ % 2)
choose_vorefootstep(source)
/datum/element/slosh/proc/choose_vorefootstep(mob/living/source)
if(step_count++ >= 5)
vore_organs_reagents = list()
var/highest_vol = 0
for(var/obj/belly/B in source.vore_organs)
var/total_volume = B.reagents.total_volume
vore_organs_reagents += total_volume
if(B.show_liquids && B.vorefootsteps_sounds && highest_vol < total_volume)
highest_vol = total_volume
if(highest_vol < 20)
vore_footstep_volume = 0
vore_footstep_chance = 0
else
vore_footstep_volume = 20 + highest_vol * 4/5
vore_footstep_chance = highest_vol/4
step_count = 0
if(!vore_footstep_volume || !vore_footstep_chance)
return
if(prob(vore_footstep_chance))
handle_vorefootstep(source)
/datum/element/slosh/proc/handle_vorefootstep(mob/living/source)
if(!CONFIG_GET(number/vorefootstep_volume) || !vore_footstep_volume)
return
var/S = pick(GLOB.slosh)
if(!S) return
var/volume = CONFIG_GET(number/vorefootstep_volume) * (vore_footstep_volume/100)
if(ishuman(source))
var/mob/living/carbon/human/human_source = source
if(!human_source.shoes || human_source.m_intent == "walk")
volume = CONFIG_GET(number/vorefootstep_volume) * (vore_footstep_volume/100) * 0.75
else if(human_source.shoes)
var/obj/item/clothing/shoes/feet = human_source.shoes
if(istype(feet))
volume = feet.step_volume_mod * CONFIG_GET(number/vorefootstep_volume) * (vore_footstep_volume/100) * 0.75
if(!human_source.has_organ(BP_L_FOOT) && !human_source.has_organ(BP_R_FOOT))
return
if(source.buckled || source.lying || source.throwing)
return
if(!has_gravity(source) && prob(75))
return
playsound(source.loc, S, volume, FALSE, preference = /datum/client_preference/digestion_noises)
return
+127
View File
@@ -0,0 +1,127 @@
/**
* Register to listen for a signal from the passed in target
*
* This sets up a listening relationship such that when the target object emits a signal
* the source datum this proc is called upon, will receive a callback to the given proctype
* Use PROC_REF(procname), TYPE_PROC_REF(type,procname) or GLOBAL_PROC_REF(procname) macros to validate the passed in proc at compile time.
* PROC_REF for procs defined on current type or it's ancestors, TYPE_PROC_REF for procs defined on unrelated type and GLOBAL_PROC_REF for global procs.
* Return values from procs registered must be a bitfield
*
* Arguments:
* * datum/target The target to listen for signals from
* * signal_type A signal name
* * proctype The proc to call back when the signal is emitted
* * override If a previous registration exists you must explicitly set this
*/
/datum/proc/RegisterSignal(datum/target, signal_type, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
return
if (islist(signal_type))
var/static/list/known_failures = list()
var/list/signal_type_list = signal_type
var/message = "([target.type]) is registering [signal_type_list.Join(", ")] as a list, the older method. Change it to RegisterSignals."
if (!(message in known_failures))
known_failures[message] = TRUE
stack_trace("[target] [message]")
RegisterSignals(target, signal_type, proctype, override)
return
var/list/procs = (_signal_procs ||= list())
var/list/target_procs = (procs[target] ||= list())
var/list/lookup = (target._listen_lookup ||= list())
var/exists = target_procs[signal_type]
target_procs[signal_type] = proctype
if(exists)
if(!override)
var/override_message = "[signal_type] overridden. Use override = TRUE to suppress this warning.\nTarget: [target] ([target.type]) Proc: [proctype]"
//log_signal(override_message) // We don't have log_signal
log_world(override_message)
stack_trace(override_message)
return
var/list/looked_up = lookup[signal_type]
if(isnull(looked_up)) // Nothing has registered here yet
lookup[signal_type] = src
else if(!islist(looked_up)) // One other thing registered here
lookup[signal_type] = list(looked_up, src)
else // Many other things have registered here
looked_up += src
/// Registers multiple signals to the same proc.
/datum/proc/RegisterSignals(datum/target, list/signal_types, proctype, override = FALSE)
for (var/signal_type in signal_types)
RegisterSignal(target, signal_type, proctype, override)
/**
* Stop listening to a given signal from target
*
* Breaks the relationship between target and source datum, removing the callback when the signal fires
*
* Doesn't care if a registration exists or not
*
* Arguments:
* * datum/target Datum to stop listening to signals from
* * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically
*/
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
var/list/lookup = target._listen_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)
if(!_signal_procs[target][sig])
if(!istext(sig))
stack_trace("We're unregistering with something that isn't a valid signal \[[sig]\], you fucked up")
continue
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 _listen_lookup")
if(src in lookup[sig])
lookup -= sig
if(!length(lookup))
target._listen_lookup = null
break
if(0)
if(lookup[sig] != src)
continue
lookup -= sig
if(!length(lookup))
target._listen_lookup = null
break
else
lookup[sig] -= src
_signal_procs[target] -= sig_type_or_types
if(!_signal_procs[target].len)
_signal_procs -= target
/**
* Internal proc to handle most all of the signaling procedure
*
* Will runtime if used on datums with an empty lookup list
*
* Use the [SEND_SIGNAL] define instead
*/
/datum/proc/_SendSignal(sigtype, list/arguments)
var/target = _listen_lookup[sigtype]
if(!length(target))
var/datum/listening_datum = target
return NONE | call(listening_datum, listening_datum._signal_procs[src][sigtype])(arglist(arguments))
. = NONE
// This exists so that even if one of the signal receivers unregisters the signal,
// all the objects that are receiving the signal get the signal this final time.
// AKA: No you can't cancel the signal reception of another object by doing an unregister in the same signal.
var/list/queued_calls = list()
for(var/datum/listening_datum as anything in target)
queued_calls[listening_datum] = listening_datum._signal_procs[src][sigtype]
for(var/datum/listening_datum as anything in queued_calls)
. |= call(listening_datum, queued_calls[listening_datum])(arglist(arguments))