Эффекты
Эффекты - это последний этап использования абилки и/или важнейший компонент статусов и защитных абилок. Эффекты уже не подразумевают, что у вас остается некая неопределенность, и вам нужно просто что-то сделать. Учтите, что эффекты используются и статусами и может быть где-то еще. Это функции Питона (да, точно как функции абилок!), которые вызываются в самом конце действия.
Эффект как функция
В функциях абилок эффекты вызываются напрямую, поскольку это буквально функции языка. В случае вызова по названию метода в виде строки, что делается в случае статусов и защитных абилок, сама функция вытаскивается по ключу из автоматически собирающегося словаря all_effects_functions, который лежит в combat.effects_container.py. Но вам не стоит пользоваться этим способом, хотя это строго и не возбраняется: никакой разницы нет.
Помните, в абилке выше, мы юзали финишер damage_target? Давайте посмотрим на его код.
def damage_target(ctx: AbilityContext, damage_type: str, amount: float | int):
from content.functions.effects import basic_damage
relative_buff_instructions = get_relative_buff_instructions(ctx)
amount = (amount + relative_buff_instructions.extra_damage) * relative_buff_instructions.mod_damage
target = ctx.energy_shield if ctx.energy_shield else ctx.t_bodypart
basic_damage(source=ctx.source, target_entity=ctx.t_entity, target=target, damage_type=damage_type,
amount=amount, traumatic=ctx.atk_ability.ability.traumatic)
Ну ты куда целишься-то?
В целом вам придется самостоятельно смотреть в эффекты, чтобы понять, что туда передавать. Но сразу скажу, что target в данном случае это что-то, имеющее ХП или энтити. Да, если вы хотите нанести урон в целом по энтити, то вы можете засунуть ctx.t_entity и в target_entity и в target, чтобы распределить урон между всеми частями тела (пропорция высчитывается исходя из шанса попадания по конечности)
Как видите, он:
- Импортирует загадочный basic_damage из combat.effect.common
- Извлекает из контекста инструкции релятивного баффа
- Модифицирует урон, исходя из релятивного баффа
- Использует basic_damage, передавая туда источник, энтити-цель, конкретную цель, которой может быть как энергощит, так и бодипарт,
В основном эффекты достаточно примитивные и с первого взгляда понятно, что они делают. Их список не ограничен и может расширяться, поэтому подробно расписываться в этой документации не будет. Но нет ничего сложного, чтобы понять их работу самостоятельно. Сложным, на данный момент, является только basic_damage, и мы рассмотрим его подробнее.
Эффективное использование эффектов
Эффекты вызываются по своему названию в случае статусов и защитных абилок. Подробнее мы уже рассматривавали выше, про статусы можно посмотреть на их страничке, но там использование эффектов это необходимость, а вот в случае с абилками это просто сахар, упрощающий вам жизнь. Если вы хотите сделать что-то комплексное, типа нанести урон (рассчитав ваще все резисты, снизить иммунитет и менталку, учесть броню и так далее), то используйте эффекты. Если вам нужно просто наложить статус (пускай даже со сложной логикой) или вычесть ОД, не обязательно и даже не нужно прибегать к помощи эффектов. Они нужны только там, где сильно и часто резаются. Поймите, что вы просто выносите часть логики функции абилки в эффект.
Как и функции абилок, эффекты это функции самого языка Python. Они более гибкие в вопросах полей и могут содержать сколько угодно разных аргументов. Простейший эффект выглядит так:
@turn_amount_into_mod
def mod_stamina(target: Entity, amount: int, **_) -> None:
target.stats.mod("stamina", amount)
Декоратор @turn_amount_into_mod превращает amount из флоата или инта в модификатор, чтобы интуитивно понятнее можно все было задавать в числах, а не строках.
Аргументы basic_damage
Теперь поговорим о basic_damage. Он достаточно сложный, целиком его приводить не будем, но поговорим про его аргументы.
@divide_entity_to_bodyparts
def basic_damage(source: Entity | str,
target_entity: Entity,
target: Entity | EnergyShield | PartContainer,
damage_type: str,
amount: int | float,
traumatic: bool = True,
damage_trauma_division: int = 0.5,
**_) -> None:
...
Итак, вам нужно:
- Обязательно передать туда источник урона, но он может быть строкой. Стоп, что? Да, это используется для статусов и аур, они не передают полностью вызвавшую их энтити, а только сообщают строку "status" или "aura", но вам об этом думать не нужно, да и лучше посмотреть на страничке статусов. Вы туда передаете энтити в 99% случаев. Это нужно для расчета баффов, если вы не хотите, чтобы на урон влияли генерализованные баффы, вроде "mod_outcome_damage", можете передать туда строку "status" или "aura". Почему не энам? Не знаю. Наверное, энам было бы лучше. Жду от вас PR.
- Target - обязательно, вне зависимости от того, что именно вы дамажите.
- Дальше вы передаете то, куда вы собстна целитесь, по энтити, по ее щиту (все проверки делать нужно заранее! Финишеры и декораторы в функции абилки это делают за вас), или по ее части тела. Если вы целитесь целиком по энтити, волшебный декоратор
@divide_entity_to_bodypartsразделит и вызовет эту функцию для каждой части тела с измененным уроном (распределенным с учетом шанса попадания по конечности, т.е. урон по голове будет меньше, чем по торсу), но фактически basic_damage никогда не вызывается с энтити в таргете. - Дальше вы передаете DamageType, так же в виде строки. Почему не энам, хотя он есть? Потому что его нужно хранить для статусов в словаре, а энамы плохо работают со словарями в используемой orm. Так что используем строки, но если боитесь ошибиться, всегда можно сделать .value для энама.
- Amount это объем наносимого урона. Он будет забаффан и снижен от резистов цели.
- Далее передается буль, может ли эта атака нанести травму. У некоторых абилок он может быть False. Тогда следует его передать из абилки.
- Далее передается экзотический интегер, означающий "во сколько раз урон должен превышать текущее ХП конечности, чтобы нанести урон". В 99% случаев лучше оставить его по умолчанию, но можно снизить или увеличить, чтобы сделать абилку более или менее травмоопасной.
На этом разговор об эффектах будет закончен. Они, кстати, никогда ничего не возвращают - дизайн Спайса в том, что все проверки должны проходиться заранее.