diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 1c4fe8344c17..f11ebaa7e001 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -7,6 +7,7 @@ #define GET_COMPONENT(varname, path) GET_COMPONENT_FROM(varname, path, src) #define COMPONENT_INCOMPATIBLE 1 +#define COMPONENT_NOTRANSFER 2 // How multiple components of the exact same type are handled in the same datum diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 6822525a61da..f23d725aac65 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -2,6 +2,10 @@ 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 @@ -154,7 +158,7 @@ return /datum/component/proc/PostTransfer() - return + 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 @@ -281,10 +285,13 @@ if(target.parent) target.RemoveComponent() target.parent = src - if(target.PostTransfer() == COMPONENT_INCOMPATIBLE) - var/c_type = target.type - qdel(target) - CRASH("Incompatible [c_type] transfer attempt to a [type]!") + 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() @@ -294,10 +301,13 @@ return var/comps = dc[/datum/component] if(islist(comps)) - for(var/I in comps) - target.TakeComponent(I) + for(var/datum/component/I in comps) + if(I.can_transfer) + target.TakeComponent(I) else - target.TakeComponent(comps) + var/datum/component/C = comps + if(C.can_transfer) + target.TakeComponent(comps) /datum/component/ui_host() return parent diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm index e5547ee0ecd3..515ca650c78f 100644 --- a/code/datums/components/decal.dm +++ b/code/datums/components/decal.dm @@ -1,6 +1,6 @@ /datum/component/decal dupe_mode = COMPONENT_DUPE_ALLOWED - + can_transfer = TRUE var/cleanable var/description var/mutable_appearance/pic @@ -72,4 +72,4 @@ qdel(src) /datum/component/decal/proc/examine(datum/source, mob/user) - to_chat(user, description) \ No newline at end of file + to_chat(user, description) diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm index 26d6caac5e2d..f5a82ce5c4aa 100644 --- a/code/datums/components/forensics.dm +++ b/code/datums/components/forensics.dm @@ -1,5 +1,6 @@ /datum/component/forensics dupe_mode = COMPONENT_DUPE_UNIQUE + can_transfer = TRUE var/list/fingerprints //assoc print = print var/list/hiddenprints //assoc ckey = realname/gloves/ckey var/list/blood_DNA //assoc dna = bloodtype @@ -21,8 +22,18 @@ blood_DNA = new_blood_DNA fibers = new_fibers check_blood() + +/datum/component/forensics/RegisterWithParent() + check_blood() RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_act) +/datum/component/forensics/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_COMPONENT_CLEAN_ACT)) + +/datum/component/forensics/PostTransfer() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + /datum/component/forensics/proc/wipe_fingerprints() fingerprints = null return TRUE diff --git a/code/datums/components/lockon_aiming.dm b/code/datums/components/lockon_aiming.dm index f944c6c5e430..bbdbb7dc6628 100644 --- a/code/datums/components/lockon_aiming.dm +++ b/code/datums/components/lockon_aiming.dm @@ -237,6 +237,3 @@ LOCKON_RANGING_BREAK_CHECK cd++ CHECK_TICK - -/datum/component/lockon_aiming/PostTransfer(datum/new_parent) - return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/mirage_border.dm b/code/datums/components/mirage_border.dm index f435a21f49ed..d5d32010cd19 100644 --- a/code/datums/components/mirage_border.dm +++ b/code/datums/components/mirage_border.dm @@ -1,4 +1,5 @@ /datum/component/mirage_border + can_transfer = TRUE var/obj/effect/abstract/mirage_holder/holder /datum/component/mirage_border/Initialize(turf/target, direction, range=world.view) diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm index f3993e0678bd..b3de6f863fbb 100644 --- a/code/datums/components/orbiter.dm +++ b/code/datums/components/orbiter.dm @@ -1,4 +1,5 @@ /datum/component/orbiter + can_transfer = TRUE dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS var/list/orbiters var/datum/callback/orbiter_spy @@ -153,4 +154,4 @@ /atom/proc/transfer_observers_to(atom/target) if(!orbiters || !istype(target) || !get_turf(target) || target == src) return - target.TakeComponent(orbiters) \ No newline at end of file + target.TakeComponent(orbiters) diff --git a/code/datums/components/rotation.dm b/code/datums/components/rotation.dm index e82d8c2ed6b4..76c3a455586a 100644 --- a/code/datums/components/rotation.dm +++ b/code/datums/components/rotation.dm @@ -44,15 +44,17 @@ if(src.rotation_flags & ROTATION_CLOCKWISE) default_rotation_direction = ROTATION_CLOCKWISE - if(src.rotation_flags & ROTATION_ALTCLICK) +/datum/component/simple_rotation/proc/add_signals() + if(rotation_flags & ROTATION_ALTCLICK) RegisterSignal(parent, COMSIG_CLICK_ALT, .proc/HandRot) RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/ExamineMessage) - if(src.rotation_flags & ROTATION_WRENCH) + if(rotation_flags & ROTATION_WRENCH) RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/WrenchRot) - if(src.rotation_flags & ROTATION_VERBS) +/datum/component/simple_rotation/proc/add_verbs() + if(rotation_flags & ROTATION_VERBS) var/atom/movable/AM = parent - if(src.rotation_flags & ROTATION_FLIP) + if(rotation_flags & ROTATION_FLIP) AM.verbs += /atom/movable/proc/simple_rotate_flip if(src.rotation_flags & ROTATION_CLOCKWISE) AM.verbs += /atom/movable/proc/simple_rotate_clockwise @@ -66,11 +68,30 @@ AM.verbs -= /atom/movable/proc/simple_rotate_clockwise AM.verbs -= /atom/movable/proc/simple_rotate_counterclockwise -/datum/component/simple_rotation/Destroy() +/datum/component/simple_rotation/proc/remove_signals() + UnregisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_PARENT_EXAMINE, COMSIG_PARENT_ATTACKBY)) + +/datum/component/simple_rotation/RegisterWithParent() + add_verbs() + add_signals() + . = ..() + +/datum/component/simple_rotation/PostTransfer() + //Because of the callbacks which we don't track cleanly we can't transfer this + //item cleanly, better to let the new of the new item create a new rotation datum + //instead (there's no real state worth transferring) + return COMPONENT_NOTRANSFER + +/datum/component/simple_rotation/UnregisterFromParent() remove_verbs() + remove_signals() + . = ..() + +/datum/component/simple_rotation/Destroy() QDEL_NULL(can_user_rotate) QDEL_NULL(can_be_rotated) QDEL_NULL(after_rotation) + //Signals + verbs removed via UnRegister . = ..() /datum/component/simple_rotation/RemoveComponent() diff --git a/code/datums/components/spill.dm b/code/datums/components/spill.dm index 9670d7993dfc..ae41590ddb16 100644 --- a/code/datums/components/spill.dm +++ b/code/datums/components/spill.dm @@ -2,6 +2,7 @@ // Yes this exists purely for the spaghetti meme /datum/component/spill + can_transfer = TRUE var/preexisting_item_flags var/list/droptext @@ -53,4 +54,4 @@ if(droptext) fool.visible_message(arglist(droptext)) if(dropsound) - playsound(master, pick(dropsound), 30) \ No newline at end of file + playsound(master, pick(dropsound), 30) diff --git a/code/datums/components/storage/concrete/_concrete.dm b/code/datums/components/storage/concrete/_concrete.dm index 4d7e8bddc5d0..709a21c0d00d 100644 --- a/code/datums/components/storage/concrete/_concrete.dm +++ b/code/datums/components/storage/concrete/_concrete.dm @@ -4,6 +4,7 @@ // /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement /datum/component/storage/concrete + can_transfer = TRUE var/drop_all_on_deconstruct = TRUE var/drop_all_on_destroy = FALSE var/transfer_contents_on_component_transfer = FALSE diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm index b7a88badd319..cdda612446fc 100644 --- a/code/datums/components/wet_floor.dm +++ b/code/datums/components/wet_floor.dm @@ -1,5 +1,6 @@ /datum/component/wet_floor dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + can_transfer = TRUE var/highest_strength = TURF_DRY var/lube_flags = NONE //why do we have this? var/list/time_left_list //In deciseconds. @@ -26,14 +27,19 @@ if(!isopenturf(parent)) return COMPONENT_INCOMPATIBLE add_wet(strength, duration_minimum, duration_add, duration_maximum) - RegisterSignal(parent, COMSIG_TURF_IS_WET, .proc/is_wet) - RegisterSignal(parent, COMSIG_TURF_MAKE_DRY, .proc/dry) permanent = _permanent if(!permanent) START_PROCESSING(SSwet_floors, src) addtimer(CALLBACK(src, .proc/gc, TRUE), 1) //GC after initialization. last_process = world.time +/datum/component/wet_floor/RegisterWithParent() + RegisterSignal(parent, COMSIG_TURF_IS_WET, .proc/is_wet) + RegisterSignal(parent, COMSIG_TURF_MAKE_DRY, .proc/dry) + +/datum/component/wet_floor/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_TURF_IS_WET, COMSIG_TURF_MAKE_DRY)) + /datum/component/wet_floor/Destroy() STOP_PROCESSING(SSwet_floors, src) var/turf/T = parent @@ -136,12 +142,19 @@ /datum/component/wet_floor/PreTransfer() var/turf/O = parent O.cut_overlay(current_overlay) + //That turf is no longer slippery, we're out of here + //Slippery components don't transfer due to callbacks + qdel(O.GetComponent(/datum/component/slippery)) /datum/component/wet_floor/PostTransfer() if(!isopenturf(parent)) return COMPONENT_INCOMPATIBLE var/turf/T = parent T.add_overlay(current_overlay) + //Make sure to add/update any slippery component on the new turf (update_flags calls LoadComponent) + update_flags() + + //NB it's possible we get deleted after this, due to inherit /datum/component/wet_floor/proc/add_wet(type, duration_minimum = 0, duration_add = 0, duration_maximum = MAXIMUM_WET_TIME, _permanent = FALSE) var/static/list/allowed_types = list(TURF_WET_WATER, TURF_WET_LUBE, TURF_WET_ICE, TURF_WET_PERMAFROST)