diff --git a/squirrelbattle/display/display.py b/squirrelbattle/display/display.py index 47e4bce..1e47189 100644 --- a/squirrelbattle/display/display.py +++ b/squirrelbattle/display/display.py @@ -33,7 +33,7 @@ class Display: self.width = width self.height = height if hasattr(self, "pad") and resize_pad: - self.pad.resize(self.height - 1, self.width - 1) + self.pad.resize(self.height, self.width) def refresh(self, *args, resize_pad: bool = True) -> None: if len(args) == 4: diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index 9ec0f88..a4636eb 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: @@ -43,7 +47,9 @@ class DisplayManager: self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.cols, resize_pad=False) self.statsdisplay.refresh(self.rows * 4 // 5, 0, - self.rows // 5, self.cols) + self.rows // 10, self.cols) + self.logsdisplay.refresh(self.rows * 9 // 10, 0, + self.rows // 10, self.cols) if self.game.state == GameMode.MAINMENU: self.mainmenudisplay.refresh(0, 0, self.rows, self.cols) if self.game.state == GameMode.SETTINGS: diff --git a/squirrelbattle/display/logsdisplay.py b/squirrelbattle/display/logsdisplay.py new file mode 100644 index 0000000..0d95915 --- /dev/null +++ b/squirrelbattle/display/logsdisplay.py @@ -0,0 +1,23 @@ +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: + print(type(self.logs.messages), flush=True) + messages = self.logs.messages[-self.height:] + messages = messages[::-1] + self.pad.clear() + for i in range(min(self.height, len(messages))): + self.pad.addstr(self.height - i - 1, self.x, + messages[i][:self.width]) + self.pad.refresh(0, 0, self.y, self.x, self.y + self.height, + self.x + 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 a36f21e..702b055 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -72,7 +72,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..0bb3024 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,8 @@ 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.logs.clear() 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..d1baa2a 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 @@ -31,6 +52,7 @@ class Map: self.start_x = start_x self.tiles = tiles self.entities = [] + self.logs = Logs() def add_entity(self, entity: "Entity") -> None: """ @@ -362,19 +384,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: """ diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 5cd6ad5..0c8ee3c 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -42,9 +42,11 @@ class TestEntities(unittest.TestCase): self.assertEqual(entity.maxhealth, entity.health) self.assertEqual(entity.strength, 2) for _ in range(9): - self.assertIsNone(entity.hit(entity)) + self.assertEqual(entity.hit(entity), + "beaver hits beaver. beaver takes 2 damage.") self.assertFalse(entity.dead) - self.assertIsNone(entity.hit(entity)) + self.assertEqual(entity.hit(entity), "beaver hits beaver. " + + "beaver takes 2 damage. beaver dies.") self.assertTrue(entity.dead) entity = Rabbit() @@ -64,6 +66,9 @@ class TestEntities(unittest.TestCase): self.map.tick() self.assertTrue(entity.y == 2 and entity.x == 6) self.assertEqual(old_health - entity.strength, self.player.health) + self.assertEqual(self.map.logs.messages[-1], + f"{entity.name} hits {self.player.name}. \ +{self.player.name} takes {entity.strength} damage.") # Fight the rabbit old_health = entity.health @@ -156,7 +161,7 @@ class TestEntities(unittest.TestCase): self.assertFalse(player.move_up()) self.assertTrue(player.move_left()) self.assertFalse(player.move_left()) - for i in range(8): + for _ in range(8): self.assertTrue(player.move_down()) self.assertFalse(player.move_down()) self.assertTrue(player.move_right()) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 424d86a..61c6c8e 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -17,6 +17,7 @@ class TestGame(unittest.TestCase): """ self.game = Game() self.game.new_game() + self.game.logs.add_message("Hello World !") display = DisplayManager(None, self.game) self.game.display_actions = display.handle_display_action @@ -256,6 +257,17 @@ class TestGame(unittest.TestCase): self.game.handle_key_pressed(KeyValues.ENTER) self.assertEqual(self.game.state, GameMode.MAINMENU) + def test_logs(self) -> None: + """ + Tests the use of logs + """ + self.assertEqual(self.game.logs.messages, ["Hello World !"]) + self.game.logs.add_messages(["Hello", "World"]) + self.assertEqual(self.game.logs.messages, ["Hello World !", + "Hello", "World"]) + self.game.logs.clear() + self.assertEqual(self.game.logs.messages, []) + def test_dead_screen(self) -> None: """ Kill player and render dead screen.