Move the renderable part of Entity to a Renderable component
Move symbol, render order, foreground, and background properties on Entity to a new Component called Renderable.
This commit is contained in:
parent
01b549bc6e
commit
def79386d8
5 changed files with 80 additions and 71 deletions
|
@ -1,7 +1,8 @@
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
import random
|
import random
|
||||||
from typing import Optional
|
from enum import Enum
|
||||||
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
class Component:
|
class Component:
|
||||||
|
@ -75,3 +76,42 @@ class Fighter(Component):
|
||||||
def _reset_passive_heal_clock(self) -> None:
|
def _reset_passive_heal_clock(self) -> None:
|
||||||
self.__ticks_since_last_passive_heal = 0
|
self.__ticks_since_last_passive_heal = 0
|
||||||
self.__ticks_for_next_passive_heal = random.randint(30, 70)
|
self.__ticks_for_next_passive_heal = random.randint(30, 70)
|
||||||
|
|
||||||
|
|
||||||
|
class Renderable(Component):
|
||||||
|
class Order(Enum):
|
||||||
|
'''
|
||||||
|
These values indicate the order that an entity with a Renderable
|
||||||
|
component should be rendered. Higher values are rendered later and
|
||||||
|
therefore on top of items with lower orderings.
|
||||||
|
'''
|
||||||
|
ITEM = 1000
|
||||||
|
ACTOR = 2000
|
||||||
|
HERO = 3000
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
order: Order = Order.ACTOR,
|
||||||
|
fg: Optional[Tuple[int, int, int]] = None,
|
||||||
|
bg: Optional[Tuple[int, int, int]] = None):
|
||||||
|
if len(symbol) != 1:
|
||||||
|
raise ValueError(f'Symbol string "{symbol}" must be of length 1')
|
||||||
|
|
||||||
|
self.symbol = symbol
|
||||||
|
'''The symbol that represents this renderable on the map'''
|
||||||
|
|
||||||
|
self.order = order
|
||||||
|
'''
|
||||||
|
Specifies the layer at which this entity is rendered. Higher values are
|
||||||
|
rendered later, and thus on top of lower values.
|
||||||
|
'''
|
||||||
|
|
||||||
|
self.foreground = fg
|
||||||
|
'''The foreground color of the entity'''
|
||||||
|
|
||||||
|
self.background = bg
|
||||||
|
'''The background color of the entity'''
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'{self.__class__.__name__}("{self.symbol}", {self.order}, {self.foreground}, {self.background})'
|
||||||
|
|
|
@ -165,7 +165,7 @@ class Engine:
|
||||||
|
|
||||||
if log.ACTIONS_TREE.isEnabledFor(log.INFO) and self.map.visible[tuple(action.actor.position)]:
|
if log.ACTIONS_TREE.isEnabledFor(log.INFO) and self.map.visible[tuple(action.actor.position)]:
|
||||||
if result.alternate:
|
if result.alternate:
|
||||||
alternate_string = f'{result.alternate.__class__.__name__}[{result.alternate.actor.symbol}]'
|
alternate_string = f'{result.alternate.__class__.__name__}[{result.alternate.actor}]'
|
||||||
else:
|
else:
|
||||||
alternate_string = str(result.alternate)
|
alternate_string = str(result.alternate)
|
||||||
log.ACTIONS_TREE.info(
|
log.ACTIONS_TREE.info(
|
||||||
|
|
|
@ -45,7 +45,8 @@ class Interface:
|
||||||
hero = self.engine.hero
|
hero = self.engine.hero
|
||||||
self.info_window.update_hero(hero)
|
self.info_window.update_hero(hero)
|
||||||
|
|
||||||
sorted_entities = sorted(self.engine.entities, key=lambda e: e.render_order.value)
|
sorted_entities = sorted(filter(lambda e: e.renderable is not None, self.engine.entities),
|
||||||
|
key=lambda e: e.renderable.order.value)
|
||||||
self.map_window.entities = sorted_entities
|
self.map_window.entities = sorted_entities
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
|
|
@ -190,6 +190,10 @@ class MapWindow(Window):
|
||||||
if not self.map.point_is_visible(entity_position):
|
if not self.map.point_is_visible(entity_position):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
renderable = ent.renderable
|
||||||
|
if not renderable:
|
||||||
|
continue
|
||||||
|
|
||||||
# Entity positions are relative to the (0, 0) point of the Map. In
|
# Entity positions are relative to the (0, 0) point of the Map. In
|
||||||
# order to render them in the correct position in the console, we
|
# order to render them in the correct position in the console, we
|
||||||
# need to transform them into viewport-relative coordinates.
|
# need to transform them into viewport-relative coordinates.
|
||||||
|
@ -200,6 +204,6 @@ class MapWindow(Window):
|
||||||
console.print(
|
console.print(
|
||||||
x=position.x,
|
x=position.x,
|
||||||
y=position.y,
|
y=position.y,
|
||||||
string=ent.symbol,
|
string=renderable.symbol,
|
||||||
fg=ent.foreground,
|
fg=renderable.foreground,
|
||||||
bg=tuple(map_tile_at_entity_position['bg'][:3]))
|
bg=tuple(map_tile_at_entity_position['bg'][:3]))
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
'''Defines a number of high-level game objects. The parent class of all game objects is the Entity class.'''
|
'''Defines a number of high-level game objects. The parent class of all game objects is the Entity class.'''
|
||||||
|
|
||||||
from enum import Enum
|
from typing import TYPE_CHECKING, Optional, Type
|
||||||
from typing import TYPE_CHECKING, Optional, Tuple, Type
|
|
||||||
|
|
||||||
import tcod
|
import tcod
|
||||||
|
|
||||||
from . import items
|
from . import items
|
||||||
from .components import Fighter
|
from .components import Fighter, Renderable
|
||||||
from .geometry import Point
|
from .geometry import Point
|
||||||
from .monsters import Species
|
from .monsters import Species
|
||||||
|
|
||||||
|
@ -16,16 +15,6 @@ if TYPE_CHECKING:
|
||||||
from .ai import AI
|
from .ai import AI
|
||||||
|
|
||||||
|
|
||||||
class RenderOrder(Enum):
|
|
||||||
'''
|
|
||||||
These values indicate the order that an Entity should be rendered. Higher values are rendered later and therefore on
|
|
||||||
top of items with lower orderings.
|
|
||||||
'''
|
|
||||||
ITEM = 1000
|
|
||||||
ACTOR = 2000
|
|
||||||
HERO = 3000
|
|
||||||
|
|
||||||
|
|
||||||
class Entity:
|
class Entity:
|
||||||
'''A single-tile drawable entity with a symbol and position
|
'''A single-tile drawable entity with a symbol and position
|
||||||
|
|
||||||
|
@ -36,19 +25,9 @@ class Entity:
|
||||||
game
|
game
|
||||||
position : Point
|
position : Point
|
||||||
The Entity's location on the map
|
The Entity's location on the map
|
||||||
foreground : Tuple[int, int, int]
|
|
||||||
The foreground color used to render this Entity
|
|
||||||
background : Tuple[int, int, int], optional
|
|
||||||
The background color used to render this Entity
|
|
||||||
symbol : str
|
|
||||||
A single character string that represents this character on the map
|
|
||||||
blocks_movement : bool
|
blocks_movement : bool
|
||||||
True if this Entity blocks other Entities from moving through its
|
True if this Entity blocks other Entities from moving through its
|
||||||
position
|
position
|
||||||
render_order : RenderOrder
|
|
||||||
One of the RenderOrder values that specifies a layer at which this
|
|
||||||
entity will be rendered. Higher values are rendered on top of lower
|
|
||||||
values.
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# A monotonically increasing identifier to help differentiate between
|
# A monotonically increasing identifier to help differentiate between
|
||||||
|
@ -57,66 +36,51 @@ class Entity:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
symbol: str,
|
|
||||||
*,
|
*,
|
||||||
position: Optional[Point] = None,
|
position: Optional[Point] = None,
|
||||||
blocks_movement: Optional[bool] = True,
|
blocks_movement: Optional[bool] = True,
|
||||||
render_order: RenderOrder = RenderOrder.ITEM,
|
renderable: Optional[Renderable] = None):
|
||||||
fg: Optional[Tuple[int, int, int]] = None,
|
|
||||||
bg: Optional[Tuple[int, int, int]] = None):
|
|
||||||
self.identifier = Entity.__next_identifier
|
self.identifier = Entity.__next_identifier
|
||||||
self.position = position if position else Point()
|
self.position = position if position else Point()
|
||||||
self.foreground = fg if fg else (255, 255, 255)
|
self.renderable = renderable
|
||||||
self.background = bg
|
|
||||||
self.symbol = symbol
|
|
||||||
self.blocks_movement = blocks_movement
|
self.blocks_movement = blocks_movement
|
||||||
self.render_order = render_order
|
|
||||||
|
|
||||||
Entity.__next_identifier += 1
|
Entity.__next_identifier += 1
|
||||||
|
|
||||||
def print_to_console(self, console: tcod.Console) -> None:
|
def __str__(self):
|
||||||
'''Render this Entity to the console'''
|
return f'{self.__class__.__name__}!{self.identifier}'
|
||||||
console.print(x=self.position.x, y=self.position.y, string=self.symbol, fg=self.foreground, bg=self.background)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __repr__(self):
|
||||||
return f'{self.symbol}!{self.identifier} at {self.position}'
|
return f'{self.__class__.__name__}(position={self.position!r}, blocks_movement={self.blocks_movement}, renderable={self.renderable!r})'
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'{self.__class__.__name__}({self.symbol!r}, position={self.position!r}, fg={self.foreground!r}, bg={self.background!r})'
|
|
||||||
|
|
||||||
|
|
||||||
class Actor(Entity):
|
class Actor(Entity):
|
||||||
'''
|
'''
|
||||||
An actor is an abstract class that defines an object that can act in the game world. Entities that are actors will
|
An actor is an abstract class that defines an object that can act in the
|
||||||
be allowed an opportunity to perform an action during each game turn.
|
game world. Entities that are actors will be allowed an opportunity to
|
||||||
|
perform an action during each game turn.
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
ai : AI, optional
|
ai : AI, optional
|
||||||
If an entity can act on its own behalf, an instance of an AI class
|
If an entity can act on its own behalf, an instance of an AI class
|
||||||
fighter : Fighter, optional
|
fighter : Fighter, optional
|
||||||
If an entity can fight or take damage, an instance of the Fighter class. This is where hit points, attack power,
|
If an entity can fight or take damage, an instance of the Fighter class.
|
||||||
defense power, etc live.
|
This is where hit points, attack power, defense power, etc live.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
symbol: str,
|
|
||||||
*,
|
*,
|
||||||
position: Optional[Point] = None,
|
position: Optional[Point] = None,
|
||||||
blocks_movement: Optional[bool] = True,
|
blocks_movement: Optional[bool] = True,
|
||||||
render_order: RenderOrder = RenderOrder.ACTOR,
|
renderable: Optional[Renderable] = None,
|
||||||
ai: Optional['AI'] = None,
|
ai: Optional['AI'] = None,
|
||||||
fighter: Optional[Fighter] = None,
|
fighter: Optional[Fighter] = None):
|
||||||
fg: Optional[Tuple[int, int, int]] = None,
|
|
||||||
bg: Optional[Tuple[int, int, int]] = None):
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
symbol,
|
|
||||||
position=position,
|
position=position,
|
||||||
blocks_movement=blocks_movement,
|
blocks_movement=blocks_movement,
|
||||||
fg=fg,
|
renderable=renderable)
|
||||||
bg=bg,
|
|
||||||
render_order=render_order)
|
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
self.ai = ai
|
self.ai = ai
|
||||||
|
@ -138,7 +102,7 @@ class Actor(Entity):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'{self.__class__.__name__}({self.symbol!r}, position={self.position!r}, fighter={self.fighter!r}, ai={self.ai!r}, fg={self.foreground!r}, bg={self.background!r})'
|
return f'{self.__class__.__name__}(position={self.position!r}, fighter={self.fighter!r}, ai={self.ai!r}, renderable={self.renderable!r})'
|
||||||
|
|
||||||
|
|
||||||
class Hero(Actor):
|
class Hero(Actor):
|
||||||
|
@ -146,11 +110,9 @@ class Hero(Actor):
|
||||||
|
|
||||||
def __init__(self, position: Point):
|
def __init__(self, position: Point):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
'@',
|
|
||||||
position=position,
|
position=position,
|
||||||
fighter=Fighter(maximum_hit_points=30, attack_power=5, defense=2),
|
fighter=Fighter(maximum_hit_points=30, attack_power=5, defense=2),
|
||||||
render_order=RenderOrder.HERO,
|
renderable=Renderable('@', Renderable.Order.HERO, tuple(tcod.white)))
|
||||||
fg=tuple(tcod.white))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
@ -176,12 +138,13 @@ class Monster(Actor):
|
||||||
defense=species.defense)
|
defense=species.defense)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
species.symbol,
|
|
||||||
ai=ai_class(self),
|
ai=ai_class(self),
|
||||||
position=position,
|
position=position,
|
||||||
fighter=fighter,
|
fighter=fighter,
|
||||||
fg=species.foreground_color,
|
renderable=Renderable(
|
||||||
bg=species.background_color)
|
symbol=species.symbol,
|
||||||
|
fg=species.foreground_color,
|
||||||
|
bg=species.background_color))
|
||||||
|
|
||||||
self.species = species
|
self.species = species
|
||||||
|
|
||||||
|
@ -206,12 +169,13 @@ class Item(Entity):
|
||||||
'''An instance of an Item'''
|
'''An instance of an Item'''
|
||||||
|
|
||||||
def __init__(self, kind: items.Item, position: Optional[Point] = None, name: Optional[str] = None):
|
def __init__(self, kind: items.Item, position: Optional[Point] = None, name: Optional[str] = None):
|
||||||
super().__init__(kind.symbol,
|
super().__init__(position=position,
|
||||||
position=position,
|
|
||||||
blocks_movement=False,
|
blocks_movement=False,
|
||||||
render_order=RenderOrder.ITEM,
|
renderable=Renderable(
|
||||||
fg=kind.foreground_color,
|
symbol=kind.symbol,
|
||||||
bg=kind.background_color)
|
order=Renderable.Order.ITEM,
|
||||||
|
fg=kind.foreground_color,
|
||||||
|
bg=kind.background_color))
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue