Source code for tests.engine.test_effect

from typing import Tuple

import pytest
import snecs
from snecs.typedefs import EntityID

import scripts.engine.core.matter
from scripts.engine.core import query, state, world
from scripts.engine.core.component import Afflictions, CombatStats, Knowledge, Position, Resources
from scripts.engine.core.effect import (
    AffectCooldownEffect,
    AffectStatEffect,
    AlterTerrainEffect,
    ApplyAfflictionEffect,
    DamageEffect,
    MoveOtherEffect,
    MoveSelfEffect,
)
from scripts.engine.internal import library
from scripts.engine.internal.constant import DamageType, Direction, PrimaryStat
from scripts.engine.internal.data import store
from scripts.engine.internal.definition import ActorData
from scripts.engine.world_objects.game_map import GameMap
from scripts.nqp.command import register_actions

##############################################################
#
# !IMPORTANT! Using pytest-benchmark causes the damage effect test to hang indefinitely and the others to fail
#
###############################################################



# register all actions with engine
register_actions()


def _create_scenario() -> Tuple[EntityID, EntityID]:

    # new world
    empty_world = snecs.World()
    world.move_world(empty_world)

    # new gamemap
    game_map = GameMap("debug", 10)
    store.current_game_map = game_map
    actor_data = ActorData(
        key="test",
        possible_names=["test_name"],
        description="test_desc",
        position_offsets=[(0, 0)],
        trait_names=["shoom", "soft_tops", "dandy"],
    )
    game_map.generate_new_map(actor_data)
    
    # add second entity
    entity1 = scripts.engine.core.matter.get_player()
    pos = scripts.engine.core.matter.get_entitys_component(entity1, Position)
    entity2 = scripts.engine.core.matter.create_actor(actor_data, (pos.x + 1, pos.y))

    return entity1, entity2


test_damage_effect_parameters = [
    # stat to target
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.CLOUT, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.SKULLDUGGERY, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.BUSTLE, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.EXACTITUDE, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),

    # accuracy
    (PrimaryStat.VIGOUR, 100, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.VIGOUR, -1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),

    # damage
    (PrimaryStat.VIGOUR, 1, 100, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.VIGOUR, 1, -1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.VIGOUR, 1, 0, DamageType.BURN, PrimaryStat.VIGOUR, 1, 1),

    # damage type
    (PrimaryStat.VIGOUR, 1, 1, DamageType.CHEMICAL, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.ASTRAL, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.COLD, PrimaryStat.VIGOUR, 1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.MUNDANE, PrimaryStat.VIGOUR, 1, 1),

    # mod stats
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.CLOUT, 1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.SKULLDUGGERY, 1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.BUSTLE, 1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.EXACTITUDE, 1, 1),

    # mod amount
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 100, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, -1, 1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 0, 1),

    # potency
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 100),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, -1),
    (PrimaryStat.VIGOUR, 1, 1, DamageType.BURN, PrimaryStat.VIGOUR, 1, 0),
]


