mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-15 20:52:41 +00:00
55
code/datums/components/COMPONENT_TEMPLATE.md
Normal file
55
code/datums/components/COMPONENT_TEMPLATE.md
Normal file
@@ -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
|
||||
*/
|
||||
```
|
||||
@@ -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)
|
||||
|
||||
@@ -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(parent && !silent) //CHOMPEdit
|
||||
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,198 +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
|
||||
|
||||
//CHOMPAdd start
|
||||
/// 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)
|
||||
//CHOMPAdd end
|
||||
* 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)
|
||||
@@ -311,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]
|
||||
@@ -355,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]
|
||||
@@ -378,113 +287,154 @@
|
||||
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)
|
||||
* 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(args)
|
||||
return _AddComponent(arguments)
|
||||
|
||||
/**
|
||||
* Removes the component from parent, ends up with a null parent
|
||||
*/
|
||||
/datum/component/proc/RemoveComponent()
|
||||
* 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
|
||||
*/
|
||||
/datum/component/proc/ClearFromParent()
|
||||
if(!parent)
|
||||
return
|
||||
var/datum/old_parent = parent
|
||||
@@ -494,18 +444,18 @@
|
||||
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
|
||||
if(target.parent)
|
||||
target.RemoveComponent()
|
||||
target.ClearFromParent()
|
||||
target.parent = src
|
||||
var/result = target.PostTransfer()
|
||||
switch(result)
|
||||
@@ -518,29 +468,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
|
||||
|
||||
Reference in New Issue
Block a user