1
0

Intra-chapter refactoring

This commit is contained in:
Timothy Warren 2022-01-12 13:45:52 -05:00
parent 8e9f088c35
commit f947338c2d
6 changed files with 50 additions and 27 deletions

View File

@ -6,14 +6,12 @@ import numpy as np # type: ignore
import tcod import tcod
from actions import Action, MeleeAction, MovementAction, WaitAction from actions import Action, MeleeAction, MovementAction, WaitAction
from components.base_component import BaseComponent
if TYPE_CHECKING: if TYPE_CHECKING:
from entity import Actor from entity import Actor
class BaseAI(Action, BaseComponent): class BaseAI(Action):
entity: Actor
def get_path_to(self, dest_x: int, dest_y: int) -> List[Tuple[int, int]]: def get_path_to(self, dest_x: int, dest_y: int) -> List[Tuple[int, int]]:
"""Compute and return a path to the target position. """Compute and return a path to the target position.

View File

@ -5,11 +5,16 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from engine import Engine from engine import Engine
from entity import Entity from entity import Entity
from game_map import GameMap
class BaseComponent: class BaseComponent:
entity: Entity # Owning entity instance parent: Entity # Owning entity instance
@property
def gamemap(self) -> GameMap:
return self.parent.gamemap
@property @property
def engine(self) -> Engine: def engine(self) -> Engine:
return self.entity.gamemap.engine return self.gamemap.engine

View File

@ -12,7 +12,7 @@ if TYPE_CHECKING:
class Fighter(BaseComponent): class Fighter(BaseComponent):
entity: Actor parent: Actor
def __init__(self, hp: int, defense: int, power: int): def __init__(self, hp: int, defense: int, power: int):
self.max_hp = hp self.max_hp = hp
@ -27,23 +27,23 @@ class Fighter(BaseComponent):
@hp.setter @hp.setter
def hp(self, value: int) -> None: def hp(self, value: int) -> None:
self._hp = max(0, min(value, self.max_hp)) self._hp = max(0, min(value, self.max_hp))
if self._hp == 0 and self.entity.ai: if self._hp == 0 and self.parent.ai:
self.die() self.die()
def die(self) -> None: def die(self) -> None:
if self.engine.player is self.entity: if self.engine.player is self.parent:
death_message = "You died!" death_message = "You died!"
death_message_color = color.player_die death_message_color = color.player_die
self.engine.event_handler = GameOverEventHandler(self.engine) self.engine.event_handler = GameOverEventHandler(self.engine)
else: else:
death_message = f"{self.entity.name} is dead!" death_message = f"{self.parent.name} is dead!"
death_message_color = color.enemy_die death_message_color = color.enemy_die
self.entity.char = "%" self.parent.char = "%"
self.entity.color = (191, 0, 0) self.parent.color = (191, 0, 0)
self.entity.blocks_movement = False self.parent.blocks_movement = False
self.entity.ai = None self.parent.ai = None
self.entity.name = f"remains of {self.entity.name}" self.parent.name = f"remains of {self.parent.name}"
self.entity.render_order = RenderOrder.CORPSE self.parent.render_order = RenderOrder.CORPSE
self.engine.message_log.add_message(death_message, death_message_color) self.engine.message_log.add_message(death_message, death_message_color)

View File

@ -18,11 +18,11 @@ class Entity:
A generic object to represent players, enemies, items, etc. A generic object to represent players, enemies, items, etc.
""" """
gamemap: GameMap parent: GameMap
def __init__( def __init__(
self, self,
gamemap: Optional[GameMap] = None, parent: Optional[GameMap] = None,
x: int = 0, x: int = 0,
y: int = 0, y: int = 0,
char: str = "?", char: str = "?",
@ -38,17 +38,21 @@ class Entity:
self.name = name self.name = name
self.blocks_movement = blocks_movement self.blocks_movement = blocks_movement
self.render_order = render_order self.render_order = render_order
if gamemap: if parent:
# If gamemap isn't provided now, it will be later. # If gamemap isn't provided now, it will be later.
self.gamemap = gamemap self.parent = parent
gamemap.entities.add(self) parent.entities.add(self)
@property
def gamemap(self) -> GameMap:
return self.parent.gamemap
def spawn(self: T, gamemap: GameMap, x: int, y: int) -> T: def spawn(self: T, gamemap: GameMap, x: int, y: int) -> T:
"""Spawn a copy of this instance at the given location.""" """Spawn a copy of this instance at the given location."""
clone = copy.deepcopy(self) clone = copy.deepcopy(self)
clone.x = x clone.x = x
clone.y = y clone.y = y
clone.gamemap = gamemap clone.parent = gamemap
gamemap.entities.add(clone) gamemap.entities.add(clone)
return clone return clone
@ -57,10 +61,11 @@ class Entity:
self.x = x self.x = x
self.y = y self.y = y
if gamemap: if gamemap:
if hasattr(self, "gamemap"): # Possibly uninitialized if hasattr(self, "parent"): # Possibly uninitialized
if self.parent is self.gamemap:
self.gamemap.entities.remove(self) self.gamemap.entities.remove(self)
self.gamemap = gamemap self.parent = gamemap
gamemap.entities.add(self) gamemap.entities.add(self)
def move(self, dx: int, dy: int): def move(self, dx: int, dy: int):
@ -94,7 +99,7 @@ class Actor(Entity):
self.ai: Optional[BaseAI] = ai_cls(self) self.ai: Optional[BaseAI] = ai_cls(self)
self.fighter = fighter self.fighter = fighter
self.fighter.entity = self self.fighter.parent = self
@property @property
def is_alive(self) -> bool: def is_alive(self) -> bool:

View File

@ -37,6 +37,10 @@ class GameMap:
order="F" order="F"
) # Tiles the player has seen before ) # Tiles the player has seen before
@property
def gamemap(self) -> GameMap:
return self
@property @property
def actors(self) -> Iterator[Actor]: def actors(self) -> Iterator[Actor]:
"""Iterate over this map's living actors.""" """Iterate over this map's living actors."""

View File

@ -1,4 +1,4 @@
from typing import List, Reversible, Tuple from typing import Iterable, List, Reversible, Tuple
import textwrap import textwrap
import tcod import tcod
@ -57,7 +57,18 @@ class MessageLog:
self.render_messages(console, x, y, width, height, self.messages) self.render_messages(console, x, y, width, height, self.messages)
@staticmethod @staticmethod
def wrap(string: str, width: int) -> Iterable[str]:
"""Return a wrapped text message."""
for line in string.splitlines(): # Handle newlines in messages.
yield from textwrap.wrap(
line,
width,
expand_tabs=True
)
@classmethod
def render_messages( def render_messages(
cls,
console: tcod.Console, console: tcod.Console,
x: int, x: int,
y: int, y: int,
@ -72,7 +83,7 @@ class MessageLog:
y_offset = height - 1 y_offset = height - 1
for message in reversed(messages): for message in reversed(messages):
for line in reversed(textwrap.wrap(message.full_text, width)): for line in reversed(list(cls.wrap(message.full_text, width))):
console.print(x=x, y=y + y_offset, string=line, fg=message.fg) console.print(x=x, y=y + y_offset, string=line, fg=message.fg)
y_offset -= 1 y_offset -= 1
if y_offset < 0: if y_offset < 0: