from __future__ import annotations
from typing import TYPE_CHECKING
import pygame
from snecs import RegisteredComponent
from nqp.base_classes.animation import Animation
from nqp.base_classes.image import Image
from nqp.command.unit import Unit
from nqp.core.constants import DamageType, EntityFacing, HealingSource
from nqp.world_elements.stats import FloatStat, IntStat
from nqp.world_elements.unit_attribute import UnitAttribute
if TYPE_CHECKING:
from typing import List, Tuple
from nqp.command.basic_entity_behaviour import BasicEntityBehaviour
__all__ = [
"Position",
"Aesthetic",
"Tracked",
"Stats",
"Allegiance",
"AI",
"RangedAttack",
"DamageReceived",
"IsDead",
"IsReadyToAttack",
"Attributes",
"HealReceived",
]
[docs]class Position(RegisteredComponent):
"""
An Entity's location in the world.
"""
[docs] def __init__(self, pos: pygame.Vector2):
self.pos: pygame.Vector2 = pos
[docs] def serialize(self):
return self.pos
[docs] @classmethod
def deserialize(cls, pos: pygame.Vector2):
return Position(pos)
@property
def x(self) -> float:
return self.pos.x
@x.setter
def x(self, value: int | float):
self.pos = pygame.Vector2(value, self.pos.y)
@property
def y(self) -> float:
return self.pos.y
@y.setter
def y(self, value: int | float):
self.pos = pygame.Vector2(self.pos.x, value)
[docs]class Aesthetic(RegisteredComponent):
"""
An Entity's visual information
"""
[docs] def __init__(self, animation: Animation):
self.animation: Animation = animation
self.facing: EntityFacing = EntityFacing.RIGHT
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return Aesthetic(*serialised)
[docs]class Tracked(RegisteredComponent):
"""
A component to track an entity's actions.
"""
[docs] def __init__(self):
pass
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return Tracked(*serialised)
[docs]class Stats(RegisteredComponent):
"""
An Entity's stats, such as attack.
"""
[docs] def __init__(self, parent_unit: Unit):
self.health: IntStat = IntStat(parent_unit.health)
self.mundane_defence: IntStat = IntStat(parent_unit.mundane_defence)
self.magic_defence: IntStat = IntStat(parent_unit.magic_defence)
self.attack: IntStat = IntStat(parent_unit.attack)
self.damage_type: DamageType = DamageType[parent_unit.damage_type.upper()]
self.range: IntStat = IntStat(parent_unit.range)
self.attack_speed: FloatStat = FloatStat(parent_unit.attack_speed)
self.move_speed: IntStat = IntStat(parent_unit.move_speed)
self.size: IntStat = IntStat(parent_unit.size)
self.weight: IntStat = IntStat(parent_unit.weight)
self.penetration: IntStat = IntStat(parent_unit.penetration)
self.crit_chance: IntStat = IntStat(parent_unit.crit_chance)
self.regen: IntStat = IntStat(parent_unit.regen)
self.dodge: IntStat = IntStat(parent_unit.dodge)
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return Stats(*serialised)
[docs] @classmethod
def get_stat_names(cls) -> List[str]:
"""
Get a list of all the stats.
N.B. this is manually populated so if a stat is missing check here.
"""
stat_attrs = [
"health",
"mundane_defence",
"magic_defence",
"attack",
"damage_type",
"range",
"attack_speed",
"move_speed",
"size",
"weight",
"penetration",
"crit_chance",
"regen",
"dodge",
]
return stat_attrs
[docs]class Allegiance(RegisteredComponent):
"""
An Entity's allegiance.
"""
[docs] def __init__(self, team: str, unit: Unit):
self.team: str = team
self.unit: Unit = unit
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return Allegiance(*serialised)
[docs]class AI(RegisteredComponent):
"""
An Entity's AI. This should handle the outputs of AI decisions and actions.
"""
[docs] def __init__(self, behaviour: BasicEntityBehaviour):
self.behaviour: BasicEntityBehaviour = behaviour
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
return AI()
[docs]class RangedAttack(RegisteredComponent):
"""
An Entity's ability to use projectiles.
"""
[docs] def __init__(self, ammo: int, projectile_sprite: Image, projectile_speed: int):
self.ammo: IntStat = IntStat(ammo)
self.projectile_sprite: Image = projectile_sprite
self.projectile_speed: int = projectile_speed
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return Allegiance(*serialised)
[docs]class DamageReceived(RegisteredComponent):
"""
Damage to be applied to the Entity.
"""
[docs] def __init__(self, amount: int, damage_type: DamageType, penetration: int, is_crit: bool):
self.amount: int = amount
self.type: DamageType = damage_type
self.penetration: int = penetration
self.is_crit: bool = is_crit
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return DamageReceived(*serialised)
[docs]class HealReceived(RegisteredComponent):
"""
Healing to be applied to the Entity.
"""
[docs] def __init__(self, amount: int, healing_source: HealingSource):
self.heals: List[Tuple[int, HealingSource]] = [(amount, healing_source)]
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return DamageReceived(*serialised)
[docs] def add_heal(self, amount: int, healing_source: HealingSource):
self.heals.append((amount, healing_source))
[docs]class IsDead(RegisteredComponent):
"""
Flag to indicate if the Entity is dead.
"""
[docs] def __init__(self):
self.is_processed: bool = False
# doesnt need serialising as will never be dead when saving.
[docs]class IsReadyToAttack(RegisteredComponent):
"""
Flag to indicate if the Entity is ready to attack.
"""
# doesnt need init as has no details
# doesnt need serialising as will never be about to attack when saving.
[docs]class Attributes(RegisteredComponent):
"""
A series of flags defining the attributes of a Unit
"""
[docs] def __init__(self):
self.can_be_healed_by_other = UnitAttribute(True)
self.can_be_healed_by_self = UnitAttribute(True)
[docs] def serialize(self):
# TODO - add serialisation
return True
[docs] @classmethod
def deserialize(cls, *serialised):
# TODO - add deserialisation
return Attributes(*serialised)