From 7b10eb45bcb8a7a68bdf91fdd9afa025e3482481 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Wed, 14 Dec 2022 01:57:58 -0600 Subject: [PATCH] Fixes cursed duffelbag's permanent curse (again), unit tests it. (#71969) ## About The Pull Request Curse of hunger did some funky stuff by checking for `slot_equipment_priority` (which ONLY BUCKETS use) and registering certain signals based on that The signals they were using instead didn't pass the unequipper, so the curse never got removed on unequip. Replaced them with just equip and drop, as equipped and dropped work just fine for it. Unit tests this. ## Why It's Good For The Game Infinite curse of clumsy and pacifism is kinda bad ## Changelog :cl: Melbert fix: Dufflebag Curse no longer lasts forever after the bag is destroyed. fix: Dufflebag Cursing someone already afflicted properly doesn't try to add the curse again /:cl: --- code/__DEFINES/traits.dm | 3 -- code/datums/components/curse_of_hunger.dm | 53 ++++++++----------- code/datums/components/fantasy/suffixes.dm | 3 +- code/game/objects/items/storage/backpack.dm | 1 - .../spell_types/touch/duffelbag_curse.dm | 4 +- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/hunger_curse.dm | 18 +++++++ 7 files changed, 43 insertions(+), 40 deletions(-) create mode 100644 code/modules/unit_tests/hunger_curse.dm diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 44cd1141938..7e0303103b4 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -337,8 +337,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_CAN_SIGN_ON_COMMS "can_sign_on_comms" /// nobody can use martial arts on this mob #define TRAIT_MARTIAL_ARTS_IMMUNE "martial_arts_immune" -/// You've been cursed with a living duffelbag, and can't have more added -#define TRAIT_DUFFEL_CURSE_PROOF "duffel_cursed" /// Immune to being afflicted by time stop (spell) #define TRAIT_TIME_STOP_IMMUNE "time_stop_immune" /// Revenants draining you only get a very small benefit. @@ -1011,4 +1009,3 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define SPEAKING_FROM_TONGUE "tongue" ///trait source that sign language should use #define SPEAKING_FROM_HANDS "hands" - diff --git a/code/datums/components/curse_of_hunger.dm b/code/datums/components/curse_of_hunger.dm index 1d54f629772..8912b704662 100644 --- a/code/datums/components/curse_of_hunger.dm +++ b/code/datums/components/curse_of_hunger.dm @@ -32,16 +32,15 @@ . = ..() var/obj/item/cursed_item = parent RegisterSignal(cursed_item, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) - //checking slot_equipment_priority is the better way to decide if it should be an equip-curse (alternative being if it has slot_flags) - //because it needs to know where to equip to (and stuff like buckets and cones can be on_pickup curses despite having slots to equip to) - if(cursed_item.slot_equipment_priority) - RegisterSignal(cursed_item, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip)) - else - RegisterSignal(cursed_item, COMSIG_ITEM_PICKUP, PROC_REF(on_pickup)) + RegisterSignal(cursed_item, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip)) /datum/component/curse_of_hunger/UnregisterFromParent() . = ..() - UnregisterSignal(parent, list(COMSIG_PARENT_EXAMINE, COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_POST_UNEQUIP, COMSIG_ITEM_PICKUP, COMSIG_ITEM_DROPPED)) + UnregisterSignal(parent, list( + COMSIG_PARENT_EXAMINE, + COMSIG_ITEM_EQUIPPED, + COMSIG_ITEM_DROPPED, + )) ///signal called on parent being examined /datum/component/curse_of_hunger/proc/on_examine(datum/source, mob/user, list/examine_list) @@ -57,23 +56,15 @@ /datum/component/curse_of_hunger/proc/on_equip(datum/source, mob/equipper, slot) SIGNAL_HANDLER var/obj/item/at_least_item = parent - if(!(at_least_item.slot_flags & slot)) + // Items with no slot flags curse on pickup (because hand slot) + if(at_least_item.slot_flags && !(at_least_item.slot_flags & slot)) return the_curse_begins(equipper) -///signal called from a successful unequip of parent -/datum/component/curse_of_hunger/proc/on_unequip(mob/living/unequipper, force, atom/newloc, no_move, invdrop, silent) - SIGNAL_HANDLER - the_curse_ends(unequipper) - -///signal called from picking up parent -/datum/component/curse_of_hunger/proc/on_pickup(datum/source, mob/grabber) - SIGNAL_HANDLER - the_curse_begins(grabber) - ///signal called from dropping parent /datum/component/curse_of_hunger/proc/on_drop(datum/source, mob/dropper) SIGNAL_HANDLER + the_curse_ends(dropper) /datum/component/curse_of_hunger/proc/the_curse_begins(mob/cursed) @@ -85,27 +76,24 @@ ADD_TRAIT(cursed, TRAIT_PACIFISM, CURSED_ITEM_TRAIT(cursed_item.type)) if(add_dropdel) cursed_item.item_flags |= DROPDEL - return - if(cursed_item.slot_equipment_priority) - RegisterSignal(cursed_item, COMSIG_ITEM_POST_UNEQUIP, PROC_REF(on_unequip)) - else - RegisterSignal(cursed_item, COMSIG_ITEM_DROPPED, PROC_REF(on_drop)) + + RegisterSignal(cursed_item, COMSIG_ITEM_DROPPED, PROC_REF(on_drop)) /datum/component/curse_of_hunger/proc/the_curse_ends(mob/uncursed) - var/obj/item/at_least_item = parent + var/obj/item/cursed_item = parent STOP_PROCESSING(SSobj, src) - REMOVE_TRAIT(parent, TRAIT_NODROP, CURSED_ITEM_TRAIT(parent.type)) - REMOVE_TRAIT(uncursed, TRAIT_CLUMSY, CURSED_ITEM_TRAIT(at_least_item.type)) - REMOVE_TRAIT(uncursed, TRAIT_PACIFISM, CURSED_ITEM_TRAIT(at_least_item.type)) + REMOVE_TRAIT(cursed_item, TRAIT_NODROP, CURSED_ITEM_TRAIT(cursed_item.type)) + REMOVE_TRAIT(uncursed, TRAIT_CLUMSY, CURSED_ITEM_TRAIT(cursed_item.type)) + REMOVE_TRAIT(uncursed, TRAIT_PACIFISM, CURSED_ITEM_TRAIT(cursed_item.type)) //remove either one of the signals that could have called this proc - UnregisterSignal(parent, list(COMSIG_ITEM_POST_UNEQUIP, COMSIG_ITEM_DROPPED)) + UnregisterSignal(cursed_item, COMSIG_ITEM_DROPPED) - var/turf/vomit_turf = get_turf(at_least_item) + var/turf/vomit_turf = get_turf(cursed_item) playsound(vomit_turf, 'sound/effects/splat.ogg', 50, TRUE) new /obj/effect/decal/cleanable/vomit(vomit_turf) - uncursed.dropItemToGround(at_least_item, force = TRUE) - if(!QDELETED(at_least_item)) //gives a head start for the person to get away from the cursed item before it begins hunting again! + uncursed.dropItemToGround(cursed_item, force = TRUE) + if(!QDELING(cursed_item)) //gives a head start for the person to get away from the cursed item before it begins hunting again! addtimer(CALLBACK(src, PROC_REF(seek_new_target)), 10 SECONDS) ///proc called after a timer to awaken the AI in the cursed item if it doesn't have a target already. @@ -166,3 +154,6 @@ cursed.visible_message(span_danger("[cursed_item] bites [cursed]!"), span_userdanger("[cursed_item] bites you to sate [cursed_item.p_their()] hunger!")) cursed.apply_damage(60, BRUTE, BODY_ZONE_CHEST, wound_bonus = -20, bare_wound_bonus = 20) current_health = min(current_health + 1, max_health) + +#undef HUNGER_THRESHOLD_WARNING +#undef HUNGER_THRESHOLD_TRY_EATING diff --git a/code/datums/components/fantasy/suffixes.dm b/code/datums/components/fantasy/suffixes.dm index 1880c0612c2..11b232f3ede 100644 --- a/code/datums/components/fantasy/suffixes.dm +++ b/code/datums/components/fantasy/suffixes.dm @@ -203,8 +203,7 @@ var/filter_color = "#8a0c0ca1" //clarified args var/new_name = pick(", eternally hungry", " of the glutton", " cursed with hunger", ", consumer of all", " of the feast") master.AddElement(/datum/element/curse_announcement, "[master] is cursed with the curse of hunger!", filter_color, new_name, comp) - var/add_dropdel = FALSE //clarified boolean - comp.appliedComponents += master.AddComponent(/datum/component/curse_of_hunger, add_dropdel) + comp.appliedComponents += master.AddComponent(/datum/component/curse_of_hunger) return newName //no spoilers! /datum/fantasy_affix/curse_of_hunger/remove(datum/component/fantasy/comp) diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index b39edcb7a2d..e7bb13b148a 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -361,7 +361,6 @@ icon_state = "duffel-curse" inhand_icon_state = "duffel-curse" slowdown = 2 - item_flags = DROPDEL max_integrity = 100 /obj/item/storage/backpack/duffelbag/cursed/Initialize(mapload) diff --git a/code/modules/spells/spell_types/touch/duffelbag_curse.dm b/code/modules/spells/spell_types/touch/duffelbag_curse.dm index 1ca5d0e1ffc..8725d7daac7 100644 --- a/code/modules/spells/spell_types/touch/duffelbag_curse.dm +++ b/code/modules/spells/spell_types/touch/duffelbag_curse.dm @@ -41,7 +41,7 @@ victim.Knockdown(5 SECONDS) // If someone's already cursed, don't try to give them another - if(HAS_TRAIT(victim, TRAIT_DUFFEL_CURSE_PROOF)) + if(istype(victim.back, /obj/item/storage/backpack/duffelbag/cursed)) to_chat(caster, span_warning("The burden of [victim]'s duffel bag becomes too much, shoving them to the floor!")) to_chat(victim, span_warning("The weight of this bag becomes overburdening!")) return TRUE @@ -53,8 +53,6 @@ span_danger("You feel something attaching itself to you, and a strong desire to discuss your [pick(elaborate_backstory)] at length!"), ) - // This duffelbag is now cuuuurrrsseed! Equip it on them - ADD_TRAIT(conjured_duffel, TRAIT_DUFFEL_CURSE_PROOF, CURSED_ITEM_TRAIT(conjured_duffel.name)) conjured_duffel.pickup(victim) conjured_duffel.forceMove(victim) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 50cc1f08586..a2eb5719b67 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -116,6 +116,7 @@ #include "heretic_rituals.dm" #include "holidays.dm" #include "human_through_recycler.dm" +#include "hunger_curse.dm" #include "hydroponics_extractor_storage.dm" #include "hydroponics_harvest.dm" #include "hydroponics_self_mutations.dm" diff --git a/code/modules/unit_tests/hunger_curse.dm b/code/modules/unit_tests/hunger_curse.dm new file mode 100644 index 00000000000..6c30ac4663d --- /dev/null +++ b/code/modules/unit_tests/hunger_curse.dm @@ -0,0 +1,18 @@ +/// Tests that the curse of hunger, from [/datum/component/curse_of_hunger], +/// is properly added when equipped to the correct slot and removed when dropped / deleted +/datum/unit_test/hunger_curse + +/datum/unit_test/hunger_curse/Run() + var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent) + var/obj/item/storage/backpack/duffelbag/cursed/cursed_bag = allocate(/obj/item/storage/backpack/duffelbag/cursed) + + dummy.equip_to_appropriate_slot(cursed_bag) + + TEST_ASSERT(HAS_TRAIT(cursed_bag, TRAIT_NODROP), "The cursed bag wasn't NODROP after being cursed!") + TEST_ASSERT(HAS_TRAIT(dummy, TRAIT_CLUMSY), "Dummy wasn't clumsy after being cursed!") + TEST_ASSERT(HAS_TRAIT(dummy, TRAIT_PACIFISM), "Dummy wasn't a pacifist after being cursed!") + + QDEL_NULL(cursed_bag) + + TEST_ASSERT(!HAS_TRAIT(dummy, TRAIT_CLUMSY), "Dummy was still clumsy after being cured of its curse!") + TEST_ASSERT(!HAS_TRAIT(dummy, TRAIT_PACIFISM), "Dummy was still a pacifist after being cured of its curse!")