diff --git a/code/datums/elements/plant_backfire.dm b/code/datums/elements/plant_backfire.dm index f753947e9ea..b9a2000ffe4 100644 --- a/code/datums/elements/plant_backfire.dm +++ b/code/datums/elements/plant_backfire.dm @@ -3,7 +3,7 @@ /// If a user is protected with something like leather gloves, they can handle them normally. /// If they're not protected properly, we invoke a callback on the user, harming or inconveniencing them. /datum/element/plant_backfire - element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH + element_flags = ELEMENT_BESPOKE id_arg_index = 2 /// Whether we stop the current action if backfire is triggered (EX: returning CANCEL_ATTACK_CHAIN) var/cancel_action = FALSE @@ -29,52 +29,67 @@ . = ..() UnregisterSignal(target, list(COMSIG_ITEM_PRE_ATTACK, COMSIG_ITEM_PICKUP, COMSIG_MOVABLE_PRE_THROW)) -/* +/** * Checks before we attack if we're okay to continue. * * source - our plant * user - the mob wielding our [source] */ -/datum/element/plant_backfire/proc/attack_safety_check(datum/source, atom/target, mob/user) +/datum/element/plant_backfire/proc/attack_safety_check(obj/item/source, atom/target, mob/user) SIGNAL_HANDLER - if(plant_safety_check(source, user)) + // Covers stuff like tk, since we aren't actually touching the plant. + if(!user.is_holding(source)) + return + if(!backfire(source, user)) return - - SEND_SIGNAL(source, COMSIG_PLANT_ON_BACKFIRE, user) - if(cancel_action) - return COMPONENT_CANCEL_ATTACK_CHAIN -/* + return cancel_action ? COMPONENT_CANCEL_ATTACK_CHAIN : NONE + +/** * Checks before we pick up the plant if we're okay to continue. * * source - our plant * user - the mob picking our [source] */ -/datum/element/plant_backfire/proc/pickup_safety_check(datum/source, mob/user) +/datum/element/plant_backfire/proc/pickup_safety_check(obj/item/source, mob/user) SIGNAL_HANDLER - if(plant_safety_check(source, user)) - return - SEND_SIGNAL(source, COMSIG_PLANT_ON_BACKFIRE, user) + backfire(source, user) -/* +/** * Checks before we throw the plant if we're okay to continue. * * source - our plant * thrower - the mob throwing our [source] */ -/datum/element/plant_backfire/proc/throw_safety_check(datum/source, list/arguments) +/datum/element/plant_backfire/proc/throw_safety_check(obj/item/source, list/arguments) SIGNAL_HANDLER - var/mob/living/thrower = arguments[4] // 4th arg = mob/thrower - if(plant_safety_check(source, thrower)) + var/mob/living/thrower = arguments[4] // the 4th arg = the mob throwing our item + if(!thrower.is_holding(source)) + return + if(!backfire(source, thrower)) return - SEND_SIGNAL(source, COMSIG_PLANT_ON_BACKFIRE, thrower) - if(cancel_action) - return COMPONENT_CANCEL_THROW -/* + return cancel_action ? COMPONENT_CANCEL_ATTACK_CHAIN : NONE + +/** + * The actual backfire occurs here. + * Checks if the user is able to safely handle the plant. + * If not, sends the backfire signal (meaning backfire will occur and be handled by one or multiple genes). + * + * Returns FALSE if the user was safe and no backfire occured. + * Returns TRUE if the user was not safe and a backfire actually happened. + */ +/datum/element/plant_backfire/proc/backfire(obj/item/plant, mob/user) + if(plant_safety_check(plant, user)) + return FALSE + + SEND_SIGNAL(plant, COMSIG_PLANT_ON_BACKFIRE, user) + return TRUE + +/** * Actually checks if our user is safely handling our plant. * * Checks for TRAIT_PLANT_SAFE, and returns TRUE if we have it. @@ -86,13 +101,10 @@ * * returns FALSE if none of the checks are successful. */ -/datum/element/plant_backfire/proc/plant_safety_check(datum/source, mob/living/carbon/user) +/datum/element/plant_backfire/proc/plant_safety_check(obj/item/plant, mob/living/carbon/user) if(!istype(user)) return TRUE - if(istype(source, /obj/item/tk_grab)) // since we aren't actually touching the plant - return TRUE - if(HAS_TRAIT(user, TRAIT_PLANT_SAFE)) return TRUE @@ -100,8 +112,7 @@ if(HAS_TRAIT(user, checked_trait)) return TRUE - var/obj/item/parent_item = source - var/obj/item/seeds/our_seed = parent_item.get_plant_seed() + var/obj/item/seeds/our_seed = plant.get_plant_seed() if(our_seed) for(var/checked_gene in extra_genes) if(!our_seed.get_gene(checked_gene)) diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm index 598f38d3adf..bc601ef167a 100644 --- a/code/datums/status_effects/debuffs/fire_stacks.dm +++ b/code/datums/status_effects/debuffs/fire_stacks.dm @@ -220,6 +220,7 @@ SEND_SIGNAL(owner, COMSIG_LIVING_IGNITED, owner) cache_stacks() update_overlay() + return TRUE /** * Handles mob extinguishing, should be the only way to set on_fire to FALSE diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm index 5ea7f604a15..7370e86f50a 100644 --- a/code/modules/hydroponics/unique_plant_genes.dm +++ b/code/modules/hydroponics/unique_plant_genes.dm @@ -38,6 +38,10 @@ name = "On Attack Trait" /// The multiplier we apply to the potency to calculate force. Set to 0 to not affect the force. var/force_multiplier = 0 + /// If TRUE, our plant will degrade in force every hit until diappearing. + var/degrades_after_hit = FALSE + /// When we fully degrade, what degraded off of us? + var/degradation_noun = "leaves" /datum/plant_gene/trait/attack/on_new_plant(obj/item/our_plant, newloc) . = ..() @@ -50,97 +54,101 @@ RegisterSignal(our_plant, COMSIG_ITEM_ATTACK, .proc/on_plant_attack) RegisterSignal(our_plant, COMSIG_ITEM_AFTERATTACK, .proc/after_plant_attack) +/// Signal proc for [COMSIG_ITEM_ATTACK] that allows for effects on attack +/datum/plant_gene/trait/attack/proc/on_plant_attack(obj/item/source, mob/living/target, mob/living/user) + SIGNAL_HANDLER + + INVOKE_ASYNC(src, .proc/attack_effect, source, target, user) + /* - * Plant effects ON attack. + * Effects done when we hit people with our plant, ON attack. + * Override on a per-plant basis. * * our_plant - our plant, that we're attacking with * user - the person who is attacking with the plant * target - the person who is attacked by the plant */ -/datum/plant_gene/trait/attack/proc/on_plant_attack(obj/item/our_plant, mob/living/target, mob/living/user) +/datum/plant_gene/trait/attack/proc/attack_effect(obj/item/our_plant, mob/living/target, mob/living/user) + return + +/// Signal proc for [COMSIG_ITEM_AFTERATTACK] that allows for effects after an attack is done +/datum/plant_gene/trait/attack/proc/after_plant_attack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters) SIGNAL_HANDLER + if(!proximity_flag) + return + + if(!ismovable(target)) + return + + if(isobj(target)) + var/obj/object_target = target + if(!(object_target.obj_flags & CAN_BE_HIT)) + return + + INVOKE_ASYNC(src, .proc/after_attack_effect, source, target, user) + /* - * Plant effects AFTER attack. + * Effects done when we hit people with our plant, AFTER the attack is done. + * Extend on a per-plant basis. * * our_plant - our plant, that we're attacking with * user - the person who is attacking with the plant * target - the atom which is attacked by the plant - * - * return TRUE if plant attack is acceptable, otherwise FALSE to early return subtypes. */ -/datum/plant_gene/trait/attack/proc/after_plant_attack(obj/item/our_plant, atom/target, mob/user, proximity_flag, click_parameters) - SIGNAL_HANDLER +/datum/plant_gene/trait/attack/proc/after_attack_effect(obj/item/our_plant, atom/target, mob/living/user) + SHOULD_CALL_PARENT(TRUE) - if(!proximity_flag) - return FALSE - return TRUE + if(!degrades_after_hit) + return + + // We probably hit something or someone. Reduce our force + if(our_plant.force > 0) + our_plant.force -= rand(1, (our_plant.force / 3) + 1) + return + + // When our force degrades to zero or below, we're all done + to_chat(user, span_warning("All the [degradation_noun] have fallen off [our_plant] from violent whacking!")) + qdel(our_plant) /// Novaflower's attack effects (sets people on fire) + degradation on attack /datum/plant_gene/trait/attack/novaflower_attack name = "Heated Petals" force_multiplier = 0.2 + degrades_after_hit = TRUE + degradation_noun = "petals" -/datum/plant_gene/trait/attack/novaflower_attack/on_plant_attack(obj/item/our_plant, mob/living/target, mob/living/user) - . = ..() - if(!.) +/datum/plant_gene/trait/attack/novaflower_attack/attack_effect(obj/item/our_plant, mob/living/target, mob/living/user) + if(!istype(target)) return var/obj/item/seeds/our_seed = our_plant.get_plant_seed() - to_chat(target, "You are lit on fire from the intense heat of [our_plant]!") - target.adjust_fire_stacks(our_seed.potency / 20) + to_chat(target, span_danger("You are lit on fire from the intense heat of [our_plant]!")) + target.adjust_fire_stacks(round(our_seed.potency / 20)) if(target.ignite_mob()) message_admins("[ADMIN_LOOKUPFLW(user)] set [ADMIN_LOOKUPFLW(target)] on fire with [our_plant] at [AREACOORD(user)]") log_game("[key_name(user)] set [key_name(target)] on fire with [our_plant] at [AREACOORD(user)]") our_plant.investigate_log("was used by [key_name(user)] to burn [key_name(target)] at [AREACOORD(user)]", INVESTIGATE_BOTANY) -/datum/plant_gene/trait/attack/novaflower_attack/after_plant_attack(obj/item/our_plant, atom/target, mob/user, proximity_flag, click_parameters) - . = ..() - if(!.) - return - - if(!ismovable(target)) - return - if(our_plant.force > 0) - our_plant.force -= rand(1, (our_plant.force / 3) + 1) - else - to_chat(user, "All the petals have fallen off [our_plant] from violent whacking!") - qdel(our_plant) - /// Sunflower's attack effect (shows cute text) /datum/plant_gene/trait/attack/sunflower_attack name = "Bright Petals" -/datum/plant_gene/trait/attack/sunflower_attack/after_plant_attack(obj/item/our_plant, atom/target, mob/user, proximity_flag, click_parameters) - . = ..() - if(!.) - return +/datum/plant_gene/trait/attack/sunflower_attack/after_attack_effect(obj/item/our_plant, atom/target, mob/user, proximity_flag, click_parameters) + if(ismob(target)) + var/mob/target_mob = target + user.visible_message("[user] smacks [target_mob] with [user.p_their()] [our_plant.name]! FLOWER POWER!", ignored_mobs = list(target_mob, user)) + if(target_mob != user) + to_chat(target_mob, "[user] smacks you with [our_plant]!FLOWER POWER!") + to_chat(user, "Your [our_plant.name]'s FLOWER POWER strikes [target_mob]!") - if(!ismob(target)) - return - var/mob/target_mob = target - user.visible_message("[user] smacks [target_mob] with [user.p_their()] [our_plant.name]! FLOWER POWER!", ignored_mobs = list(target_mob, user)) - if(target_mob != user) - to_chat(target_mob, "[user] smacks you with [our_plant]!FLOWER POWER!") - to_chat(user, "Your [our_plant.name]'s FLOWER POWER strikes [target_mob]!") + return ..() /// Normal nettle's force + degradation on attack /datum/plant_gene/trait/attack/nettle_attack name = "Sharpened Leaves" force_multiplier = 0.2 - -/datum/plant_gene/trait/attack/nettle_attack/after_plant_attack(obj/item/our_plant, atom/target, mob/user, proximity_flag, click_parameters) - . = ..() - if(!.) - return - - if(!ismovable(target)) - return - if(our_plant.force > 0) - our_plant.force -= rand(1, (our_plant.force / 3) + 1) - else - to_chat(user, "All the leaves have fallen off [our_plant] from violent whacking!") - qdel(our_plant) + degrades_after_hit = TRUE /// Deathnettle force + degradation on attack /datum/plant_gene/trait/attack/nettle_attack/death @@ -153,9 +161,9 @@ /// Whether our actions are cancelled when the backfire triggers. var/cancel_action_on_backfire = FALSE /// A list of extra traits to check to be considered safe. - var/traits_to_check + var/list/traits_to_check /// A list of extra genes to check to be considered safe. - var/genes_to_check + var/list/genes_to_check /datum/plant_gene/trait/backfire/on_new_plant(obj/item/our_plant, newloc) . = ..() @@ -163,16 +171,20 @@ return our_plant.AddElement(/datum/element/plant_backfire, cancel_action_on_backfire, traits_to_check, genes_to_check) - RegisterSignal(our_plant, COMSIG_PLANT_ON_BACKFIRE, .proc/backfire_effect) + RegisterSignal(our_plant, COMSIG_PLANT_ON_BACKFIRE, .proc/on_backfire) -/* - * The backfire effect. Override with plant-specific effects. - * - * user - the person who is carrying the plant - * our_plant - our plant +/// Signal proc for [COMSIG_PLANT_ON_BACKFIRE] that causes the backfire effect. +/datum/plant_gene/trait/backfire/proc/on_backfire(obj/item/source, mob/living/carbon/user) + SIGNAL_HANDLER + + INVOKE_ASYNC(src, .proc/backfire_effect, source, user) + +/** + * The actual backfire effect on the user. + * Override with plant-specific effects. */ /datum/plant_gene/trait/backfire/proc/backfire_effect(obj/item/our_plant, mob/living/carbon/user) - SIGNAL_HANDLER + return /// Rose's prick on backfire /datum/plant_gene/trait/backfire/rose_thorns @@ -180,18 +192,15 @@ traits_to_check = list(TRAIT_PIERCEIMMUNE) /datum/plant_gene/trait/backfire/rose_thorns/backfire_effect(obj/item/our_plant, mob/living/carbon/user) - . = ..() - var/obj/item/seeds/our_seed = our_plant.get_plant_seed() if(!our_seed.get_gene(/datum/plant_gene/trait/sticky) && prob(66)) - to_chat(user, "[our_plant]'s thorns nearly prick your hand. Best be careful.") + to_chat(user, span_danger("[our_plant]'s thorns nearly prick your hand. Best be careful.")) return - to_chat(user, "[our_plant]'s thorns prick your hand. Ouch.") + to_chat(user, span_danger("[our_plant]'s thorns prick your hand. Ouch.")) our_plant.investigate_log("rose-pricked [key_name(user)] at [AREACOORD(user)]", INVESTIGATE_BOTANY) var/obj/item/bodypart/affecting = user.get_active_hand() - if(affecting?.receive_damage(2)) - user.update_damage_overlays() + affecting?.receive_damage(2) /// Novaflower's hand burn on backfire /datum/plant_gene/trait/backfire/novaflower_heat @@ -199,26 +208,20 @@ cancel_action_on_backfire = TRUE /datum/plant_gene/trait/backfire/novaflower_heat/backfire_effect(obj/item/our_plant, mob/living/carbon/user) - . = ..() - - to_chat(user, "[our_plant] singes your bare hand!") + to_chat(user, span_danger("[our_plant] singes your bare hand!")) our_plant.investigate_log("self-burned [key_name(user)] for [our_plant.force] at [AREACOORD(user)]", INVESTIGATE_BOTANY) var/obj/item/bodypart/affecting = user.get_active_hand() - if(affecting?.receive_damage(0, our_plant.force, wound_bonus = CANT_WOUND)) - user.update_damage_overlays() + return affecting?.receive_damage(0, our_plant.force, wound_bonus = CANT_WOUND) /// Normal Nettle hannd burn on backfire /datum/plant_gene/trait/backfire/nettle_burn name = "Stinging Stem" /datum/plant_gene/trait/backfire/nettle_burn/backfire_effect(obj/item/our_plant, mob/living/carbon/user) - . = ..() - - to_chat(user, "[our_plant] burns your bare hand!") + to_chat(user, span_danger("[our_plant] burns your bare hand!")) our_plant.investigate_log("self-burned [key_name(user)] for [our_plant.force] at [AREACOORD(user)]", INVESTIGATE_BOTANY) var/obj/item/bodypart/affecting = user.get_active_hand() - if(affecting?.receive_damage(0, our_plant.force, wound_bonus = CANT_WOUND)) - user.update_damage_overlays() + return affecting?.receive_damage(0, our_plant.force, wound_bonus = CANT_WOUND) /// Deathnettle hand burn + stun on backfire /datum/plant_gene/trait/backfire/nettle_burn/death @@ -227,10 +230,11 @@ /datum/plant_gene/trait/backfire/nettle_burn/death/backfire_effect(obj/item/our_plant, mob/living/carbon/user) . = ..() + if(!. || prob(50)) + return - if(prob(50)) - user.Paralyze(100) - to_chat(user, "You are stunned by the powerful acids of [our_plant]!") + user.Paralyze(10 SECONDS) + to_chat(user, span_userdanger("You are stunned by the powerful acids of [our_plant]!")) /// Ghost-Chili heating up on backfire /datum/plant_gene/trait/backfire/chili_heat @@ -256,8 +260,6 @@ * user - the mob holding our plant */ /datum/plant_gene/trait/backfire/chili_heat/backfire_effect(obj/item/our_plant, mob/living/carbon/user) - . = ..() - held_mob = WEAKREF(user) START_PROCESSING(SSobj, src) @@ -296,11 +298,14 @@ genes_to_check = list(/datum/plant_gene/trait/squash) /datum/plant_gene/trait/backfire/bluespace/backfire_effect(obj/item/our_plant, mob/living/carbon/user) - . = ..() - if(prob(50)) - to_chat(user, "[our_plant] slips out of your hand!") - INVOKE_ASYNC(our_plant, /obj/item/.proc/attack_self, user) + return + + to_chat(user, span_danger("[our_plant] slips out of your hand!")) + + var/obj/item/seeds/our_seed = our_plant.get_plant_seed() + var/datum/plant_gene/trait/squash/squash_gene = our_seed.get_gene(/datum/plant_gene/trait/squash) + squash_gene.squash_plant(our_plant, user) /// Traits for plants that can be activated to turn into a mob. /datum/plant_gene/trait/mob_transformation @@ -347,8 +352,8 @@ return if(target != user) - to_chat(user, "[our_plant] is twitching and shaking, preventing you from feeding it to [target].") - to_chat(target, "[our_plant] is twitching and shaking, preventing you from eating it.") + to_chat(user, span_warning("[our_plant] is twitching and shaking, preventing you from feeding it to [target].")) + to_chat(target, span_warning("[our_plant] is twitching and shaking, preventing you from eating it.")) return COMPONENT_CANCEL_ATTACK_CHAIN /* @@ -365,10 +370,10 @@ return if(dangerous && HAS_TRAIT(user, TRAIT_PACIFISM)) - to_chat(user, "You decide not to awaken [our_plant]. It may be very dangerous!") + to_chat(user, span_notice("You decide not to awaken [our_plant]. It may be very dangerous!")) return - to_chat(user, "You begin to awaken [our_plant]...") + to_chat(user, span_notice("You begin to awaken [our_plant]...")) begin_awaken(our_plant, 3 SECONDS) our_plant.investigate_log("was awakened by [key_name(user)] at [AREACOORD(user)].", INVESTIGATE_BOTANY) @@ -382,7 +387,7 @@ SIGNAL_HANDLER if(!awakening && !isspaceturf(user.loc) && prob(25)) - to_chat(user, "[our_plant] begins to growl and shake!") + our_plant.visible_message(span_danger("[our_plant] begins to growl and shake!")) begin_awaken(our_plant, 1 SECONDS) our_plant.investigate_log("was awakened (via plant backfire) by [key_name(user)] at [AREACOORD(user)].", INVESTIGATE_BOTANY) @@ -416,7 +421,7 @@ spawned_simplemob.melee_damage_upper += round(our_seed.potency * mob_melee_multiplier) spawned_simplemob.move_to_delay -= round(our_seed.production * mob_speed_multiplier) our_plant.forceMove(our_plant.drop_location()) - spawned_mob.visible_message("[our_plant] growls as it suddenly awakens!") + spawned_mob.visible_message(span_notice("[our_plant] growls as it suddenly awakens!")) qdel(our_plant) /// Killer Tomato's transformation gene. @@ -505,7 +510,10 @@ our_plant.color = COLOR_RED playsound(our_plant, 'sound/effects/fuse.ogg', our_seed.potency, FALSE) - user.visible_message("[user] plucks the stem from [our_plant]!", "You pluck the stem from [our_plant], which begins to hiss loudly!") + user.visible_message( + span_warning("[user] plucks the stem from [our_plant]!"), + span_userdanger("You pluck the stem from [our_plant], which begins to hiss loudly!"), + ) log_bomber(user, "primed a", our_plant, "for detonation") detonate(our_plant) @@ -551,7 +559,10 @@ name = "Explosive Nature" /datum/plant_gene/trait/bomb_plant/potency_based/trigger_detonation(obj/item/our_plant, mob/living/user) - user.visible_message("[user] primes [our_plant]!", "You prime [our_plant]!") + user.visible_message( + span_warning("[user] primes [our_plant]!"), + span_userdanger("You prime [our_plant]!"), + ) log_bomber(user, "primed a", our_plant, "for detonation") var/obj/item/food/grown/grown_plant = our_plant diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 60a6ee4fa61..0465295c295 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -111,6 +111,7 @@ #include "mob_spawn.dm" #include "modsuit.dm" #include "modular_map_loader.dm" +#include "novaflower_burn.dm" #include "ntnetwork_tests.dm" #include "nuke_cinematic.dm" #include "objectives.dm" diff --git a/code/modules/unit_tests/novaflower_burn.dm b/code/modules/unit_tests/novaflower_burn.dm new file mode 100644 index 00000000000..c54cb9d6d15 --- /dev/null +++ b/code/modules/unit_tests/novaflower_burn.dm @@ -0,0 +1,37 @@ +/// Unit tests that the novaflower's unique genes function. +/datum/unit_test/novaflower_burn + +/datum/unit_test/novaflower_burn/Run() + var/mob/living/carbon/human/botanist = allocate(/mob/living/carbon/human) + var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human) + var/obj/item/grown/novaflower/weapon = allocate(/obj/item/grown/novaflower) + + TEST_ASSERT(weapon.force > 0, "[weapon] spawned with zero force.") + + // Keep this around for comparison later. + var/initial_force = weapon.force + // Start by having the novaflower equipped to an attacker's hands + // They are not wearing botany gloves (have plant protection), so they should take damage = the flower's force. + weapon.attack_hand(botanist) + TEST_ASSERT_EQUAL(botanist.get_active_held_item(), weapon, "The botanist failed to pick up [weapon].") + TEST_ASSERT_EQUAL(botanist.getFireLoss(), weapon.force, "The botanist picked up [weapon] with their bare hands, and took an incorrect amount of fire damage.") + + // Heal our attacker for easy comparison later + botanist.adjustFireLoss(-100) + // And give them the plant safe trait so we don't have to worry about attacks being cancelled + ADD_TRAIT(botanist, TRAIT_PLANT_SAFE, "unit_test") + + // Now, let's get a smack with the novaflower and see what happens. + weapon.melee_attack_chain(botanist, victim) + + TEST_ASSERT(botanist.getFireLoss() <= 0, "The botanist took fire damage from [weapon], even though they were plant safe.") + TEST_ASSERT_EQUAL(victim.getFireLoss(), initial_force, "The target took an incorrect amount of fire damage after being hit with [weapon].") + TEST_ASSERT(weapon.force < initial_force, "[weapon] didn't lose any force after an attack.") + TEST_ASSERT(victim.fire_stacks > 0, "[weapon] didn't apply any firestacks to the target after an attack.") + TEST_ASSERT(victim.on_fire, "[weapon] didn't set the target on fire after an attack.") + + // Lastly we should check that degredation to zero works. + weapon.force = 0 + weapon.melee_attack_chain(botanist, victim) + + TEST_ASSERT(QDELETED(weapon), "[weapon] wasn't deleted after hitting someone with zero force.")