Merge branch 'master' into 'doc'
# Conflicts: # squirrelbattle/entities/items.py # squirrelbattle/interfaces.py
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ | |||||||
|  |  | ||||||
| env/ | env/ | ||||||
| venv/ | venv/ | ||||||
|  | local/ | ||||||
|  |  | ||||||
| .coverage | .coverage | ||||||
| .pytest_cache/ | .pytest_cache/ | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| 1 6 | 1 6 | ||||||
|     #######                    #############         |     #######                    #############         | ||||||
|     #.....#                    #...........#         |     #.H...#                    #...........#         | ||||||
|     #.....#                #####...........#         |     #.....#                #####...........#         | ||||||
|     #.....#                #...............#         |     #.....#                #............H..#         | ||||||
|     #.#####                #.###...........#         |     #.#####                #.###...........#         | ||||||
|     #.#                    #.# #...........#         |     #.#                    #.# #...........#         | ||||||
|     #.#                    #.# #############         |     #.#                    #.# #############         | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| 1 17 | 1 17 | ||||||
|             ###########                        #########                         |             ###########                        #########                         | ||||||
|             #.........#                        #.......#                         |             #....H....#                        #.......#                         | ||||||
|             #.........#             ############.......#                         |             #.........#             ############.......#                         | ||||||
|             #.........###############..........#.......##############            |             #.........###############..........#.......##############            | ||||||
|             #.........#........................#....................#            |             #.........#........................#....................#            | ||||||
| @@ -13,7 +13,7 @@ | |||||||
|       ########.##########......#     #.........#              #.........#        |       ########.##########......#     #.........#              #.........#        | ||||||
|            #...........##......#     #.........#              #.........#        |            #...........##......#     #.........#              #.........#        | ||||||
|            #...........##......#     #.........#              #.........#        |            #...........##......#     #.........#              #.........#        | ||||||
|            #...........##......#     #.........#  ################.######        |            #...........##..H...#     #.........#  ################.######        | ||||||
|            #...........##......#     #.........#  #.................############ |            #...........##......#     #.........#  #.................############ | ||||||
|            #...........##......#  ########.########.......#.........#..........# |            #...........##......#  ########.########.......#.........#..........# | ||||||
|            #...........##......#  #...............#.......#.........#..........# |            #...........##......#  #...............#.......#.........#..........# | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								squirrelbattle/assets/example_map_3.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								squirrelbattle/assets/example_map_3.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | 1 6 | ||||||
|  | ################################################################################ | ||||||
|  | #..............................................................................# | ||||||
|  | #..#...........................................................................# | ||||||
|  | #...........#..................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | #..............................................................................# | ||||||
|  | ################################################################################ | ||||||
| @@ -22,13 +22,21 @@ class MapDisplay(Display): | |||||||
|                                self.pack.tile_width * self.map.width + 1) |                                self.pack.tile_width * self.map.width + 1) | ||||||
|  |  | ||||||
|     def update_pad(self) -> None: |     def update_pad(self) -> None: | ||||||
|         self.pad.resize(500, 500) |         for j in range(len(self.map.tiles)): | ||||||
|         self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack), |             for i in range(len(self.map.tiles[j])): | ||||||
|                     self.pack.tile_fg_color, self.pack.tile_bg_color) |                 if not self.map.seen_tiles[j][i]: | ||||||
|  |                     continue | ||||||
|  |                 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), fg, bg) | ||||||
|         for e in self.map.entities: |         for e in self.map.entities: | ||||||
|             self.addstr(self.pad, e.y, self.pack.tile_width * e.x, |             if self.map.visibility[e.y][e.x]: | ||||||
|                         self.pack[e.name.upper()], |                 self.addstr(self.pad, e.y, self.pack.tile_width * e.x, | ||||||
|                         self.pack.entity_fg_color, self.pack.entity_bg_color) |                             self.pack[e.name.upper()], | ||||||
|  |                             self.pack.entity_fg_color, | ||||||
|  |                             self.pack.entity_bg_color) | ||||||
|  |  | ||||||
|         # Display Path map for debug purposes |         # Display Path map for debug purposes | ||||||
|         # from squirrelbattle.entities.player import Player |         # from squirrelbattle.entities.player import Player | ||||||
|   | |||||||
| @@ -23,16 +23,18 @@ class StatsDisplay(Display): | |||||||
|         self.player = game.player |         self.player = game.player | ||||||
|  |  | ||||||
|     def update_pad(self) -> None: |     def update_pad(self) -> None: | ||||||
|         string2 = _("player").capitalize() + " -- LVL {}\nEXP {}/{}\nHP {}/{}"\ |         string2 = f"{_(self.player.name).capitalize()} " \ | ||||||
|             .format(self.player.level, self.player.current_xp, |                   f"-- LVL {self.player.level} -- " \ | ||||||
|                     self.player.max_xp, self.player.health, |                   f"FLOOR {-self.player.map.floor}\n" \ | ||||||
|                     self.player.maxhealth) |                   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) |         self.addstr(self.pad, 0, 0, string2) | ||||||
|         string3 = "STR {}\nINT {}\nCHR {}\nDEX {}\nCON {}\nCRI {}%"\ |         string3 = f"STR {self.player.strength}\n" \ | ||||||
|             .format(self.player.strength, |                   f"INT {self.player.intelligence}\n" \ | ||||||
|                     self.player.intelligence, self.player.charisma, |                   f"CHR {self.player.charisma}\n" \ | ||||||
|                     self.player.dexterity, self.player.constitution,\ |                   f"DEX {self.player.dexterity}\n" \ | ||||||
|                     self.player.critical) |                   f"CON {self.player.constitution}\n" \ | ||||||
|  |                   f"CRI {self.player.critical}%" | ||||||
|         self.addstr(self.pad, 3, 0, string3) |         self.addstr(self.pad, 3, 0, string3) | ||||||
|  |  | ||||||
|         inventory_str = _("Inventory:") + " " |         inventory_str = _("Inventory:") + " " | ||||||
| @@ -57,15 +59,16 @@ class StatsDisplay(Display): | |||||||
|         if self.player.equipped_secondary: |         if self.player.equipped_secondary: | ||||||
|             self.addstr(self.pad, 11, 0, |             self.addstr(self.pad, 11, 0, | ||||||
|                         _("Equipped secondary:") + " " |                         _("Equipped secondary:") + " " | ||||||
|                         f"{self.pack[self.player.equipped_secondary.name.upper()]}") |                         + self.pack[self.player.equipped_secondary | ||||||
|  |                                     .name.upper()]) | ||||||
|         if self.player.equipped_armor: |         if self.player.equipped_armor: | ||||||
|             self.addstr(self.pad, 12, 0, |             self.addstr(self.pad, 12, 0, | ||||||
|                         _("Equipped chestplate:") + " " |                         _("Equipped chestplate:") + " " | ||||||
|                         f"{self.pack[self.player.equipped_armor.name.upper()]}") |                         + self.pack[self.player.equipped_armor.name.upper()]) | ||||||
|         if self.player.equipped_helmet: |         if self.player.equipped_helmet: | ||||||
|             self.addstr(self.pad, 13, 0, |             self.addstr(self.pad, 13, 0, | ||||||
|                         _("Equipped helmet:") + " " |                         _("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} " |         self.addstr(self.pad, 14, 0, f"{self.pack.HAZELNUT} " | ||||||
|                                      f"x{self.player.hazel}") |                                      f"x{self.player.hazel}") | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| # SPDX-License-Identifier: GPL-3.0-or-later | # SPDX-License-Identifier: GPL-3.0-or-later | ||||||
|  |  | ||||||
| import curses | import curses | ||||||
| from typing import Any | from typing import Any, Union, Tuple | ||||||
|  |  | ||||||
|  |  | ||||||
| class TexturePack: | class TexturePack: | ||||||
| @@ -13,10 +13,11 @@ class TexturePack: | |||||||
|  |  | ||||||
|     name: str |     name: str | ||||||
|     tile_width: int |     tile_width: int | ||||||
|     tile_fg_color: int |     tile_fg_color: Union[int, Tuple[int, int, int]] | ||||||
|     tile_bg_color: int |     tile_fg_visible_color: Union[int, Tuple[int, int, int]] | ||||||
|     entity_fg_color: int |     tile_bg_color: Union[int, Tuple[int, int, int]] | ||||||
|     entity_bg_color: int |     entity_fg_color: Union[int, Tuple[int, int, int]] | ||||||
|  |     entity_bg_color: Union[int, Tuple[int, int, int]] | ||||||
|  |  | ||||||
|     BODY_SNATCH_POTION: str |     BODY_SNATCH_POTION: str | ||||||
|     BOMB: str |     BOMB: str | ||||||
| @@ -64,9 +65,10 @@ class TexturePack: | |||||||
| TexturePack.ASCII_PACK = TexturePack( | TexturePack.ASCII_PACK = TexturePack( | ||||||
|     name="ascii", |     name="ascii", | ||||||
|     tile_width=1, |     tile_width=1, | ||||||
|  |     tile_fg_visible_color=(1000, 1000, 1000), | ||||||
|     tile_fg_color=curses.COLOR_WHITE, |     tile_fg_color=curses.COLOR_WHITE, | ||||||
|     tile_bg_color=curses.COLOR_BLACK, |     tile_bg_color=curses.COLOR_BLACK, | ||||||
|     entity_fg_color=curses.COLOR_WHITE, |     entity_fg_color=(1000, 1000, 1000), | ||||||
|     entity_bg_color=curses.COLOR_BLACK, |     entity_bg_color=curses.COLOR_BLACK, | ||||||
|  |  | ||||||
|     BODY_SNATCH_POTION='S', |     BODY_SNATCH_POTION='S', | ||||||
| @@ -76,6 +78,7 @@ TexturePack.ASCII_PACK = TexturePack( | |||||||
|     EMPTY=' ', |     EMPTY=' ', | ||||||
|     EXPLOSION='%', |     EXPLOSION='%', | ||||||
|     FLOOR='.', |     FLOOR='.', | ||||||
|  |     LADDER='H', | ||||||
|     HAZELNUT='¤', |     HAZELNUT='¤', | ||||||
|     HEART='❤', |     HEART='❤', | ||||||
|     HEDGEHOG='*', |     HEDGEHOG='*', | ||||||
| @@ -97,10 +100,11 @@ TexturePack.ASCII_PACK = TexturePack( | |||||||
| TexturePack.SQUIRREL_PACK = TexturePack( | TexturePack.SQUIRREL_PACK = TexturePack( | ||||||
|     name="squirrel", |     name="squirrel", | ||||||
|     tile_width=2, |     tile_width=2, | ||||||
|  |     tile_fg_visible_color=(1000, 1000, 1000), | ||||||
|     tile_fg_color=curses.COLOR_WHITE, |     tile_fg_color=curses.COLOR_WHITE, | ||||||
|     tile_bg_color=curses.COLOR_BLACK, |     tile_bg_color=curses.COLOR_BLACK, | ||||||
|     entity_fg_color=curses.COLOR_WHITE, |     entity_fg_color=(1000, 1000, 1000), | ||||||
|     entity_bg_color=curses.COLOR_WHITE, |     entity_bg_color=(1000, 1000, 1000), | ||||||
|  |  | ||||||
|     BODY_SNATCH_POTION='🔀', |     BODY_SNATCH_POTION='🔀', | ||||||
|     BOMB='💣', |     BOMB='💣', | ||||||
| @@ -109,6 +113,8 @@ TexturePack.SQUIRREL_PACK = TexturePack( | |||||||
|     EMPTY='  ', |     EMPTY='  ', | ||||||
|     EXPLOSION='💥', |     EXPLOSION='💥', | ||||||
|     FLOOR='██', |     FLOOR='██', | ||||||
|  |     LADDER=('🪜', curses.COLOR_WHITE, (1000, 1000, 1000), | ||||||
|  |             curses.COLOR_WHITE, (1000, 1000, 1000)), | ||||||
|     HAZELNUT='🌰', |     HAZELNUT='🌰', | ||||||
|     HEART='💜', |     HEART='💜', | ||||||
|     HEDGEHOG='🦔', |     HEDGEHOG='🦔', | ||||||
|   | |||||||
| @@ -44,48 +44,17 @@ class Item(Entity): | |||||||
|         """ |         """ | ||||||
|         Indicates what should be done when the item is equipped. |         Indicates what should be done when the item is equipped. | ||||||
|         """ |         """ | ||||||
|         if isinstance(self, Chestplate): |         # Other objects are only equipped as secondary. | ||||||
|             if self.held_by.equipped_armor: |         if self.held_by.equipped_secondary: | ||||||
|                 self.held_by.equipped_armor.unequip() |             self.held_by.equipped_secondary.unequip() | ||||||
|             self.held_by.remove_from_inventory(self) |         self.held_by.remove_from_inventory(self) | ||||||
|             self.held_by.equipped_armor = self |         self.held_by.equipped_secondary = 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 |  | ||||||
|  |  | ||||||
|     def unequip(self) -> None: |     def unequip(self) -> None: | ||||||
|         """ |         """ | ||||||
|         Indicates what should be done when the item is unequipped. |         Indicates what should be done when the item is unequipped. | ||||||
|         """ |         """ | ||||||
|         if isinstance(self, Chestplate): |         self.held_by.remove_from_inventory(self) | ||||||
|             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.add_to_inventory(self) |         self.held_by.add_to_inventory(self) | ||||||
|  |  | ||||||
|     def hold(self, holder: InventoryHolder) -> None: |     def hold(self, holder: InventoryHolder) -> None: | ||||||
| @@ -236,7 +205,6 @@ class Explosion(Item): | |||||||
|         """ |         """ | ||||||
|         The player can't hold an explosion. |         The player can't hold an explosion. | ||||||
|         """ |         """ | ||||||
|         pass |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Weapon(Item): | class Weapon(Item): | ||||||
| @@ -261,7 +229,8 @@ class Weapon(Item): | |||||||
|         """ |         """ | ||||||
|         When a weapon is equipped, the player gains strength. |         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 |         self.held_by.strength += self.damage | ||||||
|  |  | ||||||
|     def unequip(self) -> None: |     def unequip(self) -> None: | ||||||
| @@ -305,33 +274,45 @@ class Armor(Item): | |||||||
|         d["constitution"] = self.constitution |         d["constitution"] = self.constitution | ||||||
|         return d |         return d | ||||||
|  |  | ||||||
|  |  | ||||||
| class Shield(Armor): | class Shield(Armor): | ||||||
|     """ |     """ | ||||||
|     Class of shield items, they can be equipped in the other hand. |     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 = 16, *args, **kwargs): |                  price: int = 16, *args, **kwargs): | ||||||
|         super().__init__(name=name, constitution=constitution, *args, **kwargs) |         super().__init__(name=name, constitution=constitution, price=price, | ||||||
|  |                          *args, **kwargs) | ||||||
|  |  | ||||||
| class Helmet(Armor): | class Helmet(Armor): | ||||||
|     """ |     """ | ||||||
|     Class of helmet items, they can be equipped on the head. |     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 = 18, *args, **kwargs): |                  price: int = 18, *args, **kwargs): | ||||||
|         super().__init__(name=name, constitution=constitution, *args, **kwargs) |         super().__init__(name=name, constitution=constitution, price=price, | ||||||
|  |                          *args, **kwargs) | ||||||
|  |  | ||||||
|  |     def equip(self) -> None: | ||||||
|  |         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): | class Chestplate(Armor): | ||||||
|     """ |     """ | ||||||
|     Class of chestplate items, they can be equipped on the body. |     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 = 30, *args, **kwargs): |                  price: int = 30, *args, **kwargs): | ||||||
|         super().__init__(name=name, constitution=constitution, *args, **kwargs) |         super().__init__(name=name, constitution=constitution, price=price, | ||||||
|  |                          *args, **kwargs) | ||||||
|  |  | ||||||
|  |     def equip(self) -> None: | ||||||
|  |         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): | class BodySnatchPotion(Item): | ||||||
|     """ |     """ | ||||||
| @@ -366,6 +347,7 @@ class BodySnatchPotion(Item): | |||||||
|  |  | ||||||
|         self.held_by.inventory.remove(self) |         self.held_by.inventory.remove(self) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Ring(Item): | class Ring(Item): | ||||||
|     """ |     """ | ||||||
|     A class of rings that boost the player's statistics. |     A class of rings that boost the player's statistics. | ||||||
| @@ -379,9 +361,9 @@ class Ring(Item): | |||||||
|     critical: int |     critical: int | ||||||
|     experience: float |     experience: float | ||||||
|  |  | ||||||
|     def __init__(self, maxhealth: int = 0, strength: int = 0,\ |     def __init__(self, maxhealth: int = 0, strength: int = 0, | ||||||
|                  intelligence: int = 0, charisma: int = 0,\ |                  intelligence: int = 0, charisma: int = 0, | ||||||
|                  dexterity: int = 0, constitution: int = 0,\ |                  dexterity: int = 0, constitution: int = 0, | ||||||
|                  critical: int = 0, experience: float = 0, *args, **kwargs): |                  critical: int = 0, experience: float = 0, *args, **kwargs): | ||||||
|         super().__init__(*args, **kwargs) |         super().__init__(*args, **kwargs) | ||||||
|         self.maxhealth = maxhealth |         self.maxhealth = maxhealth | ||||||
| @@ -417,17 +399,26 @@ class Ring(Item): | |||||||
|  |  | ||||||
|     def save_state(self) -> dict: |     def save_state(self) -> dict: | ||||||
|         d = super().save_state() |         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["constitution"] = self.constitution | ||||||
|  |         d["critical"] = self.critical | ||||||
|  |         d["experience"] = self.experience | ||||||
|         return d |         return d | ||||||
|  |  | ||||||
|  |  | ||||||
| class RingCritical(Ring): | class RingCritical(Ring): | ||||||
|     def __init__(self, name: str = "ring_of_critical_damage", price: int = 15, |     def __init__(self, name: str = "ring_of_critical_damage", price: int = 15, | ||||||
|                  critical: int = 20, *args, **kwargs): |                  critical: int = 20, *args, **kwargs): | ||||||
|         super().__init__(name=name, price=price, critical=critical, \ |         super().__init__(name=name, price=price, critical=critical, | ||||||
|                          *args, **kwargs) |                          *args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| class RingXP(Ring): | class RingXP(Ring): | ||||||
|     def __init__(self, name: str = "ring_of_more_experience", price: int = 25, |     def __init__(self, name: str = "ring_of_more_experience", price: int = 25, | ||||||
|                  experience: float = 2, *args, **kwargs): |                  experience: float = 2, *args, **kwargs): | ||||||
|         super().__init__(name=name, price=price, experience=experience, \ |         super().__init__(name=name, price=price, experience=experience, | ||||||
|                          *args, **kwargs) |                          *args, **kwargs) | ||||||
|   | |||||||
| @@ -94,9 +94,11 @@ class Rabbit(Monster): | |||||||
|     A rabbit monster. |     A rabbit monster. | ||||||
|     """ |     """ | ||||||
|     def __init__(self, name: str = "rabbit", strength: int = 1, |     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, |         super().__init__(name=name, strength=strength, | ||||||
|                          maxhealth=maxhealth, critical=critical, *args, **kwargs) |                          maxhealth=maxhealth, critical=critical, | ||||||
|  |                          *args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| class TeddyBear(Monster): | class TeddyBear(Monster): | ||||||
| @@ -108,6 +110,7 @@ class TeddyBear(Monster): | |||||||
|         super().__init__(name=name, strength=strength, |         super().__init__(name=name, strength=strength, | ||||||
|                          maxhealth=maxhealth, *args, **kwargs) |                          maxhealth=maxhealth, *args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| class GiantSeaEagle(Monster): | class GiantSeaEagle(Monster): | ||||||
|     """ |     """ | ||||||
|     An eagle boss |     An eagle boss | ||||||
|   | |||||||
| @@ -26,10 +26,10 @@ class Player(InventoryHolder, FightingEntity): | |||||||
|                  dexterity: int = 1, constitution: int = 1, level: int = 1, |                  dexterity: int = 1, constitution: int = 1, level: int = 1, | ||||||
|                  current_xp: int = 0, max_xp: int = 10, inventory: list = None, |                  current_xp: int = 0, max_xp: int = 10, inventory: list = None, | ||||||
|                  hazel: int = 42, equipped_main: Optional[Item] = None, |                  hazel: int = 42, equipped_main: Optional[Item] = None, | ||||||
|                  equipped_armor: Optional[Item] = None, critical: int = 5,\ |                  equipped_armor: Optional[Item] = None, critical: int = 5, | ||||||
|                  equipped_secondary: Optional[Item] = None, \ |                  equipped_secondary: Optional[Item] = None, | ||||||
|                  equipped_helmet: Optional[Item] = None, xp_buff: float = 1,\ |                  equipped_helmet: Optional[Item] = None, xp_buff: float = 1, | ||||||
|                  *args, **kwargs) -> None: |                  vision: int = 5, *args, **kwargs) -> None: | ||||||
|         super().__init__(name=name, maxhealth=maxhealth, strength=strength, |         super().__init__(name=name, maxhealth=maxhealth, strength=strength, | ||||||
|                          intelligence=intelligence, charisma=charisma, |                          intelligence=intelligence, charisma=charisma, | ||||||
|                          dexterity=dexterity, constitution=constitution, |                          dexterity=dexterity, constitution=constitution, | ||||||
| @@ -40,18 +40,15 @@ class Player(InventoryHolder, FightingEntity): | |||||||
|         self.inventory = self.translate_inventory(inventory or []) |         self.inventory = self.translate_inventory(inventory or []) | ||||||
|         self.paths = dict() |         self.paths = dict() | ||||||
|         self.hazel = hazel |         self.hazel = hazel | ||||||
|         if isinstance(equipped_main, dict): |         self.equipped_main = self.dict_to_item(equipped_main) \ | ||||||
|             equipped_main = self.dict_to_item(equipped_main) |             if isinstance(equipped_main, dict) else equipped_main | ||||||
|         if isinstance(equipped_armor, dict): |         self.equipped_armor = self.dict_to_item(equipped_armor) \ | ||||||
|             equipped_armor = self.dict_to_item(equipped_armor) |             if isinstance(equipped_armor, dict) else equipped_armor | ||||||
|         if isinstance(equipped_secondary, dict): |         self.equipped_secondary = self.dict_to_item(equipped_secondary) \ | ||||||
|             equipped_secondary = self.dict_to_item(equipped_secondary) |             if isinstance(equipped_secondary, dict) else equipped_secondary | ||||||
|         if isinstance(equipped_helmet, dict): |         self.equipped_helmet = self.dict_to_item(equipped_helmet) \ | ||||||
|             equipped_helmet = self.dict_to_item(equipped_helmet) |             if isinstance(equipped_helmet, dict) else equipped_helmet | ||||||
|         self.equipped_main = equipped_main |         self.vision = vision | ||||||
|         self.equipped_armor = equipped_armor |  | ||||||
|         self.equipped_secondary = equipped_secondary |  | ||||||
|         self.equipped_helmet = equipped_helmet |  | ||||||
|  |  | ||||||
|     def move(self, y: int, x: int) -> None: |     def move(self, y: int, x: int) -> None: | ||||||
|         """ |         """ | ||||||
| @@ -62,6 +59,7 @@ class Player(InventoryHolder, FightingEntity): | |||||||
|         self.map.currenty = y |         self.map.currenty = y | ||||||
|         self.map.currentx = x |         self.map.currentx = x | ||||||
|         self.recalculate_paths() |         self.recalculate_paths() | ||||||
|  |         self.map.compute_visibility(self.y, self.x, self.vision) | ||||||
|  |  | ||||||
|     def level_up(self) -> None: |     def level_up(self) -> None: | ||||||
|         """ |         """ | ||||||
| @@ -82,7 +80,7 @@ class Player(InventoryHolder, FightingEntity): | |||||||
|         Adds some experience to the player. |         Adds some experience to the player. | ||||||
|         If the required amount is reached, the player levels up. |         If the required amount is reached, the player levels up. | ||||||
|         """ |         """ | ||||||
|         self.current_xp += int(xp*self.xp_buff) |         self.current_xp += int(xp * self.xp_buff) | ||||||
|         self.level_up() |         self.level_up() | ||||||
|  |  | ||||||
|     def remove_from_inventory(self, obj: Item) -> None: |     def remove_from_inventory(self, obj: Item) -> None: | ||||||
|   | |||||||
| @@ -47,6 +47,7 @@ class KeyValues(Enum): | |||||||
|     SPACE = auto() |     SPACE = auto() | ||||||
|     CHAT = auto() |     CHAT = auto() | ||||||
|     WAIT = auto() |     WAIT = auto() | ||||||
|  |     LADDER = auto() | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: |     def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: | ||||||
| @@ -81,4 +82,6 @@ class KeyValues(Enum): | |||||||
|             return KeyValues.CHAT |             return KeyValues.CHAT | ||||||
|         elif key == settings.KEY_WAIT: |         elif key == settings.KEY_WAIT: | ||||||
|             return KeyValues.WAIT |             return KeyValues.WAIT | ||||||
|  |         elif key == settings.KEY_LADDER: | ||||||
|  |             return KeyValues.LADDER | ||||||
|         return None |         return None | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|  |  | ||||||
| from json import JSONDecodeError | from json import JSONDecodeError | ||||||
| from random import randint | from random import randint | ||||||
| from typing import Any, Optional | from typing import Any, Optional, List | ||||||
| import curses | import curses | ||||||
| import json | import json | ||||||
| import os | import os | ||||||
| @@ -22,7 +22,8 @@ class Game: | |||||||
|     """ |     """ | ||||||
|     The game object controls all actions in the game. |     The game object controls all actions in the game. | ||||||
|     """ |     """ | ||||||
|     map: Map |     maps: List[Map] | ||||||
|  |     map_index: int | ||||||
|     player: Player |     player: Player | ||||||
|     screen: Any |     screen: Any | ||||||
|     # display_actions is a display interface set by the bootstrapper |     # display_actions is a display interface set by the bootstrapper | ||||||
| @@ -52,7 +53,9 @@ class Game: | |||||||
|         Creates a new game on the screen. |         Creates a new game on the screen. | ||||||
|         """ |         """ | ||||||
|         # TODO generate a new map procedurally |         # TODO generate a new map procedurally | ||||||
|         self.map = Map.load(ResourceManager.get_asset_path("example_map_2.txt")) |         self.maps = [] | ||||||
|  |         self.map_index = 0 | ||||||
|  |         self.map = Map.load(ResourceManager.get_asset_path("example_map.txt")) | ||||||
|         self.map.logs = self.logs |         self.map.logs = self.logs | ||||||
|         self.logs.clear() |         self.logs.clear() | ||||||
|         self.player = Player() |         self.player = Player() | ||||||
| @@ -61,6 +64,24 @@ class Game: | |||||||
|         self.map.spawn_random_entities(randint(3, 10)) |         self.map.spawn_random_entities(randint(3, 10)) | ||||||
|         self.inventory_menu.update_player(self.player) |         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 |     def run(self, screen: Any) -> None:  # pragma no cover | ||||||
|         """ |         """ | ||||||
|         Main infinite loop. |         Main infinite loop. | ||||||
| @@ -110,7 +131,7 @@ class Game: | |||||||
|             self.state = GameMode.MAINMENU |             self.state = GameMode.MAINMENU | ||||||
|         self.display_actions(DisplayActions.REFRESH) |         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. |         In play mode, arrows or zqsd move the main character. | ||||||
|         """ |         """ | ||||||
| @@ -130,7 +151,10 @@ class Game: | |||||||
|             self.state = GameMode.INVENTORY |             self.state = GameMode.INVENTORY | ||||||
|             self.display_actions(DisplayActions.UPDATE) |             self.display_actions(DisplayActions.UPDATE) | ||||||
|         elif key == KeyValues.USE and self.player.equipped_main: |         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: |         elif key == KeyValues.SPACE: | ||||||
|             self.state = GameMode.MAINMENU |             self.state = GameMode.MAINMENU | ||||||
|         elif key == KeyValues.CHAT: |         elif key == KeyValues.CHAT: | ||||||
| @@ -138,6 +162,54 @@ class Game: | |||||||
|             self.waiting_for_friendly_key = True |             self.waiting_for_friendly_key = True | ||||||
|         elif key == KeyValues.WAIT: |         elif key == KeyValues.WAIT: | ||||||
|             self.map.tick(self.player) |             self.map.tick(self.player) | ||||||
|  |         elif key == KeyValues.LADDER: | ||||||
|  |             self.handle_ladder() | ||||||
|  |  | ||||||
|  |     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 | ||||||
|  |  | ||||||
|  |         # 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: |     def handle_friendly_entity_chat(self, key: KeyValues) -> None: | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -2,13 +2,11 @@ | |||||||
| # SPDX-License-Identifier: GPL-3.0-or-later | # SPDX-License-Identifier: GPL-3.0-or-later | ||||||
|  |  | ||||||
| from enum import Enum, auto | from enum import Enum, auto | ||||||
| from math import sqrt | from math import ceil, sqrt | ||||||
| from random import choice, randint | from random import choice, choices, randint | ||||||
| from typing import List, Optional, Any, Dict, Tuple | from typing import List, Optional, Any, Dict, Tuple | ||||||
| from queue import PriorityQueue | from queue import PriorityQueue | ||||||
| from functools import reduce | from functools import reduce | ||||||
| from random import choice, randint, choices |  | ||||||
| from typing import List, Optional, Any |  | ||||||
|  |  | ||||||
| from .display.texturepack import TexturePack | from .display.texturepack import TexturePack | ||||||
| from .translations import gettext as _ | from .translations import gettext as _ | ||||||
| @@ -34,16 +32,47 @@ class Logs: | |||||||
|         self.messages = [] |         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: "Slope") -> int: | ||||||
|  |         y, x = other.Y, other.X | ||||||
|  |         return self.Y * x - self.X * y | ||||||
|  |  | ||||||
|  |     def __lt__(self, other: "Slope") -> bool: | ||||||
|  |         return self.compare(other) < 0 | ||||||
|  |  | ||||||
|  |     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: | class Map: | ||||||
|     """ |     """ | ||||||
|     The Map object represents a with its width, height |     The Map object represents a with its width, height | ||||||
|     and tiles, that have their custom properties. |     and tiles, that have their custom properties. | ||||||
|     """ |     """ | ||||||
|  |     floor: int | ||||||
|     width: int |     width: int | ||||||
|     height: int |     height: int | ||||||
|     start_y: int |     start_y: int | ||||||
|     start_x: int |     start_x: int | ||||||
|     tiles: List[List["Tile"]] |     tiles: List[List["Tile"]] | ||||||
|  |     visibility: List[List[bool]] | ||||||
|  |     seen_tiles: List[List[bool]] | ||||||
|     entities: List["Entity"] |     entities: List["Entity"] | ||||||
|     logs: Logs |     logs: Logs | ||||||
|     # coordinates of the point that should be |     # coordinates of the point that should be | ||||||
| @@ -53,11 +82,16 @@ class Map: | |||||||
|  |  | ||||||
|     def __init__(self, width: int, height: int, tiles: list, |     def __init__(self, width: int, height: int, tiles: list, | ||||||
|                  start_y: int, start_x: int): |                  start_y: int, start_x: int): | ||||||
|  |         self.floor = 0 | ||||||
|         self.width = width |         self.width = width | ||||||
|         self.height = height |         self.height = height | ||||||
|         self.start_y = start_y |         self.start_y = start_y | ||||||
|         self.start_x = start_x |         self.start_x = start_x | ||||||
|         self.tiles = tiles |         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.entities = [] | ||||||
|         self.logs = Logs() |         self.logs = Logs() | ||||||
|  |  | ||||||
| @@ -147,18 +181,138 @@ class Map: | |||||||
|         """ |         """ | ||||||
|         Puts randomly {count} entities on the map, only on empty ground tiles. |         Puts randomly {count} entities on the map, only on empty ground tiles. | ||||||
|         """ |         """ | ||||||
|         for ignored in range(count): |         for _ignored in range(count): | ||||||
|             y, x = 0, 0 |             y, x = 0, 0 | ||||||
|             while True: |             while True: | ||||||
|                 y, x = randint(0, self.height - 1), randint(0, self.width - 1) |                 y, x = randint(0, self.height - 1), randint(0, self.width - 1) | ||||||
|                 tile = self.tiles[y][x] |                 tile = self.tiles[y][x] | ||||||
|                 if tile.can_walk(): |                 if tile.can_walk(): | ||||||
|                     break |                     break | ||||||
|             entity = choices(Entity.get_all_entity_classes(),\ |             entity = choices(Entity.get_all_entity_classes(), | ||||||
|                             weights = Entity.get_weights(), k=1)[0]() |                              weights=Entity.get_weights(), k=1)[0]() | ||||||
|             entity.move(y, x) |             entity.move(y, x) | ||||||
|             self.add_entity(entity) |             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.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)) | ||||||
|  |  | ||||||
|  |     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 = ceil(((x * 2 - 1) * top.Y + top.X) / (top.X * 2)) | ||||||
|  |             if self.is_wall(top_y, x, octant, origin): | ||||||
|  |                 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 | ||||||
|  |                 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], | ||||||
|  |                                x: int, bottom: Slope) -> int: | ||||||
|  |         if bottom.Y == 0: | ||||||
|  |             bottom_y = 0 | ||||||
|  |         else: | ||||||
|  |             bottom_y = ceil(((x * 2 - 1) * bottom.Y + bottom.X) | ||||||
|  |                             / (bottom.X * 2)) | ||||||
|  |             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], | ||||||
|  |                                   max_range: int, distance: int, top: Slope, | ||||||
|  |                                   bottom: Slope) -> None: | ||||||
|  |         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 x + y > max_range: | ||||||
|  |                     continue | ||||||
|  |                 is_opaque = self.is_wall(y, x, octant, origin) | ||||||
|  |                 is_visible = is_opaque\ | ||||||
|  |                     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: | ||||||
|  |                     continue | ||||||
|  |                 if is_opaque and was_opaque == 0: | ||||||
|  |                     nx, ny = x * 2, y * 2 + 1 | ||||||
|  |                     nx -= self.is_wall(y + 1, x, octant, origin) | ||||||
|  |                     if top > Slope(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)) | ||||||
|  |                     elif y == bottom_y:  # pragma: no cover | ||||||
|  |                         return | ||||||
|  |                 elif not is_opaque and was_opaque == 1: | ||||||
|  |                     nx, ny = x * 2, y * 2 + 1 | ||||||
|  |                     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 | ||||||
|  |             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 ny - y, nx + x | ||||||
|  |         elif octant == 1: | ||||||
|  |             return ny - x, nx + y | ||||||
|  |         elif octant == 2: | ||||||
|  |             return ny - x, nx - y | ||||||
|  |         elif octant == 3: | ||||||
|  |             return ny - y, nx - x | ||||||
|  |         elif octant == 4: | ||||||
|  |             return ny + y, nx - x | ||||||
|  |         elif octant == 5: | ||||||
|  |             return ny + x, nx - y | ||||||
|  |         elif octant == 6: | ||||||
|  |             return ny + x, nx + y | ||||||
|  |         elif octant == 7: | ||||||
|  |             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 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) | ||||||
|  |         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, p: Any) -> None: |     def tick(self, p: Any) -> None: | ||||||
|         """ |         """ | ||||||
|         Triggers all entity events. |         Triggers all entity events. | ||||||
| @@ -210,6 +364,7 @@ class Tile(Enum): | |||||||
|     EMPTY = auto() |     EMPTY = auto() | ||||||
|     WALL = auto() |     WALL = auto() | ||||||
|     FLOOR = auto() |     FLOOR = auto() | ||||||
|  |     LADDER = auto() | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def from_ascii_char(ch: str) -> "Tile": |     def from_ascii_char(ch: str) -> "Tile": | ||||||
| @@ -226,7 +381,25 @@ class Tile(Enum): | |||||||
|         Translates a Tile to the corresponding character according |         Translates a Tile to the corresponding character according | ||||||
|         to the texture pack. |         to the texture pack. | ||||||
|         """ |         """ | ||||||
|         return getattr(pack, self.name) |         val = getattr(pack, self.name) | ||||||
|  |         return val[0] if isinstance(val, tuple) else val | ||||||
|  |  | ||||||
|  |     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[3]) if isinstance(val, tuple) else \ | ||||||
|  |             (pack.tile_fg_color, pack.tile_bg_color) | ||||||
|  |  | ||||||
|     def is_wall(self) -> bool: |     def is_wall(self) -> bool: | ||||||
|         """ |         """ | ||||||
| @@ -234,6 +407,12 @@ class Tile(Enum): | |||||||
|         """ |         """ | ||||||
|         return self == Tile.WALL |         return self == Tile.WALL | ||||||
|  |  | ||||||
|  |     def is_ladder(self) -> bool: | ||||||
|  |         """ | ||||||
|  |         Is this Tile a ladder? | ||||||
|  |         """ | ||||||
|  |         return self == Tile.LADDER | ||||||
|  |  | ||||||
|     def can_walk(self) -> bool: |     def can_walk(self) -> bool: | ||||||
|         """ |         """ | ||||||
|         Checks if an entity (player or not) can move in this tile. |         Checks if an entity (player or not) can move in this tile. | ||||||
| @@ -448,7 +627,7 @@ class Entity: | |||||||
|         from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \ |         from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \ | ||||||
|             TeddyBear, GiantSeaEagle |             TeddyBear, GiantSeaEagle | ||||||
|         from squirrelbattle.entities.friendly import Merchant, Sunflower, \ |         from squirrelbattle.entities.friendly import Merchant, Sunflower, \ | ||||||
|              Trumpet |             Trumpet | ||||||
|         from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ |         from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ | ||||||
|             Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP |             Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP | ||||||
|         return { |         return { | ||||||
| @@ -525,12 +704,12 @@ class FightingEntity(Entity): | |||||||
|         The entity deals damage to the opponent |         The entity deals damage to the opponent | ||||||
|         based on their respective stats. |         based on their respective stats. | ||||||
|         """ |         """ | ||||||
|         diceroll = randint(0, 100) |         diceroll = randint(1, 100) | ||||||
|         damage = self.strength |         damage = self.strength | ||||||
|         string = " " |         string = " " | ||||||
|         if diceroll <= self.critical: # It is a critical hit |         if diceroll <= self.critical:  # It is a critical hit | ||||||
|             damage *= 4 |             damage *= 4 | ||||||
|             string = _(" It's a critical hit! ") |             string = " " + _("It's a critical hit!") + " " | ||||||
|         return _("{name} hits {opponent}.")\ |         return _("{name} hits {opponent}.")\ | ||||||
|             .format(name=_(self.translated_name.capitalize()), |             .format(name=_(self.translated_name.capitalize()), | ||||||
|                     opponent=_(opponent.translated_name)) + string + \ |                     opponent=_(opponent.translated_name)) + string + \ | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: squirrelbattle 3.14.1\n" | "Project-Id-Version: squirrelbattle 3.14.1\n" | ||||||
| "Report-Msgid-Bugs-To: squirrel-battle@crans.org\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" | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | "Language-Team: LANGUAGE <LL@li.org>\n" | ||||||
| @@ -17,19 +17,44 @@ msgstr "" | |||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\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" | msgid "INVENTORY" | ||||||
| msgstr "BESTAND" | msgstr "BESTAND" | ||||||
|  |  | ||||||
| #: squirrelbattle/display/menudisplay.py:164 | #: squirrelbattle/display/menudisplay.py:202 | ||||||
| msgid "STALL" | msgid "STALL" | ||||||
| msgstr "STAND" | 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:" | msgid "Inventory:" | ||||||
| msgstr "Bestand:" | msgstr "Bestand:" | ||||||
|  |  | ||||||
| #: squirrelbattle/display/statsdisplay.py:52 | #: 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" | msgid "YOU ARE DEAD" | ||||||
| msgstr "SIE WURDEN GESTORBEN" | msgstr "SIE WURDEN GESTORBEN" | ||||||
|  |  | ||||||
| @@ -49,20 +74,30 @@ msgstr "Die Sonne ist warm heute" | |||||||
| #. The bomb is exploding. | #. The bomb is exploding. | ||||||
| #. Each entity that is close to the bomb takes damages. | #. Each entity that is close to the bomb takes damages. | ||||||
| #. The player earn XP if the entity was killed. | #. The player earn XP if the entity was killed. | ||||||
| #: squirrelbattle/entities/items.py:151 | #: squirrelbattle/entities/items.py:163 | ||||||
| msgid "Bomb is exploding." | msgid "Bomb is exploding." | ||||||
| msgstr "Die Bombe explodiert." | msgstr "Die Bombe explodiert." | ||||||
|  |  | ||||||
| #: squirrelbattle/entities/items.py:248 | #: squirrelbattle/entities/items.py:344 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{player} exchanged its body with {entity}." | msgid "{player} exchanged its body with {entity}." | ||||||
| msgstr "{player} täuscht seinem Körper mit {entity} aus." | msgstr "{player} täuscht seinem Körper mit {entity} aus." | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 | #: 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" | msgid "The buyer does not have enough money" | ||||||
| msgstr "Der Kaufer hat nicht genug Geld" | msgstr "Der Kaufer hat nicht genug Geld" | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:249 | #: squirrelbattle/game.py:328 | ||||||
| msgid "" | msgid "" | ||||||
| "Some keys are missing in your save file.\n" | "Some keys are missing in your save file.\n" | ||||||
| "Your save seems to be corrupt. It got deleted." | "Your save seems to be corrupt. It got deleted." | ||||||
| @@ -70,7 +105,7 @@ msgstr "" | |||||||
| "In Ihrer Speicherdatei fehlen einige Schlüssel.\n" | "In Ihrer Speicherdatei fehlen einige Schlüssel.\n" | ||||||
| "Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." | "Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:257 | #: squirrelbattle/game.py:336 | ||||||
| msgid "" | msgid "" | ||||||
| "No player was found on this map!\n" | "No player was found on this map!\n" | ||||||
| "Maybe you died?" | "Maybe you died?" | ||||||
| @@ -78,7 +113,7 @@ msgstr "" | |||||||
| "Auf dieser Karte wurde kein Spieler gefunden!\n" | "Auf dieser Karte wurde kein Spieler gefunden!\n" | ||||||
| "Vielleicht sind Sie gestorben?" | "Vielleicht sind Sie gestorben?" | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:277 | #: squirrelbattle/game.py:356 | ||||||
| msgid "" | msgid "" | ||||||
| "The JSON file is not correct.\n" | "The JSON file is not correct.\n" | ||||||
| "Your save seems corrupted. It got deleted." | "Your save seems corrupted. It got deleted." | ||||||
| @@ -86,22 +121,26 @@ msgstr "" | |||||||
| "Die JSON-Datei ist nicht korrekt.\n" | "Die JSON-Datei ist nicht korrekt.\n" | ||||||
| "Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht." | "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 | #, python-brace-format | ||||||
| msgid "{name} hits {opponent}." | msgid "{name} hits {opponent}." | ||||||
| msgstr "{name} schlägt {opponent}." | msgstr "{name} schlägt {opponent}." | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:441 | #: squirrelbattle/interfaces.py:465 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{name} takes {amount} damage." | msgid "{name} takes {damage} damage." | ||||||
| msgstr "{name} nimmt {amount} Schadenspunkte." | msgstr "" | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:443 | #: squirrelbattle/interfaces.py:467 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{name} dies." | msgid "{name} dies." | ||||||
| msgstr "{name} stirbt." | msgstr "{name} stirbt." | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:477 | #: squirrelbattle/interfaces.py:501 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{entity} said: {message}" | msgid "{entity} said: {message}" | ||||||
| msgstr "{entity} hat gesagt: {message}" | msgstr "{entity} hat gesagt: {message}" | ||||||
| @@ -110,8 +149,8 @@ msgstr "{entity} hat gesagt: {message}" | |||||||
| msgid "Back" | msgid "Back" | ||||||
| msgstr "Zurück" | msgstr "Zurück" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 | #: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 | ||||||
| #: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 | #: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 | ||||||
| #: squirrelbattle/tests/translations_test.py:16 | #: squirrelbattle/tests/translations_test.py:16 | ||||||
| msgid "New game" | msgid "New game" | ||||||
| msgstr "Neu Spiel" | msgstr "Neu Spiel" | ||||||
| @@ -197,57 +236,57 @@ msgid "Key used to wait" | |||||||
| msgstr "Wartentaste" | msgstr "Wartentaste" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:56 | #: squirrelbattle/tests/translations_test.py:56 | ||||||
|  | msgid "Key used to use ladders" | ||||||
|  | msgstr "Leitertaste" | ||||||
|  |  | ||||||
|  | #: squirrelbattle/tests/translations_test.py:58 | ||||||
| msgid "Texture pack" | msgid "Texture pack" | ||||||
| msgstr "Textur-Packung" | msgstr "Textur-Packung" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:57 | #: squirrelbattle/tests/translations_test.py:59 | ||||||
| msgid "Language" | msgid "Language" | ||||||
| msgstr "Sprache" | msgstr "Sprache" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:60 | #: squirrelbattle/tests/translations_test.py:64 | ||||||
| msgid "player" |  | ||||||
| msgstr "Spieler" |  | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:62 |  | ||||||
| msgid "hedgehog" | msgid "hedgehog" | ||||||
| msgstr "Igel" | msgstr "Igel" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:63 | #: squirrelbattle/tests/translations_test.py:65 | ||||||
| msgid "merchant" | msgid "merchant" | ||||||
| msgstr "Kaufmann" | msgstr "Kaufmann" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:64 | #: squirrelbattle/tests/translations_test.py:66 | ||||||
| msgid "rabbit" | msgid "rabbit" | ||||||
| msgstr "Kanninchen" | msgstr "Kanninchen" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:65 | #: squirrelbattle/tests/translations_test.py:67 | ||||||
| msgid "sunflower" | msgid "sunflower" | ||||||
| msgstr "Sonnenblume" | msgstr "Sonnenblume" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:66 | #: squirrelbattle/tests/translations_test.py:68 | ||||||
| msgid "teddy bear" | msgid "teddy bear" | ||||||
| msgstr "Teddybär" | msgstr "Teddybär" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:67 | #: squirrelbattle/tests/translations_test.py:69 | ||||||
| msgid "tiger" | msgid "tiger" | ||||||
| msgstr "Tiger" | msgstr "Tiger" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:69 | #: squirrelbattle/tests/translations_test.py:71 | ||||||
| msgid "body snatch potion" | msgid "body snatch potion" | ||||||
| msgstr "Leichenfleddererzaubertrank" | msgstr "Leichenfleddererzaubertrank" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:70 | #: squirrelbattle/tests/translations_test.py:72 | ||||||
| msgid "bomb" | msgid "bomb" | ||||||
| msgstr "Bombe" | msgstr "Bombe" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:71 | #: squirrelbattle/tests/translations_test.py:73 | ||||||
| msgid "explosion" | msgid "explosion" | ||||||
| msgstr "Explosion" | msgstr "Explosion" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:72 | #: squirrelbattle/tests/translations_test.py:74 | ||||||
| msgid "heart" | msgid "heart" | ||||||
| msgstr "Herz" | msgstr "Herz" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:73 | #: squirrelbattle/tests/translations_test.py:75 | ||||||
| msgid "sword" | msgid "sword" | ||||||
| msgstr "schwert" | msgstr "schwert" | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: squirrelbattle 3.14.1\n" | "Project-Id-Version: squirrelbattle 3.14.1\n" | ||||||
| "Report-Msgid-Bugs-To: squirrel-battle@crans.org\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 15:19+0100\n" | ||||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | "Language-Team: LANGUAGE <LL@li.org>\n" | ||||||
| @@ -17,19 +17,44 @@ msgstr "" | |||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\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" | msgid "INVENTORY" | ||||||
| msgstr "INVENTORIO" | msgstr "INVENTORIO" | ||||||
|  |  | ||||||
| #: squirrelbattle/display/menudisplay.py:164 | #: squirrelbattle/display/menudisplay.py:202 | ||||||
| msgid "STALL" | msgid "STALL" | ||||||
| msgstr "PUESTO" | 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:" | msgid "Inventory:" | ||||||
| msgstr "Inventorio :" | msgstr "Inventorio :" | ||||||
|  |  | ||||||
| #: squirrelbattle/display/statsdisplay.py:52 | #: 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" | msgid "YOU ARE DEAD" | ||||||
| msgstr "ERES MUERTO" | msgstr "ERES MUERTO" | ||||||
|  |  | ||||||
| @@ -48,20 +73,30 @@ msgstr "El sol está caliente hoy" | |||||||
| #. The bomb is exploding. | #. The bomb is exploding. | ||||||
| #. Each entity that is close to the bomb takes damages. | #. Each entity that is close to the bomb takes damages. | ||||||
| #. The player earn XP if the entity was killed. | #. The player earn XP if the entity was killed. | ||||||
| #: squirrelbattle/entities/items.py:151 | #: squirrelbattle/entities/items.py:163 | ||||||
| msgid "Bomb is exploding." | msgid "Bomb is exploding." | ||||||
| msgstr "La bomba está explotando." | msgstr "La bomba está explotando." | ||||||
|  |  | ||||||
| #: squirrelbattle/entities/items.py:248 | #: squirrelbattle/entities/items.py:344 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{player} exchanged its body with {entity}." | msgid "{player} exchanged its body with {entity}." | ||||||
| msgstr "{player} intercambió su cuerpo con {entity}." | msgstr "{player} intercambió su cuerpo con {entity}." | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 | #: 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" | msgid "The buyer does not have enough money" | ||||||
| msgstr "El comprador no tiene suficiente dinero" | msgstr "El comprador no tiene suficiente dinero" | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:249 | #: squirrelbattle/game.py:328 | ||||||
| msgid "" | msgid "" | ||||||
| "Some keys are missing in your save file.\n" | "Some keys are missing in your save file.\n" | ||||||
| "Your save seems to be corrupt. It got deleted." | "Your save seems to be corrupt. It got deleted." | ||||||
| @@ -69,7 +104,7 @@ msgstr "" | |||||||
| "Algunas claves faltan en su archivo de guarda.\n" | "Algunas claves faltan en su archivo de guarda.\n" | ||||||
| "Su guarda parece a ser corruptido. Fue eliminado." | "Su guarda parece a ser corruptido. Fue eliminado." | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:257 | #: squirrelbattle/game.py:336 | ||||||
| msgid "" | msgid "" | ||||||
| "No player was found on this map!\n" | "No player was found on this map!\n" | ||||||
| "Maybe you died?" | "Maybe you died?" | ||||||
| @@ -77,7 +112,7 @@ msgstr "" | |||||||
| "No jugador encontrado sobre la carta !\n" | "No jugador encontrado sobre la carta !\n" | ||||||
| "¿ Quizas murió ?" | "¿ Quizas murió ?" | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:277 | #: squirrelbattle/game.py:356 | ||||||
| msgid "" | msgid "" | ||||||
| "The JSON file is not correct.\n" | "The JSON file is not correct.\n" | ||||||
| "Your save seems corrupted. It got deleted." | "Your save seems corrupted. It got deleted." | ||||||
| @@ -85,22 +120,26 @@ msgstr "" | |||||||
| "El JSON archivo no es correcto.\n" | "El JSON archivo no es correcto.\n" | ||||||
| "Su guarda parece corrupta. Fue eliminada." | "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 | #, python-brace-format | ||||||
| msgid "{name} hits {opponent}." | msgid "{name} hits {opponent}." | ||||||
| msgstr "{name} golpea a {opponent}." | msgstr "{name} golpea a {opponent}." | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:441 | #: squirrelbattle/interfaces.py:465 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{name} takes {amount} damage." | msgid "{name} takes {damage} damage." | ||||||
| msgstr "{name} recibe {amount} daño." | msgstr "" | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:443 | #: squirrelbattle/interfaces.py:467 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{name} dies." | msgid "{name} dies." | ||||||
| msgstr "{name} se muere." | msgstr "{name} se muere." | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:477 | #: squirrelbattle/interfaces.py:501 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{entity} said: {message}" | msgid "{entity} said: {message}" | ||||||
| msgstr "{entity} dijo : {message}" | msgstr "{entity} dijo : {message}" | ||||||
| @@ -109,8 +148,8 @@ msgstr "{entity} dijo : {message}" | |||||||
| msgid "Back" | msgid "Back" | ||||||
| msgstr "Volver" | msgstr "Volver" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 | #: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 | ||||||
| #: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 | #: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 | ||||||
| #: squirrelbattle/tests/translations_test.py:16 | #: squirrelbattle/tests/translations_test.py:16 | ||||||
| msgid "New game" | msgid "New game" | ||||||
| msgstr "Nuevo partido" | msgstr "Nuevo partido" | ||||||
| @@ -196,57 +235,57 @@ msgid "Key used to wait" | |||||||
| msgstr "Tecla para espera" | msgstr "Tecla para espera" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:56 | #: 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" | msgid "Texture pack" | ||||||
| msgstr "Paquete de texturas" | msgstr "Paquete de texturas" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:57 | #: squirrelbattle/tests/translations_test.py:59 | ||||||
| msgid "Language" | msgid "Language" | ||||||
| msgstr "Languaje" | msgstr "Languaje" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:60 | #: squirrelbattle/tests/translations_test.py:64 | ||||||
| msgid "player" |  | ||||||
| msgstr "jugador" |  | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:62 |  | ||||||
| msgid "hedgehog" | msgid "hedgehog" | ||||||
| msgstr "erizo" | msgstr "erizo" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:63 | #: squirrelbattle/tests/translations_test.py:65 | ||||||
| msgid "merchant" | msgid "merchant" | ||||||
| msgstr "comerciante" | msgstr "comerciante" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:64 | #: squirrelbattle/tests/translations_test.py:66 | ||||||
| msgid "rabbit" | msgid "rabbit" | ||||||
| msgstr "conejo" | msgstr "conejo" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:65 | #: squirrelbattle/tests/translations_test.py:67 | ||||||
| msgid "sunflower" | msgid "sunflower" | ||||||
| msgstr "girasol" | msgstr "girasol" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:66 | #: squirrelbattle/tests/translations_test.py:68 | ||||||
| msgid "teddy bear" | msgid "teddy bear" | ||||||
| msgstr "osito de peluche" | msgstr "osito de peluche" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:67 | #: squirrelbattle/tests/translations_test.py:69 | ||||||
| msgid "tiger" | msgid "tiger" | ||||||
| msgstr "tigre" | msgstr "tigre" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:69 | #: squirrelbattle/tests/translations_test.py:71 | ||||||
| msgid "body snatch potion" | msgid "body snatch potion" | ||||||
| msgstr "poción de intercambio" | msgstr "poción de intercambio" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:70 | #: squirrelbattle/tests/translations_test.py:72 | ||||||
| msgid "bomb" | msgid "bomb" | ||||||
| msgstr "bomba" | msgstr "bomba" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:71 | #: squirrelbattle/tests/translations_test.py:73 | ||||||
| msgid "explosion" | msgid "explosion" | ||||||
| msgstr "explosión" | msgstr "explosión" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:72 | #: squirrelbattle/tests/translations_test.py:74 | ||||||
| msgid "heart" | msgid "heart" | ||||||
| msgstr "corazón" | msgstr "corazón" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:73 | #: squirrelbattle/tests/translations_test.py:75 | ||||||
| msgid "sword" | msgid "sword" | ||||||
| msgstr "espada" | msgstr "espada" | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: squirrelbattle 3.14.1\n" | "Project-Id-Version: squirrelbattle 3.14.1\n" | ||||||
| "Report-Msgid-Bugs-To: squirrel-battle@crans.org\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 15:19+0100\n" | ||||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | "Language-Team: LANGUAGE <LL@li.org>\n" | ||||||
| @@ -17,19 +17,44 @@ msgstr "" | |||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\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" | msgid "INVENTORY" | ||||||
| msgstr "INVENTAIRE" | msgstr "INVENTAIRE" | ||||||
|  |  | ||||||
| #: squirrelbattle/display/menudisplay.py:164 | #: squirrelbattle/display/menudisplay.py:202 | ||||||
| msgid "STALL" | msgid "STALL" | ||||||
| msgstr "STAND" | 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:" | msgid "Inventory:" | ||||||
| msgstr "Inventaire :" | msgstr "Inventaire :" | ||||||
|  |  | ||||||
| #: squirrelbattle/display/statsdisplay.py:52 | #: 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" | msgid "YOU ARE DEAD" | ||||||
| msgstr "VOUS ÊTES MORT" | msgstr "VOUS ÊTES MORT" | ||||||
|  |  | ||||||
| @@ -49,20 +74,30 @@ msgstr "Le soleil est chaud aujourd'hui" | |||||||
| #. The bomb is exploding. | #. The bomb is exploding. | ||||||
| #. Each entity that is close to the bomb takes damages. | #. Each entity that is close to the bomb takes damages. | ||||||
| #. The player earn XP if the entity was killed. | #. The player earn XP if the entity was killed. | ||||||
| #: squirrelbattle/entities/items.py:151 | #: squirrelbattle/entities/items.py:163 | ||||||
| msgid "Bomb is exploding." | msgid "Bomb is exploding." | ||||||
| msgstr "La bombe explose." | msgstr "La bombe explose." | ||||||
|  |  | ||||||
| #: squirrelbattle/entities/items.py:248 | #: squirrelbattle/entities/items.py:344 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{player} exchanged its body with {entity}." | msgid "{player} exchanged its body with {entity}." | ||||||
| msgstr "{player} a échangé son corps avec {entity}." | msgstr "{player} a échangé son corps avec {entity}." | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:205 squirrelbattle/tests/game_test.py:573 | #: 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" | msgid "The buyer does not have enough money" | ||||||
| msgstr "L'acheteur n'a pas assez d'argent" | msgstr "L'acheteur n'a pas assez d'argent" | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:249 | #: squirrelbattle/game.py:328 | ||||||
| msgid "" | msgid "" | ||||||
| "Some keys are missing in your save file.\n" | "Some keys are missing in your save file.\n" | ||||||
| "Your save seems to be corrupt. It got deleted." | "Your save seems to be corrupt. It got deleted." | ||||||
| @@ -70,7 +105,7 @@ msgstr "" | |||||||
| "Certaines clés de votre ficher de sauvegarde sont manquantes.\n" | "Certaines clés de votre ficher de sauvegarde sont manquantes.\n" | ||||||
| "Votre sauvegarde semble corrompue. Elle a été supprimée." | "Votre sauvegarde semble corrompue. Elle a été supprimée." | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:257 | #: squirrelbattle/game.py:336 | ||||||
| msgid "" | msgid "" | ||||||
| "No player was found on this map!\n" | "No player was found on this map!\n" | ||||||
| "Maybe you died?" | "Maybe you died?" | ||||||
| @@ -78,7 +113,7 @@ msgstr "" | |||||||
| "Aucun joueur n'a été trouvé sur la carte !\n" | "Aucun joueur n'a été trouvé sur la carte !\n" | ||||||
| "Peut-être êtes-vous mort ?" | "Peut-être êtes-vous mort ?" | ||||||
|  |  | ||||||
| #: squirrelbattle/game.py:277 | #: squirrelbattle/game.py:356 | ||||||
| msgid "" | msgid "" | ||||||
| "The JSON file is not correct.\n" | "The JSON file is not correct.\n" | ||||||
| "Your save seems corrupted. It got deleted." | "Your save seems corrupted. It got deleted." | ||||||
| @@ -86,22 +121,26 @@ msgstr "" | |||||||
| "Le fichier JSON de sauvegarde est incorrect.\n" | "Le fichier JSON de sauvegarde est incorrect.\n" | ||||||
| "Votre sauvegarde semble corrompue. Elle a été supprimée." | "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 | #, python-brace-format | ||||||
| msgid "{name} hits {opponent}." | msgid "{name} hits {opponent}." | ||||||
| msgstr "{name} frappe {opponent}." | msgstr "{name} frappe {opponent}." | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:441 | #: squirrelbattle/interfaces.py:465 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{name} takes {amount} damage." | msgid "{name} takes {damage} damage." | ||||||
| msgstr "{name} prend {amount} points de dégât." | msgstr "{name} prend {damage} dégâts." | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:443 | #: squirrelbattle/interfaces.py:467 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{name} dies." | msgid "{name} dies." | ||||||
| msgstr "{name} meurt." | msgstr "{name} meurt." | ||||||
|  |  | ||||||
| #: squirrelbattle/interfaces.py:477 | #: squirrelbattle/interfaces.py:501 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{entity} said: {message}" | msgid "{entity} said: {message}" | ||||||
| msgstr "{entity} a dit : {message}" | msgstr "{entity} a dit : {message}" | ||||||
| @@ -110,8 +149,8 @@ msgstr "{entity} a dit : {message}" | |||||||
| msgid "Back" | msgid "Back" | ||||||
| msgstr "Retour" | msgstr "Retour" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/game_test.py:344 squirrelbattle/tests/game_test.py:347 | #: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 | ||||||
| #: squirrelbattle/tests/game_test.py:350 squirrelbattle/tests/game_test.py:353 | #: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 | ||||||
| #: squirrelbattle/tests/translations_test.py:16 | #: squirrelbattle/tests/translations_test.py:16 | ||||||
| msgid "New game" | msgid "New game" | ||||||
| msgstr "Nouvelle partie" | msgstr "Nouvelle partie" | ||||||
| @@ -197,57 +236,57 @@ msgid "Key used to wait" | |||||||
| msgstr "Touche pour attendre" | msgstr "Touche pour attendre" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:56 | #: 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" | msgid "Texture pack" | ||||||
| msgstr "Pack de textures" | msgstr "Pack de textures" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:57 | #: squirrelbattle/tests/translations_test.py:59 | ||||||
| msgid "Language" | msgid "Language" | ||||||
| msgstr "Langue" | msgstr "Langue" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:60 | #: squirrelbattle/tests/translations_test.py:64 | ||||||
| msgid "player" |  | ||||||
| msgstr "joueur" |  | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:62 |  | ||||||
| msgid "hedgehog" | msgid "hedgehog" | ||||||
| msgstr "hérisson" | msgstr "hérisson" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:63 | #: squirrelbattle/tests/translations_test.py:65 | ||||||
| msgid "merchant" | msgid "merchant" | ||||||
| msgstr "marchand" | msgstr "marchand" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:64 | #: squirrelbattle/tests/translations_test.py:66 | ||||||
| msgid "rabbit" | msgid "rabbit" | ||||||
| msgstr "lapin" | msgstr "lapin" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:65 | #: squirrelbattle/tests/translations_test.py:67 | ||||||
| msgid "sunflower" | msgid "sunflower" | ||||||
| msgstr "tournesol" | msgstr "tournesol" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:66 | #: squirrelbattle/tests/translations_test.py:68 | ||||||
| msgid "teddy bear" | msgid "teddy bear" | ||||||
| msgstr "nounours" | msgstr "nounours" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:67 | #: squirrelbattle/tests/translations_test.py:69 | ||||||
| msgid "tiger" | msgid "tiger" | ||||||
| msgstr "tigre" | msgstr "tigre" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:69 | #: squirrelbattle/tests/translations_test.py:71 | ||||||
| msgid "body snatch potion" | msgid "body snatch potion" | ||||||
| msgstr "potion d'arrachage de corps" | msgstr "potion d'arrachage de corps" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:70 | #: squirrelbattle/tests/translations_test.py:72 | ||||||
| msgid "bomb" | msgid "bomb" | ||||||
| msgstr "bombe" | msgstr "bombe" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:71 | #: squirrelbattle/tests/translations_test.py:73 | ||||||
| msgid "explosion" | msgid "explosion" | ||||||
| msgstr "" | msgstr "explosion" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:72 | #: squirrelbattle/tests/translations_test.py:74 | ||||||
| msgid "heart" | msgid "heart" | ||||||
| msgstr "cœur" | msgstr "cœur" | ||||||
|  |  | ||||||
| #: squirrelbattle/tests/translations_test.py:73 | #: squirrelbattle/tests/translations_test.py:75 | ||||||
| msgid "sword" | msgid "sword" | ||||||
| msgstr "épée" | msgstr "épée" | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ class Settings: | |||||||
|         self.KEY_DROP = ['r', 'Key used to drop an item in the inventory'] |         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_CHAT = ['t', 'Key used to talk to a friendly entity'] | ||||||
|         self.KEY_WAIT = ['w', 'Key used to wait'] |         self.KEY_WAIT = ['w', 'Key used to wait'] | ||||||
|  |         self.KEY_LADDER = ['<', 'Key used to use ladders'] | ||||||
|         self.TEXTURE_PACK = ['ascii', 'Texture pack'] |         self.TEXTURE_PACK = ['ascii', 'Texture pack'] | ||||||
|         self.LOCALE = [locale.getlocale()[0][:2], 'Language'] |         self.LOCALE = [locale.getlocale()[0][:2], 'Language'] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -78,6 +78,7 @@ class TestEntities(unittest.TestCase): | |||||||
| {self.player.name.capitalize()} takes {entity.strength} damage.") | {self.player.name.capitalize()} takes {entity.strength} damage.") | ||||||
|  |  | ||||||
|         # Fight the rabbit |         # Fight the rabbit | ||||||
|  |         self.player.critical = 0 | ||||||
|         old_health = entity.health |         old_health = entity.health | ||||||
|         self.player.move_down() |         self.player.move_down() | ||||||
|         self.assertEqual(entity.health, old_health - self.player.strength) |         self.assertEqual(entity.health, old_health - self.player.strength) | ||||||
| @@ -177,7 +178,7 @@ class TestEntities(unittest.TestCase): | |||||||
|         self.assertEqual(item.y, 42) |         self.assertEqual(item.y, 42) | ||||||
|         self.assertEqual(item.x, 42) |         self.assertEqual(item.x, 42) | ||||||
|         # Wait for the explosion |         # Wait for the explosion | ||||||
|         for ignored in range(5): |         for _ignored in range(5): | ||||||
|             item.act(self.map) |             item.act(self.map) | ||||||
|         self.assertTrue(hedgehog.dead) |         self.assertTrue(hedgehog.dead) | ||||||
|         self.assertTrue(teddy_bear.dead) |         self.assertTrue(teddy_bear.dead) | ||||||
|   | |||||||
| @@ -2,13 +2,16 @@ | |||||||
| # SPDX-License-Identifier: GPL-3.0-or-later | # SPDX-License-Identifier: GPL-3.0-or-later | ||||||
|  |  | ||||||
| import os | import os | ||||||
|  | import random | ||||||
| import unittest | import unittest | ||||||
|  |  | ||||||
| from ..bootstrap import Bootstrap | from ..bootstrap import Bootstrap | ||||||
| from ..display.display import Display | from ..display.display import Display | ||||||
| from ..display.display_manager import DisplayManager | from ..display.display_manager import DisplayManager | ||||||
| from ..entities.friendly import Merchant, Sunflower | 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 ..entities.player import Player | ||||||
| from ..enums import DisplayActions | from ..enums import DisplayActions | ||||||
| from ..game import Game, KeyValues, GameMode | from ..game import Game, KeyValues, GameMode | ||||||
| @@ -46,11 +49,6 @@ class TestGame(unittest.TestCase): | |||||||
|         bomb.hold(self.game.player) |         bomb.hold(self.game.player) | ||||||
|         sword.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 |         # Ensure that merchants can be saved | ||||||
|         merchant = Merchant() |         merchant = Merchant() | ||||||
|         merchant.move(3, 6) |         merchant.move(3, 6) | ||||||
| @@ -153,6 +151,9 @@ class TestGame(unittest.TestCase): | |||||||
|         self.assertEqual(KeyValues.translate_key( |         self.assertEqual(KeyValues.translate_key( | ||||||
|             self.game.settings.KEY_WAIT, self.game.settings), |             self.game.settings.KEY_WAIT, self.game.settings), | ||||||
|             KeyValues.WAIT) |             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), |         self.assertEqual(KeyValues.translate_key(' ', self.game.settings), | ||||||
|                          KeyValues.SPACE) |                          KeyValues.SPACE) | ||||||
|         self.assertEqual(KeyValues.translate_key('plop', self.game.settings), |         self.assertEqual(KeyValues.translate_key('plop', self.game.settings), | ||||||
| @@ -343,7 +344,7 @@ class TestGame(unittest.TestCase): | |||||||
|         self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a') |         self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a') | ||||||
|  |  | ||||||
|         # Navigate to "texture pack" |         # Navigate to "texture pack" | ||||||
|         for ignored in range(11): |         for ignored in range(12): | ||||||
|             self.game.handle_key_pressed(KeyValues.DOWN) |             self.game.handle_key_pressed(KeyValues.DOWN) | ||||||
|  |  | ||||||
|         # Change texture pack |         # Change texture pack | ||||||
| @@ -615,6 +616,131 @@ class TestGame(unittest.TestCase): | |||||||
|         self.game.handle_key_pressed(KeyValues.SPACE) |         self.game.handle_key_pressed(KeyValues.SPACE) | ||||||
|         self.assertEqual(self.game.state, GameMode.PLAY) |         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) | ||||||
|  |  | ||||||
|  |     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) | ||||||
|  |  | ||||||
|     def test_credits(self) -> None: |     def test_credits(self) -> None: | ||||||
|         """ |         """ | ||||||
|         Load credits menu. |         Load credits menu. | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ | |||||||
| import unittest | import unittest | ||||||
|  |  | ||||||
| from squirrelbattle.display.texturepack import TexturePack | from squirrelbattle.display.texturepack import TexturePack | ||||||
| from squirrelbattle.interfaces import Map, Tile | from squirrelbattle.interfaces import Map, Tile, Slope | ||||||
| from squirrelbattle.resources import ResourceManager | from squirrelbattle.resources import ResourceManager | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -37,3 +37,21 @@ class TestInterfaces(unittest.TestCase): | |||||||
|         self.assertFalse(Tile.WALL.can_walk()) |         self.assertFalse(Tile.WALL.can_walk()) | ||||||
|         self.assertFalse(Tile.EMPTY.can_walk()) |         self.assertFalse(Tile.EMPTY.can_walk()) | ||||||
|         self.assertRaises(ValueError, Tile.from_ascii_char, 'unknown') |         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) | ||||||
|   | |||||||
| @@ -53,6 +53,8 @@ class TestTranslations(unittest.TestCase): | |||||||
|         self.assertEqual(_("Key used to talk to a friendly entity"), |         self.assertEqual(_("Key used to talk to a friendly entity"), | ||||||
|                          "Touche pour parler à une entité pacifique") |                          "Touche pour parler à une entité pacifique") | ||||||
|         self.assertEqual(_("Key used to wait"), "Touche pour attendre") |         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(_("Texture pack"), "Pack de textures") | ||||||
|         self.assertEqual(_("Language"), "Langue") |         self.assertEqual(_("Language"), "Langue") | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user