Fixes Novaflowers not lighting people on fire, again. Unit tests it. Cleans up some unique plant genes stuff too. (#67597)

This commit is contained in:
MrMelbert
2022-06-11 04:04:32 -05:00
committed by GitHub
parent 6e75c07b75
commit c34afedcfc
5 changed files with 182 additions and 121 deletions
+105 -94
View File
@@ -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, "<span class='danger'>You are lit on fire from the intense heat of [our_plant]!</span>")
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, "<span class='warning'>All the petals have fallen off [our_plant] from violent whacking!</span>")
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("<font color='green'>[user] smacks [target_mob] with [user.p_their()] [our_plant.name]! <font color='orange'><b>FLOWER POWER!</b></font></font>", ignored_mobs = list(target_mob, user))
if(target_mob != user)
to_chat(target_mob, "<font color='green'>[user] smacks you with [our_plant]!<font color='orange'><b>FLOWER POWER!</b></font></font>")
to_chat(user, "<font color='green'>Your [our_plant.name]'s <font color='orange'><b>FLOWER POWER</b></font> strikes [target_mob]!</font>")
if(!ismob(target))
return
var/mob/target_mob = target
user.visible_message("<font color='green'>[user] smacks [target_mob] with [user.p_their()] [our_plant.name]! <font color='orange'><b>FLOWER POWER!</b></font></font>", ignored_mobs = list(target_mob, user))
if(target_mob != user)
to_chat(target_mob, "<font color='green'>[user] smacks you with [our_plant]!<font color='orange'><b>FLOWER POWER!</b></font></font>")
to_chat(user, "<font color='green'>Your [our_plant.name]'s <font color='orange'><b>FLOWER POWER</b></font> strikes [target_mob]!</font>")
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, "<span class='warning'>All the leaves have fallen off [our_plant] from violent whacking!</span>")
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, "<span class='danger'>[our_plant]'s thorns nearly prick your hand. Best be careful.</span>")
to_chat(user, span_danger("[our_plant]'s thorns nearly prick your hand. Best be careful."))
return
to_chat(user, "<span class='danger'>[our_plant]'s thorns prick your hand. Ouch.</span>")
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, "<span class='danger'>[our_plant] singes your bare hand!</span>")
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, "<span class='danger'>[our_plant] burns your bare hand!</span>")
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, "<span class='userdanger'>You are stunned by the powerful acids of [our_plant]!</span>")
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, "<span class='danger'>[our_plant] slips out of your hand!</span>")
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, "<span class='warning'>[our_plant] is twitching and shaking, preventing you from feeding it to [target].</span>")
to_chat(target, "<span class='warning'>[our_plant] is twitching and shaking, preventing you from eating it.</span>")
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, "<span class='notice'>You decide not to awaken [our_plant]. It may be very dangerous!</span>")
to_chat(user, span_notice("You decide not to awaken [our_plant]. It may be very dangerous!"))
return
to_chat(user, "<span class='notice'>You begin to awaken [our_plant]...</span>")
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, "<span class='danger'>[our_plant] begins to growl and shake!</span>")
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("<span class='notice'>[our_plant] growls as it suddenly awakens!</span>")
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("<span class='warning'>[user] plucks the stem from [our_plant]!</span>", "<span class='userdanger'>You pluck the stem from [our_plant], which begins to hiss loudly!</span>")
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("<span class='warning'>[user] primes [our_plant]!</span>", "<span class='userdanger'>You prime [our_plant]!</span>")
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
+1
View File
@@ -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"
@@ -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.")