from abc import abstractmethod
from pydantic import BaseModel
from simaple.core.base import AttackType, BaseStatType, Stat
from simaple.spec.loadable import ( # pylint:disable=unused-import
TaggedNamespacedABCMeta,
)
[docs]class DamageLogic(BaseModel, metaclass=TaggedNamespacedABCMeta(kind="DamageLogic")):
attack_range_constant: float
mastery: float
[docs] @abstractmethod
def get_base_stat_factor(self, stat: Stat) -> float:
...
[docs] @abstractmethod
def get_major_stat(self, stat: Stat) -> float:
...
[docs] def get_attack_type_factor(self, stat: Stat) -> float:
return stat.get_attack_coefficient(self.get_attack_type())
[docs] @abstractmethod
def get_attack_type(self) -> AttackType:
...
[docs] @abstractmethod
def get_best_level_based_stat(self, level: int) -> Stat:
...
[docs] @abstractmethod
def get_symbol_stat(self, value: int) -> Stat:
"""value may given as hundreds; to represent arcane and authentic both"""
[docs] def get_maximum_attack_range(self, stat: Stat) -> float:
"""Maximum stat power in character status window."""
return (
(1 + (stat.damage_multiplier) * 0.01)
* (1 + 0.01 * stat.final_damage_multiplier)
* self.get_base_stat_factor(stat)
* self.get_attack_type_factor(stat)
* self.attack_range_constant
* 0.01
)
[docs] def get_minimum_attack_range(self, stat: Stat) -> float:
return self.get_maximum_attack_range(stat) * self.mastery
def _get_general_damage_factor(self, stat: Stat) -> float:
return (1 + (stat.boss_damage_multiplier + stat.damage_multiplier) * 0.01) * (
1 + 0.01 * stat.final_damage_multiplier
)
[docs] def get_armor_factor(self, stat: Stat, armor: int) -> float:
return 1 - 0.0001 * (armor * (100 - stat.ignored_defence))
[docs] def get_critical_factor(self, stat: Stat) -> float:
return 1 + (35 + stat.critical_damage) * min(100, stat.critical_rate) * 0.0001
[docs] def get_damage_factor(self, stat: Stat, armor: int = 300) -> float:
"""Averaged Multiplier for skill damage."""
return (
self._get_general_damage_factor(stat)
* self.get_armor_factor(stat, armor)
* self.get_critical_factor(stat)
* self.get_base_stat_factor(stat)
* self.get_attack_type_factor(stat)
* self.get_elemental_disadvantage(stat)
* self.attack_range_constant
* 0.01
* ((1 + self.mastery) / 2)
)
[docs] def get_dot_factor(self, stat: Stat, armor: int = 300) -> float:
return (
self.get_base_stat_factor(stat)
* self.get_attack_type_factor(stat)
* self.attack_range_constant
* 0.01
)
[docs] def get_elemental_disadvantage(self, stat: Stat) -> float:
return 0.5 * (1 + min(100, stat.elemental_resistance) * 0.01)
[docs]class STRBasedDamageLogic(DamageLogic):
[docs] def get_major_stat(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(BaseStatType.STR)
[docs] def get_base_stat_factor(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(
BaseStatType.STR
) * 4 + stat.get_base_stat_coefficient(BaseStatType.DEX)
[docs] def get_attack_type(self) -> AttackType:
return AttackType.attack_power
[docs] def get_best_level_based_stat(self, level: int) -> Stat:
return Stat(
STR=level * 5 + 18,
DEX=4,
INT=4,
LUK=4,
)
[docs] def get_symbol_stat(self, value: int) -> Stat:
return Stat(STR_static=value)
[docs]class INTBasedDamageLogic(DamageLogic):
[docs] def get_major_stat(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(BaseStatType.INT)
[docs] def get_base_stat_factor(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(
BaseStatType.INT
) * 4 + stat.get_base_stat_coefficient(BaseStatType.LUK)
[docs] def get_attack_type(self) -> AttackType:
return AttackType.magic_attack
[docs] def get_best_level_based_stat(self, level: int) -> Stat:
return Stat(
STR=4,
DEX=4,
INT=level * 5 + 18,
LUK=4,
)
[docs] def get_symbol_stat(self, value: int) -> Stat:
return Stat(INT_static=value)
[docs]class DEXBasedDamageLogic(DamageLogic):
[docs] def get_major_stat(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(BaseStatType.DEX)
[docs] def get_base_stat_factor(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(
BaseStatType.DEX
) * 4 + stat.get_base_stat_coefficient(BaseStatType.STR)
[docs] def get_attack_type(self) -> AttackType:
return AttackType.attack_power
[docs] def get_best_level_based_stat(self, level: int) -> Stat:
return Stat(
STR=4,
DEX=level * 5 + 18,
INT=4,
LUK=4,
)
[docs] def get_symbol_stat(self, value: int) -> Stat:
return Stat(DEX_static=value)
[docs]class LUKBasedDamageLogic(DamageLogic):
[docs] def get_major_stat(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(BaseStatType.LUK)
[docs] def get_base_stat_factor(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(
BaseStatType.LUK
) * 4 + stat.get_base_stat_coefficient(BaseStatType.DEX)
[docs] def get_attack_type(self) -> AttackType:
return AttackType.attack_power
[docs] def get_best_level_based_stat(self, level: int) -> Stat:
return Stat(
STR=4,
DEX=4,
INT=4,
LUK=level * 5 + 18,
)
[docs] def get_symbol_stat(self, value: int) -> Stat:
return Stat(LUK_static=value)
[docs]class LUKBasedDualSubDamageLogic(DamageLogic):
[docs] def get_major_stat(self, stat: Stat) -> float:
return stat.get_base_stat_coefficient(BaseStatType.LUK)
[docs] def get_base_stat_factor(self, stat: Stat) -> float:
return (
stat.get_base_stat_coefficient(BaseStatType.LUK) * 4
+ stat.get_base_stat_coefficient(BaseStatType.DEX)
+ stat.get_base_stat_coefficient(BaseStatType.STR)
)
[docs] def get_attack_type(self) -> AttackType:
return AttackType.attack_power
[docs] def get_best_level_based_stat(self, level: int) -> Stat:
return Stat(
STR=4,
DEX=4,
INT=4,
LUK=level * 5 + 18,
)
[docs] def get_symbol_stat(self, value: int) -> Stat:
return Stat(LUK_static=value)