diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm index eb5a84456fe..7c7181afc83 100644 --- a/code/modules/surgery/organs/external/tails.dm +++ b/code/modules/surgery/organs/external/tails.dm @@ -33,7 +33,7 @@ /obj/item/organ/external/tail/Remove(mob/living/carbon/organ_owner, special, moving) if(wag_flags & WAG_WAGGING) - wag(FALSE) + wag(organ_owner, start = FALSE) return ..() @@ -46,40 +46,42 @@ organ_owner.add_mood_event("tail_lost", /datum/mood_event/tail_lost) organ_owner.add_mood_event("tail_balance_lost", /datum/mood_event/tail_balance_lost) - -/obj/item/organ/external/tail/proc/wag(mob/user, start = TRUE, stop_after = 0) +/obj/item/organ/external/tail/proc/wag(mob/living/carbon/organ_owner, start = TRUE, stop_after = 0) if(!(wag_flags & WAG_ABLE)) return if(start) - start_wag() - if(stop_after) - addtimer(CALLBACK(src, PROC_REF(wag), FALSE), stop_after, TIMER_STOPPABLE|TIMER_DELETE_ME) + if(start_wag(organ_owner) && stop_after) + addtimer(CALLBACK(src, PROC_REF(wag), organ_owner, FALSE), stop_after, TIMER_STOPPABLE|TIMER_DELETE_ME) else - stop_wag() - owner.update_body_parts() + stop_wag(organ_owner) ///We need some special behaviour for accessories, wrapped here so we can easily add more interactions later -/obj/item/organ/external/tail/proc/start_wag() +/obj/item/organ/external/tail/proc/start_wag(mob/living/carbon/organ_owner) + if(wag_flags & WAG_WAGGING) // we are already wagging + return FALSE + if(organ_owner.stat == DEAD || organ_owner != owner) // no wagging when owner is dead or tail has been disembodied + return FALSE + var/datum/bodypart_overlay/mutant/tail/accessory = bodypart_overlay wag_flags |= WAG_WAGGING accessory.wagging = TRUE - // SKYRAT EDIT ADDITION START - SOON TO BE UPSTREAM - if(owner) - owner.update_body_parts() - RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(stop_wag)) - // SKYRAT EDIT ADDITION END + organ_owner.update_body_parts() + RegisterSignal(organ_owner, COMSIG_LIVING_DEATH, PROC_REF(stop_wag)) + return TRUE ///We need some special behaviour for accessories, wrapped here so we can easily add more interactions later -/obj/item/organ/external/tail/proc/stop_wag() +/obj/item/organ/external/tail/proc/stop_wag(mob/living/carbon/organ_owner) + SIGNAL_HANDLER + var/datum/bodypart_overlay/mutant/tail/accessory = bodypart_overlay wag_flags &= ~WAG_WAGGING accessory.wagging = FALSE - // SKYRAT EDIT ADDITION START - SOON TO BE UPSTREAM - if(owner) - owner.update_body_parts() - UnregisterSignal(owner, COMSIG_LIVING_DEATH) - // SKYRAT EDIT ADDITION END + if(isnull(organ_owner)) + return + + organ_owner.update_body_parts() + UnregisterSignal(organ_owner, COMSIG_LIVING_DEATH) ///Tail parent type (which is MONKEEEEEEEEEEE by default), with wagging functionality /datum/bodypart_overlay/mutant/tail diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index db33db6572a..c14f807aa19 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -255,6 +255,7 @@ #include "subsystem_init.dm" #include "suit_storage_icons.dm" #include "surgeries.dm" +#include "tail_wag.dm" #include "teleporters.dm" #include "tgui_create_message.dm" #include "timer_sanity.dm" diff --git a/code/modules/unit_tests/tail_wag.dm b/code/modules/unit_tests/tail_wag.dm new file mode 100644 index 00000000000..0d828557953 --- /dev/null +++ b/code/modules/unit_tests/tail_wag.dm @@ -0,0 +1,90 @@ +/// Tests to make sure tail wagging behaves as expected +/datum/unit_test/tail_wag + // used by the stop_after test + var/timer_finished = FALSE + +/datum/unit_test/tail_wag/Run() + var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent) + var/obj/item/organ/external/tail/cat/dummy_tail = allocate(/obj/item/organ/external/tail/cat) + dummy_tail.Insert(dummy, special = TRUE, drop_if_replaced = FALSE) + + // SANITY TEST + + // start wagging + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE) + if(!(dummy_tail.wag_flags & WAG_WAGGING)) + TEST_FAIL("Tail did not start wagging when it should have!") + + // stop wagging + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, FALSE) + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("Tail did not stop wagging when it should have!") + + // TESTING WAG_ABLE FLAG + + // flip the wag flag to unwaggable + dummy_tail.wag_flags &= ~WAG_ABLE + + // try to wag it again + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE) + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("Tail should not have the ability to wag, yet it did!") + + // flip the wag flag to waggable again + dummy_tail.wag_flags |= WAG_ABLE + + // start wagging again + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE) + if(!(dummy_tail.wag_flags & WAG_WAGGING)) + TEST_FAIL("Tail did not start wagging when it should have!") + + // TESTING STOP_AFTER + + // stop wagging + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, FALSE) + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("Tail did not stop wagging when it should have!") + + // start wagging, stop after 0.1 seconds + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE, 0.1 SECONDS) + // because timers are a pain + addtimer(VARSET_CALLBACK(src, timer_finished, TRUE), 0.2 SECONDS) + if(!(dummy_tail.wag_flags & WAG_WAGGING)) + TEST_FAIL("Tail did not start wagging when it should have!") + + UNTIL(timer_finished) // wait a little bit + + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("Tail was supposed to stop wagging on its own after 0.1 seconds but it did not!") + + // TESTING TAIL REMOVAL + + // remove the tail + dummy_tail.Remove(dummy, special = TRUE) + + // check if tail is still wagging after being removed + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("Tail was still wagging after being removed!") + + // try to wag the removed tail + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE) + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("A disembodied tail was able to start wagging!") + + // TESTING MOB DEATH + + // put it back and start wagging again + dummy_tail.Insert(dummy, special = TRUE, drop_if_replaced = FALSE) + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE) + if(!(dummy_tail.wag_flags & WAG_WAGGING)) + TEST_FAIL("Tail did not start wagging when it should have!") + + // kill the mob, see if it stops wagging + dummy.adjustBruteLoss(9001) + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("A mob's tail was still wagging after being killed!") + + // check if we are still able to wag the tail after death + SEND_SIGNAL(dummy, COMSIG_ORGAN_WAG_TAIL, TRUE) + if(dummy_tail.wag_flags & WAG_WAGGING) + TEST_FAIL("A dead mob was able to wag their tail!")