Cataclysm-DDA/tests/magic_spell_test.cpp

682 lines
24 KiB
C++

#include <iosfwd>
#include <memory>
#include <string>
#include "avatar.h"
#include "cata_catch.h"
#include "creature_tracker.h"
#include "game.h"
#include "magic.h"
#include "map_helpers.h"
#include "monster.h"
#include "pimpl.h"
#include "player_helpers.h"
#include "point.h"
#include "type_id.h"
static const spell_id spell_test_spell_box( "test_spell_box" );
static const spell_id spell_test_spell_tp_mummy( "test_spell_tp_mummy" );
// Magic Spell tests
// -----------------
//
// Each test case relates to some spell feature, in terms of:
//
// - JSON spell content, from data/json/*.json, as documented in doc/MAGIC.md
// - C++ spell functions, defined in src/magic.cpp and src/magic_spell_effect.cpp
//
// To run all tests in this file:
//
// tests/cata_test [magic][spell]
//
// Other tags used include: [level], [damage], [duration], [range], [aoe]
//
// All these test cases use spells from data/mods/TEST_DATA/magic.json, to have predictable data
// unaffected by in-game balance and mods.
// Spell name
// ----------
//
// Related JSON fields:
// "name"
//
// Functions:
// spell::name
//
TEST_CASE( "spell name", "[magic][spell][name]" )
{
// Test spells from data/mods/TEST_DATA/magic.json
spell_id pew_id( "test_spell_pew" );
spell_id lava_id( "test_spell_lava" );
spell_id kiss_id( "test_spell_kiss" );
spell_id montage_id( "test_spell_montage" );
spell pew_spell( pew_id );
spell lava_spell( lava_id );
spell kiss_spell( kiss_id );
spell montage_spell( montage_id );
CHECK( pew_spell.name() == "Pew, Pew" );
CHECK( lava_spell.name() == "The Floor is Lava" );
CHECK( kiss_spell.name() == "Kiss the Owie" );
CHECK( montage_spell.name() == "Sports Training Montage" );
}
// Spell level
// -----------
//
// Related JSON fields:
// "max_level"
//
// Functions:
// spell::get_level
// spell::set_level
// spell::exp_to_next_level
TEST_CASE( "spell level", "[magic][spell][level]" )
{
spell_id pew_id( "test_spell_pew" );
const spell_type &pew_type = pew_id.obj();
REQUIRE( pew_type.max_level == 10 );
GIVEN( "spell level 0" ) {
spell pew_spell( pew_id );
REQUIRE( pew_spell.get_level() == 0 );
WHEN( "spell levels up once" ) {
pew_spell.gain_level();
THEN( "it is level 1" ) {
CHECK( pew_spell.get_level() == 1 );
}
AND_WHEN( "spell levels up to max_level" ) {
pew_spell.set_level( pew_type.max_level );
THEN( "it is maximum level" ) {
CHECK( pew_spell.get_level() == pew_type.max_level );
}
AND_THEN( "it cannot level up beyond max_level" ) {
pew_spell.set_level( pew_type.max_level + 1 );
CHECK( pew_spell.get_level() == pew_type.max_level );
}
}
}
}
}
// Return experience points needed to level up a spell, starting at from_level
static int spell_xp_to_next_level( const spell_id &sp_id, const int from_level )
{
spell test_spell( sp_id );
test_spell.set_level( from_level );
return test_spell.exp_to_next_level();
}
TEST_CASE( "experience to gain spell levels", "[magic][spell][level][xp]" )
{
spell_id pew_id( "test_spell_pew" );
spell_id lava_id( "test_spell_lava" );
int level_1_xp = 0;
int level_2_xp = 0;
int current_xp = 0;
spell lava_spell( lava_id );
GIVEN( "spell is level 0" ) {
lava_spell.set_level( 0 );
REQUIRE( lava_spell.get_level() == 0 );
THEN( "exp_to_next_level returns experience needed to reach level 1" ) {
level_1_xp = lava_spell.exp_to_next_level();
CHECK( level_1_xp > 1000 );
// These sections all make the same assertion in different ways, basically:
// - Starting from 0, exp_to_next_level is the total XP to reach level 1
// - As spell experience accumulates, exp_to_next_level diminishes toward 0
// - Upon reaching level 1, exp_to_next_level returns XP needed to reach level 2
WHEN( "some experience is gained" ) {
current_xp = 500;
lava_spell.set_exp( current_xp );
THEN( "exp_to_next_level is that much closer to level 1" ) {
CHECK( lava_spell.exp_to_next_level() == level_1_xp - current_xp );
}
}
WHEN( "experience is half way to level 1" ) {
current_xp = level_1_xp / 2;
lava_spell.set_exp( current_xp );
THEN( "exp_to_next_level is half way to zero" ) {
CHECK( lava_spell.exp_to_next_level() == level_1_xp - current_xp );
}
}
WHEN( "experience is 10 points away from level 1" ) {
current_xp = level_1_xp - 10;
lava_spell.set_exp( current_xp );
THEN( "exp_to_next_level is 10" ) {
CHECK( lava_spell.exp_to_next_level() == 10 );
}
}
WHEN( "experience is just enough to reach level 1" ) {
current_xp = level_1_xp;
lava_spell.set_exp( current_xp );
THEN( "spell is level 1" ) {
CHECK( lava_spell.get_level() == 1 );
}
THEN( "exp_to_next_level returns experience needed to reach level 2" ) {
level_2_xp = lava_spell.exp_to_next_level();
CHECK( level_2_xp > 1000 );
}
}
}
}
SECTION( "experience needed to level up spells" ) {
SECTION( "same for all spells" ) {
CHECK( spell_xp_to_next_level( lava_id, 0 ) == spell_xp_to_next_level( pew_id, 0 ) );
CHECK( spell_xp_to_next_level( lava_id, 1 ) == spell_xp_to_next_level( pew_id, 1 ) );
CHECK( spell_xp_to_next_level( lava_id, 2 ) == spell_xp_to_next_level( pew_id, 2 ) );
}
SECTION( "getting from level 0 to 1 is the initial XP hurdle" ) {
CHECK( spell_xp_to_next_level( lava_id, 0 ) == 4881 );
}
SECTION( "levels 2-8 take progressively more XP, but still less XP than 0-1" ) {
CHECK( spell_xp_to_next_level( lava_id, 1 ) == 1751 );
CHECK( spell_xp_to_next_level( lava_id, 2 ) == 2027 );
CHECK( spell_xp_to_next_level( lava_id, 3 ) == 2347 );
CHECK( spell_xp_to_next_level( lava_id, 4 ) == 2717 );
CHECK( spell_xp_to_next_level( lava_id, 5 ) == 3147 );
CHECK( spell_xp_to_next_level( lava_id, 6 ) == 3644 );
CHECK( spell_xp_to_next_level( lava_id, 7 ) == 4220 );
}
SECTION( "getting from level 8 to 9 takes about as much XP as getting from 0 to 1" ) {
CHECK( spell_xp_to_next_level( lava_id, 8 ) == 4886 );
}
SECTION( "level 9 and above take progressively more XP to level up" ) {
CHECK( spell_xp_to_next_level( lava_id, 9 ) == 5659 );
CHECK( spell_xp_to_next_level( lava_id, 10 ) == 6552 );
CHECK( spell_xp_to_next_level( lava_id, 11 ) == 7587 );
CHECK( spell_xp_to_next_level( lava_id, 12 ) == 8785 );
CHECK( spell_xp_to_next_level( lava_id, 13 ) == 10173 );
CHECK( spell_xp_to_next_level( lava_id, 14 ) == 11780 );
CHECK( spell_xp_to_next_level( lava_id, 15 ) == 13641 );
CHECK( spell_xp_to_next_level( lava_id, 16 ) == 15795 );
CHECK( spell_xp_to_next_level( lava_id, 17 ) == 18291 );
CHECK( spell_xp_to_next_level( lava_id, 18 ) == 21180 );
CHECK( spell_xp_to_next_level( lava_id, 19 ) == 24525 );
CHECK( spell_xp_to_next_level( lava_id, 20 ) == 28400 );
}
}
}
// Spell damage
// ------------
//
// Related JSON fields:
// "min_damage"
// "max_damage"
// "damage_increment"
// "max_level"
//
// Functions:
// spell::damage
// Return spell damage at a given level
static int spell_damage( const spell_id &sp_id, const int spell_level )
{
spell test_spell( sp_id );
test_spell.set_level( spell_level );
return test_spell.damage();
}
TEST_CASE( "spell damage", "[magic][spell][damage]" )
{
spell_id pew_id( "test_spell_pew" );
const spell_type &pew_type = pew_id.obj();
// Level 0 damage for this spell is 1
REQUIRE( pew_type.min_damage == 1 );
// and 1 damage is added at each level
REQUIRE( pew_type.damage_increment == 1 );
// however, maximum damage is 5
REQUIRE( pew_type.max_damage == 5 );
// so maximum damage will be reached at level 4, when
//
// Lv4 damage = 1 + 4 * 1 = 5
// min lvl inc max
// Because this spell has a maximum level of 10
REQUIRE( pew_type.max_level == 10 );
// damage from level 5-10 remains at 5.
SECTION( "spell damage varies from min_damage to max_damage as level increases" ) {
CHECK( spell_damage( pew_id, 0 ) == 1 );
CHECK( spell_damage( pew_id, 1 ) == 2 );
CHECK( spell_damage( pew_id, 2 ) == 3 );
CHECK( spell_damage( pew_id, 3 ) == 4 );
CHECK( spell_damage( pew_id, 4 ) == 5 );
// Max damage reached
CHECK( spell_damage( pew_id, 5 ) == 5 );
CHECK( spell_damage( pew_id, 9 ) == 5 );
CHECK( spell_damage( pew_id, 10 ) == 5 );
}
}
// Spell duration
// --------------
//
// Related JSON fields:
// "min_duration"
// "max_duration"
// "duration_increment"
// "max_level"
//
// Functions:
// spell::duration_string
// Return spell duration at a given level
static std::string spell_duration_string( const spell_id &sp_id, const int spell_level )
{
spell test_spell( sp_id );
test_spell.set_level( spell_level );
return test_spell.duration_string();
}
TEST_CASE( "spell duration", "[magic][spell][duration]" )
{
spell_id lava_id( "test_spell_lava" );
const spell_type &lava_type = lava_id.obj();
// Level 0 duration for this spell is 100 seconds
REQUIRE( lava_type.min_duration == 10000 );
// and 10 seconds are added at each level
REQUIRE( lava_type.duration_increment == 1000 );
// however, maximum duration is 250 seconds
REQUIRE( lava_type.max_duration == 25000 );
// maximum duration will be reached at level 15, when
//
// Lv15 duration = 100 + 15 * 10 = 250
// min lvl inc max
// Because this spell has a maximum level of 20
REQUIRE( lava_type.max_level == 20 );
// duration from level 16-20 remains 250 seconds.
SECTION( "spell duration varies from min_duration to max_duration as level increases" ) {
// At level 0, duration is 100 seconds = 1m 40s
CHECK( spell_duration_string( lava_id, 0 ) == "1 minute and 40 seconds" );
// Gain 10 seconds per level
CHECK( spell_duration_string( lava_id, 1 ) == "1 minute and 50 seconds" );
CHECK( spell_duration_string( lava_id, 2 ) == "2 minutes" );
// Gain 6 more levels for 60 more seconds
CHECK( spell_duration_string( lava_id, 8 ) == "3 minutes" );
// Keep gaining 10 seconds per level
CHECK( spell_duration_string( lava_id, 9 ) == "3 minutes and 10 seconds" );
CHECK( spell_duration_string( lava_id, 10 ) == "3 minutes and 20 seconds" );
CHECK( spell_duration_string( lava_id, 11 ) == "3 minutes and 30 seconds" );
CHECK( spell_duration_string( lava_id, 12 ) == "3 minutes and 40 seconds" );
CHECK( spell_duration_string( lava_id, 13 ) == "3 minutes and 50 seconds" );
CHECK( spell_duration_string( lava_id, 14 ) == "4 minutes" );
// Maximum duration is 250 seconds = 4m 10s
CHECK( spell_duration_string( lava_id, 15 ) == "4 minutes and 10 seconds" );
// At levels beyond, max_duration remains the same
CHECK( spell_duration_string( lava_id, 16 ) == "4 minutes and 10 seconds" );
CHECK( spell_duration_string( lava_id, 17 ) == "4 minutes and 10 seconds" );
CHECK( spell_duration_string( lava_id, 18 ) == "4 minutes and 10 seconds" );
CHECK( spell_duration_string( lava_id, 19 ) == "4 minutes and 10 seconds" );
CHECK( spell_duration_string( lava_id, 20 ) == "4 minutes and 10 seconds" );
}
// TODO: Random duration
}
// Spells with the PERMANENT flag have behavior that depends on what kind of spell it is
// - If spell has "effect": "spawn_item", the spawned item only has permanent duration at maximum level
// - If spell has "effect": "summon", the summoned monster can have permanent duration at any level
TEST_CASE( "permanent spell duration depends on effect and level", "[magic][spell][permanent]" )
{
GIVEN( "spell with spawn_item effect, nonzero duration, and PERMANENT flag" ) {
const spell_type &box_type = spell_test_spell_box.obj();
const spell box_spell( spell_test_spell_box );
REQUIRE( box_type.effect_name == "spawn_item" );
REQUIRE( box_type.duration_increment > 0 );
REQUIRE( box_type.min_duration > 0 );
REQUIRE( box_type.max_duration > 0 );
REQUIRE( box_spell.has_flag( spell_flag::PERMANENT ) );
REQUIRE( box_spell.get_max_level() > 9 );
THEN( "spell has increasing duration before reaching max level" ) {
CHECK( spell_duration_string( spell_test_spell_box, 0 ) == "10 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 1 ) == "15 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 2 ) == "20 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 3 ) == "25 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 4 ) == "30 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 5 ) == "35 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 6 ) == "40 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 7 ) == "45 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 8 ) == "50 minutes" );
CHECK( spell_duration_string( spell_test_spell_box, 9 ) == "55 minutes" );
}
THEN( "spell is permanent at max level" ) {
CHECK( spell_duration_string( spell_test_spell_box, box_spell.get_max_level() ) == "Permanent" );
}
}
GIVEN( "spell with summon effect, zero duration, and PERMANENT flag" ) {
const spell_type &mummy_type = spell_test_spell_tp_mummy.obj();
const spell mummy_spell( spell_test_spell_tp_mummy );
REQUIRE( mummy_type.effect_name == "summon" );
REQUIRE( mummy_type.min_duration == 0 );
REQUIRE( mummy_type.max_duration == 0 );
REQUIRE( mummy_spell.has_flag( spell_flag::PERMANENT ) );
REQUIRE( mummy_spell.get_max_level() > 0 );
THEN( "spell has permanent duration at every level" ) {
CHECK( spell_duration_string( spell_test_spell_tp_mummy, 0 ) == "Permanent" );
CHECK( spell_duration_string( spell_test_spell_tp_mummy, 1 ) == "Permanent" );
CHECK( spell_duration_string( spell_test_spell_tp_mummy, 2 ) == "Permanent" );
CHECK( spell_duration_string( spell_test_spell_tp_mummy,
mummy_spell.get_max_level() ) == "Permanent" );
}
}
}
// Spell range
// -----------
//
// Related JSON fields:
// "min_range"
// "max_range"
// "range_increment"
// "max_level"
//
// Functions:
// spell::range
// Return spell range at a given level
static int spell_range( const spell_id &sp_id, const int spell_level )
{
spell test_spell( sp_id );
test_spell.set_level( spell_level );
return test_spell.range();
}
TEST_CASE( "spell range", "[magic][spell][range]" )
{
spell_id pew_id( "test_spell_pew" );
const spell_type &pew_type = pew_id.obj();
// Level 0 range for this spell is 10
REQUIRE( pew_type.min_range == 10 );
// with 2.0 added at each level
REQUIRE( pew_type.range_increment == 2 );
// reaching a maximum range of 30
REQUIRE( pew_type.max_range == 30 );
// maximum range will be reached at level 10, when
//
// Lv10 range = 10 + 10 * 2.0 = 30
// min lvl inc max
// This coincides with the maximum level of the spell
REQUIRE( pew_type.max_level == 10 );
// giving an even spread of ranges across all levels.
SECTION( "spell range varies from min_range to max_range as level increases" ) {
CHECK( spell_range( pew_id, 0 ) == 10 );
CHECK( spell_range( pew_id, 1 ) == 12 );
CHECK( spell_range( pew_id, 2 ) == 14 );
CHECK( spell_range( pew_id, 3 ) == 16 );
CHECK( spell_range( pew_id, 4 ) == 18 );
CHECK( spell_range( pew_id, 5 ) == 20 );
CHECK( spell_range( pew_id, 6 ) == 22 );
CHECK( spell_range( pew_id, 7 ) == 24 );
CHECK( spell_range( pew_id, 8 ) == 26 );
CHECK( spell_range( pew_id, 9 ) == 28 );
CHECK( spell_range( pew_id, 10 ) == 30 );
}
}
// Spell area of effect
// --------------------
//
// Related JSON fields:
// "min_aoe"
// "max_aoe"
// "aoe_increment"
// "max_level"
//
// Functions:
// spell::aoe
// Return spell AOE at a given level
static int spell_aoe( const spell_id &sp_id, const int spell_level )
{
spell test_spell( sp_id );
test_spell.set_level( spell_level );
return test_spell.aoe();
}
TEST_CASE( "spell area of effect", "[magic][spell][aoe]" )
{
spell_id lava_id( "test_spell_lava" );
const spell_type &lava_type = lava_id.obj();
// Level 0 AOE for this spell is 4
REQUIRE( lava_type.min_aoe == 4 );
// with 1.0 added at each level
REQUIRE( lava_type.aoe_increment == 1 );
// however, maximum AOE is 15
REQUIRE( lava_type.max_aoe == 15 );
// so maximum AOE will be reached at level 11, when
//
// Lv11 aoe = 4 + 11 * 1.0 = 15
// min lvl inc max
// Because this spell has a maximum level of 20
REQUIRE( lava_type.max_level == 20 );
// AOE from level 12-20 remains 15.
SECTION( "spell area of effect varies from min_aoe to max_aoe as level increases" ) {
CHECK( spell_aoe( lava_id, 0 ) == 4 );
CHECK( spell_aoe( lava_id, 1 ) == 5 );
CHECK( spell_aoe( lava_id, 2 ) == 6 );
// One, two, skip a few
CHECK( spell_aoe( lava_id, 10 ) == 14 );
CHECK( spell_aoe( lava_id, 11 ) == 15 );
CHECK( spell_aoe( lava_id, 12 ) == 15 );
// 99, 100
CHECK( spell_aoe( lava_id, 19 ) == 15 );
CHECK( spell_aoe( lava_id, 20 ) == 15 );
}
}
// Spell effects
// -------------
//
// Related JSON fields/values:
// "effect"
// "effect_str"
// "base_energy_cost"
// "energy_source"
//
// Functions:
// spell::cast_spell_effect
//
// TODO:
// spell_effect::spawn_item
// spell_effect::projectile_attack
// spell_effect::cone_attack
// spell_effect::target_attack
TEST_CASE( "spell effect - target_attack", "[magic][spell][effect][target_attack]" )
{
// World setup
clear_map();
// Locations for avatar and monster
const tripoint dummy_loc = { 60, 60, 0 };
const tripoint mummy_loc = { 62, 60, 0 };
// For tracking spell damage
int before_hp = 0;
int after_hp = 0;
creature_tracker &creatures = get_creature_tracker();
// Avatar/spellcaster
avatar &dummy = get_avatar();
clear_character( dummy );
dummy.setpos( dummy_loc );
REQUIRE( dummy.pos() == dummy_loc );
REQUIRE( creatures.creature_at( dummy_loc ) );
REQUIRE( g->num_creatures() == 1 );
// Monster/defender
monster &mummy = spawn_test_monster( "mon_zombie", mummy_loc );
REQUIRE( mummy.pos() == mummy_loc );
REQUIRE( creatures.creature_at( mummy_loc ) );
REQUIRE( g->num_creatures() == 2 );
// Spell with ranged target_attack effect
spell_id pew_id( "test_spell_pew" );
// Ensure the spell has the needed attributes
const spell_type &pew_type = pew_id.obj();
REQUIRE( pew_type.effect_name == "attack" );
REQUIRE( pew_type.min_damage > 0 );
REQUIRE( pew_type.min_range >= 2 );
// The spell itself
spell pew_spell( pew_id );
pew_spell.set_level( 5 );
REQUIRE( pew_spell.damage() > 0 );
REQUIRE( pew_spell.range() >= 2 );
// Ensure avatar has enough mana to cast
REQUIRE( dummy.magic->has_enough_energy( dummy, pew_spell ) );
// Cast the spell and measure the defender's change in HP
before_hp = mummy.get_hp();
pew_spell.cast_spell_effect( dummy, mummy_loc );
after_hp = mummy.get_hp();
// Should do approximately the expected damage
CHECK( before_hp - pew_spell.damage() == Approx( after_hp ).margin( 1 ) );
}
// spell_effect::spawn_summoned_monster
TEST_CASE( "spell effect - summon", "[magic][spell][effect][summon]" )
{
clear_map();
// Avatar/spellcaster and summoned mummy locations
const tripoint dummy_loc = { 60, 60, 0 };
const tripoint mummy_loc = { 61, 60, 0 };
avatar &dummy = get_avatar();
creature_tracker &creatures = get_creature_tracker();
clear_character( dummy );
dummy.setpos( dummy_loc );
REQUIRE( dummy.pos() == dummy_loc );
REQUIRE( creatures.creature_at( dummy_loc ) );
REQUIRE( g->num_creatures() == 1 );
spell_id mummy_id( "test_spell_tp_mummy" );
spell mummy_spell( mummy_id );
REQUIRE( dummy.magic->has_enough_energy( dummy, mummy_spell ) );
// Summon the mummy in the adjacent space
mummy_spell.cast_spell_effect( dummy, mummy_loc );
CHECK( creatures.creature_at( mummy_loc ) );
CHECK( g->num_creatures() == 2 );
}
// spell_effect::recover_energy
TEST_CASE( "spell effect - recover_energy", "[magic][spell][effect][recover_energy]" )
{
// Takes recovery amount from sp.damage
// Takes energy source from sp.effect_data
// For these effects, positive "damage" is good (adding to internal reservoirs):
// MANA: p.magic.mod_mana
// STAMINA: p.mod_stamina
// HEALTH: p.mod_livestyle (hidden health stat)
// BIONIC: p.mod_power_level (positive) OR p.mod_stamina (negative)
//
// For these effects, negative "damage" is good (reducing the amount of a bad thing)
// FATIGUE: p.mod_fatigue
// PAIN: p.mod_pain_resist or p_mod_pain
// NOTE: This spell effect cannot be used for healing HP.
// For that, "target_attack" with a negative damage is used.
// Yer a wizard, ya dummy
avatar &dummy = get_avatar();
clear_character( dummy );
clear_map();
SECTION( "recover stamina" ) {
spell_id montage_id( "test_spell_montage" );
const spell_type &montage_type = montage_id.obj();
// This spell recovers stamina
REQUIRE( montage_type.effect_name == "recover_energy" );
REQUIRE( montage_type.effect_str == "STAMINA" );
// at the cost of a substantial amount of mana
REQUIRE( montage_type.base_energy_cost == 800 );
REQUIRE( montage_type.energy_source == magic_energy_type::mana );
// At level 0, recovers 1000 stamina (10% of maximum)
REQUIRE( montage_type.min_damage == 1000 );
// and at level 10, recovers 10000 stamina (all of it)
REQUIRE( montage_type.max_damage == 10000 );
// Ensure avatar needs some stamina
int start_stamina = dummy.get_stamina_max() / 2;
dummy.set_stamina( start_stamina );
REQUIRE( dummy.get_stamina() == start_stamina );
// Cast montage spell on avatar
spell montage_spell( montage_id );
montage_spell.cast_spell_effect( dummy, dummy.pos() );
// Get stamina back equal to min_damage (at level 0)
CHECK( dummy.get_stamina() == start_stamina + montage_type.min_damage );
}
SECTION( "reduce pain" ) {
spell_id kiss_id( "test_spell_kiss" );
const spell_type &kiss_type = kiss_id.obj();
REQUIRE( kiss_type.effect_name == "recover_energy" );
REQUIRE( kiss_type.effect_str == "PAIN" );
// Positive "damage" for pain gives relief from pain
REQUIRE( kiss_type.min_damage == 1 );
REQUIRE( kiss_type.max_damage == 10 );
REQUIRE( kiss_type.damage_increment == 1 );
spell kiss_spell( kiss_id );
dummy.set_pain( 5 );
REQUIRE( dummy.get_pain() == 5 );
kiss_spell.cast_spell_effect( dummy, dummy.pos() );
CHECK( dummy.get_pain() == 4 );
kiss_spell.cast_spell_effect( dummy, dummy.pos() );
CHECK( dummy.get_pain() == 3 );
kiss_spell.cast_spell_effect( dummy, dummy.pos() );
CHECK( dummy.get_pain() == 2 );
}
}