Skip to content

Commit

Permalink
gh-1843 Способность монстров "уничтожить вещь из рюкзака" (#2177)
Browse files Browse the repository at this point in the history
gh-1834 Способность монстров "уничтожить вещь из рюкзака"
  • Loading branch information
Redneck-prm authored and Tiendil committed Dec 9, 2017
1 parent 5ec712b commit 57988f5
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/the_tale/the_tale/game/actions/battle.py
Expand Up @@ -68,6 +68,10 @@ def max_health(self): return self.actor.max_health
@property
def mob_type(self): return self.actor.mob_type

@property
def bag(self):
return getattr(self.actor, 'bag', None)

def change_health(self, value):
# TODO: change for heal & kick methods?
old_health = self.actor.health
Expand Down
3 changes: 2 additions & 1 deletion src/the_tale/the_tale/game/actions/fake.py
Expand Up @@ -7,14 +7,15 @@

class FakeActor(object):

def __init__(self, name='fake_actor', damage=None, max_health=100, context=None, level=7, mob_type=None):
def __init__(self, name='fake_actor', damage=None, max_health=100, context=None, level=7, mob_type=None, bag=None):
self.name = name
self.basic_damage = damage if damage is not None else Damage(10, 10)
self.max_health = max_health
self.health = max_health
self.level = level
self.context = context if context else BattleContext()
self.mob_type = mob_type
self.bag = bag

@property
def initiative(self): return 1
Expand Down
8 changes: 8 additions & 0 deletions src/the_tale/the_tale/game/heroes/bag.py
@@ -1,4 +1,5 @@
# coding: utf-8
import random

from the_tale.game.balance.power import Power

Expand Down Expand Up @@ -54,6 +55,13 @@ def pop_artifact(self, artifact):
self.mark_updated()
del self.bag[artifact.bag_uuid]

def pop_random_artifact(self):
if self.is_empty:
return None
artifact = random.choice(list(self.values()))
self.pop_artifact(artifact)
return artifact

def get(self, artifact_id):
return self.bag.get(artifact_id, None)

Expand Down
55 changes: 55 additions & 0 deletions src/the_tale/the_tale/game/heroes/habilities/battle.py
Expand Up @@ -36,6 +36,61 @@ def on_miss(self, messenger, actor, enemy):
messenger.add_message('hero_ability_hit_miss', attacker=actor, defender=enemy)


class CHARGE(AbilityPrototype):
TYPE = relations.ABILITY_TYPE.BATTLE
ACTIVATION_TYPE = relations.ABILITY_ACTIVATION_TYPE.ACTIVE
LOGIC_TYPE = relations.ABILITY_LOGIC_TYPE.WITH_CONTACT
AVAILABILITY = relations.ABILITY_AVAILABILITY.FOR_MONSTERS
MAX_LEVEL = 1
HAS_DAMAGE = True
NAME = 'Заряд'
normalized_name = NAME
DESCRIPTION = 'Боец создаёт электрический разряд, который может повредить не только противника, но и его вещи (не экипировку).'
DAMAGE_MODIFIER = [1.00]

# расчитываем приоритет так, чтобы математическое ожидание вероятности поломки предмета в бою было равно 1
# (STRIKES_NUMBER * PRIORITY / MEDIUM_ABILITIES_PRIORITY) * STAFF_DESTROY_CHANCE = 1
# PRIORITY = 1 / (STRIKES_NUMBER / MEDIUM_ABILITIES_PRIORITY * STAFF_DESTROY_CHANCE)

STRIKES_NUMBER = c.BATTLE_LENGTH / 2 # один ход - один удар, т.е. один противник в среднем атакует в 2 раза меньше
MEDIUM_ABILITIES_PRIORITY = HIT.PRIORITY[0] + 10 * 2.5 # средний суммарный приоритет: приоритет удара + приоритет среднего колличества способностей у монстров
STAFF_DESTROY_CHANCE = 0.5 # подобран чтобы способность имела разумный приоритет

PRIORITY = [int(MEDIUM_ABILITIES_PRIORITY / (STRIKES_NUMBER * STAFF_DESTROY_CHANCE))]

@property
def damage_modifier(self):
return self.DAMAGE_MODIFIER[self.level - 1]

def pop_random_item_from_bag(self, enemy):
if enemy.bag is None:
return None
return enemy.bag.pop_random_artifact()

def use(self, messenger, actor, enemy):
damage = actor.basic_damage * self.damage_modifier
damage = actor.context.modify_outcoming_damage(damage)
damage = enemy.context.modify_incoming_damage(damage)
enemy.change_health(-damage.total)

if self.STAFF_DESTROY_CHANCE >= random.random():
artifact = self.pop_random_item_from_bag(enemy)
if artifact:
messenger.add_message('hero_ability_charge_hit_and_destroy',
attacker=actor,
defender=enemy,
damage=damage.total,
artifact=artifact)
return

messenger.add_message('hero_ability_charge_hit_only',
attacker=actor,
defender=enemy,
damage=damage.total)

def on_miss(self, messenger, actor, enemy):
messenger.add_message('hero_ability_charge_miss', attacker=actor, defender=enemy)

class STRONG_HIT(AbilityPrototype):

TYPE = relations.ABILITY_TYPE.BATTLE
Expand Down
13 changes: 13 additions & 0 deletions src/the_tale/the_tale/game/heroes/tests/test_bag.py
Expand Up @@ -75,6 +75,19 @@ def test_pop_artifact(self):

self.assertEqual(self.bag.bag, {})

def test_pop_random_artifact_empty_bag(self):
popped_artifact = self.bag.pop_random_artifact()
self.assertEqual(self.bag.bag, {})
self.assertIsNone(popped_artifact)

def test_pop_random_artifact(self):
for i in range(3):
artifact = artifacts_storage.generate_artifact_from_list(artifacts_storage.artifacts, 1, rarity=RARITY.NORMAL)
self.bag.put_artifact(artifact)
for i in range(3):
popped_artifact = self.bag.pop_random_artifact()
self.assertIsNotNone(popped_artifact)
self.assertEqual(self.bag.bag, {})

def test_ui_info_cache(self):
artifact = artifacts_storage.generate_artifact_from_list(artifacts_storage.artifacts, 1, rarity=RARITY.NORMAL)
Expand Down
29 changes: 29 additions & 0 deletions src/the_tale/the_tale/game/heroes/tests/test_habilities.py
Expand Up @@ -23,6 +23,9 @@
from the_tale.game.actions.contexts.battle import Damage

from the_tale.game.heroes.fake import FakeMessenger
from the_tale.game.heroes import bag
from the_tale.game.artifacts.storage import artifacts_storage
from the_tale.game.artifacts.relations import RARITY

from the_tale.game.heroes.habilities import battle as battle_abilities
from the_tale.game.heroes.habilities import modifiers as modifiers_abilities
Expand Down Expand Up @@ -151,6 +154,8 @@ def setUp(self):
self.attacker = FakeActor(name='attacker')
self.defender = FakeActor(name='defender')

create_test_map()

def tearDown(self):
pass

Expand All @@ -169,6 +174,30 @@ def test_hit(self):
self.assertTrue(self.defender.health < self.defender.max_health)
self.assertEqual(self.messenger.messages, ['hero_ability_hit'])

def test_charge_enemy_without_bag(self):
battle_abilities.CHARGE().use(self.messenger, self.attacker, self.defender)
self.assertTrue(self.defender.health < self.defender.max_health)
self.assertEqual(self.messenger.messages, ['hero_ability_charge_hit_only'])

def test_charge_enemy_with_empty_bag(self):
self.defender.bag = bag.Bag()
battle_abilities.CHARGE().use(self.messenger, self.attacker, self.defender)
self.assertTrue(self.defender.health < self.defender.max_health)
self.assertEqual(self.messenger.messages, ['hero_ability_charge_hit_only'])

@mock.patch('the_tale.game.heroes.habilities.battle.CHARGE.STAFF_DESTROY_CHANCE', 1)
def test_charge_enemy_with_not_empty_bag(self):
self.defender.bag = bag.Bag()
artifact = artifacts_storage.generate_artifact_from_list(artifacts_storage.artifacts,
1,
rarity=RARITY.NORMAL)

self.defender.bag.put_artifact(artifact)
charge = battle_abilities.CHARGE()
charge.use(self.messenger, self.attacker, self.defender)
self.assertEqual(self.messenger.messages, ['hero_ability_charge_hit_and_destroy'])
self.assertTrue(self.defender.bag.is_empty)

def test_magic_mushroom(self):
battle_abilities.MAGIC_MUSHROOM().use(self.messenger, self.attacker, self.defender)
self.assertTrue(self.attacker.context.ability_magic_mushroom)
Expand Down
13 changes: 13 additions & 0 deletions src/the_tale/the_tale/linguistics/lexicon/groups/hero_ability.py
Expand Up @@ -82,4 +82,17 @@
'Атакующий промахнулся при использовании способности «Безрассудная атака»',
[V.DATE, V.TIME, V.ATTACKER, V.DEFENDER], None),

('HERO_ABILITY_CHARGE_HIT_AND_DESTROY', 280020, 'Журнал: Заряд (урон по герою и разрушение вещи)', LEXICON_GROUP.HERO_ABILITY,
'Атакующий провел удар-заряд, и сломал одну вещь в рюкзаке',
[V.DATE, V.TIME, V.ATTACKER, V.DEFENDER, V.ARTIFACT], None),

('HERO_ABILITY_CHARGE_HIT_ONLY', 280021, 'Журнал: Заряд (только урон по герою)', LEXICON_GROUP.HERO_ABILITY,
'Атакующий провел удар-заряд, нанес урон, но не попал по рюкзаку',
[V.DATE, V.TIME, V.ATTACKER, V.DEFENDER], None),

('HERO_ABILITY_CHARGE_MISS', 280022, 'Журнал: Заряд (промах)', LEXICON_GROUP.HERO_ABILITY,
'Атакующий промахнулся при использовании способности Заряд',
[V.DATE, V.TIME, V.ATTACKER, V.DEFENDER], None),


]
Expand Up @@ -65,7 +65,7 @@ class LEXICON_GROUP(DjangoEnum):

('HERO_ABILITY', 14, 'Способности', 140000,
'Описание применения способностей героем (или монстром)',
{V.ATTACKER: 'атакующий', V.HEALTH: 'количество вылеченного здоровья', V.DAMAGE: 'количество урона', V.ATTACKER_DAMAGE: 'количество урона по атакующему', V.ACTOR: 'герой или монстр', V.DEFENDER: 'защищающийся', V.COMPANION: 'спутник', V.DATE: 'дата', V.TIME: 'время'}),
{V.ATTACKER: 'атакующий', V.HEALTH: 'количество вылеченного здоровья', V.DAMAGE: 'количество урона', V.ATTACKER_DAMAGE: 'количество урона по атакующему', V.ACTOR: 'герой или монстр', V.DEFENDER: 'защищающийся', V.COMPANION: 'спутник', V.DATE: 'дата', V.TIME: 'время', V.ARTIFACT: 'артефакт'}),

('HERO_COMMON', 15, 'Общие сообщения, относящиеся к герою', 150000,
'Сообщение, относящиеся к герою и не вошедшие в другие модули',
Expand Down

0 comments on commit 57988f5

Please sign in to comment.