From 1cf5e7bd8b7329b6d3254a83685a3a3b11de0860 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 11 Dec 2020 19:23:21 +0100 Subject: [PATCH 01/24] First implementation of visibility, not tested, nor used for now --- .gitignore | 1 + squirrelbattle/interfaces.py | 148 ++++++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 8499d7c..e477e04 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ env/ venv/ +local/ .coverage .pytest_cache/ diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 3567ea0..91a2188 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -4,7 +4,7 @@ from enum import Enum, auto from math import sqrt from random import choice, randint -from typing import List, Optional +from typing import List, Optional, Union, Tuple from .display.texturepack import TexturePack from .translations import gettext as _ @@ -30,6 +30,28 @@ class Logs: self.messages = [] +class Slope(): + X: int + Y: int + + def __init__(self, y: int, x: int) -> None: + self.Y = y + self.X = x + + def compare(self, other: Union[Tuple[int, int], "Slope"]) -> int: + if isinstance(other, Slope): + y, x = other.Y, other.X + else: + y, x = other + return self.Y * x - self.X * y + + def __lt__(self, other: Union[Tuple[int, int], "Slope"]) -> bool: + return self.compare(other) < 0 + + def __eq__(self, other: Union[Tuple[int, int], "Slope"]) -> bool: + return self.compare(other) == 0 + + class Map: """ Object that represents a Map with its width, height @@ -40,6 +62,7 @@ class Map: start_y: int start_x: int tiles: List[List["Tile"]] + visibility: List[List[bool]] entities: List["Entity"] logs: Logs # coordinates of the point that should be @@ -54,6 +77,8 @@ class Map: self.start_y = start_y self.start_x = start_x self.tiles = tiles + self.visibility = [[False for _ in range(len(tiles[0]))] + for _ in range(len(tiles))] self.entities = [] self.logs = Logs() @@ -129,7 +154,7 @@ class Map: """ Put randomly {count} hedgehogs on the map, where it is available. """ - for ignored in range(count): + for _ignored in range(count): y, x = 0, 0 while True: y, x = randint(0, self.height - 1), randint(0, self.width - 1) @@ -140,6 +165,125 @@ class Map: entity.move(y, x) self.add_entity(entity) + def compute_visibility(self, y: int, x: int, max_range: int) -> None: + """ + Sets the visible tiles to be the ones visible by an entity at point + (y, x), using a twaked shadow casting algorithm + """ + + for line in self.visibility: + for i in range(len(line)): + line[i] = False + self.visibility[y][x] = True + for octant in range(8): + self.compute_visibility_octant(octant, (y, x), max_range, 1, + Slope(1, 1), Slope(0, 1)) + + def crop_top_visibility(self, octant: int, origin: Tuple[int, int], + x: int, top: Slope) -> int: + if top.X == 1: + top_y = x + else: + top_y = ((x * 2 - 1) * top.Y + top.X) / (top.X * 2) + if self.is_wall(top_y, x, octant, origin): + if top >= (top_y * 2 + 1, x * 2) and not\ + self.is_wall(top_y + 1, x, octant, origin): + top_y += 1 + else: + ax = x * 2 + if self.is_wall(top_y + 1, x + 1, octant, origin): + ax += 1 + if top > (top_y * 2 + 1, ax): + top_y += 1 + return top_y + + def crop_bottom_visibility(self, octant: int, origin: Tuple[int, int], + x: int, bottom: Slope) -> int: + if bottom.Y == 0: + bottom_y = 0 + else: + bottom_y = ((x * 2 + 1) * bottom.Y + bottom.X) /\ + (bottom.X * 2) + if bottom >= (bottom_y * 2 + 1, x * 2) and\ + self.is_wall(bottom_y, x, octant, origin) and\ + not self.is_wall(bottom_y + 1, x, octant, origin): + bottom_y += 1 + return bottom_y + + def compute_visibility_octant(self, octant: int, origin: Tuple[int, int], + max_range: int, distance: int, top: Slope, + bottom: Slope) -> None: + for x in range(distance, max_range): + top_y = self.crop_top_visibility(octant, origin, x, top) + bottom_y = self.crop_bottom_visibility(octant, origin, x, bottom) + was_opaque = -1 + for y in range(top_y, bottom_y - 1, -1): + if sqrt(x**2 + y**2) > max_range: + continue + is_opaque = self.is_wall(y, x, octant, origin) + is_visible = is_opaque\ + or ((y != top_y or top > (y * 4 - 1, x * 4 - 1)) + and (y != bottom_y or bottom < (y * 4 + 1, x * 4 + 1))) + if is_visible: + self.set_visible(y, x, octant, origin) + if x == max_range: + continue + if is_opaque and was_opaque == 0: + nx, ny = x * 2, y * 2 + 1 + if self.is_wall(y + 1, x, octant, origin): + nx -= 1 + if top > (ny, nx): + if y == bottom_y: + bottom = Slope(ny, nx) + break + else: + self.compute_visibility_octant( + octant, origin, max_range, x + 1, top, + Slope(ny, nx)) + else: + if y == bottom_y: + return + elif not is_opaque and was_opaque == 1: + nx, ny = x * 2, y * 2 + 1 + if self.is_wall(y + 1, x + 1, octant, origin): + nx += 1 + if bottom >= (ny, nx): + return + was_opaque = is_opaque + if was_opaque != 0: + break + + @staticmethod + def translate_coord(y: int, x: int, octant: int, + origin: Tuple[int, int]) -> Tuple[int, int]: + ny, nx = origin + if octant == 0: + return nx + x, ny - y + elif octant == 1: + return nx + y, ny - x + elif octant == 2: + return nx - y, ny - x + elif octant == 3: + return nx - x, ny - y + elif octant == 4: + return nx - x, ny + y + elif octant == 5: + return nx - y, ny + x + elif octant == 6: + return nx + y, ny + x + elif octant == 7: + return nx + x, ny + y + + def is_wall(self, y: int, x: int, octant: int, + origin: Tuple[int, int]) -> bool: + y, x = self.translate_coord(y, x, octant, origin) + return self.tiles[y][x].is_wall() + + def set_visible(self, y: int, x: int, octant: int, + origin: Tuple[int, int]) -> None: + y, x = self.translate_coord(y, x, octant, origin) + self.visibility[y][x] = True + def tick(self) -> None: """ Trigger all entity events. From 86628fdea6b6f9b279ae65c9562e3a2086b93f1f Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 18 Dec 2020 17:04:45 +0100 Subject: [PATCH 02/24] Working visibility and displaying it, still need to hide things that aren't visible --- squirrelbattle/display/mapdisplay.py | 11 ++++- squirrelbattle/display/texturepack.py | 19 ++++--- squirrelbattle/entities/player.py | 4 +- squirrelbattle/interfaces.py | 71 ++++++++++++++++----------- 4 files changed, 64 insertions(+), 41 deletions(-) diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index 54d9432..c4f29e3 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -15,8 +15,15 @@ class MapDisplay(Display): self.pad = self.newpad(m.height, self.pack.tile_width * m.width + 1) def update_pad(self) -> None: - self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack), - self.pack.tile_fg_color, self.pack.tile_bg_color) + for j in range(len(self.map.tiles)): + for i in range(len(self.map.tiles[j])): + color = self.pack.tile_fg_visible_color if \ + self.map.visibility[j][i] else self.pack.tile_fg_color + self.addstr(self.pad, j, self.pack.tile_width * i, + self.map.tiles[j][i].char(self.pack), + color, self.pack.tile_bg_color) + # self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack), + # self.pack.tile_fg_color, self.pack.tile_bg_color) for e in self.map.entities: self.addstr(self.pad, e.y, self.pack.tile_width * e.x, self.pack[e.name.upper()], diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index f72cd97..fb56dd5 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later import curses -from typing import Any +from typing import Any, Union, Tuple class TexturePack: @@ -10,10 +10,11 @@ class TexturePack: name: str tile_width: int - tile_fg_color: int - tile_bg_color: int - entity_fg_color: int - entity_bg_color: int + tile_fg_color: Union[int, Tuple[int, int, int]] + tile_fg_visible_color: Union[int, Tuple[int, int, int]] + tile_bg_color: Union[int, Tuple[int, int, int]] + entity_fg_color: Union[int, Tuple[int, int, int]] + entity_bg_color: Union[int, Tuple[int, int, int]] BODY_SNATCH_POTION: str BOMB: str @@ -54,9 +55,10 @@ class TexturePack: TexturePack.ASCII_PACK = TexturePack( name="ascii", tile_width=1, + tile_fg_visible_color=(1000, 1000, 1000), tile_fg_color=curses.COLOR_WHITE, tile_bg_color=curses.COLOR_BLACK, - entity_fg_color=curses.COLOR_WHITE, + entity_fg_color=(1000, 1000, 1000), entity_bg_color=curses.COLOR_BLACK, BODY_SNATCH_POTION='S', @@ -80,10 +82,11 @@ TexturePack.ASCII_PACK = TexturePack( TexturePack.SQUIRREL_PACK = TexturePack( name="squirrel", tile_width=2, + tile_fg_visible_color=(1000, 1000, 1000), tile_fg_color=curses.COLOR_WHITE, tile_bg_color=curses.COLOR_BLACK, - entity_fg_color=curses.COLOR_WHITE, - entity_bg_color=curses.COLOR_WHITE, + entity_fg_color=(1000, 1000, 1000), + entity_bg_color=(1000, 1000, 1000), BODY_SNATCH_POTION='🔀', BOMB='💣', diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 19c8348..6d18884 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -21,7 +21,7 @@ class Player(InventoryHolder, FightingEntity): strength: int = 5, intelligence: int = 1, charisma: int = 1, dexterity: int = 1, constitution: int = 1, level: int = 1, current_xp: int = 0, max_xp: int = 10, inventory: list = None, - hazel: int = 42, *args, **kwargs) \ + hazel: int = 42, vision: int = 5, *args, **kwargs) \ -> None: super().__init__(name=name, maxhealth=maxhealth, strength=strength, intelligence=intelligence, charisma=charisma, @@ -32,6 +32,7 @@ class Player(InventoryHolder, FightingEntity): self.inventory = self.translate_inventory(inventory or []) self.paths = dict() self.hazel = hazel + self.vision = vision def move(self, y: int, x: int) -> None: """ @@ -42,6 +43,7 @@ class Player(InventoryHolder, FightingEntity): self.map.currenty = y self.map.currentx = x self.recalculate_paths() + self.map.compute_visibility(self.y, self.x, self.vision) def level_up(self) -> None: """ diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 6458df7..89399f5 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -4,7 +4,8 @@ from enum import Enum, auto from math import sqrt from random import choice, randint -from typing import List, Optional, Union, Tuple, Any +from typing import List, Optional, Tuple, Any +from math import ceil from .display.texturepack import TexturePack from .translations import gettext as _ @@ -38,19 +39,25 @@ class Slope(): self.Y = y self.X = x - def compare(self, other: Union[Tuple[int, int], "Slope"]) -> int: - if isinstance(other, Slope): - y, x = other.Y, other.X - else: - y, x = other + def compare(self, other: "Slope") -> int: + y, x = other.Y, other.X return self.Y * x - self.X * y - def __lt__(self, other: Union[Tuple[int, int], "Slope"]) -> bool: + def __lt__(self, other: "Slope") -> bool: return self.compare(other) < 0 - def __eq__(self, other: Union[Tuple[int, int], "Slope"]) -> bool: + def __eq__(self, other: "Slope") -> bool: return self.compare(other) == 0 + def __gt__(self, other: "Slope") -> bool: + return self.compare(other) > 0 + + def __le__(self, other: "Slope") -> bool: + return self.compare(other) <= 0 + + def __ge__(self, other: "Slope") -> bool: + return self.compare(other) >= 0 + class Map: """ @@ -194,16 +201,16 @@ class Map: if top.X == 1: top_y = x else: - top_y = ((x * 2 - 1) * top.Y + top.X) / (top.X * 2) + top_y = ceil(((x * 2 - 1) * top.Y + top.X) / (top.X * 2)) if self.is_wall(top_y, x, octant, origin): - if top >= (top_y * 2 + 1, x * 2) and not\ + if top >= Slope(top_y * 2 + 1, x * 2) and not\ self.is_wall(top_y + 1, x, octant, origin): top_y += 1 else: ax = x * 2 if self.is_wall(top_y + 1, x + 1, octant, origin): ax += 1 - if top > (top_y * 2 + 1, ax): + if top > Slope(top_y * 2 + 1, ax): top_y += 1 return top_y @@ -212,9 +219,9 @@ class Map: if bottom.Y == 0: bottom_y = 0 else: - bottom_y = ((x * 2 + 1) * bottom.Y + bottom.X) /\ - (bottom.X * 2) - if bottom >= (bottom_y * 2 + 1, x * 2) and\ + bottom_y = ceil(((x * 2 - 1) * bottom.Y + bottom.X) + / (bottom.X * 2)) + if bottom >= Slope(bottom_y * 2 + 1, x * 2) and\ self.is_wall(bottom_y, x, octant, origin) and\ not self.is_wall(bottom_y + 1, x, octant, origin): bottom_y += 1 @@ -223,17 +230,18 @@ class Map: def compute_visibility_octant(self, octant: int, origin: Tuple[int, int], max_range: int, distance: int, top: Slope, bottom: Slope) -> None: - for x in range(distance, max_range): + for x in range(distance, max_range + 1): top_y = self.crop_top_visibility(octant, origin, x, top) bottom_y = self.crop_bottom_visibility(octant, origin, x, bottom) was_opaque = -1 for y in range(top_y, bottom_y - 1, -1): - if sqrt(x**2 + y**2) > max_range: + if x + y > max_range: continue is_opaque = self.is_wall(y, x, octant, origin) is_visible = is_opaque\ - or ((y != top_y or top > (y * 4 - 1, x * 4 - 1)) - and (y != bottom_y or bottom < (y * 4 + 1, x * 4 + 1))) + or ((y != top_y or top > Slope(y * 4 - 1, x * 4 + 1)) + and (y != bottom_y + or bottom < Slope(y * 4 + 1, x * 4 - 1))) if is_visible: self.set_visible(y, x, octant, origin) if x == max_range: @@ -242,7 +250,7 @@ class Map: nx, ny = x * 2, y * 2 + 1 if self.is_wall(y + 1, x, octant, origin): nx -= 1 - if top > (ny, nx): + if top > Slope(ny, nx): if y == bottom_y: bottom = Slope(ny, nx) break @@ -257,8 +265,9 @@ class Map: nx, ny = x * 2, y * 2 + 1 if self.is_wall(y + 1, x + 1, octant, origin): nx += 1 - if bottom >= (ny, nx): + if bottom >= Slope(ny, nx): return + top = Slope(ny, nx) was_opaque = is_opaque if was_opaque != 0: break @@ -268,31 +277,33 @@ class Map: origin: Tuple[int, int]) -> Tuple[int, int]: ny, nx = origin if octant == 0: - return nx + x, ny - y + return ny - y, nx + x elif octant == 1: - return nx + y, ny - x + return ny - x, nx + y elif octant == 2: - return nx - y, ny - x + return ny - x, nx - y elif octant == 3: - return nx - x, ny - y + return ny - y, nx - x elif octant == 4: - return nx - x, ny + y + return ny + y, nx - x elif octant == 5: - return nx - y, ny + x + return ny + x, nx - y elif octant == 6: - return nx + y, ny + x + return ny + x, nx + y elif octant == 7: - return nx + x, ny + y + return ny + y, nx + x def is_wall(self, y: int, x: int, octant: int, origin: Tuple[int, int]) -> bool: y, x = self.translate_coord(y, x, octant, origin) - return self.tiles[y][x].is_wall() + return 0 <= y < len(self.tiles) and 0 <= x < len(self.tiles[0]) and \ + self.tiles[y][x].is_wall() def set_visible(self, y: int, x: int, octant: int, origin: Tuple[int, int]) -> None: y, x = self.translate_coord(y, x, octant, origin) - self.visibility[y][x] = True + if 0 <= y < len(self.tiles) and 0 <= x < len(self.tiles[0]): + self.visibility[y][x] = True def tick(self) -> None: """ From 762bed888af208d9df1ba2eeb5846187811f8bc9 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 18 Dec 2020 21:21:00 +0100 Subject: [PATCH 03/24] Working visibility (at least relatively good), but a few lines untested --- squirrelbattle/assets/example_map_3.txt | 41 +++++++++++++++++++++++++ squirrelbattle/display/mapdisplay.py | 10 ++++-- squirrelbattle/entities/player.py | 2 +- squirrelbattle/interfaces.py | 9 +++++- squirrelbattle/tests/entities_test.py | 2 +- squirrelbattle/tests/interfaces_test.py | 20 +++++++++++- 6 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 squirrelbattle/assets/example_map_3.txt diff --git a/squirrelbattle/assets/example_map_3.txt b/squirrelbattle/assets/example_map_3.txt new file mode 100644 index 0000000..c5dd8e3 --- /dev/null +++ b/squirrelbattle/assets/example_map_3.txt @@ -0,0 +1,41 @@ +1 6 +################################################################################ +#..............................................................................# +#..#...........................................................................# +#...........#..................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +#..............................................................................# +################################################################################ \ No newline at end of file diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index c4f29e3..17d7d6d 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -17,6 +17,8 @@ class MapDisplay(Display): def update_pad(self) -> None: for j in range(len(self.map.tiles)): for i in range(len(self.map.tiles[j])): + if not self.map.seen_tiles[j][i]: + continue color = self.pack.tile_fg_visible_color if \ self.map.visibility[j][i] else self.pack.tile_fg_color self.addstr(self.pad, j, self.pack.tile_width * i, @@ -25,9 +27,11 @@ class MapDisplay(Display): # self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack), # self.pack.tile_fg_color, self.pack.tile_bg_color) for e in self.map.entities: - self.addstr(self.pad, e.y, self.pack.tile_width * e.x, - self.pack[e.name.upper()], - self.pack.entity_fg_color, self.pack.entity_bg_color) + if self.map.visibility[e.y][e.x]: + self.addstr(self.pad, e.y, self.pack.tile_width * e.x, + self.pack[e.name.upper()], + self.pack.entity_fg_color, + self.pack.entity_bg_color) # Display Path map for debug purposes # from squirrelbattle.entities.player import Player diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 6d18884..aad35dd 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -21,7 +21,7 @@ class Player(InventoryHolder, FightingEntity): strength: int = 5, intelligence: int = 1, charisma: int = 1, dexterity: int = 1, constitution: int = 1, level: int = 1, current_xp: int = 0, max_xp: int = 10, inventory: list = None, - hazel: int = 42, vision: int = 5, *args, **kwargs) \ + hazel: int = 42, vision: int = 50, *args, **kwargs) \ -> None: super().__init__(name=name, maxhealth=maxhealth, strength=strength, intelligence=intelligence, charisma=charisma, diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 89399f5..30e59f4 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -70,6 +70,7 @@ class Map: start_x: int tiles: List[List["Tile"]] visibility: List[List[bool]] + seen_tiles: List[List[bool]] entities: List["Entity"] logs: Logs # coordinates of the point that should be @@ -86,6 +87,8 @@ class Map: self.tiles = tiles self.visibility = [[False for _ in range(len(tiles[0]))] for _ in range(len(tiles))] + self.seen_tiles = [[False for _ in range(len(tiles[0]))] + for _ in range(len(tiles))] self.entities = [] self.logs = Logs() @@ -191,7 +194,7 @@ class Map: for line in self.visibility: for i in range(len(line)): line[i] = False - self.visibility[y][x] = True + self.set_visible(0, 0, 0, (y, x)) for octant in range(8): self.compute_visibility_octant(octant, (y, x), max_range, 1, Slope(1, 1), Slope(0, 1)) @@ -242,6 +245,9 @@ class Map: or ((y != top_y or top > Slope(y * 4 - 1, x * 4 + 1)) and (y != bottom_y or bottom < Slope(y * 4 + 1, x * 4 - 1))) + # is_visible = is_opaque\ + # or ((y != top_y or top >= Slope(y, x)) + # and (y != bottom_y or bottom <= Slope(y, x))) if is_visible: self.set_visible(y, x, octant, origin) if x == max_range: @@ -304,6 +310,7 @@ class Map: y, x = self.translate_coord(y, x, octant, origin) if 0 <= y < len(self.tiles) and 0 <= x < len(self.tiles[0]): self.visibility[y][x] = True + self.seen_tiles[y][x] = True def tick(self) -> None: """ diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 70e3748..c51fd56 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -133,7 +133,7 @@ class TestEntities(unittest.TestCase): self.assertEqual(item.y, 42) self.assertEqual(item.x, 42) # Wait for the explosion - for ignored in range(5): + for _ignored in range(5): item.act(self.map) self.assertTrue(hedgehog.dead) self.assertTrue(teddy_bear.dead) diff --git a/squirrelbattle/tests/interfaces_test.py b/squirrelbattle/tests/interfaces_test.py index c9f7253..1713f77 100644 --- a/squirrelbattle/tests/interfaces_test.py +++ b/squirrelbattle/tests/interfaces_test.py @@ -4,7 +4,7 @@ import unittest from squirrelbattle.display.texturepack import TexturePack -from squirrelbattle.interfaces import Map, Tile +from squirrelbattle.interfaces import Map, Tile, Slope from squirrelbattle.resources import ResourceManager @@ -37,3 +37,21 @@ class TestInterfaces(unittest.TestCase): self.assertFalse(Tile.WALL.can_walk()) self.assertFalse(Tile.EMPTY.can_walk()) self.assertRaises(ValueError, Tile.from_ascii_char, 'unknown') + + def test_slope(self) -> None: + """ + Test good behaviour of slopes (basically vectors, compared according to + the determinant) + """ + a = Slope(1, 1) + b = Slope(0, 1) + self.assertTrue(b < a) + self.assertTrue(b <= a) + self.assertTrue(a <= a) + self.assertTrue(a == a) + self.assertTrue(a > b) + self.assertTrue(a >= b) + + # def test_visibility(self) -> None: + # m = Map.load(ResourceManager.get_asset_path("example_map_3.txt")) + # m.compute_visibility(1, 1, 50) From ad5ae22e5f869523bbc3ddb19c83f2ac3366f145 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 26 Dec 2020 00:45:17 +0100 Subject: [PATCH 04/24] Manage multiple maps in one game --- squirrelbattle/game.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 0553d2e..7ef8fc9 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -3,7 +3,7 @@ from json import JSONDecodeError from random import randint -from typing import Any, Optional +from typing import Any, Optional, List import curses import json import os @@ -22,7 +22,8 @@ class Game: """ The game object controls all actions in the game. """ - map: Map + maps: List[Map] + map_index: int player: Player screen: Any # display_actions is a display interface set by the bootstrapper @@ -52,6 +53,8 @@ class Game: Create a new game on the screen. """ # TODO generate a new map procedurally + self.maps = [] + self.map_index = 0 self.map = Map.load(ResourceManager.get_asset_path("example_map_2.txt")) self.map.logs = self.logs self.logs.clear() @@ -61,6 +64,24 @@ class Game: self.map.spawn_random_entities(randint(3, 10)) self.inventory_menu.update_player(self.player) + @property + def map(self) -> Map: + """ + Return the current map where the user is. + """ + return self.maps[self.map_index] + + @map.setter + def map(self, m: Map) -> None: + """ + Redefine the current map. + """ + if len(self.maps) == self.map_index: + # Insert new map + self.maps.append(m) + # Redefine the current map + self.maps[self.map_index] = m + def run(self, screen: Any) -> None: # pragma no cover """ Main infinite loop. From 8636d571b597095f176fe634d7e1537f74356556 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 26 Dec 2020 00:52:47 +0100 Subject: [PATCH 05/24] Add ladders in the map --- squirrelbattle/assets/example_map.txt | 4 ++-- squirrelbattle/assets/example_map_2.txt | 4 ++-- squirrelbattle/display/texturepack.py | 2 ++ squirrelbattle/interfaces.py | 7 +++++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/squirrelbattle/assets/example_map.txt b/squirrelbattle/assets/example_map.txt index 5aaade9..e52172c 100644 --- a/squirrelbattle/assets/example_map.txt +++ b/squirrelbattle/assets/example_map.txt @@ -1,8 +1,8 @@ 1 6 ####### ############# - #.....# #...........# + #.H...# #...........# #.....# #####...........# - #.....# #...............# + #.....# #............H..# #.##### #.###...........# #.# #.# #...........# #.# #.# ############# diff --git a/squirrelbattle/assets/example_map_2.txt b/squirrelbattle/assets/example_map_2.txt index 8864f04..c959659 100644 --- a/squirrelbattle/assets/example_map_2.txt +++ b/squirrelbattle/assets/example_map_2.txt @@ -1,6 +1,6 @@ 1 17 ########### ######### - #.........# #.......# + #....H....# #.......# #.........# ############.......# #.........###############..........#.......############## #.........#........................#....................# @@ -13,7 +13,7 @@ ########.##########......# #.........# #.........# #...........##......# #.........# #.........# #...........##......# #.........# #.........# - #...........##......# #.........# ################.###### + #...........##..H...# #.........# ################.###### #...........##......# #.........# #.................############ #...........##......# ########.########.......#.........#..........# #...........##......# #...............#.......#.........#..........# diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 1f6dc76..77bbfc7 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -64,6 +64,7 @@ TexturePack.ASCII_PACK = TexturePack( EMPTY=' ', EXPLOSION='%', FLOOR='.', + LADDER='H', HAZELNUT='¤', HEART='❤', HEDGEHOG='*', @@ -90,6 +91,7 @@ TexturePack.SQUIRREL_PACK = TexturePack( EMPTY=' ', EXPLOSION='💥', FLOOR='██', + LADDER='🪜', HAZELNUT='🌰', HEART='💜', HEDGEHOG='🦔', diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 94025bd..3394af8 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -198,6 +198,7 @@ class Tile(Enum): EMPTY = auto() WALL = auto() FLOOR = auto() + LADDER = auto() @staticmethod def from_ascii_char(ch: str) -> "Tile": @@ -222,6 +223,12 @@ class Tile(Enum): """ return self == Tile.WALL + def is_ladder(self) -> bool: + """ + Is this Tile a ladder? + """ + return self == Tile.LADDER + def can_walk(self) -> bool: """ Check if an entity (player or not) can move in this tile. From 9a56b4d7e99cf43404e30973b2784f5a969d0503 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 26 Dec 2020 01:08:43 +0100 Subject: [PATCH 06/24] Navigate through different maps while climbing ladders --- squirrelbattle/assets/example_map.txt | 4 ++-- squirrelbattle/assets/example_map_2.txt | 4 ++-- squirrelbattle/game.py | 20 +++++++++++++++++++- squirrelbattle/interfaces.py | 11 +++++++---- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/squirrelbattle/assets/example_map.txt b/squirrelbattle/assets/example_map.txt index e52172c..be2e798 100644 --- a/squirrelbattle/assets/example_map.txt +++ b/squirrelbattle/assets/example_map.txt @@ -1,8 +1,8 @@ 1 6 ####### ############# - #.H...# #...........# + #.H...# #...........# #.....# #####...........# - #.....# #............H..# + #.....# #............H..# #.##### #.###...........# #.# #.# #...........# #.# #.# ############# diff --git a/squirrelbattle/assets/example_map_2.txt b/squirrelbattle/assets/example_map_2.txt index c959659..b9c751f 100644 --- a/squirrelbattle/assets/example_map_2.txt +++ b/squirrelbattle/assets/example_map_2.txt @@ -1,6 +1,6 @@ 1 17 ########### ######### - #....H....# #.......# + #....H....# #.......# #.........# ############.......# #.........###############..........#.......############## #.........#........................#....................# @@ -13,7 +13,7 @@ ########.##########......# #.........# #.........# #...........##......# #.........# #.........# #...........##......# #.........# #.........# - #...........##..H...# #.........# ################.###### + #...........##..H...# #.........# ################.###### #...........##......# #.........# #.................############ #...........##......# ########.########.......#.........#..........# #...........##......# #...............#.......#.........#..........# diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 7ef8fc9..7807882 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -55,7 +55,7 @@ class Game: # TODO generate a new map procedurally self.maps = [] self.map_index = 0 - self.map = Map.load(ResourceManager.get_asset_path("example_map_2.txt")) + self.map = Map.load(ResourceManager.get_asset_path("example_map.txt")) self.map.logs = self.logs self.logs.clear() self.player = Player() @@ -102,6 +102,24 @@ class Game: self.handle_key_pressed( KeyValues.translate_key(key, self.settings), key) + # FIXME This code should not be there, but rather in Map.tick + y, x = self.player.y, self.player.x + if self.map.tiles[y][x].is_ladder(): + # We move up on the ladder of the beginning, + # down at the end of the stage + move_down = y != self.map.start_y and x != self.map.start_x + old_map = self.map + self.map_index += 1 if move_down else -1 + self.map_index = max(0, self.map_index) + while self.map_index >= len(self.maps): + self.maps.append(Map.load(ResourceManager.get_asset_path( + "example_map_2.txt"))) + new_map = self.map + old_map.remove_entity(self.player) + new_map.add_entity(self.player) + self.player.move(self.map.start_y, self.map.start_x) + self.display_actions(DisplayActions.UPDATE) + def handle_key_pressed(self, key: Optional[KeyValues], raw_key: str = '')\ -> None: """ diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 3394af8..788edaa 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -140,12 +140,15 @@ class Map: Put randomly {count} entities on the map, where it is available. """ for ignored in range(count): - y, x = 0, 0 while True: y, x = randint(0, self.height - 1), randint(0, self.width - 1) - tile = self.tiles[y][x] - if tile.can_walk(): - break + try: + tile = self.tiles[y][x] + except Exception as e: + raise Exception(y, x, len(self.tiles)) + else: + if tile.can_walk(): + break entity = choice(Entity.get_all_entity_classes())() entity.move(y, x) self.add_entity(entity) From 6b7f8867facfecb817cc8da9a4c74bc961efc447 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 26 Dec 2020 14:02:35 +0100 Subject: [PATCH 07/24] Tile colors can be overwritten --- squirrelbattle/display/mapdisplay.py | 7 +++++-- squirrelbattle/display/texturepack.py | 2 +- squirrelbattle/interfaces.py | 16 ++++++++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index 7e21adb..3a21470 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -19,8 +19,11 @@ class MapDisplay(Display): def update_pad(self) -> None: self.pad.resize(500, 500) - self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack), - self.pack.tile_fg_color, self.pack.tile_bg_color) + for i in range(self.map.height): + for j in range(self.map.width): + self.addstr(self.pad, i, j * self.pack.tile_width, + self.map.tiles[i][j].char(self.pack), + *self.map.tiles[i][j].color(self.pack)) for e in self.map.entities: self.addstr(self.pad, e.y, self.pack.tile_width * e.x, self.pack[e.name.upper()], diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 77bbfc7..44a8265 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -91,7 +91,7 @@ TexturePack.SQUIRREL_PACK = TexturePack( EMPTY=' ', EXPLOSION='💥', FLOOR='██', - LADDER='🪜', + LADDER=('🪜', curses.COLOR_WHITE, curses.COLOR_WHITE), HAZELNUT='🌰', HEART='💜', HEDGEHOG='🦔', diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 788edaa..ff94cc6 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -4,7 +4,7 @@ from enum import Enum, auto from math import sqrt from random import choice, randint -from typing import List, Optional, Any +from typing import List, Optional, Any, Tuple from .display.texturepack import TexturePack from .translations import gettext as _ @@ -218,7 +218,19 @@ class Tile(Enum): Translates a Tile to the corresponding character according to the texture pack """ - return getattr(pack, self.name) + val = getattr(pack, self.name) + if isinstance(val, tuple): + return val[0] + return val + + def color(self, pack: TexturePack) -> Tuple[int, int]: + """ + Retrieve the tuple (fg_color, bg_color) of the current Tile. + """ + val = getattr(pack, self.name) + if isinstance(val, tuple): + return val[1], val[2] + return pack.tile_fg_color, pack.tile_bg_color def is_wall(self) -> bool: """ From 663fc0eecd451ff48c43cd045e645be9aa06b77d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 26 Dec 2020 21:13:17 +0100 Subject: [PATCH 08/24] Better teleport --- squirrelbattle/game.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 7807882..1b856e7 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -110,14 +110,28 @@ class Game: move_down = y != self.map.start_y and x != self.map.start_x old_map = self.map self.map_index += 1 if move_down else -1 - self.map_index = max(0, self.map_index) + if self.map_index == -1: + self.map_index = 0 + return while self.map_index >= len(self.maps): self.maps.append(Map.load(ResourceManager.get_asset_path( "example_map_2.txt"))) new_map = self.map old_map.remove_entity(self.player) new_map.add_entity(self.player) - self.player.move(self.map.start_y, self.map.start_x) + if move_down: + self.player.move(self.map.start_y, self.map.start_x) + elif KeyValues.translate_key(key, self.settings) \ + in [KeyValues.UP, KeyValues.DOWN, + KeyValues.LEFT, KeyValues.RIGHT]: + ladder_y, ladder_x = -1, -1 + for y in range(self.map.height): + for x in range(self.map.width): + if (y, x) != (self.map.start_y, self.map.start_x) \ + and self.map.tiles[y][x].is_ladder(): + ladder_y, ladder_x = y, x + break + self.player.move(ladder_y, ladder_x) self.display_actions(DisplayActions.UPDATE) def handle_key_pressed(self, key: Optional[KeyValues], raw_key: str = '')\ From d06a405120acb02ae80e9ee70bde8b6d72d9b45c Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 14:55:16 +0100 Subject: [PATCH 09/24] Use a key to use ladders --- squirrelbattle/enums.py | 3 + squirrelbattle/game.py | 66 ++++++++++--------- .../locale/de/LC_MESSAGES/squirrelbattle.po | 60 +++++++++-------- .../locale/es/LC_MESSAGES/squirrelbattle.po | 60 +++++++++-------- .../locale/fr/LC_MESSAGES/squirrelbattle.po | 60 +++++++++-------- squirrelbattle/settings.py | 1 + squirrelbattle/tests/translations_test.py | 2 + 7 files changed, 136 insertions(+), 116 deletions(-) diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index 7e4efa4..5495bd3 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -47,6 +47,7 @@ class KeyValues(Enum): SPACE = auto() CHAT = auto() WAIT = auto() + LADDER = auto() @staticmethod def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: @@ -81,4 +82,6 @@ class KeyValues(Enum): return KeyValues.CHAT elif key == settings.KEY_WAIT: return KeyValues.WAIT + elif key == settings.KEY_LADDER: + return KeyValues.LADDER return None diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 1b856e7..dff09e0 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -102,38 +102,6 @@ class Game: self.handle_key_pressed( KeyValues.translate_key(key, self.settings), key) - # FIXME This code should not be there, but rather in Map.tick - y, x = self.player.y, self.player.x - if self.map.tiles[y][x].is_ladder(): - # We move up on the ladder of the beginning, - # down at the end of the stage - move_down = y != self.map.start_y and x != self.map.start_x - old_map = self.map - self.map_index += 1 if move_down else -1 - if self.map_index == -1: - self.map_index = 0 - return - while self.map_index >= len(self.maps): - self.maps.append(Map.load(ResourceManager.get_asset_path( - "example_map_2.txt"))) - new_map = self.map - old_map.remove_entity(self.player) - new_map.add_entity(self.player) - if move_down: - self.player.move(self.map.start_y, self.map.start_x) - elif KeyValues.translate_key(key, self.settings) \ - in [KeyValues.UP, KeyValues.DOWN, - KeyValues.LEFT, KeyValues.RIGHT]: - ladder_y, ladder_x = -1, -1 - for y in range(self.map.height): - for x in range(self.map.width): - if (y, x) != (self.map.start_y, self.map.start_x) \ - and self.map.tiles[y][x].is_ladder(): - ladder_y, ladder_x = y, x - break - self.player.move(ladder_y, ladder_x) - self.display_actions(DisplayActions.UPDATE) - def handle_key_pressed(self, key: Optional[KeyValues], raw_key: str = '')\ -> None: """ @@ -186,6 +154,40 @@ class Game: self.waiting_for_friendly_key = True elif key == KeyValues.WAIT: self.map.tick() + elif key == KeyValues.LADDER: + # On a ladder, we switch level + y, x = self.player.y, self.player.x + if not self.map.tiles[y][x].is_ladder(): + return + + # We move up on the ladder of the beginning, + # down at the end of the stage + move_down = y != self.map.start_y and x != self.map.start_x + old_map = self.map + self.map_index += 1 if move_down else -1 + if self.map_index == -1: + self.map_index = 0 + return + while self.map_index >= len(self.maps): + # TODO: generate a new map + self.maps.append(Map.load(ResourceManager.get_asset_path( + "example_map_2.txt"))) + new_map = self.map + old_map.remove_entity(self.player) + new_map.add_entity(self.player) + if move_down: + self.player.move(self.map.start_y, self.map.start_x) + else: + # Find the ladder of the end of the game + ladder_y, ladder_x = -1, -1 + for y in range(self.map.height): + for x in range(self.map.width): + if (y, x) != (self.map.start_y, self.map.start_x) \ + and self.map.tiles[y][x].is_ladder(): + ladder_y, ladder_x = y, x + break + self.player.move(ladder_y, ladder_x) + self.display_actions(DisplayActions.UPDATE) def handle_friendly_entity_chat(self, key: KeyValues) -> None: """ diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 29bf10e..591b5ae 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2020-12-12 18:02+0100\n" +"POT-Creation-Date: 2021-01-06 14:53+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,19 +17,19 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: squirrelbattle/display/menudisplay.py:139 +#: squirrelbattle/display/menudisplay.py:160 msgid "INVENTORY" msgstr "BESTAND" -#: squirrelbattle/display/menudisplay.py:164 +#: squirrelbattle/display/menudisplay.py:202 msgid "STALL" msgstr "STAND" -#: squirrelbattle/display/statsdisplay.py:33 +#: squirrelbattle/display/statsdisplay.py:34 msgid "Inventory:" msgstr "Bestand:" -#: squirrelbattle/display/statsdisplay.py:52 +#: squirrelbattle/display/statsdisplay.py:53 msgid "YOU ARE DEAD" msgstr "SIE WURDEN GESTORBEN" @@ -58,11 +58,11 @@ msgstr "Die Bombe explodiert." msgid "{player} exchanged its body with {entity}." msgstr "{player} täuscht seinem Körper mit {entity} aus." -#: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 +#: squirrelbattle/game.py:277 squirrelbattle/tests/game_test.py:592 msgid "The buyer does not have enough money" msgstr "Der Kaufer hat nicht genug Geld" -#: squirrelbattle/game.py:249 +#: squirrelbattle/game.py:320 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -70,7 +70,7 @@ msgstr "" "In Ihrer Speicherdatei fehlen einige Schlüssel.\n" "Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." -#: squirrelbattle/game.py:257 +#: squirrelbattle/game.py:328 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -78,7 +78,7 @@ msgstr "" "Auf dieser Karte wurde kein Spieler gefunden!\n" "Vielleicht sind Sie gestorben?" -#: squirrelbattle/game.py:277 +#: squirrelbattle/game.py:348 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -86,22 +86,22 @@ msgstr "" "Die JSON-Datei ist nicht korrekt.\n" "Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht." -#: squirrelbattle/interfaces.py:429 +#: squirrelbattle/interfaces.py:451 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} schlägt {opponent}." -#: squirrelbattle/interfaces.py:441 +#: squirrelbattle/interfaces.py:463 #, python-brace-format msgid "{name} takes {amount} damage." msgstr "{name} nimmt {amount} Schadenspunkte." -#: squirrelbattle/interfaces.py:443 +#: squirrelbattle/interfaces.py:465 #, python-brace-format msgid "{name} dies." msgstr "{name} stirbt." -#: squirrelbattle/interfaces.py:477 +#: squirrelbattle/interfaces.py:499 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} hat gesagt: {message}" @@ -110,8 +110,8 @@ msgstr "{entity} hat gesagt: {message}" msgid "Back" msgstr "Zurück" -#: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 -#: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 +#: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 +#: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Neu Spiel" @@ -197,57 +197,61 @@ msgid "Key used to wait" msgstr "Wartentaste" #: squirrelbattle/tests/translations_test.py:56 +msgid "Key used to use ladders" +msgstr "Leitertaste" + +#: squirrelbattle/tests/translations_test.py:58 msgid "Texture pack" msgstr "Textur-Packung" -#: squirrelbattle/tests/translations_test.py:57 +#: squirrelbattle/tests/translations_test.py:59 msgid "Language" msgstr "Sprache" -#: squirrelbattle/tests/translations_test.py:60 +#: squirrelbattle/tests/translations_test.py:62 msgid "player" msgstr "Spieler" -#: squirrelbattle/tests/translations_test.py:62 +#: squirrelbattle/tests/translations_test.py:64 msgid "hedgehog" msgstr "Igel" -#: squirrelbattle/tests/translations_test.py:63 +#: squirrelbattle/tests/translations_test.py:65 msgid "merchant" msgstr "Kaufmann" -#: squirrelbattle/tests/translations_test.py:64 +#: squirrelbattle/tests/translations_test.py:66 msgid "rabbit" msgstr "Kanninchen" -#: squirrelbattle/tests/translations_test.py:65 +#: squirrelbattle/tests/translations_test.py:67 msgid "sunflower" msgstr "Sonnenblume" -#: squirrelbattle/tests/translations_test.py:66 +#: squirrelbattle/tests/translations_test.py:68 msgid "teddy bear" msgstr "Teddybär" -#: squirrelbattle/tests/translations_test.py:67 +#: squirrelbattle/tests/translations_test.py:69 msgid "tiger" msgstr "Tiger" -#: squirrelbattle/tests/translations_test.py:69 +#: squirrelbattle/tests/translations_test.py:71 msgid "body snatch potion" msgstr "Leichenfleddererzaubertrank" -#: squirrelbattle/tests/translations_test.py:70 +#: squirrelbattle/tests/translations_test.py:72 msgid "bomb" msgstr "Bombe" -#: squirrelbattle/tests/translations_test.py:71 +#: squirrelbattle/tests/translations_test.py:73 msgid "explosion" msgstr "Explosion" -#: squirrelbattle/tests/translations_test.py:72 +#: squirrelbattle/tests/translations_test.py:74 msgid "heart" msgstr "Herz" -#: squirrelbattle/tests/translations_test.py:73 +#: squirrelbattle/tests/translations_test.py:75 msgid "sword" msgstr "schwert" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 8a7bc3e..035cad2 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2020-12-12 18:02+0100\n" +"POT-Creation-Date: 2021-01-06 14:53+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,19 +17,19 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: squirrelbattle/display/menudisplay.py:139 +#: squirrelbattle/display/menudisplay.py:160 msgid "INVENTORY" msgstr "INVENTORIO" -#: squirrelbattle/display/menudisplay.py:164 +#: squirrelbattle/display/menudisplay.py:202 msgid "STALL" msgstr "PUESTO" -#: squirrelbattle/display/statsdisplay.py:33 +#: squirrelbattle/display/statsdisplay.py:34 msgid "Inventory:" msgstr "Inventorio :" -#: squirrelbattle/display/statsdisplay.py:52 +#: squirrelbattle/display/statsdisplay.py:53 msgid "YOU ARE DEAD" msgstr "ERES MUERTO" @@ -57,11 +57,11 @@ msgstr "La bomba está explotando." msgid "{player} exchanged its body with {entity}." msgstr "{player} intercambió su cuerpo con {entity}." -#: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 +#: squirrelbattle/game.py:277 squirrelbattle/tests/game_test.py:592 msgid "The buyer does not have enough money" msgstr "El comprador no tiene suficiente dinero" -#: squirrelbattle/game.py:249 +#: squirrelbattle/game.py:320 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -69,7 +69,7 @@ msgstr "" "Algunas claves faltan en su archivo de guarda.\n" "Su guarda parece a ser corruptido. Fue eliminado." -#: squirrelbattle/game.py:257 +#: squirrelbattle/game.py:328 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -77,7 +77,7 @@ msgstr "" "No jugador encontrado sobre la carta !\n" "¿ Quizas murió ?" -#: squirrelbattle/game.py:277 +#: squirrelbattle/game.py:348 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -85,22 +85,22 @@ msgstr "" "El JSON archivo no es correcto.\n" "Su guarda parece corrupta. Fue eliminada." -#: squirrelbattle/interfaces.py:429 +#: squirrelbattle/interfaces.py:451 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} golpea a {opponent}." -#: squirrelbattle/interfaces.py:441 +#: squirrelbattle/interfaces.py:463 #, python-brace-format msgid "{name} takes {amount} damage." msgstr "{name} recibe {amount} daño." -#: squirrelbattle/interfaces.py:443 +#: squirrelbattle/interfaces.py:465 #, python-brace-format msgid "{name} dies." msgstr "{name} se muere." -#: squirrelbattle/interfaces.py:477 +#: squirrelbattle/interfaces.py:499 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} dijo : {message}" @@ -109,8 +109,8 @@ msgstr "{entity} dijo : {message}" msgid "Back" msgstr "Volver" -#: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 -#: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 +#: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 +#: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Nuevo partido" @@ -196,57 +196,61 @@ msgid "Key used to wait" msgstr "Tecla para espera" #: squirrelbattle/tests/translations_test.py:56 +msgid "Key used to use ladders" +msgstr "Tecla para el uso de las escaleras" + +#: squirrelbattle/tests/translations_test.py:58 msgid "Texture pack" msgstr "Paquete de texturas" -#: squirrelbattle/tests/translations_test.py:57 +#: squirrelbattle/tests/translations_test.py:59 msgid "Language" msgstr "Languaje" -#: squirrelbattle/tests/translations_test.py:60 +#: squirrelbattle/tests/translations_test.py:62 msgid "player" msgstr "jugador" -#: squirrelbattle/tests/translations_test.py:62 +#: squirrelbattle/tests/translations_test.py:64 msgid "hedgehog" msgstr "erizo" -#: squirrelbattle/tests/translations_test.py:63 +#: squirrelbattle/tests/translations_test.py:65 msgid "merchant" msgstr "comerciante" -#: squirrelbattle/tests/translations_test.py:64 +#: squirrelbattle/tests/translations_test.py:66 msgid "rabbit" msgstr "conejo" -#: squirrelbattle/tests/translations_test.py:65 +#: squirrelbattle/tests/translations_test.py:67 msgid "sunflower" msgstr "girasol" -#: squirrelbattle/tests/translations_test.py:66 +#: squirrelbattle/tests/translations_test.py:68 msgid "teddy bear" msgstr "osito de peluche" -#: squirrelbattle/tests/translations_test.py:67 +#: squirrelbattle/tests/translations_test.py:69 msgid "tiger" msgstr "tigre" -#: squirrelbattle/tests/translations_test.py:69 +#: squirrelbattle/tests/translations_test.py:71 msgid "body snatch potion" msgstr "poción de intercambio" -#: squirrelbattle/tests/translations_test.py:70 +#: squirrelbattle/tests/translations_test.py:72 msgid "bomb" msgstr "bomba" -#: squirrelbattle/tests/translations_test.py:71 +#: squirrelbattle/tests/translations_test.py:73 msgid "explosion" msgstr "explosión" -#: squirrelbattle/tests/translations_test.py:72 +#: squirrelbattle/tests/translations_test.py:74 msgid "heart" msgstr "corazón" -#: squirrelbattle/tests/translations_test.py:73 +#: squirrelbattle/tests/translations_test.py:75 msgid "sword" msgstr "espada" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index ffbfdce..a64d96c 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2020-12-12 18:02+0100\n" +"POT-Creation-Date: 2021-01-06 14:53+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,19 +17,19 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: squirrelbattle/display/menudisplay.py:139 +#: squirrelbattle/display/menudisplay.py:160 msgid "INVENTORY" msgstr "INVENTAIRE" -#: squirrelbattle/display/menudisplay.py:164 +#: squirrelbattle/display/menudisplay.py:202 msgid "STALL" msgstr "STAND" -#: squirrelbattle/display/statsdisplay.py:33 +#: squirrelbattle/display/statsdisplay.py:34 msgid "Inventory:" msgstr "Inventaire :" -#: squirrelbattle/display/statsdisplay.py:52 +#: squirrelbattle/display/statsdisplay.py:53 msgid "YOU ARE DEAD" msgstr "VOUS ÊTES MORT" @@ -58,11 +58,11 @@ msgstr "La bombe explose." msgid "{player} exchanged its body with {entity}." msgstr "{player} a échangé son corps avec {entity}." -#: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 +#: squirrelbattle/game.py:277 squirrelbattle/tests/game_test.py:592 msgid "The buyer does not have enough money" msgstr "L'acheteur n'a pas assez d'argent" -#: squirrelbattle/game.py:249 +#: squirrelbattle/game.py:320 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -70,7 +70,7 @@ msgstr "" "Certaines clés de votre ficher de sauvegarde sont manquantes.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/game.py:257 +#: squirrelbattle/game.py:328 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -78,7 +78,7 @@ msgstr "" "Aucun joueur n'a été trouvé sur la carte !\n" "Peut-être êtes-vous mort ?" -#: squirrelbattle/game.py:277 +#: squirrelbattle/game.py:348 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -86,22 +86,22 @@ msgstr "" "Le fichier JSON de sauvegarde est incorrect.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/interfaces.py:429 +#: squirrelbattle/interfaces.py:451 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} frappe {opponent}." -#: squirrelbattle/interfaces.py:441 +#: squirrelbattle/interfaces.py:463 #, python-brace-format msgid "{name} takes {amount} damage." msgstr "{name} prend {amount} points de dégât." -#: squirrelbattle/interfaces.py:443 +#: squirrelbattle/interfaces.py:465 #, python-brace-format msgid "{name} dies." msgstr "{name} meurt." -#: squirrelbattle/interfaces.py:477 +#: squirrelbattle/interfaces.py:499 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} a dit : {message}" @@ -110,8 +110,8 @@ msgstr "{entity} a dit : {message}" msgid "Back" msgstr "Retour" -#: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 -#: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 +#: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 +#: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Nouvelle partie" @@ -197,57 +197,61 @@ msgid "Key used to wait" msgstr "Touche pour attendre" #: squirrelbattle/tests/translations_test.py:56 +msgid "Key used to use ladders" +msgstr "Touche pour utiliser les échelles" + +#: squirrelbattle/tests/translations_test.py:58 msgid "Texture pack" msgstr "Pack de textures" -#: squirrelbattle/tests/translations_test.py:57 +#: squirrelbattle/tests/translations_test.py:59 msgid "Language" msgstr "Langue" -#: squirrelbattle/tests/translations_test.py:60 +#: squirrelbattle/tests/translations_test.py:62 msgid "player" msgstr "joueur" -#: squirrelbattle/tests/translations_test.py:62 +#: squirrelbattle/tests/translations_test.py:64 msgid "hedgehog" msgstr "hérisson" -#: squirrelbattle/tests/translations_test.py:63 +#: squirrelbattle/tests/translations_test.py:65 msgid "merchant" msgstr "marchand" -#: squirrelbattle/tests/translations_test.py:64 +#: squirrelbattle/tests/translations_test.py:66 msgid "rabbit" msgstr "lapin" -#: squirrelbattle/tests/translations_test.py:65 +#: squirrelbattle/tests/translations_test.py:67 msgid "sunflower" msgstr "tournesol" -#: squirrelbattle/tests/translations_test.py:66 +#: squirrelbattle/tests/translations_test.py:68 msgid "teddy bear" msgstr "nounours" -#: squirrelbattle/tests/translations_test.py:67 +#: squirrelbattle/tests/translations_test.py:69 msgid "tiger" msgstr "tigre" -#: squirrelbattle/tests/translations_test.py:69 +#: squirrelbattle/tests/translations_test.py:71 msgid "body snatch potion" msgstr "potion d'arrachage de corps" -#: squirrelbattle/tests/translations_test.py:70 +#: squirrelbattle/tests/translations_test.py:72 msgid "bomb" msgstr "bombe" -#: squirrelbattle/tests/translations_test.py:71 +#: squirrelbattle/tests/translations_test.py:73 msgid "explosion" msgstr "" -#: squirrelbattle/tests/translations_test.py:72 +#: squirrelbattle/tests/translations_test.py:74 msgid "heart" msgstr "cœur" -#: squirrelbattle/tests/translations_test.py:73 +#: squirrelbattle/tests/translations_test.py:75 msgid "sword" msgstr "épée" diff --git a/squirrelbattle/settings.py b/squirrelbattle/settings.py index 91edfa4..ad986eb 100644 --- a/squirrelbattle/settings.py +++ b/squirrelbattle/settings.py @@ -33,6 +33,7 @@ class Settings: self.KEY_DROP = ['r', 'Key used to drop an item in the inventory'] self.KEY_CHAT = ['t', 'Key used to talk to a friendly entity'] self.KEY_WAIT = ['w', 'Key used to wait'] + self.KEY_LADDER = ['<', 'Key used to use ladders'] self.TEXTURE_PACK = ['ascii', 'Texture pack'] self.LOCALE = [locale.getlocale()[0][:2], 'Language'] diff --git a/squirrelbattle/tests/translations_test.py b/squirrelbattle/tests/translations_test.py index 0bd8873..9ec39c7 100644 --- a/squirrelbattle/tests/translations_test.py +++ b/squirrelbattle/tests/translations_test.py @@ -53,6 +53,8 @@ class TestTranslations(unittest.TestCase): self.assertEqual(_("Key used to talk to a friendly entity"), "Touche pour parler à une entité pacifique") self.assertEqual(_("Key used to wait"), "Touche pour attendre") + self.assertEqual(_("Key used to use ladders"), + "Touche pour utiliser les échelles") self.assertEqual(_("Texture pack"), "Pack de textures") self.assertEqual(_("Language"), "Langue") From 4cd4bc90052a1bd5ccd932a551115ddb7de1fb3e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 15:17:02 +0100 Subject: [PATCH 10/24] Display the current floor in the StatsDisplay --- squirrelbattle/display/statsdisplay.py | 18 ++++++++++-------- squirrelbattle/game.py | 1 + squirrelbattle/interfaces.py | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/squirrelbattle/display/statsdisplay.py b/squirrelbattle/display/statsdisplay.py index ef358bb..fb5160f 100644 --- a/squirrelbattle/display/statsdisplay.py +++ b/squirrelbattle/display/statsdisplay.py @@ -20,15 +20,17 @@ class StatsDisplay(Display): self.player = game.player def update_pad(self) -> None: - string2 = "Player -- LVL {}\nEXP {}/{}\nHP {}/{}"\ - .format(self.player.level, self.player.current_xp, - self.player.max_xp, self.player.health, - self.player.maxhealth) + string2 = f"{_(self.player.name).capitalize()} " \ + f"-- LVL {self.player.level} -- " \ + f"FLOOR {-self.player.map.floor}\n" \ + f"EXP {self.player.current_xp}/{self.player.max_xp}\n" \ + f"HP {self.player.health}/{self.player.maxhealth}" self.addstr(self.pad, 0, 0, string2) - string3 = "STR {}\nINT {}\nCHR {}\nDEX {}\nCON {}"\ - .format(self.player.strength, - self.player.intelligence, self.player.charisma, - self.player.dexterity, self.player.constitution) + string3 = f"STR {self.player.strength}\n" \ + f"INT {self.player.intelligence}\n" \ + f"CHR {self.player.charisma}\n" \ + f"DEX {self.player.dexterity}\n" \ + f"CON {self.player.constitution}" self.addstr(self.pad, 3, 0, string3) inventory_str = _("Inventory:") + " " diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index dff09e0..05b4003 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -173,6 +173,7 @@ class Game: self.maps.append(Map.load(ResourceManager.get_asset_path( "example_map_2.txt"))) new_map = self.map + new_map.floor = self.map_index old_map.remove_entity(self.player) new_map.add_entity(self.player) if move_down: diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index ff94cc6..556f55c 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -35,6 +35,7 @@ class Map: Object that represents a Map with its width, height and tiles, that have their custom properties. """ + floor: int width: int height: int start_y: int @@ -49,6 +50,7 @@ class Map: def __init__(self, width: int, height: int, tiles: list, start_y: int, start_x: int): + self.floor = 0 self.width = width self.height = height self.start_y = start_y From a48e6325fe2dd48640d8f45a2c3ef2114b5a510e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 15:55:44 +0100 Subject: [PATCH 11/24] Add log message when the player switches floor --- squirrelbattle/game.py | 7 ++++ .../locale/de/LC_MESSAGES/squirrelbattle.po | 32 ++++++++++++------- .../locale/es/LC_MESSAGES/squirrelbattle.po | 32 ++++++++++++------- .../locale/fr/LC_MESSAGES/squirrelbattle.po | 32 ++++++++++++------- 4 files changed, 70 insertions(+), 33 deletions(-) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 05b4003..f522196 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -178,6 +178,9 @@ class Game: new_map.add_entity(self.player) if move_down: self.player.move(self.map.start_y, self.map.start_x) + self.logs.add_message( + _("The player climbs down to the floor {floor}.") + .format(floor=-self.map_index)) else: # Find the ladder of the end of the game ladder_y, ladder_x = -1, -1 @@ -188,6 +191,10 @@ class Game: ladder_y, ladder_x = y, x break self.player.move(ladder_y, ladder_x) + self.logs.add_message( + _("The player climbs up the floor {floor}.") + .format(floor=-self.map_index)) + self.display_actions(DisplayActions.UPDATE) def handle_friendly_entity_chat(self, key: KeyValues) -> None: diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 591b5ae..56d4540 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-06 14:53+0100\n" +"POT-Creation-Date: 2021-01-06 15:19+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,11 +25,11 @@ msgstr "BESTAND" msgid "STALL" msgstr "STAND" -#: squirrelbattle/display/statsdisplay.py:34 +#: squirrelbattle/display/statsdisplay.py:36 msgid "Inventory:" msgstr "Bestand:" -#: squirrelbattle/display/statsdisplay.py:53 +#: squirrelbattle/display/statsdisplay.py:55 msgid "YOU ARE DEAD" msgstr "SIE WURDEN GESTORBEN" @@ -58,11 +58,21 @@ msgstr "Die Bombe explodiert." msgid "{player} exchanged its body with {entity}." msgstr "{player} täuscht seinem Körper mit {entity} aus." -#: squirrelbattle/game.py:277 squirrelbattle/tests/game_test.py:592 +#: squirrelbattle/game.py:182 +#, python-brace-format +msgid "The player climbs down to the floor {floor}." +msgstr "Der Spieler klettert auf dem Stock {floor} hinunter." + +#: squirrelbattle/game.py:195 +#, python-brace-format +msgid "The player climbs up the floor {floor}." +msgstr "Der Spieler klettert auf dem Stock {floor} hinoben." + +#: squirrelbattle/game.py:285 squirrelbattle/tests/game_test.py:592 msgid "The buyer does not have enough money" msgstr "Der Kaufer hat nicht genug Geld" -#: squirrelbattle/game.py:320 +#: squirrelbattle/game.py:328 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -70,7 +80,7 @@ msgstr "" "In Ihrer Speicherdatei fehlen einige Schlüssel.\n" "Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." -#: squirrelbattle/game.py:328 +#: squirrelbattle/game.py:336 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -78,7 +88,7 @@ msgstr "" "Auf dieser Karte wurde kein Spieler gefunden!\n" "Vielleicht sind Sie gestorben?" -#: squirrelbattle/game.py:348 +#: squirrelbattle/game.py:356 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -86,22 +96,22 @@ msgstr "" "Die JSON-Datei ist nicht korrekt.\n" "Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht." -#: squirrelbattle/interfaces.py:451 +#: squirrelbattle/interfaces.py:453 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} schlägt {opponent}." -#: squirrelbattle/interfaces.py:463 +#: squirrelbattle/interfaces.py:465 #, python-brace-format msgid "{name} takes {amount} damage." msgstr "{name} nimmt {amount} Schadenspunkte." -#: squirrelbattle/interfaces.py:465 +#: squirrelbattle/interfaces.py:467 #, python-brace-format msgid "{name} dies." msgstr "{name} stirbt." -#: squirrelbattle/interfaces.py:499 +#: squirrelbattle/interfaces.py:501 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} hat gesagt: {message}" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 035cad2..f85e681 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-06 14:53+0100\n" +"POT-Creation-Date: 2021-01-06 15:19+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,11 +25,11 @@ msgstr "INVENTORIO" msgid "STALL" msgstr "PUESTO" -#: squirrelbattle/display/statsdisplay.py:34 +#: squirrelbattle/display/statsdisplay.py:36 msgid "Inventory:" msgstr "Inventorio :" -#: squirrelbattle/display/statsdisplay.py:53 +#: squirrelbattle/display/statsdisplay.py:55 msgid "YOU ARE DEAD" msgstr "ERES MUERTO" @@ -57,11 +57,21 @@ msgstr "La bomba está explotando." msgid "{player} exchanged its body with {entity}." msgstr "{player} intercambió su cuerpo con {entity}." -#: squirrelbattle/game.py:277 squirrelbattle/tests/game_test.py:592 +#: squirrelbattle/game.py:182 +#, python-brace-format +msgid "The player climbs down to the floor {floor}." +msgstr "" + +#: squirrelbattle/game.py:195 +#, python-brace-format +msgid "The player climbs up the floor {floor}." +msgstr "" + +#: squirrelbattle/game.py:285 squirrelbattle/tests/game_test.py:592 msgid "The buyer does not have enough money" msgstr "El comprador no tiene suficiente dinero" -#: squirrelbattle/game.py:320 +#: squirrelbattle/game.py:328 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -69,7 +79,7 @@ msgstr "" "Algunas claves faltan en su archivo de guarda.\n" "Su guarda parece a ser corruptido. Fue eliminado." -#: squirrelbattle/game.py:328 +#: squirrelbattle/game.py:336 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -77,7 +87,7 @@ msgstr "" "No jugador encontrado sobre la carta !\n" "¿ Quizas murió ?" -#: squirrelbattle/game.py:348 +#: squirrelbattle/game.py:356 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -85,22 +95,22 @@ msgstr "" "El JSON archivo no es correcto.\n" "Su guarda parece corrupta. Fue eliminada." -#: squirrelbattle/interfaces.py:451 +#: squirrelbattle/interfaces.py:453 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} golpea a {opponent}." -#: squirrelbattle/interfaces.py:463 +#: squirrelbattle/interfaces.py:465 #, python-brace-format msgid "{name} takes {amount} damage." msgstr "{name} recibe {amount} daño." -#: squirrelbattle/interfaces.py:465 +#: squirrelbattle/interfaces.py:467 #, python-brace-format msgid "{name} dies." msgstr "{name} se muere." -#: squirrelbattle/interfaces.py:499 +#: squirrelbattle/interfaces.py:501 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} dijo : {message}" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index a64d96c..81a4ab3 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-06 14:53+0100\n" +"POT-Creation-Date: 2021-01-06 15:19+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,11 +25,11 @@ msgstr "INVENTAIRE" msgid "STALL" msgstr "STAND" -#: squirrelbattle/display/statsdisplay.py:34 +#: squirrelbattle/display/statsdisplay.py:36 msgid "Inventory:" msgstr "Inventaire :" -#: squirrelbattle/display/statsdisplay.py:53 +#: squirrelbattle/display/statsdisplay.py:55 msgid "YOU ARE DEAD" msgstr "VOUS ÊTES MORT" @@ -58,11 +58,21 @@ msgstr "La bombe explose." msgid "{player} exchanged its body with {entity}." msgstr "{player} a échangé son corps avec {entity}." -#: squirrelbattle/game.py:277 squirrelbattle/tests/game_test.py:592 +#: squirrelbattle/game.py:182 +#, python-brace-format +msgid "The player climbs down to the floor {floor}." +msgstr "Le joueur descend à l'étage {floor}." + +#: squirrelbattle/game.py:195 +#, python-brace-format +msgid "The player climbs up the floor {floor}." +msgstr "Le joueur monte à l'étage {floor}." + +#: squirrelbattle/game.py:285 squirrelbattle/tests/game_test.py:592 msgid "The buyer does not have enough money" msgstr "L'acheteur n'a pas assez d'argent" -#: squirrelbattle/game.py:320 +#: squirrelbattle/game.py:328 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -70,7 +80,7 @@ msgstr "" "Certaines clés de votre ficher de sauvegarde sont manquantes.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/game.py:328 +#: squirrelbattle/game.py:336 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -78,7 +88,7 @@ msgstr "" "Aucun joueur n'a été trouvé sur la carte !\n" "Peut-être êtes-vous mort ?" -#: squirrelbattle/game.py:348 +#: squirrelbattle/game.py:356 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -86,22 +96,22 @@ msgstr "" "Le fichier JSON de sauvegarde est incorrect.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/interfaces.py:451 +#: squirrelbattle/interfaces.py:453 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} frappe {opponent}." -#: squirrelbattle/interfaces.py:463 +#: squirrelbattle/interfaces.py:465 #, python-brace-format msgid "{name} takes {amount} damage." msgstr "{name} prend {amount} points de dégât." -#: squirrelbattle/interfaces.py:465 +#: squirrelbattle/interfaces.py:467 #, python-brace-format msgid "{name} dies." msgstr "{name} meurt." -#: squirrelbattle/interfaces.py:499 +#: squirrelbattle/interfaces.py:501 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} a dit : {message}" From 41548504de19231b5b15b7f9c8685e16fd4bf64f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 16:09:53 +0100 Subject: [PATCH 12/24] Test ladders --- squirrelbattle/interfaces.py | 10 +++------ squirrelbattle/tests/game_test.py | 36 ++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 556f55c..472ee95 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -144,13 +144,9 @@ class Map: for ignored in range(count): while True: y, x = randint(0, self.height - 1), randint(0, self.width - 1) - try: - tile = self.tiles[y][x] - except Exception as e: - raise Exception(y, x, len(self.tiles)) - else: - if tile.can_walk(): - break + tile = self.tiles[y][x] + if tile.can_walk(): + break entity = choice(Entity.get_all_entity_classes())() entity.move(y, x) self.add_entity(entity) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 6751016..ab66cb3 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -148,6 +148,9 @@ class TestGame(unittest.TestCase): self.assertEqual(KeyValues.translate_key( self.game.settings.KEY_WAIT, self.game.settings), KeyValues.WAIT) + self.assertEqual(KeyValues.translate_key( + self.game.settings.KEY_LADDER, self.game.settings), + KeyValues.LADDER) self.assertEqual(KeyValues.translate_key(' ', self.game.settings), KeyValues.SPACE) self.assertEqual(KeyValues.translate_key('plop', self.game.settings), @@ -338,7 +341,7 @@ class TestGame(unittest.TestCase): self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a') # Navigate to "texture pack" - for ignored in range(11): + for ignored in range(12): self.game.handle_key_pressed(KeyValues.DOWN) # Change texture pack @@ -609,3 +612,34 @@ class TestGame(unittest.TestCase): # Exit the menu self.game.handle_key_pressed(KeyValues.SPACE) self.assertEqual(self.game.state, GameMode.PLAY) + + def test_ladders(self) -> None: + """ + Ensure that the player can climb on ladders. + """ + self.game.state = GameMode.PLAY + + self.assertEqual(self.game.player.map.floor, 0) + self.game.handle_key_pressed(KeyValues.LADDER) + self.assertEqual(self.game.player.map.floor, 0) + + # Move nowhere + self.game.player.move(10, 10) + self.game.handle_key_pressed(KeyValues.LADDER) + self.assertEqual(self.game.player.map.floor, 0) + + # Move down + self.game.player.move(3, 40) # Move on a ladder + self.game.handle_key_pressed(KeyValues.LADDER) + self.assertEqual(self.game.map_index, 1) + self.assertEqual(self.game.player.map.floor, 1) + self.assertEqual(self.game.player.y, 1) + self.assertEqual(self.game.player.x, 17) + self.game.display_actions(DisplayActions.UPDATE) + + # Move up + self.game.handle_key_pressed(KeyValues.LADDER) + self.assertEqual(self.game.player.map.floor, 0) + self.assertEqual(self.game.player.y, 3) + self.assertEqual(self.game.player.x, 40) + self.game.display_actions(DisplayActions.UPDATE) From 887a190f113c143dc9bcc813d9380c4ab45c49d8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 17:00:43 +0100 Subject: [PATCH 13/24] Less complexity on the handle key function --- squirrelbattle/game.py | 84 ++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index f522196..bb9ee86 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -155,47 +155,53 @@ class Game: elif key == KeyValues.WAIT: self.map.tick() elif key == KeyValues.LADDER: - # On a ladder, we switch level - y, x = self.player.y, self.player.x - if not self.map.tiles[y][x].is_ladder(): - return + self.handle_ladder() - # We move up on the ladder of the beginning, - # down at the end of the stage - move_down = y != self.map.start_y and x != self.map.start_x - old_map = self.map - self.map_index += 1 if move_down else -1 - if self.map_index == -1: - self.map_index = 0 - return - while self.map_index >= len(self.maps): - # TODO: generate a new map - self.maps.append(Map.load(ResourceManager.get_asset_path( - "example_map_2.txt"))) - new_map = self.map - new_map.floor = self.map_index - old_map.remove_entity(self.player) - new_map.add_entity(self.player) - if move_down: - self.player.move(self.map.start_y, self.map.start_x) - self.logs.add_message( - _("The player climbs down to the floor {floor}.") - .format(floor=-self.map_index)) - else: - # Find the ladder of the end of the game - ladder_y, ladder_x = -1, -1 - for y in range(self.map.height): - for x in range(self.map.width): - if (y, x) != (self.map.start_y, self.map.start_x) \ - and self.map.tiles[y][x].is_ladder(): - ladder_y, ladder_x = y, x - break - self.player.move(ladder_y, ladder_x) - self.logs.add_message( - _("The player climbs up the floor {floor}.") - .format(floor=-self.map_index)) + def handle_ladder(self) -> None: + """ + The player pressed the ladder key to switch map + """ + # On a ladder, we switch level + y, x = self.player.y, self.player.x + if not self.map.tiles[y][x].is_ladder(): + return - self.display_actions(DisplayActions.UPDATE) + # We move up on the ladder of the beginning, + # down at the end of the stage + move_down = y != self.map.start_y and x != self.map.start_x + old_map = self.map + self.map_index += 1 if move_down else -1 + if self.map_index == -1: + self.map_index = 0 + return + while self.map_index >= len(self.maps): + # TODO: generate a new map + self.maps.append(Map.load(ResourceManager.get_asset_path( + "example_map_2.txt"))) + new_map = self.map + new_map.floor = self.map_index + old_map.remove_entity(self.player) + new_map.add_entity(self.player) + if move_down: + self.player.move(self.map.start_y, self.map.start_x) + self.logs.add_message( + _("The player climbs down to the floor {floor}.") + .format(floor=-self.map_index)) + else: + # Find the ladder of the end of the game + ladder_y, ladder_x = -1, -1 + for y in range(self.map.height): + for x in range(self.map.width): + if (y, x) != (self.map.start_y, self.map.start_x) \ + and self.map.tiles[y][x].is_ladder(): + ladder_y, ladder_x = y, x + break + self.player.move(ladder_y, ladder_x) + self.logs.add_message( + _("The player climbs up the floor {floor}.") + .format(floor=-self.map_index)) + + self.display_actions(DisplayActions.UPDATE) def handle_friendly_entity_chat(self, key: KeyValues) -> None: """ From 0c2b10b0310834fe1ff92b776669d2a291468845 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 17:21:17 +0100 Subject: [PATCH 14/24] Use ternary conditions to gain coverage --- squirrelbattle/interfaces.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 472ee95..8fde7a9 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -217,18 +217,15 @@ class Tile(Enum): to the texture pack """ val = getattr(pack, self.name) - if isinstance(val, tuple): - return val[0] - return val + return val[0] if isinstance(val, tuple) else val def color(self, pack: TexturePack) -> Tuple[int, int]: """ Retrieve the tuple (fg_color, bg_color) of the current Tile. """ val = getattr(pack, self.name) - if isinstance(val, tuple): - return val[1], val[2] - return pack.tile_fg_color, pack.tile_bg_color + return (val[1], val[2]) if isinstance(val, tuple) else \ + (pack.tile_fg_color, pack.tile_bg_color) def is_wall(self) -> bool: """ From ae505166b7815ef4acc4fb5d552f7367643492ef Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 17:39:13 +0100 Subject: [PATCH 15/24] Disable critical hits during tests --- squirrelbattle/interfaces.py | 4 ++-- squirrelbattle/tests/entities_test.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index b6a3a51..e4557d1 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -444,10 +444,10 @@ class FightingEntity(Entity): """ Deals damage to the opponent, based on the stats """ - diceroll = randint(0, 100) + diceroll = randint(1, 100) damage = self.strength string = " " - if diceroll <= self.critical: # It is a critical hit + if diceroll <= self.critical: # It is a critical hit damage *= 4 string = _(" It's a critical hit! ") return _("{name} hits {opponent}.")\ diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 1d7a7a9..97f0ef8 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -77,6 +77,7 @@ class TestEntities(unittest.TestCase): {self.player.name.capitalize()} takes {entity.strength} damage.") # Fight the rabbit + self.player.critical = 0 old_health = entity.health self.player.move_down() self.assertEqual(entity.health, old_health - self.player.strength) From 093c105120290c038b85c44a79155679f64e9751 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 17:54:43 +0100 Subject: [PATCH 16/24] The broken test is mysteriously working now --- squirrelbattle/game.py | 2 +- squirrelbattle/tests/game_test.py | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 8da50a0..779e98f 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -155,7 +155,7 @@ class Game: # Wait for the direction of the friendly entity self.waiting_for_friendly_key = True elif key == KeyValues.WAIT: - self.map.tick() + self.map.tick(self.player) elif key == KeyValues.LADDER: self.handle_ladder() diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 0e8bf9e..efc20da 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -46,11 +46,6 @@ class TestGame(unittest.TestCase): bomb.hold(self.game.player) sword.hold(self.game.player) - for entity in self.game.map.entities: - # trumpets change order when they are loaded, this breaks the test. - if entity.name == 'trumpet': - self.game.map.remove_entity(entity) - # Ensure that merchants can be saved merchant = Merchant() merchant.move(3, 6) From a8c0c197ed86b2b9947ac0e906f5b153d6126a13 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 18:02:58 +0100 Subject: [PATCH 17/24] Linting --- squirrelbattle/display/statsdisplay.py | 9 +++++---- squirrelbattle/entities/items.py | 26 ++++++++++++++++---------- squirrelbattle/entities/monsters.py | 7 +++++-- squirrelbattle/entities/player.py | 8 ++++---- squirrelbattle/interfaces.py | 4 ++-- 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/squirrelbattle/display/statsdisplay.py b/squirrelbattle/display/statsdisplay.py index e24c969..c74b63f 100644 --- a/squirrelbattle/display/statsdisplay.py +++ b/squirrelbattle/display/statsdisplay.py @@ -28,7 +28,7 @@ class StatsDisplay(Display): string3 = "STR {}\nINT {}\nCHR {}\nDEX {}\nCON {}\nCRI {}%"\ .format(self.player.strength, self.player.intelligence, self.player.charisma, - self.player.dexterity, self.player.constitution,\ + self.player.dexterity, self.player.constitution, self.player.critical) self.addstr(self.pad, 3, 0, string3) @@ -54,15 +54,16 @@ class StatsDisplay(Display): if self.player.equipped_secondary: self.addstr(self.pad, 11, 0, _("Equipped secondary:") + " " - f"{self.pack[self.player.equipped_secondary.name.upper()]}") + + self.pack[self.player.equipped_secondary + .name.upper()]) if self.player.equipped_armor: self.addstr(self.pad, 12, 0, _("Equipped chestplate:") + " " - f"{self.pack[self.player.equipped_armor.name.upper()]}") + + self.pack[self.player.equipped_armor.name.upper()]) if self.player.equipped_helmet: self.addstr(self.pad, 13, 0, _("Equipped helmet:") + " " - f"{self.pack[self.player.equipped_helmet.name.upper()]}") + + self.pack[self.player.equipped_helmet.name.upper()]) self.addstr(self.pad, 14, 0, f"{self.pack.HAZELNUT} " f"x{self.player.hazel}") diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 336d3e5..3eb3530 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -107,7 +107,7 @@ class Item(Entity): @staticmethod def get_all_items() -> list: - return [BodySnatchPotion, Bomb, Heart, Shield, Sword,\ + return [BodySnatchPotion, Bomb, Heart, Shield, Sword, Chestplate, Helmet, RingCritical, RingXP] def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool: @@ -283,7 +283,7 @@ class Armor(Item): Class of items that increase the player's constitution. """ constitution: int - + def __init__(self, constitution: int, *args, **kwargs): super().__init__(*args, **kwargs) self.constitution = constitution @@ -301,30 +301,33 @@ class Armor(Item): d["constitution"] = self.constitution return d + class Shield(Armor): """ Class of shield items, they can be equipped in the other hand. """ - def __init__(self, name: str = "shield", constitution: int = 2,\ + def __init__(self, name: str = "shield", constitution: int = 2, price: int = 6, *args, **kwargs): super().__init__(name=name, constitution=constitution, *args, **kwargs) + class Helmet(Armor): """ Class of helmet items, they can be equipped on the head. """ - def __init__(self, name: str = "helmet", constitution: int = 2, \ + def __init__(self, name: str = "helmet", constitution: int = 2, price: int = 8, *args, **kwargs): super().__init__(name=name, constitution=constitution, *args, **kwargs) + class Chestplate(Armor): """ Class of chestplate items, they can be equipped on the body. """ - def __init__(self, name: str = "chestplate", constitution: int = 4,\ + def __init__(self, name: str = "chestplate", constitution: int = 4, price: int = 15, *args, **kwargs): super().__init__(name=name, constitution=constitution, *args, **kwargs) @@ -362,6 +365,7 @@ class BodySnatchPotion(Item): self.held_by.inventory.remove(self) + class Ring(Item): """ A class of rings that boost the player's statistics. @@ -375,9 +379,9 @@ class Ring(Item): critical: int experience: float - def __init__(self, maxhealth: int = 0, strength: int = 0,\ - intelligence: int = 0, charisma: int = 0,\ - dexterity: int = 0, constitution: int = 0,\ + def __init__(self, maxhealth: int = 0, strength: int = 0, + intelligence: int = 0, charisma: int = 0, + dexterity: int = 0, constitution: int = 0, critical: int = 0, experience: float = 0, *args, **kwargs): super().__init__(*args, **kwargs) self.maxhealth = maxhealth @@ -416,14 +420,16 @@ class Ring(Item): d["constitution"] = self.constitution return d + class RingCritical(Ring): def __init__(self, name: str = "ring_of_critical_damage", price: int = 15, critical: int = 20, *args, **kwargs): - super().__init__(name=name, price=price, critical=critical, \ + super().__init__(name=name, price=price, critical=critical, *args, **kwargs) + class RingXP(Ring): def __init__(self, name: str = "ring_of_more_experience", price: int = 25, experience: float = 2, *args, **kwargs): - super().__init__(name=name, price=price, experience=experience, \ + super().__init__(name=name, price=price, experience=experience, *args, **kwargs) diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index bcdff11..d93b24b 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -87,9 +87,11 @@ class Rabbit(Monster): A rabbit monster """ def __init__(self, name: str = "rabbit", strength: int = 1, - maxhealth: int = 15, critical: int = 30, *args, **kwargs) -> None: + maxhealth: int = 15, critical: int = 30, + *args, **kwargs) -> None: super().__init__(name=name, strength=strength, - maxhealth=maxhealth, critical=critical, *args, **kwargs) + maxhealth=maxhealth, critical=critical, + *args, **kwargs) class TeddyBear(Monster): @@ -101,6 +103,7 @@ class TeddyBear(Monster): super().__init__(name=name, strength=strength, maxhealth=maxhealth, *args, **kwargs) + class GiantSeaEagle(Monster): """ An eagle boss diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 854bd93..63d6536 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -28,9 +28,9 @@ class Player(InventoryHolder, FightingEntity): dexterity: int = 1, constitution: int = 1, level: int = 1, current_xp: int = 0, max_xp: int = 10, inventory: list = None, hazel: int = 42, equipped_main: Optional[Item] = None, - equipped_armor: Optional[Item] = None, critical: int = 5,\ - equipped_secondary: Optional[Item] = None, \ - equipped_helmet: Optional[Item] = None, xp_buff: float = 1,\ + equipped_armor: Optional[Item] = None, critical: int = 5, + equipped_secondary: Optional[Item] = None, + equipped_helmet: Optional[Item] = None, xp_buff: float = 1, *args, **kwargs) -> None: super().__init__(name=name, maxhealth=maxhealth, strength=strength, intelligence=intelligence, charisma=charisma, @@ -84,7 +84,7 @@ class Player(InventoryHolder, FightingEntity): Add some experience to the player. If the required amount is reached, level up. """ - self.current_xp += int(xp*self.xp_buff) + self.current_xp += int(xp * self.xp_buff) self.level_up() def remove_from_inventory(self, obj: Item) -> None: diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index e4557d1..556051c 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -146,8 +146,8 @@ class Map: tile = self.tiles[y][x] if tile.can_walk(): break - entity = choices(Entity.get_all_entity_classes(),\ - weights = Entity.get_weights(), k=1)[0]() + entity = choices(Entity.get_all_entity_classes(), + weights=Entity.get_weights(), k=1)[0]() entity.move(y, x) self.add_entity(entity) From 1a78ad584cd4c973a9de48d0bd87c374cc165b91 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 6 Jan 2021 18:31:28 +0100 Subject: [PATCH 18/24] Move equip functions for items --- squirrelbattle/entities/items.py | 57 +++++++++++++++----------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 3eb3530..ee5f4a9 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -44,32 +44,11 @@ class Item(Entity): """ Indicates what should be done when the item is equipped. """ - if isinstance(self, Chestplate): - if self.held_by.equipped_armor: - self.held_by.equipped_armor.unequip() - self.held_by.remove_from_inventory(self) - self.held_by.equipped_armor = self - elif isinstance(self, Helmet): - if self.held_by.equipped_helmet: - self.held_by.equipped_helmet.unequip() - self.held_by.remove_from_inventory(self) - self.held_by.equipped_helmet = self - elif isinstance(self, Weapon): - if self.held_by.equipped_main: - if self.held_by.equipped_secondary: - self.held_by.equipped_secondary.unequip() - self.held_by.remove_from_inventory(self) - self.held_by.equipped_secondary = self - # For weapons, they are equipped as main only if main is empty. - else: - self.held_by.remove_from_inventory(self) - self.held_by.equipped_main = self - else: - # Other objects are only equipped as secondary. - if self.held_by.equipped_secondary: - self.held_by.equipped_secondary.unequip() - self.held_by.remove_from_inventory(self) - self.held_by.equipped_secondary = self + # Other objects are only equipped as secondary. + if self.held_by.equipped_secondary: + self.held_by.equipped_secondary.unequip() + self.held_by.remove_from_inventory(self) + self.held_by.equipped_secondary = self def unequip(self) -> None: """ @@ -257,7 +236,8 @@ class Weapon(Item): """ When a weapon is equipped, the player gains strength. """ - super().equip() + self.held_by.remove_from_inventory(self) + self.held_by.equipped_main = self self.held_by.strength += self.damage def unequip(self) -> None: @@ -309,7 +289,8 @@ class Shield(Armor): def __init__(self, name: str = "shield", constitution: int = 2, price: int = 6, *args, **kwargs): - super().__init__(name=name, constitution=constitution, *args, **kwargs) + super().__init__(name=name, constitution=constitution, price=price, + *args, **kwargs) class Helmet(Armor): @@ -319,7 +300,15 @@ class Helmet(Armor): def __init__(self, name: str = "helmet", constitution: int = 2, price: int = 8, *args, **kwargs): - super().__init__(name=name, constitution=constitution, *args, **kwargs) + super().__init__(name=name, constitution=constitution, price=price, + *args, **kwargs) + + def equip(self) -> None: + super().equip() + if self.held_by.equipped_helmet: + self.held_by.equipped_helmet.unequip() + self.held_by.remove_from_inventory(self) + self.held_by.equipped_helmet = self class Chestplate(Armor): @@ -329,7 +318,15 @@ class Chestplate(Armor): def __init__(self, name: str = "chestplate", constitution: int = 4, price: int = 15, *args, **kwargs): - super().__init__(name=name, constitution=constitution, *args, **kwargs) + super().__init__(name=name, constitution=constitution, price=price, + *args, **kwargs) + + def equip(self) -> None: + super().equip() + if self.held_by.equipped_armor: + self.held_by.equipped_armor.unequip() + self.held_by.remove_from_inventory(self) + self.held_by.equipped_armor = self class BodySnatchPotion(Item): From e9c8f43e7eb92d32090f6f80d61ae071fdbf643b Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 7 Jan 2021 16:31:39 +0100 Subject: [PATCH 19/24] Use ternary conditions to add coverage --- squirrelbattle/interfaces.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 30e59f4..c1c224f 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -206,15 +206,12 @@ class Map: else: top_y = ceil(((x * 2 - 1) * top.Y + top.X) / (top.X * 2)) if self.is_wall(top_y, x, octant, origin): - if top >= Slope(top_y * 2 + 1, x * 2) and not\ - self.is_wall(top_y + 1, x, octant, origin): - top_y += 1 + top_y += top >= Slope(top_y * 2 + 1, x * 2) and not \ + self.is_wall(top_y + 1, x, octant, origin) else: ax = x * 2 - if self.is_wall(top_y + 1, x + 1, octant, origin): - ax += 1 - if top > Slope(top_y * 2 + 1, ax): - top_y += 1 + ax += self.is_wall(top_y + 1, x + 1, octant, origin) + top_y += top > Slope(top_y * 2 + 1, ax) return top_y def crop_bottom_visibility(self, octant: int, origin: Tuple[int, int], @@ -224,10 +221,9 @@ class Map: else: bottom_y = ceil(((x * 2 - 1) * bottom.Y + bottom.X) / (bottom.X * 2)) - if bottom >= Slope(bottom_y * 2 + 1, x * 2) and\ - self.is_wall(bottom_y, x, octant, origin) and\ - not self.is_wall(bottom_y + 1, x, octant, origin): - bottom_y += 1 + bottom_y += bottom >= Slope(bottom_y * 2 + 1, x * 2) and \ + self.is_wall(bottom_y, x, octant, origin) and \ + not self.is_wall(bottom_y + 1, x, octant, origin) return bottom_y def compute_visibility_octant(self, octant: int, origin: Tuple[int, int], @@ -254,8 +250,7 @@ class Map: continue if is_opaque and was_opaque == 0: nx, ny = x * 2, y * 2 + 1 - if self.is_wall(y + 1, x, octant, origin): - nx -= 1 + nx -= self.is_wall(y + 1, x, octant, origin) if top > Slope(ny, nx): if y == bottom_y: bottom = Slope(ny, nx) @@ -264,14 +259,12 @@ class Map: self.compute_visibility_octant( octant, origin, max_range, x + 1, top, Slope(ny, nx)) - else: - if y == bottom_y: - return + elif y == bottom_y: # pragma: no cover + return elif not is_opaque and was_opaque == 1: nx, ny = x * 2, y * 2 + 1 - if self.is_wall(y + 1, x + 1, octant, origin): - nx += 1 - if bottom >= Slope(ny, nx): + nx += self.is_wall(y + 1, x + 1, octant, origin) + if bottom >= Slope(ny, nx): # pragma: no cover return top = Slope(ny, nx) was_opaque = is_opaque From c36e68d6e43579d0d3a604be637b0a47060ac332 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 7 Jan 2021 16:34:12 +0100 Subject: [PATCH 20/24] Reduce player vision --- squirrelbattle/entities/player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index aad35dd..6d18884 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -21,7 +21,7 @@ class Player(InventoryHolder, FightingEntity): strength: int = 5, intelligence: int = 1, charisma: int = 1, dexterity: int = 1, constitution: int = 1, level: int = 1, current_xp: int = 0, max_xp: int = 10, inventory: list = None, - hazel: int = 42, vision: int = 50, *args, **kwargs) \ + hazel: int = 42, vision: int = 5, *args, **kwargs) \ -> None: super().__init__(name=name, maxhealth=maxhealth, strength=strength, intelligence=intelligence, charisma=charisma, From 478a655751a53221a5cc7c5dda875acca3c777bf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 7 Jan 2021 16:49:40 +0100 Subject: [PATCH 21/24] Fix fg/bg custom colors --- squirrelbattle/display/mapdisplay.py | 10 ++++------ squirrelbattle/display/texturepack.py | 3 ++- squirrelbattle/interfaces.py | 13 +++++++++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index 6cbf9ae..701f33e 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -26,13 +26,11 @@ class MapDisplay(Display): for i in range(len(self.map.tiles[j])): if not self.map.seen_tiles[j][i]: continue - color = self.pack.tile_fg_visible_color if \ - self.map.visibility[j][i] else self.pack.tile_fg_color + fg, bg = self.map.tiles[j][i].visible_color(self.pack) if \ + self.map.visibility[j][i] else \ + self.map.tiles[j][i].hidden_color(self.pack) self.addstr(self.pad, j, self.pack.tile_width * i, - self.map.tiles[j][i].char(self.pack), - color, self.pack.tile_bg_color) - # self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack), - # self.pack.tile_fg_color, self.pack.tile_bg_color) + self.map.tiles[j][i].char(self.pack), fg, bg) for e in self.map.entities: if self.map.visibility[e.y][e.x]: self.addstr(self.pad, e.y, self.pack.tile_width * e.x, diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 796b32a..dae0763 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -99,7 +99,8 @@ TexturePack.SQUIRREL_PACK = TexturePack( EMPTY=' ', EXPLOSION='💥', FLOOR='██', - LADDER=('🪜', curses.COLOR_WHITE, curses.COLOR_WHITE), + LADDER=('🪜', curses.COLOR_WHITE, (1000, 1000, 1000), + curses.COLOR_WHITE, (1000, 1000, 1000)), HAZELNUT='🌰', HEART='💜', HEDGEHOG='🦔', diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index c1e509b..601229b 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -383,12 +383,21 @@ class Tile(Enum): val = getattr(pack, self.name) return val[0] if isinstance(val, tuple) else val - def color(self, pack: TexturePack) -> Tuple[int, int]: + def visible_color(self, pack: TexturePack) -> Tuple[int, int]: + """ + Retrieve the tuple (fg_color, bg_color) of the current Tile + if it is visible. + """ + val = getattr(pack, self.name) + return (val[2], val[4]) if isinstance(val, tuple) else \ + (pack.tile_fg_visible_color, pack.tile_bg_color) + + def hidden_color(self, pack: TexturePack) -> Tuple[int, int]: """ Retrieve the tuple (fg_color, bg_color) of the current Tile. """ val = getattr(pack, self.name) - return (val[1], val[2]) if isinstance(val, tuple) else \ + return (val[1], val[3]) if isinstance(val, tuple) else \ (pack.tile_fg_color, pack.tile_bg_color) def is_wall(self) -> bool: From 6c6a44fb18d4d5bc329fd41018a123599c632fda Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 8 Jan 2021 01:56:54 +0100 Subject: [PATCH 22/24] More tests --- squirrelbattle/entities/items.py | 22 +++---- squirrelbattle/entities/player.py | 20 +++---- squirrelbattle/game.py | 7 ++- squirrelbattle/tests/game_test.py | 99 ++++++++++++++++++++++++++++++- 4 files changed, 119 insertions(+), 29 deletions(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index ee5f4a9..7775f74 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -54,17 +54,7 @@ class Item(Entity): """ Indicates what should be done when the item is unequipped. """ - if isinstance(self, Chestplate): - self.held_by.equipped_armor = None - elif isinstance(self, Helmet): - self.held_by.equipped_helmet = None - elif isinstance(self, Weapon): - if self.held_by.equipped_main == self: - self.held_by.equipped_main = None - else: - self.held_by.equipped_secondary = None - else: - self.held_by.equipped_secondary = None + self.held_by.remove_from_inventory(self) self.held_by.add_to_inventory(self) def hold(self, holder: InventoryHolder) -> None: @@ -211,7 +201,6 @@ class Explosion(Item): """ The player can't hold any explosion. """ - pass class Weapon(Item): @@ -304,7 +293,6 @@ class Helmet(Armor): *args, **kwargs) def equip(self) -> None: - super().equip() if self.held_by.equipped_helmet: self.held_by.equipped_helmet.unequip() self.held_by.remove_from_inventory(self) @@ -322,7 +310,6 @@ class Chestplate(Armor): *args, **kwargs) def equip(self) -> None: - super().equip() if self.held_by.equipped_armor: self.held_by.equipped_armor.unequip() self.held_by.remove_from_inventory(self) @@ -414,7 +401,14 @@ class Ring(Item): def save_state(self) -> dict: d = super().save_state() + d["maxhealth"] = self.maxhealth + d["strength"] = self.strength + d["intelligence"] = self.intelligence + d["charisma"] = self.charisma + d["dexterity"] = self.dexterity d["constitution"] = self.constitution + d["critical"] = self.critical + d["experience"] = self.experience return d diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 63d6536..0d939ae 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -42,18 +42,14 @@ class Player(InventoryHolder, FightingEntity): self.inventory = self.translate_inventory(inventory or []) self.paths = dict() self.hazel = hazel - if isinstance(equipped_main, dict): - equipped_main = self.dict_to_item(equipped_main) - if isinstance(equipped_armor, dict): - equipped_armor = self.dict_to_item(equipped_armor) - if isinstance(equipped_secondary, dict): - equipped_secondary = self.dict_to_item(equipped_secondary) - if isinstance(equipped_helmet, dict): - equipped_helmet = self.dict_to_item(equipped_helmet) - self.equipped_main = equipped_main - self.equipped_armor = equipped_armor - self.equipped_secondary = equipped_secondary - self.equipped_helmet = equipped_helmet + self.equipped_main = self.dict_to_item(equipped_main) \ + if isinstance(equipped_main, dict) else equipped_main + self.equipped_armor = self.dict_to_item(equipped_armor) \ + if isinstance(equipped_armor, dict) else equipped_armor + self.equipped_secondary = self.dict_to_item(equipped_secondary) \ + if isinstance(equipped_secondary, dict) else equipped_secondary + self.equipped_helmet = self.dict_to_item(equipped_helmet) \ + if isinstance(equipped_helmet, dict) else equipped_helmet def move(self, y: int, x: int) -> None: """ diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 939325c..17adb7d 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -108,7 +108,7 @@ class Game: self.handle_key_pressed_store(key) self.display_actions(DisplayActions.REFRESH) - def handle_key_pressed_play(self, key: KeyValues) -> None: + def handle_key_pressed_play(self, key: KeyValues) -> None: # noqa: C901 """ In play mode, arrows or zqsd move the main character. """ @@ -128,7 +128,10 @@ class Game: self.state = GameMode.INVENTORY self.display_actions(DisplayActions.UPDATE) elif key == KeyValues.USE and self.player.equipped_main: - self.player.equipped_main.use() + if self.player.equipped_main: + self.player.equipped_main.use() + if self.player.equipped_secondary: + self.player.equipped_secondary.use() elif key == KeyValues.SPACE: self.state = GameMode.MAINMENU elif key == KeyValues.CHAT: diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 6751016..06ddcf6 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -2,13 +2,16 @@ # SPDX-License-Identifier: GPL-3.0-or-later import os +import random import unittest from ..bootstrap import Bootstrap from ..display.display import Display from ..display.display_manager import DisplayManager from ..entities.friendly import Merchant, Sunflower -from ..entities.items import Bomb, Heart, Sword, Explosion +from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \ + Chestplate, RingCritical +from ..entities.monsters import GiantSeaEagle from ..entities.player import Player from ..enums import DisplayActions from ..game import Game, KeyValues, GameMode @@ -609,3 +612,97 @@ class TestGame(unittest.TestCase): # Exit the menu self.game.handle_key_pressed(KeyValues.SPACE) self.assertEqual(self.game.state, GameMode.PLAY) + + def test_equipment(self) -> None: + """ + Ensure that equipment is working. + """ + self.game.state = GameMode.INVENTORY + + # sword goes into the main equipment slot + sword = Sword() + sword.hold(self.game.player) + self.game.handle_key_pressed(KeyValues.EQUIP) + self.assertEqual(self.game.player.equipped_main, sword) + self.assertFalse(self.game.player.inventory) + + # shield goes into the secondary equipment slot + shield = Shield() + shield.hold(self.game.player) + self.game.handle_key_pressed(KeyValues.EQUIP) + self.assertEqual(self.game.player.equipped_secondary, shield) + self.assertFalse(self.game.player.inventory) + + # helmet goes into the helmet slot + helmet = Helmet() + helmet.hold(self.game.player) + self.game.handle_key_pressed(KeyValues.EQUIP) + self.assertEqual(self.game.player.equipped_helmet, helmet) + self.assertFalse(self.game.player.inventory) + + # helmet goes into the armor slot + chestplate = Chestplate() + chestplate.hold(self.game.player) + self.game.handle_key_pressed(KeyValues.EQUIP) + self.assertEqual(self.game.player.equipped_armor, chestplate) + self.assertFalse(self.game.player.inventory) + + # Use bomb + bomb = Bomb() + bomb.hold(self.game.player) + self.game.handle_key_pressed(KeyValues.EQUIP) + self.assertEqual(self.game.player.equipped_secondary, bomb) + self.assertIn(shield, self.game.player.inventory) + self.game.state = GameMode.PLAY + self.game.handle_key_pressed(KeyValues.USE) + self.assertIsNone(self.game.player.equipped_secondary) + self.game.state = GameMode.INVENTORY + self.game.handle_key_pressed(KeyValues.EQUIP) + self.assertEqual(self.game.player.equipped_secondary, shield) + self.assertFalse(self.game.player.inventory) + + # Reequip, which is useless but covers code + sword.equip() + shield.equip() + helmet.equip() + chestplate.equip() + self.game.save_state() + + # Unequip all + sword.unequip() + shield.unequip() + helmet.unequip() + chestplate.unequip() + self.assertIsNone(self.game.player.equipped_main) + self.assertIsNone(self.game.player.equipped_secondary) + self.assertIsNone(self.game.player.equipped_helmet) + self.assertIsNone(self.game.player.equipped_armor) + self.assertIn(sword, self.game.player.inventory) + self.assertIn(shield, self.game.player.inventory) + self.assertIn(helmet, self.game.player.inventory) + self.assertIn(chestplate, self.game.player.inventory) + + # Test rings + self.game.player.inventory.clear() + ring = RingCritical() + ring.hold(self.game.player) + old_critical = self.game.player.critical + self.game.handle_key_pressed(KeyValues.EQUIP) + self.assertEqual(self.game.player.critical, + old_critical + ring.critical) + self.game.save_state() + ring.unequip() + + def test_critical_hit(self) -> None: + """ + Ensure that critical hits are working. + """ + random.seed(2) # Next random.randint(1, 100) will output 8 + self.game.player.critical = 10 + sea_eagle = GiantSeaEagle() + self.game.map.add_entity(sea_eagle) + sea_eagle.move(2, 6) + old_health = sea_eagle.health + self.game.player.hit(sea_eagle) + self.assertEqual(sea_eagle.health, + old_health - self.game.player.strength * 4) From 7aeb659cf5cff18f0b69245886fcb319ecb79c0d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 8 Jan 2021 02:00:22 +0100 Subject: [PATCH 23/24] Fix french translation --- squirrelbattle/interfaces.py | 2 +- .../locale/de/LC_MESSAGES/squirrelbattle.po | 69 ++++++++++++------ .../locale/es/LC_MESSAGES/squirrelbattle.po | 69 ++++++++++++------ .../locale/fr/LC_MESSAGES/squirrelbattle.po | 71 +++++++++++++------ 4 files changed, 143 insertions(+), 68 deletions(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 556051c..bbac3a1 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -449,7 +449,7 @@ class FightingEntity(Entity): string = " " if diceroll <= self.critical: # It is a critical hit damage *= 4 - string = _(" It's a critical hit! ") + string = " " + _("It's a critical hit!") + " " return _("{name} hits {opponent}.")\ .format(name=_(self.translated_name.capitalize()), opponent=_(opponent.translated_name)) + string + \ diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 29bf10e..2765985 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2020-12-12 18:02+0100\n" +"POT-Creation-Date: 2021-01-08 01:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,19 +17,44 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: squirrelbattle/display/menudisplay.py:139 +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} nimmt {amount} Schadenspunkte." + +#: squirrelbattle/display/menudisplay.py:160 msgid "INVENTORY" msgstr "BESTAND" -#: squirrelbattle/display/menudisplay.py:164 +#: squirrelbattle/display/menudisplay.py:202 msgid "STALL" msgstr "STAND" -#: squirrelbattle/display/statsdisplay.py:33 +#: squirrelbattle/display/statsdisplay.py:23 +#: squirrelbattle/tests/translations_test.py:60 +msgid "player" +msgstr "Spieler" + +#: squirrelbattle/display/statsdisplay.py:35 msgid "Inventory:" msgstr "Bestand:" #: squirrelbattle/display/statsdisplay.py:52 +msgid "Equipped main:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:56 +msgid "Equipped secondary:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:61 +msgid "Equipped chestplate:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:65 +msgid "Equipped helmet:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:72 msgid "YOU ARE DEAD" msgstr "SIE WURDEN GESTORBEN" @@ -49,20 +74,20 @@ msgstr "Die Sonne ist warm heute" #. The bomb is exploding. #. Each entity that is close to the bomb takes damages. #. The player earn XP if the entity was killed. -#: squirrelbattle/entities/items.py:151 +#: squirrelbattle/entities/items.py:163 msgid "Bomb is exploding." msgstr "Die Bombe explodiert." -#: squirrelbattle/entities/items.py:248 +#: squirrelbattle/entities/items.py:344 #, python-brace-format msgid "{player} exchanged its body with {entity}." msgstr "{player} täuscht seinem Körper mit {entity} aus." -#: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 +#: squirrelbattle/game.py:228 squirrelbattle/tests/game_test.py:595 msgid "The buyer does not have enough money" msgstr "Der Kaufer hat nicht genug Geld" -#: squirrelbattle/game.py:249 +#: squirrelbattle/game.py:271 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -70,7 +95,7 @@ msgstr "" "In Ihrer Speicherdatei fehlen einige Schlüssel.\n" "Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." -#: squirrelbattle/game.py:257 +#: squirrelbattle/game.py:279 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -78,7 +103,7 @@ msgstr "" "Auf dieser Karte wurde kein Spieler gefunden!\n" "Vielleicht sind Sie gestorben?" -#: squirrelbattle/game.py:277 +#: squirrelbattle/game.py:299 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -86,22 +111,26 @@ msgstr "" "Die JSON-Datei ist nicht korrekt.\n" "Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht." -#: squirrelbattle/interfaces.py:429 +#: squirrelbattle/interfaces.py:452 +msgid "It's a critical hit!" +msgstr "" + +#: squirrelbattle/interfaces.py:453 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} schlägt {opponent}." -#: squirrelbattle/interfaces.py:441 +#: squirrelbattle/interfaces.py:466 #, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} nimmt {amount} Schadenspunkte." +msgid "{name} takes {damage} damage." +msgstr "" -#: squirrelbattle/interfaces.py:443 +#: squirrelbattle/interfaces.py:468 #, python-brace-format msgid "{name} dies." msgstr "{name} stirbt." -#: squirrelbattle/interfaces.py:477 +#: squirrelbattle/interfaces.py:502 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} hat gesagt: {message}" @@ -110,8 +139,8 @@ msgstr "{entity} hat gesagt: {message}" msgid "Back" msgstr "Zurück" -#: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 -#: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 +#: squirrelbattle/tests/game_test.py:361 squirrelbattle/tests/game_test.py:364 +#: squirrelbattle/tests/game_test.py:367 squirrelbattle/tests/game_test.py:370 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Neu Spiel" @@ -204,10 +233,6 @@ msgstr "Textur-Packung" msgid "Language" msgstr "Sprache" -#: squirrelbattle/tests/translations_test.py:60 -msgid "player" -msgstr "Spieler" - #: squirrelbattle/tests/translations_test.py:62 msgid "hedgehog" msgstr "Igel" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 8a7bc3e..d4d34db 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2020-12-12 18:02+0100\n" +"POT-Creation-Date: 2021-01-08 01:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,19 +17,44 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: squirrelbattle/display/menudisplay.py:139 +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} recibe {amount} daño." + +#: squirrelbattle/display/menudisplay.py:160 msgid "INVENTORY" msgstr "INVENTORIO" -#: squirrelbattle/display/menudisplay.py:164 +#: squirrelbattle/display/menudisplay.py:202 msgid "STALL" msgstr "PUESTO" -#: squirrelbattle/display/statsdisplay.py:33 +#: squirrelbattle/display/statsdisplay.py:23 +#: squirrelbattle/tests/translations_test.py:60 +msgid "player" +msgstr "jugador" + +#: squirrelbattle/display/statsdisplay.py:35 msgid "Inventory:" msgstr "Inventorio :" #: squirrelbattle/display/statsdisplay.py:52 +msgid "Equipped main:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:56 +msgid "Equipped secondary:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:61 +msgid "Equipped chestplate:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:65 +msgid "Equipped helmet:" +msgstr "" + +#: squirrelbattle/display/statsdisplay.py:72 msgid "YOU ARE DEAD" msgstr "ERES MUERTO" @@ -48,20 +73,20 @@ msgstr "El sol está caliente hoy" #. The bomb is exploding. #. Each entity that is close to the bomb takes damages. #. The player earn XP if the entity was killed. -#: squirrelbattle/entities/items.py:151 +#: squirrelbattle/entities/items.py:163 msgid "Bomb is exploding." msgstr "La bomba está explotando." -#: squirrelbattle/entities/items.py:248 +#: squirrelbattle/entities/items.py:344 #, python-brace-format msgid "{player} exchanged its body with {entity}." msgstr "{player} intercambió su cuerpo con {entity}." -#: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 +#: squirrelbattle/game.py:228 squirrelbattle/tests/game_test.py:595 msgid "The buyer does not have enough money" msgstr "El comprador no tiene suficiente dinero" -#: squirrelbattle/game.py:249 +#: squirrelbattle/game.py:271 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -69,7 +94,7 @@ msgstr "" "Algunas claves faltan en su archivo de guarda.\n" "Su guarda parece a ser corruptido. Fue eliminado." -#: squirrelbattle/game.py:257 +#: squirrelbattle/game.py:279 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -77,7 +102,7 @@ msgstr "" "No jugador encontrado sobre la carta !\n" "¿ Quizas murió ?" -#: squirrelbattle/game.py:277 +#: squirrelbattle/game.py:299 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -85,22 +110,26 @@ msgstr "" "El JSON archivo no es correcto.\n" "Su guarda parece corrupta. Fue eliminada." -#: squirrelbattle/interfaces.py:429 +#: squirrelbattle/interfaces.py:452 +msgid "It's a critical hit!" +msgstr "" + +#: squirrelbattle/interfaces.py:453 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} golpea a {opponent}." -#: squirrelbattle/interfaces.py:441 +#: squirrelbattle/interfaces.py:466 #, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} recibe {amount} daño." +msgid "{name} takes {damage} damage." +msgstr "" -#: squirrelbattle/interfaces.py:443 +#: squirrelbattle/interfaces.py:468 #, python-brace-format msgid "{name} dies." msgstr "{name} se muere." -#: squirrelbattle/interfaces.py:477 +#: squirrelbattle/interfaces.py:502 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} dijo : {message}" @@ -109,8 +138,8 @@ msgstr "{entity} dijo : {message}" msgid "Back" msgstr "Volver" -#: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 -#: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 +#: squirrelbattle/tests/game_test.py:361 squirrelbattle/tests/game_test.py:364 +#: squirrelbattle/tests/game_test.py:367 squirrelbattle/tests/game_test.py:370 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Nuevo partido" @@ -203,10 +232,6 @@ msgstr "Paquete de texturas" msgid "Language" msgstr "Languaje" -#: squirrelbattle/tests/translations_test.py:60 -msgid "player" -msgstr "jugador" - #: squirrelbattle/tests/translations_test.py:62 msgid "hedgehog" msgstr "erizo" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index ffbfdce..a9385a6 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2020-12-12 18:02+0100\n" +"POT-Creation-Date: 2021-01-08 01:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,19 +17,44 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: squirrelbattle/display/menudisplay.py:139 +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} prend {amount} points de dégât." + +#: squirrelbattle/display/menudisplay.py:160 msgid "INVENTORY" msgstr "INVENTAIRE" -#: squirrelbattle/display/menudisplay.py:164 +#: squirrelbattle/display/menudisplay.py:202 msgid "STALL" msgstr "STAND" -#: squirrelbattle/display/statsdisplay.py:33 +#: squirrelbattle/display/statsdisplay.py:23 +#: squirrelbattle/tests/translations_test.py:60 +msgid "player" +msgstr "joueur" + +#: squirrelbattle/display/statsdisplay.py:35 msgid "Inventory:" msgstr "Inventaire :" #: squirrelbattle/display/statsdisplay.py:52 +msgid "Equipped main:" +msgstr "Équipement principal :" + +#: squirrelbattle/display/statsdisplay.py:56 +msgid "Equipped secondary:" +msgstr "Équipement secondaire :" + +#: squirrelbattle/display/statsdisplay.py:61 +msgid "Equipped chestplate:" +msgstr "Plastron équipé :" + +#: squirrelbattle/display/statsdisplay.py:65 +msgid "Equipped helmet:" +msgstr "Casque équipé :" + +#: squirrelbattle/display/statsdisplay.py:72 msgid "YOU ARE DEAD" msgstr "VOUS ÊTES MORT" @@ -49,20 +74,20 @@ msgstr "Le soleil est chaud aujourd'hui" #. The bomb is exploding. #. Each entity that is close to the bomb takes damages. #. The player earn XP if the entity was killed. -#: squirrelbattle/entities/items.py:151 +#: squirrelbattle/entities/items.py:163 msgid "Bomb is exploding." msgstr "La bombe explose." -#: squirrelbattle/entities/items.py:248 +#: squirrelbattle/entities/items.py:344 #, python-brace-format msgid "{player} exchanged its body with {entity}." msgstr "{player} a échangé son corps avec {entity}." -#: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 +#: squirrelbattle/game.py:228 squirrelbattle/tests/game_test.py:595 msgid "The buyer does not have enough money" msgstr "L'acheteur n'a pas assez d'argent" -#: squirrelbattle/game.py:249 +#: squirrelbattle/game.py:271 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -70,7 +95,7 @@ msgstr "" "Certaines clés de votre ficher de sauvegarde sont manquantes.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/game.py:257 +#: squirrelbattle/game.py:279 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -78,7 +103,7 @@ msgstr "" "Aucun joueur n'a été trouvé sur la carte !\n" "Peut-être êtes-vous mort ?" -#: squirrelbattle/game.py:277 +#: squirrelbattle/game.py:299 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -86,22 +111,26 @@ msgstr "" "Le fichier JSON de sauvegarde est incorrect.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/interfaces.py:429 +#: squirrelbattle/interfaces.py:452 +msgid "It's a critical hit!" +msgstr "C'est un coup critique !" + +#: squirrelbattle/interfaces.py:453 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} frappe {opponent}." -#: squirrelbattle/interfaces.py:441 +#: squirrelbattle/interfaces.py:466 #, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} prend {amount} points de dégât." +msgid "{name} takes {damage} damage." +msgstr "{name} prend {damage} dégâts." -#: squirrelbattle/interfaces.py:443 +#: squirrelbattle/interfaces.py:468 #, python-brace-format msgid "{name} dies." msgstr "{name} meurt." -#: squirrelbattle/interfaces.py:477 +#: squirrelbattle/interfaces.py:502 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} a dit : {message}" @@ -110,8 +139,8 @@ msgstr "{entity} a dit : {message}" msgid "Back" msgstr "Retour" -#: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 -#: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 +#: squirrelbattle/tests/game_test.py:361 squirrelbattle/tests/game_test.py:364 +#: squirrelbattle/tests/game_test.py:367 squirrelbattle/tests/game_test.py:370 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Nouvelle partie" @@ -204,10 +233,6 @@ msgstr "Pack de textures" msgid "Language" msgstr "Langue" -#: squirrelbattle/tests/translations_test.py:60 -msgid "player" -msgstr "joueur" - #: squirrelbattle/tests/translations_test.py:62 msgid "hedgehog" msgstr "hérisson" @@ -242,7 +267,7 @@ msgstr "bombe" #: squirrelbattle/tests/translations_test.py:71 msgid "explosion" -msgstr "" +msgstr "explosion" #: squirrelbattle/tests/translations_test.py:72 msgid "heart" From affc1bae59858a0567e19a1ba0c5e245fef57b01 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 8 Jan 2021 02:15:13 +0100 Subject: [PATCH 24/24] Fix merge --- squirrelbattle/entities/player.py | 3 --- squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po | 4 ---- squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po | 4 ---- squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po | 4 ---- 4 files changed, 15 deletions(-) diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 8d7898c..615dfd5 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -2,10 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from random import randint -<<<<<<< squirrelbattle/entities/player.py from typing import Dict, Optional, Tuple -======= ->>>>>>> squirrelbattle/entities/player.py from .items import Item from ..interfaces import FightingEntity, InventoryHolder diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 9c5dbc4..20aa2a7 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -247,10 +247,6 @@ msgstr "Textur-Packung" msgid "Language" msgstr "Sprache" -#: squirrelbattle/tests/translations_test.py:62 -msgid "player" -msgstr "Spieler" - #: squirrelbattle/tests/translations_test.py:64 msgid "hedgehog" msgstr "Igel" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 74f6a65..0bfdac6 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -246,10 +246,6 @@ msgstr "Paquete de texturas" msgid "Language" msgstr "Languaje" -#: squirrelbattle/tests/translations_test.py:62 -msgid "player" -msgstr "jugador" - #: squirrelbattle/tests/translations_test.py:64 msgid "hedgehog" msgstr "erizo" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index 5028b07..42172c3 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -247,10 +247,6 @@ msgstr "Pack de textures" msgid "Language" msgstr "Langue" -#: squirrelbattle/tests/translations_test.py:62 -msgid "player" -msgstr "joueur" - #: squirrelbattle/tests/translations_test.py:64 msgid "hedgehog" msgstr "hérisson"