From 6e71146aa2e81917f328983d7078fdd9cf9eeaf0 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Thu, 19 Nov 2020 12:03:05 +0100 Subject: [PATCH] First pass on the logs The newly-added logs manage a list of messages. Entities do register a message to it when hitting each other. Display is created, but not yet added to the layout actually displayed. --- squirrelbattle/display/display_manager.py | 6 ++++- squirrelbattle/display/logsdisplay.py | 17 +++++++++++++ squirrelbattle/entities/monsters.py | 2 +- squirrelbattle/entities/player.py | 2 +- squirrelbattle/game.py | 4 ++- squirrelbattle/interfaces.py | 30 ++++++++++++++++++++--- 6 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 squirrelbattle/display/logsdisplay.py diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index 9ec0f88..408086c 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -3,6 +3,7 @@ from squirrelbattle.display.mapdisplay import MapDisplay from squirrelbattle.display.statsdisplay import StatsDisplay from squirrelbattle.display.menudisplay import SettingsMenuDisplay, \ MainMenuDisplay +from squirrelbattle.display.logsdisplay import LogsDisplay from squirrelbattle.display.texturepack import TexturePack from typing import Any from squirrelbattle.game import Game, GameMode @@ -20,8 +21,10 @@ class DisplayManager: self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, screen, pack) self.settingsmenudisplay = SettingsMenuDisplay(screen, pack) + self.logsdisplay = LogsDisplay(screen, pack) self.displays = [self.statsdisplay, self.mapdisplay, - self.mainmenudisplay, self.settingsmenudisplay] + self.mainmenudisplay, self.settingsmenudisplay, + self.logsdisplay] self.update_game_components() def handle_display_action(self, action: DisplayActions) -> None: @@ -36,6 +39,7 @@ class DisplayManager: self.mapdisplay.update_map(self.game.map) self.statsdisplay.update_player(self.game.player) self.settingsmenudisplay.update_menu(self.game.settings_menu) + self.logsdisplay.update_logs(self.game.logs) def refresh(self) -> None: if self.game.state == GameMode.PLAY: diff --git a/squirrelbattle/display/logsdisplay.py b/squirrelbattle/display/logsdisplay.py new file mode 100644 index 0000000..bacbbf7 --- /dev/null +++ b/squirrelbattle/display/logsdisplay.py @@ -0,0 +1,17 @@ +from squirrelbattle.display.display import Display +from squirrelbattle.interfaces import Logs + + +class LogsDisplay(Display): + + def __init__(self, *args) -> None: + super().__init__(*args) + self.pad = self.newpad(self.rows, self.cols) + + def update_logs(self, logs: Logs) -> None: + self.logs = logs + + def display(self) -> None: + messages = self.logs.messages[-self.height:].reverse() + for i, y in enumerate(range(self.y + self.height - 1, self.y - 1, - 1)): + self.pad.addstr(y, self.x, messages[i][:self.width]) diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index 1f04372..8192b8e 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -44,7 +44,7 @@ class Monster(FightingEntity): next_y, next_x = target.paths[(self.y, self.x)] moved = self.check_move(next_y, next_x, True) if not moved and self.distance_squared(target) <= 1: - self.hit(target) + self.map.logs.add_message(self.hit(target)) else: for _ in range(100): if choice([self.move_up, self.move_down, diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 873da32..1f9ff0e 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -70,7 +70,7 @@ class Player(FightingEntity): for entity in self.map.entities: if entity.y == y and entity.x == x: if entity.is_fighting_entity(): - self.hit(entity) + self.map.logs.add_message(self.hit(entity)) if entity.dead: self.add_xp(randint(3, 7)) return True diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index f06843d..320ecff 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -6,7 +6,7 @@ import sys from .entities.player import Player from .enums import GameMode, KeyValues, DisplayActions -from .interfaces import Map +from .interfaces import Map, Logs from .resources import ResourceManager from .settings import Settings from . import menus @@ -33,6 +33,7 @@ class Game: self.settings.load_settings() self.settings.write_settings() self.settings_menu.update_values(self.settings) + self.logs = Logs() def new_game(self) -> None: """ @@ -40,6 +41,7 @@ class Game: """ # TODO generate a new map procedurally self.map = Map.load(ResourceManager.get_asset_path("example_map_2.txt")) + self.map.logs = self.logs self.player = Player() self.map.add_entity(self.player) self.player.move(self.map.start_y, self.map.start_x) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 10001fb..cf94aed 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -7,6 +7,26 @@ from typing import List, Optional from squirrelbattle.display.texturepack import TexturePack +class Logs: + """ + The logs object stores the messages to display. It is encapsulating a list + of such messages, to allow multiple pointers to keep track of it even if + the list was to be reassigned. + """ + + def __init__(self) -> None: + self.messages = [] + + def add_message(self, msg: str) -> None: + self.messages.append(msg) + + def add_messages(self, msg: List[str]) -> None: + self.messages += msg + + def clear(self) -> None: + self.messages = [] + + class Map: """ Object that represents a Map with its width, height @@ -18,6 +38,7 @@ class Map: start_x: int tiles: List[List["Tile"]] entities: List["Entity"] + logs: Logs # coordinates of the point that should be # on the topleft corner of the screen currentx: int @@ -362,19 +383,22 @@ class FightingEntity(Entity): def dead(self) -> bool: return self.health <= 0 - def hit(self, opponent: "FightingEntity") -> None: + def hit(self, opponent: "FightingEntity") -> str: """ Deals damage to the opponent, based on the stats """ - opponent.take_damage(self, self.strength) + return f"{self.name} hits {opponent.name}. "\ + + opponent.take_damage(self, self.strength) - def take_damage(self, attacker: "Entity", amount: int) -> None: + def take_damage(self, attacker: "Entity", amount: int) -> str: """ Take damage from the attacker, based on the stats """ self.health -= amount if self.health <= 0: self.die() + return f"{self.name} takes {amount} damage."\ + + (f" {self.name} dies." if self.health <= 0 else "") def die(self) -> None: """