mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
## About The Pull Request Refactors status effects to track their durations and tick intervals using counters. In effect, [var/duration] now directly refers to how many deciseconds are left on the status effect. I've also moved the old [var/tick_interval] [world.time] implementation to a tick-based [var/time_until_next_tick] counter. There are a couple, less noteworthy changes in here as well. The main one is that there was an unused bit of bloat code for setting tick intervals based on a random lower and upper threshold, but that can be done in tick() now so it's completely redundant, and I thus removed it entirely. That makes parts of [proc/process] much easier to read. I added/modified some unit tests (which I expect to fail) to verify that [var/duration] and [var/tick_interval] are both multiples of the subsystem wait assigned to the status effect. If the programmer wants a duration of 2.5 seconds, they expect it to work that way, but it won't because SSfastprocess only ticks once every 0.2 seconds, which 2.5 is not a multiple of. This becomes way more apparent when a status effect is set to use SSprocessing. The final, perhaps most important unit test I've added, is one that verifies that the overall tick count and overall accumulated [seconds_between_ticks] are equal to "[var/duration] / [var/tick_interval]" and "[var/duration]" respectively. ## Why It's Good For The Game The main thing this PR fixes is timing inconsistencies. Before this PR, durations and tick intervals were tracked using world.time, while the [proc/tick] call timing was dependent on the wait time of the subsystem the status effect was processing on. Thing is, SSfastprocess and SSprocessing rarely run completely in one tick during real gameplay. This led to a continuous desync where status effects were consistently inconsistent in their overall tick count. This is a big problem as [seconds_between_ticks] is constant and thus doesn't account for this difference in tick count. As an example, Changeling's Fleshmend has a duration of 10 seconds, a tick interval of 1 second and a healing rate of 4 brute per tick. Previously, if the server was lagging even slightly and it only ticked 8 times over the course of 10 seconds, you would heal 32 health rather than the 40 that a full Fleshmend would give you. The total effect potency of a status effect being reliant on server lag is incredibly stupid, especially for status effects that have an associated cost. (like the aforementioned Fleshmend) As for the refactors, they make status effect code easier to read and debug. Unit tests also make verifying things are working as intended much easier. ## Changelog 🆑 fix: Status effects now tick consistently, with Fleshmend and such giving a consistent total healing amount. Report any oddities. refactor: Status effect code is now easier to read and makes more sense. Again, report any oddities, the changes are major. /🆑
618 lines
33 KiB
Plaintext
618 lines
33 KiB
Plaintext
/// Tests to make sure mob damage procs are working correctly
|
|
/datum/unit_test/mob_damage
|
|
priority = TEST_LONGER
|
|
|
|
/datum/unit_test/mob_damage/Destroy()
|
|
SSmobs.ignite()
|
|
return ..()
|
|
|
|
/datum/unit_test/mob_damage/Run()
|
|
SSmobs.pause()
|
|
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
|
|
dummy.maxHealth = 200 // tank mode
|
|
|
|
/* The sanity tests: here we make sure that:
|
|
1) That damage procs are returning the expected values. They should be returning the actual amount of damage taken/healed.
|
|
(Negative values mean damage was taken, positive mean healing)
|
|
2) Verifying that the damage has been accurately applied to the mob afterwards. */
|
|
|
|
test_sanity_simple(dummy)
|
|
test_sanity_complex(dummy)
|
|
|
|
// Testing if biotypes are working as intended
|
|
test_biotypes(dummy)
|
|
|
|
// Testing whether or not TRAIT_NOBREATH is working as intended
|
|
test_nobreath(dummy)
|
|
|
|
// Testing whether or not TRAIT_TOXINLOVER and TRAIT_TOXIMMUNE are working as intended
|
|
test_toxintraits(dummy)
|
|
|
|
// Testing the proc ordered_healing()
|
|
test_ordered_healing(dummy)
|
|
|
|
// testing with godmode enabled
|
|
test_godmode(dummy)
|
|
|
|
/**
|
|
* Test whether the adjust damage procs return the correct values and that the mob's health is the expected value afterwards.
|
|
*
|
|
* By default this calls apply_damage(amount) followed by verify_damage(amount_after) and returns TRUE if both succeeded.
|
|
* amount_after defaults to the mob's current stamina loss but can be overridden as needed.
|
|
*
|
|
* Arguments:
|
|
* * testing_mob - the mob to apply the damage to
|
|
* * amount - the amount of damage to apply to the mob
|
|
* * expected - what the expected return value of the damage proc is
|
|
* * amount_after - in case you want to specify what the damage amount on the mob should be afterwards
|
|
* * included_types - Bitflag of damage types to apply
|
|
* * biotypes - the biotypes of damage to apply
|
|
* * bodytypes - the bodytypes of damage to apply
|
|
* * forced - whether or not this is forced damage
|
|
*/
|
|
/datum/unit_test/mob_damage/proc/test_apply_damage(mob/living/testing_mob, amount, expected = -amount, amount_after, included_types, biotypes, bodytypes, forced)
|
|
if(isnull(amount_after))
|
|
amount_after = testing_mob.getStaminaLoss() - expected // stamina loss applies to both carbon and basic mobs the same way, so that's why we're using it here
|
|
if(!apply_damage(testing_mob, amount, expected, included_types, biotypes, bodytypes, forced))
|
|
return FALSE
|
|
if(!verify_damage(testing_mob, amount_after, included_types))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* Test whether the set damage procs return the correct values and that the mob's health is the expected value afterwards.
|
|
*
|
|
* By default this calls set_damage(amount) followed by verify_damage(amount_after) and returns TRUE if both succeeded.
|
|
* amount_after defaults to the mob's current stamina loss but can be overridden as needed.
|
|
*
|
|
* Arguments:
|
|
* * testing_mob - the mob to apply the damage to
|
|
* * amount - the amount of damage to apply to the mob
|
|
* * expected - what the expected return value of the damage proc is
|
|
* * amount_after - in case you want to specify what the damage amount on the mob should be afterwards
|
|
* * included_types - Bitflag of damage types to apply
|
|
* * biotypes - the biotypes of damage to apply
|
|
* * bodytypes - the bodytypes of damage to apply
|
|
* * forced - whether or not this is forced damage
|
|
*/
|
|
/datum/unit_test/mob_damage/proc/test_set_damage(mob/living/testing_mob, amount, expected, amount_after, included_types, biotypes, bodytypes, forced)
|
|
if(isnull(amount_after))
|
|
amount_after = testing_mob.getStaminaLoss() - expected
|
|
if(!set_damage(testing_mob, amount, expected, included_types, biotypes, bodytypes, forced))
|
|
return FALSE
|
|
if(!verify_damage(testing_mob, amount_after, included_types))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* Check that the mob has a specific amount of damage
|
|
*
|
|
* By default this checks that the mob has <amount> of every type of damage.
|
|
* Arguments:
|
|
* * testing_mob - the mob to check the damage of
|
|
* * amount - the amount of damage to verify that the mob has
|
|
* * included_types - Bitflag of damage types to check.
|
|
*/
|
|
/datum/unit_test/mob_damage/proc/verify_damage(mob/living/carbon/testing_mob, amount, included_types = ALL)
|
|
if(included_types & TOXLOSS)
|
|
TEST_ASSERT_EQUAL(testing_mob.getToxLoss(), amount, \
|
|
"[testing_mob] should have [amount] toxin damage, instead they have [testing_mob.getToxLoss()]!")
|
|
if(included_types & BRUTELOSS)
|
|
TEST_ASSERT_EQUAL(round(testing_mob.getBruteLoss(), 1), amount, \
|
|
"(Testing getBruteLoss()) [testing_mob] should have [amount] brute damage, instead they have [testing_mob.getBruteLoss()]!")
|
|
TEST_ASSERT_EQUAL(round(testing_mob.getBruteLossForType(BODYTYPE_ORGANIC), 1), amount, \
|
|
"(Testing getBruteLossForType(BODYTYPE_ORGANIC)) [testing_mob] should have [amount] brute damage, instead they have [testing_mob.getBruteLossForType(BODYTYPE_ORGANIC)]!")
|
|
if(included_types & FIRELOSS)
|
|
TEST_ASSERT_EQUAL(round(testing_mob.getFireLoss(), 1), amount, \
|
|
"(Testing getFireLoss()) [testing_mob] should have [amount] burn damage, instead they have [testing_mob.getFireLoss()]!")
|
|
TEST_ASSERT_EQUAL(round(testing_mob.getFireLossForType(BODYTYPE_ORGANIC), 1), amount, \
|
|
"(Testing getFireLossForType(BODYTYPE_ORGANIC)) [testing_mob] should have [amount] burn damage, instead they have [testing_mob.getFireLossForType(BODYTYPE_ORGANIC)]!")
|
|
if(included_types & OXYLOSS)
|
|
TEST_ASSERT_EQUAL(testing_mob.getOxyLoss(), amount, \
|
|
"[testing_mob] should have [amount] oxy damage, instead they have [testing_mob.getOxyLoss()]!")
|
|
if(included_types & STAMINALOSS)
|
|
TEST_ASSERT_EQUAL(testing_mob.getStaminaLoss(), amount, \
|
|
"[testing_mob] should have [amount] stamina damage, instead they have [testing_mob.getStaminaLoss()]!")
|
|
return TRUE
|
|
|
|
/**
|
|
* Apply a specific amount of damage to the mob using adjustBruteLoss(), adjustToxLoss(), etc.
|
|
*
|
|
* By default this applies <amount> damage of every type to the mob, and checks that the damage procs return the <expected> value
|
|
* Arguments:
|
|
* * testing_mob - the mob to apply the damage to
|
|
* * amount - the amount of damage to apply to the mob
|
|
* * expected - what the expected return value of the damage proc is
|
|
* * included_types - Bitflag of damage types to apply
|
|
* * biotypes - the biotypes of damage to apply
|
|
* * bodytypes - the bodytypes of damage to apply
|
|
* * forced - whether or not this is forced damage
|
|
*/
|
|
/datum/unit_test/mob_damage/proc/apply_damage(mob/living/carbon/testing_mob, amount, expected = -amount, included_types = ALL, biotypes = ALL, bodytypes = ALL, forced = FALSE)
|
|
var/damage_returned
|
|
if(included_types & TOXLOSS)
|
|
damage_returned = testing_mob.adjustToxLoss(amount, updating_health = FALSE, forced = forced, required_biotype = biotypes)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"adjustToxLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & BRUTELOSS)
|
|
damage_returned = round(testing_mob.adjustBruteLoss(amount, updating_health = FALSE, forced = forced, required_bodytype = bodytypes), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"adjustBruteLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & FIRELOSS)
|
|
damage_returned = round(testing_mob.adjustFireLoss(amount, updating_health = FALSE, forced = forced, required_bodytype = bodytypes), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"adjustFireLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & OXYLOSS)
|
|
damage_returned = testing_mob.adjustOxyLoss(amount, updating_health = FALSE, forced = forced, required_biotype = biotypes)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"adjustOxyLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & STAMINALOSS)
|
|
damage_returned = testing_mob.adjustStaminaLoss(amount, updating_stamina = FALSE, forced = forced, required_biotype = biotypes)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"adjustStaminaLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
return TRUE
|
|
|
|
/**
|
|
* Set a specific amount of damage for the mob using setBruteLoss(), setToxLoss(), etc.
|
|
*
|
|
* By default this sets every type of damage to <amount> for the mob, and checks that the damage procs return the <expected> value
|
|
* Arguments:
|
|
* * testing_mob - the mob to apply the damage to
|
|
* * amount - the amount of damage to apply to the mob
|
|
* * expected - what the expected return value of the damage proc is
|
|
* * included_types - Bitflag of damage types to apply
|
|
* * biotypes - the biotypes of damage to apply
|
|
* * bodytypes - the bodytypes of damage to apply
|
|
* * forced - whether or not this is forced damage
|
|
*/
|
|
/datum/unit_test/mob_damage/proc/set_damage(mob/living/carbon/testing_mob, amount, expected = -amount, included_types = ALL, biotypes = ALL, bodytypes = ALL, forced = FALSE)
|
|
var/damage_returned
|
|
if(included_types & TOXLOSS)
|
|
damage_returned = testing_mob.setToxLoss(amount, updating_health = FALSE, forced = forced, required_biotype = biotypes)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"setToxLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & BRUTELOSS)
|
|
damage_returned = round(testing_mob.setBruteLoss(amount, updating_health = FALSE, forced = forced), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"setBruteLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & FIRELOSS)
|
|
damage_returned = round(testing_mob.setFireLoss(amount, updating_health = FALSE, forced = forced), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"setFireLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & OXYLOSS)
|
|
damage_returned = testing_mob.setOxyLoss(amount, updating_health = FALSE, forced = forced, required_biotype = biotypes)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"setOxyLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
if(included_types & STAMINALOSS)
|
|
damage_returned = testing_mob.setStaminaLoss(amount, updating_stamina = FALSE, forced = forced, required_biotype = biotypes)
|
|
TEST_ASSERT_EQUAL(damage_returned, expected, \
|
|
"setStaminaLoss() should have returned [expected], but returned [damage_returned] instead!")
|
|
return TRUE
|
|
|
|
/// Sanity tests damage and healing using adjustToxLoss, adjustBruteLoss, etc
|
|
/datum/unit_test/mob_damage/proc/test_sanity_simple(mob/living/carbon/human/consistent/dummy)
|
|
// Apply 5 damage and then heal it
|
|
if(!test_apply_damage(dummy, amount = 5))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! damage was not applied correctly")
|
|
|
|
if(!test_apply_damage(dummy, amount = -5))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! healing was not applied correctly")
|
|
|
|
// Apply 15 damage and heal 3
|
|
if(!test_apply_damage(dummy, amount = 15))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! damage was not applied correctly")
|
|
|
|
if(!test_apply_damage(dummy, amount = -3))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! underhealing was not applied correctly")
|
|
|
|
// Now overheal by 666. It should heal for 12.
|
|
|
|
if(!test_apply_damage(dummy, amount = -666, expected = 12))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! overhealing was not applied correctly")
|
|
|
|
// Now test the damage setter procs
|
|
|
|
// set all types of damage to 5
|
|
if(!test_set_damage(dummy, amount = 5, expected = -5))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! failed to set damage to 5")
|
|
// now try healing 5
|
|
if(!test_set_damage(dummy, amount = 0, expected = 5))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! failed to set damage to 0")
|
|
|
|
/// Sanity tests damage and healing using the more complex procs like take_overall_damage(), heal_overall_damage(), etc
|
|
/datum/unit_test/mob_damage/proc/test_sanity_complex(mob/living/carbon/human/consistent/dummy)
|
|
// Heal up, so that errors from the previous tests we won't cause this one to fail
|
|
dummy.fully_heal(HEAL_DAMAGE)
|
|
|
|
var/damage_returned
|
|
// take 5 brute, 2 burn
|
|
damage_returned = round(dummy.take_bodypart_damage(5, 2, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, -7, \
|
|
"take_bodypart_damage() should have returned -7, but returned [damage_returned] instead!")
|
|
|
|
TEST_ASSERT_EQUAL(round(dummy.getBruteLoss(), 1), 5, \
|
|
"Dummy should have 5 brute damage, instead they have [dummy.getBruteLoss()]!")
|
|
TEST_ASSERT_EQUAL(round(dummy.getFireLoss(), 1), 2, \
|
|
"Dummy should have 2 burn damage, instead they have [dummy.getFireLoss()]!")
|
|
|
|
// heal 4 brute, 1 burn
|
|
damage_returned = round(dummy.heal_bodypart_damage(4, 1, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, 5, \
|
|
"heal_bodypart_damage() should have returned 5, but returned [damage_returned] instead!")
|
|
|
|
if(!verify_damage(dummy, 1, included_types = BRUTELOSS|FIRELOSS))
|
|
TEST_FAIL("heal_bodypart_damage did not apply its healing correctly on the mob!")
|
|
|
|
// heal 1 brute, 1 burn
|
|
damage_returned = round(dummy.heal_overall_damage(1, 1, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, 2, \
|
|
"heal_overall_damage() should have returned 2, but returned [damage_returned] instead!")
|
|
|
|
if(!verify_damage(dummy, 0, included_types = BRUTELOSS|FIRELOSS))
|
|
TEST_FAIL("heal_overall_damage did not apply its healing correctly on the mob!")
|
|
|
|
// take 50 brute, 50 burn
|
|
damage_returned = round(dummy.take_overall_damage(50, 50, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, -100, \
|
|
"take_overall_damage() should have returned -100, but returned [damage_returned] instead!")
|
|
|
|
if(!verify_damage(dummy, 50, included_types = BRUTELOSS|FIRELOSS))
|
|
TEST_FAIL("take_overall_damage did not apply its damage correctly on the mob!")
|
|
|
|
// testing negative damage amount args with the overall damage procs - the sign should be ignored for these procs
|
|
|
|
damage_returned = round(dummy.take_bodypart_damage(-5, -5, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, -10, \
|
|
"take_bodypart_damage() should have returned -10, but returned [damage_returned] instead!")
|
|
|
|
damage_returned = round(dummy.heal_bodypart_damage(-5, -5, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, 10, \
|
|
"heal_bodypart_damage() should have returned 10, but returned [damage_returned] instead!")
|
|
|
|
damage_returned = round(dummy.take_overall_damage(-5, -5, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, -10, \
|
|
"take_overall_damage() should have returned -10, but returned [damage_returned] instead!")
|
|
|
|
damage_returned = round(dummy.heal_overall_damage(-5, -5, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, 10, \
|
|
"heal_overall_damage() should have returned 10, but returned [damage_returned] instead!")
|
|
|
|
if(!verify_damage(dummy, 50, included_types = BRUTELOSS|FIRELOSS))
|
|
TEST_FAIL("heal_overall_damage did not apply its healingcorrectly on the mob!")
|
|
|
|
// testing overhealing
|
|
|
|
damage_returned = round(dummy.heal_overall_damage(75, 99, updating_health = FALSE), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, 100, \
|
|
"heal_overall_damage() should have returned 100, but returned [damage_returned] instead!")
|
|
|
|
if(!verify_damage(dummy, 0, included_types = BRUTELOSS|FIRELOSS))
|
|
TEST_FAIL("heal_overall_damage did not apply its healing correctly on the mob!")
|
|
|
|
/// Tests damage procs with godmode on
|
|
/datum/unit_test/mob_damage/proc/test_godmode(mob/living/carbon/human/consistent/dummy)
|
|
// Heal up, so that errors from the previous tests we won't cause this one to fail
|
|
dummy.fully_heal(HEAL_DAMAGE)
|
|
// add godmode
|
|
ADD_TRAIT(dummy, TRAIT_GODMODE, TRAIT_GENERIC)
|
|
|
|
// Apply 9 damage and then heal it
|
|
if(!test_apply_damage(dummy, amount = 9, expected = 0))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_godmode! mob took damage despite having godmode enabled.")
|
|
|
|
if(!test_apply_damage(dummy, amount = -9, expected = 0))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_godmode! mob healed when they should've been at full health.")
|
|
|
|
// Apply 11 damage and then heal it, this time with forced enabled. The damage should go through regardless of godmode.
|
|
if(!test_apply_damage(dummy, amount = 11, forced = TRUE))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_godmode! godmode did not respect forced = TRUE")
|
|
|
|
if(!test_apply_damage(dummy, amount = -11, forced = TRUE))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_godmode! godmode did not respect forced = TRUE")
|
|
|
|
// remove godmode
|
|
REMOVE_TRAIT(dummy, TRAIT_GODMODE, TRAIT_GENERIC)
|
|
|
|
/// Testing biotypes
|
|
/datum/unit_test/mob_damage/proc/test_biotypes(mob/living/carbon/human/consistent/dummy)
|
|
// Heal up, so that errors from the previous tests we won't cause this one to fail
|
|
dummy.fully_heal(HEAL_DAMAGE)
|
|
// Testing biotypes using a plasmaman, who is MOB_MINERAL and MOB_HUMANOID
|
|
dummy.set_species(/datum/species/plasmaman)
|
|
|
|
// argumentless default: should default to required_biotype = ALL. The damage should be applied in that case.
|
|
if(!test_apply_damage(dummy, 1, included_types = TOXLOSS|STAMINALOSS))
|
|
TEST_FAIL("ABOVE FAILURE: plasmaman did not take damage with biotypes = ALL")
|
|
|
|
// If we specify MOB_ORGANIC, the damage should not get applied because plasmamen lack that biotype.
|
|
if(!test_apply_damage(dummy, 1, expected = 0, included_types = TOXLOSS|STAMINALOSS, biotypes = MOB_ORGANIC))
|
|
TEST_FAIL("ABOVE FAILURE: plasmaman took damage with biotypes = MOB_ORGANIC")
|
|
|
|
// Now if we specify MOB_MINERAL the damage should get applied.
|
|
if(!test_apply_damage(dummy, 1, included_types = TOXLOSS|STAMINALOSS, biotypes = MOB_MINERAL))
|
|
TEST_FAIL("ABOVE FAILURE: plasmaman did not take damage with biotypes = MOB_MINERAL")
|
|
|
|
// Transform back to human
|
|
dummy.set_species(/datum/species/human)
|
|
|
|
// We have 2 damage presently.
|
|
// Try to heal it; let's specify MOB_MINERAL, which should no longer work because we have changed back to a human.
|
|
if(!test_apply_damage(dummy, -2, expected = 0, included_types = TOXLOSS|STAMINALOSS, biotypes = MOB_MINERAL))
|
|
TEST_FAIL("ABOVE FAILURE: human took damage with biotypes = MOB_MINERAL")
|
|
|
|
// Force heal some of the damage. When forced = TRUE the damage/healing gets applied no matter what.
|
|
if(!test_apply_damage(dummy, -1, included_types = TOXLOSS|STAMINALOSS, biotypes = MOB_MINERAL, forced = TRUE))
|
|
TEST_FAIL("ABOVE FAILURE: human did not get healed when biotypes = MOB_MINERAL and forced = TRUE")
|
|
|
|
// Now heal the rest of it with the correct biotype. Make sure that this works. We should have 0 damage afterwards.
|
|
if(!test_apply_damage(dummy, -1, included_types = TOXLOSS|STAMINALOSS, biotypes = MOB_ORGANIC))
|
|
TEST_FAIL("ABOVE FAILURE: human did not get healed with biotypes = MOB_ORGANIC")
|
|
|
|
/// Testing oxyloss with the TRAIT_NOBREATH
|
|
/datum/unit_test/mob_damage/proc/test_nobreath(mob/living/carbon/human/consistent/dummy)
|
|
// Heal up, so that errors from the previous tests we won't cause this one to fail
|
|
dummy.fully_heal(HEAL_DAMAGE)
|
|
|
|
// TRAIT_NOBREATH is supposed to prevent oxyloss damage (but not healing). Let's make sure that's the case.
|
|
ADD_TRAIT(dummy, TRAIT_NOBREATH, TRAIT_SOURCE_UNIT_TESTS)
|
|
// force some oxyloss here
|
|
dummy.setOxyLoss(2, updating_health = FALSE, forced = TRUE)
|
|
|
|
// Try to take more oxyloss damage with TRAIT_NOBREATH. It should not work.
|
|
if(!test_apply_damage(dummy, 2, expected = 0, amount_after = dummy.getOxyLoss(), included_types = OXYLOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_nobreath! mob took oxyloss damage while having TRAIT_NOBREATH")
|
|
|
|
// Make sure we are still be able to heal the oxyloss. This should work.
|
|
if(!test_apply_damage(dummy, -2, amount_after = dummy.getOxyLoss()-2, included_types = OXYLOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_nobreath! mob could not heal oxyloss damage while having TRAIT_NOBREATH")
|
|
|
|
REMOVE_TRAIT(dummy, TRAIT_NOBREATH, TRAIT_SOURCE_UNIT_TESTS)
|
|
|
|
/// Testing toxloss with TRAIT_TOXINLOVER and TRAIT_TOXIMMUNE
|
|
/datum/unit_test/mob_damage/proc/test_toxintraits(mob/living/carbon/human/consistent/dummy)
|
|
// Heal up, so that errors from the previous tests we won't cause this one to fail
|
|
dummy.fully_heal(HEAL_DAMAGE)
|
|
|
|
// TRAIT_TOXINLOVER is supposed to invert toxin damage and healing. Things that would normally cause toxloss now heal it, and vice versa.
|
|
ADD_TRAIT(dummy, TRAIT_TOXINLOVER, TRAIT_SOURCE_UNIT_TESTS)
|
|
// force some toxloss here
|
|
dummy.setToxLoss(2, updating_health = FALSE, forced = TRUE)
|
|
|
|
// Try to take more toxloss damage with TRAIT_TOXINLOVER. It should heal instead.
|
|
if(!test_apply_damage(dummy, 2, expected = 2, amount_after = dummy.getToxLoss()-2, included_types = TOXLOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_toxintraits! mob did not heal from toxin damage with TRAIT_TOXINLOVER")
|
|
|
|
// If we try to heal the toxloss we should take damage instead
|
|
if(!test_apply_damage(dummy, -2, expected = -2, amount_after = dummy.getToxLoss()+2, included_types = TOXLOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_toxintraits! mob did not take damage from toxin healing with TRAIT_TOXINLOVER")
|
|
|
|
// TOXIMMUNE trait should prevent the damage you get from being healed by toxins medicines while having TRAIT_TOXINLOVER
|
|
ADD_TRAIT(dummy, TRAIT_TOXIMMUNE, TRAIT_SOURCE_UNIT_TESTS)
|
|
|
|
// need to force apply some toxin damage since the TOXIMUNNE trait sets toxloss to 0 upon being added
|
|
dummy.setToxLoss(2, updating_health = FALSE, forced = TRUE)
|
|
|
|
// try to 'heal' again - this time it should just do nothing because we should be immune to any sort of toxin damage - including from inverted healing
|
|
if(!test_apply_damage(dummy, -2, expected = 0, amount_after = dummy.getToxLoss(), included_types = TOXLOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_toxintraits! mob should not have taken any damage or healing with TRAIT_TOXINLOVER + TRAIT_TOXIMMUNE")
|
|
|
|
// ok, let's try taking 'damage'. The inverted damage should still heal mobs with the TOXIMMUNE trait.
|
|
if(!test_apply_damage(dummy, 2, expected = 2, amount_after = dummy.getToxLoss()-2, included_types = TOXLOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_toxintraits! mob did not heal from taking toxin damage with TRAIT_TOXINLOVER + TRAIT_TOXIMMUNE")
|
|
|
|
REMOVE_TRAIT(dummy, TRAIT_TOXINLOVER, TRAIT_SOURCE_UNIT_TESTS)
|
|
REMOVE_TRAIT(dummy, TRAIT_TOXIMMUNE, TRAIT_SOURCE_UNIT_TESTS)
|
|
|
|
/// Testing heal_ordered_damage()
|
|
/datum/unit_test/mob_damage/proc/test_ordered_healing(mob/living/carbon/human/consistent/dummy)
|
|
// Heal up, so that errors from the previous tests we won't cause this one to fail
|
|
dummy.fully_heal(HEAL_DAMAGE)
|
|
var/damage_returned
|
|
|
|
// We apply 20 brute, 20 burn, and 20 toxin damage. 60 damage total
|
|
apply_damage(dummy, 20, included_types = TOXLOSS|BRUTELOSS|FIRELOSS)
|
|
|
|
// Heal 30 damage of that, starting from brute
|
|
damage_returned = round(dummy.heal_ordered_damage(30, list(BRUTE, BURN, TOX)), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, 30, \
|
|
"heal_ordered_damage() should have returned 30, but returned [damage_returned] instead!")
|
|
|
|
// Should have 10 burn damage and 20 toxins damage remaining, let's check
|
|
TEST_ASSERT_EQUAL(dummy.getBruteLoss(), 0, \
|
|
"[src] should have 0 brute damage, but has [dummy.getBruteLoss()] instead!")
|
|
TEST_ASSERT_EQUAL(dummy.getFireLoss(), 10, \
|
|
"[src] should have 10 burn damage, but has [dummy.getFireLoss()] instead!")
|
|
TEST_ASSERT_EQUAL(dummy.getToxLoss(), 20, \
|
|
"[src] should have 20 toxin damage, but has [dummy.getToxLoss()] instead!")
|
|
|
|
// Now heal the remaining 30, overhealing by 5.
|
|
damage_returned = round(dummy.heal_ordered_damage(35, list(BRUTE, BURN, TOX)), 1)
|
|
TEST_ASSERT_EQUAL(damage_returned, 30, \
|
|
"heal_ordered_damage() should have returned 30, but returned [damage_returned] instead!")
|
|
|
|
// Should have no damage remaining
|
|
TEST_ASSERT_EQUAL(dummy.getBruteLoss(), 0, \
|
|
"[src] should have 0 brute damage, but has [dummy.getBruteLoss()] instead!")
|
|
TEST_ASSERT_EQUAL(dummy.getFireLoss(), 0, \
|
|
"[src] should have 0 burn damage, but has [dummy.getFireLoss()] instead!")
|
|
TEST_ASSERT_EQUAL(dummy.getToxLoss(), 0, \
|
|
"[src] should have 0 toxin damage, but has [dummy.getToxLoss()] instead!")
|
|
|
|
/// Tests that mob damage procs are working as intended for basic and simple mobs
|
|
/datum/unit_test/mob_damage/animal
|
|
|
|
/datum/unit_test/mob_damage/animal/Run()
|
|
SSmobs.pause()
|
|
|
|
// Basic mobs
|
|
var/mob/living/basic/mouse/gray/gusgus = allocate(/mob/living/basic/mouse/gray)
|
|
// give gusgus a damage_coeff of 1 for this test
|
|
gusgus.damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 1, OXY = 1)
|
|
// tank mouse
|
|
gusgus.maxHealth = 200
|
|
|
|
test_sanity_simple(gusgus)
|
|
test_sanity_complex(gusgus)
|
|
|
|
// Simplemobs
|
|
var/mob/living/simple_animal/abstract_thing = allocate(/mob/living/simple_animal)
|
|
// give the mob a damage_coeff of 1 for this test
|
|
abstract_thing.damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 1, OXY = 1)
|
|
abstract_thing.maxHealth = 200
|
|
|
|
test_sanity_simple(abstract_thing)
|
|
test_sanity_complex(abstract_thing)
|
|
|
|
/**
|
|
* Check that the mob has a specific amount of damage. Note: basic mobs have all incoming damage types besides stam converted into brute damage.
|
|
*
|
|
* By default this checks that the mob has <amount> of every type of damage.
|
|
* Arguments:
|
|
* * testing_mob - the mob to check the damage of
|
|
* * amount - the amount of damage to verify that the mob has
|
|
* * expected - the expected return value of the damage procs, if it differs from the default of (amount * 4)
|
|
* * included_types - Bitflag of damage types to check.
|
|
*/
|
|
/datum/unit_test/mob_damage/animal/verify_damage(mob/living/carbon/testing_mob, amount, expected, included_types = ALL)
|
|
if(included_types & TOXLOSS)
|
|
TEST_ASSERT_EQUAL(testing_mob.getToxLoss(), 0, \
|
|
"[testing_mob] should have [0] toxin damage, instead they have [testing_mob.getToxLoss()]!")
|
|
if(included_types & BRUTELOSS)
|
|
TEST_ASSERT_EQUAL(round(testing_mob.getBruteLoss(), 1), expected || amount * 4, \
|
|
"[testing_mob] should have [expected || amount * 4] brute damage, instead they have [testing_mob.getBruteLoss()]!")
|
|
if(included_types & FIRELOSS)
|
|
TEST_ASSERT_EQUAL(round(testing_mob.getFireLoss(), 1), 0, \
|
|
"[testing_mob] should have [0] burn damage, instead they have [testing_mob.getFireLoss()]!")
|
|
if(included_types & OXYLOSS)
|
|
TEST_ASSERT_EQUAL(testing_mob.getOxyLoss(), 0, \
|
|
"[testing_mob] should have [0] oxy damage, instead they have [testing_mob.getOxyLoss()]!")
|
|
if(included_types & STAMINALOSS)
|
|
TEST_ASSERT_EQUAL(testing_mob.getStaminaLoss(), amount, \
|
|
"[testing_mob] should have [amount] stamina damage, instead they have [testing_mob.getStaminaLoss()]!")
|
|
return TRUE
|
|
|
|
/datum/unit_test/mob_damage/animal/test_sanity_simple(mob/living/carbon/test_mob)
|
|
// check to see if basic mob damage works
|
|
|
|
// Simple damage and healing
|
|
// Take 1 damage, heal for 1
|
|
if(!test_apply_damage(test_mob, amount = 1))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! damage was not applied correctly")
|
|
|
|
if(!test_apply_damage(test_mob, amount = -1))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! healing was not applied correctly")
|
|
|
|
// Give 2 damage of every time (translates to 8 brute, 2 staminaloss)
|
|
if(!test_apply_damage(test_mob, amount = 2))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! damage was not applied correctly")
|
|
|
|
// underhealing: heal 1 damage of every type (translates to 4 brute, 1 staminaloss)
|
|
if(!test_apply_damage(test_mob, amount = -1))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! healing was not applied correctly")
|
|
|
|
// overhealing
|
|
|
|
// heal 11 points of toxloss (should take care of all 4 brute damage remaining)
|
|
if(!apply_damage(test_mob, -11, expected = 4, included_types = TOXLOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! toxloss was not applied correctly")
|
|
// heal the remaining point of staminaloss
|
|
if(!apply_damage(test_mob, -11, expected = 1, included_types = STAMINALOSS))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! failed to heal staminaloss correctly")
|
|
// heal 35 points of each type, we should already be at full health so nothing should happen
|
|
if(!test_apply_damage(test_mob, amount = -35, expected = 0))
|
|
TEST_FAIL("ABOVE FAILURE: failed test_sanity_simple! overhealing was not applied correctly")
|
|
|
|
/datum/unit_test/mob_damage/animal/test_sanity_complex(mob/living/carbon/test_mob)
|
|
// Heal up, so that errors from the previous tests we won't cause this one to fail
|
|
test_mob.fully_heal(HEAL_DAMAGE)
|
|
var/damage_returned
|
|
// overall damage procs
|
|
|
|
var/type_string = isbasicmob(test_mob) ? "basic" : "simple"
|
|
// take 5 brute, 2 burn
|
|
damage_returned = test_mob.take_bodypart_damage(5, 2, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, -7, \
|
|
"take_bodypart_damage() should have returned -7, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
TEST_ASSERT_EQUAL(test_mob.bruteloss, 7, \
|
|
"Mouse should have 7 brute damage, instead they have [test_mob.bruteloss] on a [type_string] mob!")
|
|
TEST_ASSERT_EQUAL(test_mob.fireloss, 0, \
|
|
"Mouse should have 0 burn damage, instead they have [test_mob.fireloss] on a [type_string] mob!")
|
|
|
|
// heal 4 brute, 1 burn
|
|
damage_returned = test_mob.heal_bodypart_damage(4, 1, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, 5, \
|
|
"heal_bodypart_damage() should have returned 5, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
TEST_ASSERT_EQUAL(test_mob.bruteloss, 2, \
|
|
"Mouse should have 2 brute damage, instead they have [test_mob.bruteloss] on a [type_string] mob!")
|
|
TEST_ASSERT_EQUAL(test_mob.fireloss, 0, \
|
|
"Mouse should have 0 burn damage, instead they have [test_mob.fireloss] on a [type_string] mob!")
|
|
|
|
// heal 1 brute, 1 burn
|
|
damage_returned = test_mob.heal_overall_damage(1, 1, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, 2, \
|
|
"heal_overall_damage() should have returned 2, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
TEST_ASSERT_EQUAL(test_mob.bruteloss, 0, \
|
|
"Mouse should have 0 brute damage, instead they have [test_mob.bruteloss] on a [type_string] mob!")
|
|
TEST_ASSERT_EQUAL(test_mob.fireloss, 0, \
|
|
"Mouse should have 0 burn damage, instead they have [test_mob.fireloss] on a [type_string] mob!")
|
|
|
|
// take 50 brute, 50 burn
|
|
damage_returned = test_mob.take_overall_damage(3, 3, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, -6, \
|
|
"take_overall_damage() should have returned -6, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
if(!verify_damage(test_mob, 1, expected = 6, included_types = BRUTELOSS))
|
|
TEST_FAIL("take_overall_damage did not apply its damage correctly on a [type_string] mob!")
|
|
|
|
// testing negative args with the overall damage procs
|
|
|
|
damage_returned = test_mob.take_bodypart_damage(-1, -1, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, -2, \
|
|
"take_bodypart_damage() should have returned -2, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
damage_returned = test_mob.heal_bodypart_damage(-1, -1, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, 2, \
|
|
"heal_bodypart_damage() should have returned 2, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
damage_returned = test_mob.take_overall_damage(-1, -1, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, -2, \
|
|
"take_overall_damage() should have returned -2, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
damage_returned = test_mob.heal_overall_damage(-1, -1, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, 2, \
|
|
"heal_overall_damage() should have returned 2, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
if(!verify_damage(test_mob, 1, expected = 6, included_types = BRUTELOSS))
|
|
TEST_FAIL("heal_overall_damage did not apply its healing correctly on a [type_string] mob!")
|
|
|
|
// testing overhealing
|
|
|
|
damage_returned = test_mob.heal_overall_damage(75, 99, updating_health = FALSE)
|
|
TEST_ASSERT_EQUAL(damage_returned, 6, \
|
|
"heal_overall_damage() should have returned 6, but returned [damage_returned] instead on a [type_string] mob!")
|
|
|
|
if(!verify_damage(test_mob, 0, included_types = BRUTELOSS))
|
|
TEST_FAIL("heal_overall_damage did not apply its healing correctly on a [type_string] mob!")
|
|
|
|
/// Tests that humans get the tox_vomit status effect when heavily poisoned
|
|
/datum/unit_test/human_tox_damage
|
|
|
|
/datum/unit_test/human_tox_damage/Run()
|
|
// Spawn a dummy, give it a bunch of tox damage. It should get the status effect.
|
|
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
|
|
dummy.setToxLoss(75)
|
|
var/datum/status_effect/tox_effect = dummy.has_status_effect(/datum/status_effect/tox_vomit)
|
|
TEST_ASSERT_NOTNULL(tox_effect, "Dummy didn't get tox_vomit status effect despite at [dummy.getToxLoss()] toxin damage (Method: SET)!")
|
|
// Clear the toxin damage away, and force a status effect tick: It should delete itself
|
|
dummy.setToxLoss(0)
|
|
tox_effect.tick(tox_effect.tick_interval)
|
|
TEST_ASSERT(QDELETED(tox_effect), "Dummy still has tox_vomit status effect despite at [dummy.getToxLoss()] toxin damage (Method: SET)!")
|
|
// Test another method of gaining tox damage, use an entirely clean slate just to be sure
|
|
var/mob/living/carbon/human/dummy_two = allocate(/mob/living/carbon/human/consistent)
|
|
dummy_two.adjustToxLoss(75)
|
|
var/datum/status_effect/tox_effect_two = dummy_two.has_status_effect(/datum/status_effect/tox_vomit)
|
|
TEST_ASSERT_NOTNULL(tox_effect_two, "Dummy didn't get tox_vomit status effect at [dummy_two.getToxLoss()] toxin damage (METHOD: ADJUST)!")
|