diff --git a/components/__init__.py b/components/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/ai.py b/components/ai.py new file mode 100644 index 0000000..e3997be --- /dev/null +++ b/components/ai.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import List, Tuple + +import numpy as np # type: ignore +import tcod + +from actions import Action +from components.base_component import BaseComponent + + +class BaseAI(Action, BaseComponent): + def perform(self) -> None: + raise NotImplementedError() + + def get_path_to(self, dest_x: int, dest_y: int) -> List[Tuple[int, int]]: + """Compute and return a path to the target position. + + IF there is no valid path then returns an empty list. + """ + # Copy the walkable array. + cost = np.array(self.entity.gamemap.tiles["walkable"], dtype=np.int8) + + for entity in self.entity.gamemap.entities: + # Check that an entity blocks movement and the cost isn't zero (blocking.) + if entity.blocks_movement and cost[entity.x, entity.y]: + # Add to the cost of a blocked position. + # A lower number means more enemies will crowd behind each other in + # hallways. A higher number means enemies will take longer paths in + # order to surround the player. + cost[entity.x, entity.y] += 10 + + # Create a graph from the cost array and pass that graph to a new pathfinder. + graph = tcod.path.SimpleGraph(cost, cardinal=2, diagonal=3) + pathfinder = tcod.path.Pathfinder(graph) + + pathfinder.add_root((self.entity.x, self.entity.y)) # Start position + + # Compute the path to the destination and remove the starting point. + path: List[List[int]] = pathfinder.path_to((dest_x, dest_y))[1:].tolist() + + # Convert from List[List[int]] to List[Tuple[int, int]]. + return [(index[0], index[1]) for index in path] \ No newline at end of file diff --git a/components/base_component.py b/components/base_component.py new file mode 100644 index 0000000..58a8b99 --- /dev/null +++ b/components/base_component.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from engine import Engine + from entity import Entity + + +class BaseComponent: + entity: Entity # Owning entity instance + + @property + def engine(self) -> Engine: + return self.entity.gamemap.engine diff --git a/components/fighter.py b/components/fighter.py new file mode 100644 index 0000000..f39cd9b --- /dev/null +++ b/components/fighter.py @@ -0,0 +1,17 @@ +from components.base_component import BaseComponent + + +class Fighter(BaseComponent): + def __init__(self, hp: int, defense: int, power: int): + self.max_hp = hp + self._hp = hp + self.defense = defense + self.power = power + + @property + def hp(self) -> int: + return self._hp + + @hp.setter + def hp(self, value: int) -> None: + self._hp = max(0, min(value, self.max_hp))