[docs]@pytest.mark.parametrize(["stat_to_target", "accuracy", "damage", "damage_type", "mod_stat", "mod_amount", "potency"], test_damage_effect_parameters) def test_damage_effect( stat_to_target, accuracy, damage, damage_type, mod_stat, mod_amount, potency, ): entity, other_entity = _create_scenario() effect = DamageEffect( origin=entity, target=entity, success_effects=[], failure_effects=[], stat_to_target=stat_to_target, accuracy=accuracy, damage=damage, damage_type=damage_type, mod_stat=mod_stat, mod_amount=mod_amount, potency=potency ) start_hp = scripts.engine.core.matter.get_entitys_component(entity, Resources).health effect.evaluate() end_hp = scripts.engine.core.matter.get_entitys_component(entity, Resources).health # assess results # We are just testing damage can be applied, not the damage formula and as such this is simplified, otherwise # would need to consider potency, stats etc. if damage > 0: assert start_hp != end_hp elif damage <= 0: assert start_hp == end_hp
test_move_effect_parameters = [ (Direction.UP, "other"), (Direction.UP_RIGHT, "other"), (Direction.UP_LEFT, "other"), (Direction.DOWN, "other"), (Direction.DOWN_RIGHT, "other"), (Direction.DOWN_LEFT, "other"), (Direction.UP, "self"), (Direction.UP_RIGHT, "self"), (Direction.UP_LEFT, "self"), (Direction.DOWN, "self"), (Direction.DOWN_RIGHT, "self"), (Direction.DOWN_LEFT, "self"), ]
[docs]@pytest.mark.parametrize(["direction", "self_or_other"], test_move_effect_parameters) def test_move_self_effect( direction, self_or_other, ): entity, other_entity = _create_scenario() if self_or_other == "self": origin = entity target = entity else: origin = entity target = other_entity effect = MoveSelfEffect( origin=origin, target=target, direction=direction, success_effects=[], failure_effects=[], move_amount=1 ) position = scripts.engine.core.matter.get_entitys_component(target, Position) start_pos = (position.x, position.y) success = effect.evaluate()[0] end_pos = (position.x, position.y) # check didnt move other if self_or_other == "other": assert not success # assess results if success: assert start_pos[0] != end_pos[0] or start_pos[1] != end_pos[1] else: assert start_pos[0] == end_pos[0] or start_pos[1] == end_pos[1]
[docs]@pytest.mark.parametrize(["direction", "self_or_other"], test_move_effect_parameters) def test_move_other_effect( direction, self_or_other, ): entity, other_entity = _create_scenario() if self_or_other == "self": origin = entity target = entity else: origin = entity target = other_entity effect = MoveOtherEffect( origin=origin, target=target, direction=direction, success_effects=[], failure_effects=[], move_amount=1 ) position = scripts.engine.core.matter.get_entitys_component(target, Position) start_pos = (position.x, position.y) success = effect.evaluate()[0] end_pos = (position.x, position.y) # check didnt move self if self_or_other == "self": assert not success # assess results if success: assert start_pos[0] != end_pos[0] or start_pos[1] != end_pos[1] else: assert start_pos[0] == end_pos[0] or start_pos[1] == end_pos[1]
test_affect_stat_parameters = [ # stats ("foo", PrimaryStat.VIGOUR, 1), ("foo", PrimaryStat.CLOUT, 1), ("foo", PrimaryStat.EXACTITUDE, 1), ("foo", PrimaryStat.BUSTLE, 1), ("foo", PrimaryStat.SKULLDUGGERY, 1), # amounts ("foo", PrimaryStat.VIGOUR, -1), ("foo", PrimaryStat.VIGOUR, 100), ("foo", PrimaryStat.VIGOUR, -100), ]
[docs]@pytest.mark.parametrize(["cause_name", "stat_to_target", "affect_amount"], test_affect_stat_parameters) def test_affect_stat_effect( cause_name, stat_to_target, affect_amount, ): entity, other_entity = _create_scenario() effect = AffectStatEffect( origin=entity, target=entity, success_effects=[], failure_effects=[], cause_name=cause_name, stat_to_target=stat_to_target, affect_amount=affect_amount, ) stats = scripts.engine.core.matter.get_entitys_component(entity, CombatStats) start_mod = stats._get_mod_value(stat_to_target) # get mod amount success = effect.evaluate()[0] end_mod = stats._get_mod_value(stat_to_target) if success: assert start_mod + affect_amount == end_mod # N.B. should never be less than 1 assert getattr(stats, stat_to_target) >= 1 else: # no change assert start_mod == end_mod
_affliction_names = [] for name in library.AFFLICTIONS.keys(): _affliction_names.append(name)
[docs]@pytest.fixture(params=_affliction_names) def affliction_name(request): return request.param
[docs]def test_apply_affliction_effect( affliction_name, ): entity, other_entity = _create_scenario() effect = ApplyAfflictionEffect( origin=entity, target=entity, success_effects=[], failure_effects=[], affliction_name=affliction_name, duration=1, ) success = effect.evaluate()[0] end_afflictions = scripts.engine.core.matter.get_entitys_component(entity, Afflictions) names = [] for affliction in end_afflictions.active: names.append(affliction.name) if success: assert affliction_name in names else: assert affliction_name not in names
_skill_names = [] for name in library.SKILLS.keys(): _skill_names.append(name)
[docs]@pytest.fixture(params=_skill_names) def skill_name(request): return request.param
[docs]@pytest.fixture(params=[-100, -1, 0, 1, 100]) def affect_cooldown_amount(request): return request.param
[docs]def test_affect_cooldown_effect( skill_name, affect_cooldown_amount ): entity, other_entity = _create_scenario() # assign skill scripts.engine.core.matter.learn_skill(entity, skill_name) effect = AffectCooldownEffect( origin=entity, target=entity, success_effects=[], failure_effects=[], skill_name=skill_name, affect_amount=affect_cooldown_amount, ) cooldown = 99 knowledge = scripts.engine.core.matter.get_entitys_component(entity, Knowledge) knowledge.set_skill_cooldown(skill_name, cooldown) success = effect.evaluate()[0] end_cooldown = knowledge.cooldowns[skill_name] if success: assert max(cooldown - affect_cooldown_amount, 0) == end_cooldown else: assert cooldown == end_cooldown
_terrain_names = [] for name in library.TERRAIN.keys(): _terrain_names.append(name)
[docs]@pytest.fixture(params=_terrain_names) def terrain_name(request): return request.param
[docs]@pytest.fixture(params=[1, 100]) def affect_terrain_amount(request): return request.param
[docs]def test_alter_terrain_effect_create( terrain_name, affect_terrain_amount ): entity, other_entity = _create_scenario() effect = AlterTerrainEffect( origin=entity, target=entity, success_effects=[], failure_effects=[], terrain_name=terrain_name, affect_amount=affect_terrain_amount, ) success = effect.evaluate()[0] target_pos = scripts.engine.core.matter.get_entitys_component(entity, Position) if success: confirmed = False for entity, (position, identity, lifespan) in query.position_and_identity_and_lifespan: if identity.name == terrain_name and position.x == target_pos.x and position.y == target_pos.y: confirmed = True assert confirmed else: confirmed = False for entity, (position, identity, lifespan) in query.position_and_identity_and_lifespan: if identity.name == terrain_name and position.x == target_pos.x and position.y == target_pos.y: confirmed = True assert not confirmed
[docs]@pytest.fixture(params=[0, -1, -100]) def affect_terrain_negative_amount(request): return request.param
[docs]def test_alter_terrain_effect_reduce_duration( terrain_name, affect_terrain_negative_amount ): entity, other_entity = _create_scenario() # create terrain on the spot base_duration = 10 effect = AlterTerrainEffect( origin=entity, target=entity, success_effects=[], failure_effects=[], terrain_name=terrain_name, affect_amount=base_duration, ) effect.evaluate() effect = AlterTerrainEffect( origin=entity, target=entity, success_effects=[], failure_effects=[], terrain_name=terrain_name, affect_amount=affect_terrain_negative_amount, ) success = effect.evaluate()[0] target_pos = scripts.engine.core.matter.get_entitys_component(entity, Position) if success and (base_duration > affect_terrain_negative_amount): # test case: reduced duration but still exists still_exists = False duration_remaining = None for entity, (position, identity, lifespan) in query.position_and_identity_and_lifespan: if identity.name == terrain_name and position.x == target_pos.x and position.y == target_pos.y: still_exists = True duration_remaining = lifespan.duration assert still_exists assert duration_remaining == base_duration - affect_terrain_negative_amount elif success and (base_duration < affect_terrain_negative_amount): # test case: duration sufficient to remove still_exists = False for entity, (position, identity, lifespan) in query.position_and_identity_and_lifespan: if identity.name == terrain_name and position.x == target_pos.x and position.y == target_pos.y: still_exists = True assert not still_exists else: # test case: failed to create terrain; check valid reason data = library.TERRAIN[terrain_name] if data.blocks_movement: assert True else: assert False