mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-11 10:11:09 +00:00
GetComponent gets a singular component, if you're trying to get a component which allows duplicates on the object then you're doing something that can very easily break. Stop it. I'll straight up disable the capability once everything that does this has been cleaned up but each and every one is its own project.
317 lines
9.2 KiB
Plaintext
317 lines
9.2 KiB
Plaintext
/datum/component
|
|
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
|
|
var/dupe_type
|
|
var/datum/parent
|
|
//only set to true if you are able to properly transfer this component
|
|
//At a minimum RegisterWithParent and UnregisterFromParent should be used
|
|
//Make sure you also implement PostTransfer for any post transfer handling
|
|
var/can_transfer = FALSE
|
|
|
|
/datum/component/New(datum/P, ...)
|
|
parent = P
|
|
var/list/arguments = args.Copy(2)
|
|
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
|
|
qdel(src, TRUE, TRUE)
|
|
CRASH("Incompatible [type] assigned to a [P.type]! args: [json_encode(arguments)]")
|
|
|
|
_JoinParent(P)
|
|
|
|
/datum/component/proc/_JoinParent()
|
|
var/datum/P = parent
|
|
//lazy init the parent's dc list
|
|
var/list/dc = P.datum_components
|
|
if(!dc)
|
|
P.datum_components = dc = list()
|
|
|
|
//set up the typecache
|
|
var/our_type = type
|
|
for(var/I in _GetInverseTypeList(our_type))
|
|
var/test = dc[I]
|
|
if(test) //already another component of this type here
|
|
var/list/components_of_type
|
|
if(!length(test))
|
|
components_of_type = list(test)
|
|
dc[I] = components_of_type
|
|
else
|
|
components_of_type = test
|
|
if(I == our_type) //exact match, take priority
|
|
var/inserted = FALSE
|
|
for(var/J in 1 to components_of_type.len)
|
|
var/datum/component/C = components_of_type[J]
|
|
if(C.type != our_type) //but not over other exact matches
|
|
components_of_type.Insert(J, I)
|
|
inserted = TRUE
|
|
break
|
|
if(!inserted)
|
|
components_of_type += src
|
|
else //indirect match, back of the line with ya
|
|
components_of_type += src
|
|
else //only component of this type, no list
|
|
dc[I] = src
|
|
|
|
RegisterWithParent()
|
|
|
|
// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
|
|
/datum/component/proc/RegisterWithParent()
|
|
return
|
|
|
|
/datum/component/proc/Initialize(...)
|
|
return
|
|
|
|
/datum/component/Destroy(force=FALSE, silent=FALSE)
|
|
if(!force && parent)
|
|
_RemoveFromParent()
|
|
if(!silent)
|
|
SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
|
|
parent = null
|
|
return ..()
|
|
|
|
/datum/component/proc/_RemoveFromParent()
|
|
var/datum/P = parent
|
|
var/list/dc = P.datum_components
|
|
for(var/I in _GetInverseTypeList())
|
|
var/list/components_of_type = dc[I]
|
|
if(length(components_of_type)) //
|
|
var/list/subtracted = components_of_type - src
|
|
if(subtracted.len == 1) //only 1 guy left
|
|
dc[I] = subtracted[1] //make him special
|
|
else
|
|
dc[I] = subtracted
|
|
else //just us
|
|
dc -= I
|
|
if(!dc.len)
|
|
P.datum_components = null
|
|
|
|
UnregisterFromParent()
|
|
|
|
/datum/component/proc/UnregisterFromParent()
|
|
return
|
|
|
|
/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proc_or_callback, override = FALSE)
|
|
if(QDELETED(src) || QDELETED(target))
|
|
return
|
|
|
|
var/list/procs = signal_procs
|
|
if(!procs)
|
|
signal_procs = procs = list()
|
|
if(!procs[target])
|
|
procs[target] = list()
|
|
var/list/lookup = target.comp_lookup
|
|
if(!lookup)
|
|
target.comp_lookup = lookup = list()
|
|
|
|
if(!istype(proc_or_callback, /datum/callback)) //if it wasnt a callback before, it is now
|
|
proc_or_callback = CALLBACK(src, proc_or_callback)
|
|
|
|
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
|
|
for(var/sig_type in sig_types)
|
|
if(!override && procs[target][sig_type])
|
|
stack_trace("[sig_type] overridden. Use override = TRUE to suppress this warning")
|
|
|
|
procs[target][sig_type] = proc_or_callback
|
|
|
|
if(!lookup[sig_type]) // Nothing has registered here yet
|
|
lookup[sig_type] = src
|
|
else if(lookup[sig_type] == src) // We already registered here
|
|
continue
|
|
else if(!length(lookup[sig_type])) // One other thing registered here
|
|
lookup[sig_type] = list(lookup[sig_type]=TRUE)
|
|
lookup[sig_type][src] = TRUE
|
|
else // Many other things have registered here
|
|
lookup[sig_type][src] = TRUE
|
|
|
|
signal_enabled = TRUE
|
|
|
|
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
|
|
var/list/lookup = target.comp_lookup
|
|
if(!signal_procs || !signal_procs[target] || !lookup)
|
|
return
|
|
if(!islist(sig_type_or_types))
|
|
sig_type_or_types = list(sig_type_or_types)
|
|
for(var/sig in sig_type_or_types)
|
|
switch(length(lookup[sig]))
|
|
if(2)
|
|
lookup[sig] = (lookup[sig]-src)[1]
|
|
if(1)
|
|
stack_trace("[target] ([target.type]) somehow has single length list inside comp_lookup")
|
|
if(src in lookup[sig])
|
|
lookup -= sig
|
|
if(!length(lookup))
|
|
target.comp_lookup = null
|
|
break
|
|
if(0)
|
|
lookup -= sig
|
|
if(!length(lookup))
|
|
target.comp_lookup = null
|
|
break
|
|
else
|
|
lookup[sig] -= src
|
|
|
|
signal_procs[target] -= sig_type_or_types
|
|
if(!signal_procs[target].len)
|
|
signal_procs -= target
|
|
|
|
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
|
|
return
|
|
|
|
/datum/component/proc/PreTransfer()
|
|
return
|
|
|
|
/datum/component/proc/PostTransfer()
|
|
return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
|
|
|
|
/datum/component/proc/_GetInverseTypeList(our_type = type)
|
|
//we can do this one simple trick
|
|
var/current_type = parent_type
|
|
. = list(our_type, current_type)
|
|
//and since most components are root level + 1, this won't even have to run
|
|
while (current_type != /datum/component)
|
|
current_type = type2parent(current_type)
|
|
. += current_type
|
|
|
|
/datum/proc/_SendSignal(sigtype, list/arguments)
|
|
var/target = comp_lookup[sigtype]
|
|
if(!length(target))
|
|
var/datum/C = target
|
|
if(!C.signal_enabled)
|
|
return NONE
|
|
var/datum/callback/CB = C.signal_procs[src][sigtype]
|
|
return CB.InvokeAsync(arglist(arguments))
|
|
. = NONE
|
|
for(var/I in target)
|
|
var/datum/C = I
|
|
if(!C.signal_enabled)
|
|
continue
|
|
var/datum/callback/CB = C.signal_procs[src][sigtype]
|
|
. |= CB.InvokeAsync(arglist(arguments))
|
|
|
|
// The type arg is casted so initial works, you shouldn't be passing a real instance into this
|
|
/datum/proc/GetComponent(datum/component/c_type)
|
|
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED)
|
|
stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
|
|
var/list/dc = datum_components
|
|
if(!dc)
|
|
return null
|
|
. = dc[c_type]
|
|
if(length(.))
|
|
return .[1]
|
|
|
|
/datum/proc/GetExactComponent(c_type)
|
|
var/list/dc = datum_components
|
|
if(!dc)
|
|
return null
|
|
var/datum/component/C = dc[c_type]
|
|
if(C)
|
|
if(length(C))
|
|
C = C[1]
|
|
if(C.type == c_type)
|
|
return C
|
|
return null
|
|
|
|
/datum/proc/GetComponents(c_type)
|
|
var/list/dc = datum_components
|
|
if(!dc)
|
|
return null
|
|
. = dc[c_type]
|
|
if(!length(.))
|
|
return list(.)
|
|
|
|
/datum/proc/AddComponent(new_type, ...)
|
|
var/datum/component/nt = new_type
|
|
var/dm = initial(nt.dupe_mode)
|
|
var/dt = initial(nt.dupe_type)
|
|
|
|
var/datum/component/old_comp
|
|
var/datum/component/new_comp
|
|
|
|
if(ispath(nt))
|
|
if(nt == /datum/component)
|
|
CRASH("[nt] attempted instantiation!")
|
|
else
|
|
new_comp = nt
|
|
nt = new_comp.type
|
|
|
|
args[1] = src
|
|
|
|
if(dm != COMPONENT_DUPE_ALLOWED)
|
|
if(!dt)
|
|
old_comp = GetExactComponent(nt)
|
|
else
|
|
old_comp = GetComponent(dt)
|
|
if(old_comp)
|
|
switch(dm)
|
|
if(COMPONENT_DUPE_UNIQUE)
|
|
if(!new_comp)
|
|
new_comp = new nt(arglist(args))
|
|
if(!QDELETED(new_comp))
|
|
old_comp.InheritComponent(new_comp, TRUE)
|
|
QDEL_NULL(new_comp)
|
|
if(COMPONENT_DUPE_HIGHLANDER)
|
|
if(!new_comp)
|
|
new_comp = new nt(arglist(args))
|
|
if(!QDELETED(new_comp))
|
|
new_comp.InheritComponent(old_comp, FALSE)
|
|
QDEL_NULL(old_comp)
|
|
if(COMPONENT_DUPE_UNIQUE_PASSARGS)
|
|
if(!new_comp)
|
|
var/list/arguments = args.Copy(2)
|
|
old_comp.InheritComponent(null, TRUE, arguments)
|
|
else
|
|
old_comp.InheritComponent(new_comp, TRUE)
|
|
else if(!new_comp)
|
|
new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
|
|
else if(!new_comp)
|
|
new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
|
|
|
|
if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
|
|
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp)
|
|
return new_comp
|
|
return old_comp
|
|
|
|
/datum/proc/LoadComponent(component_type, ...)
|
|
. = GetComponent(component_type)
|
|
if(!.)
|
|
return AddComponent(arglist(args))
|
|
|
|
/datum/component/proc/RemoveComponent()
|
|
if(!parent)
|
|
return
|
|
var/datum/old_parent = parent
|
|
PreTransfer()
|
|
_RemoveFromParent()
|
|
parent = null
|
|
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
|
|
|
|
/datum/proc/TakeComponent(datum/component/target)
|
|
if(!target || target.parent == src)
|
|
return
|
|
if(target.parent)
|
|
target.RemoveComponent()
|
|
target.parent = src
|
|
var/result = target.PostTransfer()
|
|
switch(result)
|
|
if(COMPONENT_INCOMPATIBLE)
|
|
var/c_type = target.type
|
|
qdel(target)
|
|
CRASH("Incompatible [c_type] transfer attempt to a [type]!")
|
|
|
|
if(target == AddComponent(target))
|
|
target._JoinParent()
|
|
|
|
/datum/proc/TransferComponents(datum/target)
|
|
var/list/dc = datum_components
|
|
if(!dc)
|
|
return
|
|
var/comps = dc[/datum/component]
|
|
if(islist(comps))
|
|
for(var/datum/component/I in comps)
|
|
if(I.can_transfer)
|
|
target.TakeComponent(I)
|
|
else
|
|
var/datum/component/C = comps
|
|
if(C.can_transfer)
|
|
target.TakeComponent(comps)
|
|
|
|
/datum/component/ui_host()
|
|
return parent
|