Merge branch 'master' into 'equipment'
# Conflicts: # squirrelbattle/display/statsdisplay.py # squirrelbattle/entities/items.py # squirrelbattle/entities/player.py # squirrelbattle/interfaces.py # squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po # squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po # squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po # squirrelbattle/tests/game_test.py
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from functools import reduce
|
||||
from queue import PriorityQueue
|
||||
from random import randint
|
||||
<<<<<<< squirrelbattle/entities/player.py
|
||||
from typing import Dict, Optional, Tuple
|
||||
=======
|
||||
>>>>>>> squirrelbattle/entities/player.py
|
||||
|
||||
from .items import Item
|
||||
from ..interfaces import FightingEntity, InventoryHolder
|
||||
@@ -12,7 +13,7 @@ from ..interfaces import FightingEntity, InventoryHolder
|
||||
|
||||
class Player(InventoryHolder, FightingEntity):
|
||||
"""
|
||||
The class of the player
|
||||
The class of the player.
|
||||
"""
|
||||
current_xp: int = 0
|
||||
max_xp: int = 10
|
||||
@@ -31,7 +32,7 @@ class Player(InventoryHolder, FightingEntity):
|
||||
equipped_armor: Optional[Item] = None, critical: int = 5,
|
||||
equipped_secondary: Optional[Item] = None,
|
||||
equipped_helmet: Optional[Item] = None, xp_buff: float = 1,
|
||||
*args, **kwargs) -> None:
|
||||
vision: int = 5, *args, **kwargs) -> None:
|
||||
super().__init__(name=name, maxhealth=maxhealth, strength=strength,
|
||||
intelligence=intelligence, charisma=charisma,
|
||||
dexterity=dexterity, constitution=constitution,
|
||||
@@ -50,6 +51,7 @@ class Player(InventoryHolder, FightingEntity):
|
||||
if isinstance(equipped_secondary, dict) else equipped_secondary
|
||||
self.equipped_helmet = self.dict_to_item(equipped_helmet) \
|
||||
if isinstance(equipped_helmet, dict) else equipped_helmet
|
||||
self.vision = vision
|
||||
|
||||
def move(self, y: int, x: int) -> None:
|
||||
"""
|
||||
@@ -60,10 +62,11 @@ class Player(InventoryHolder, FightingEntity):
|
||||
self.map.currenty = y
|
||||
self.map.currentx = x
|
||||
self.recalculate_paths()
|
||||
self.map.compute_visibility(self.y, self.x, self.vision)
|
||||
|
||||
def level_up(self) -> None:
|
||||
"""
|
||||
Add levels to the player as much as it is possible.
|
||||
Add as many levels as possible to the player.
|
||||
"""
|
||||
while self.current_xp > self.max_xp:
|
||||
self.level += 1
|
||||
@@ -77,8 +80,8 @@ class Player(InventoryHolder, FightingEntity):
|
||||
|
||||
def add_xp(self, xp: int) -> None:
|
||||
"""
|
||||
Add some experience to the player.
|
||||
If the required amount is reached, level up.
|
||||
Adds some experience to the player.
|
||||
If the required amount is reached, the player levels up.
|
||||
"""
|
||||
self.current_xp += int(xp * self.xp_buff)
|
||||
self.level_up()
|
||||
@@ -120,56 +123,6 @@ class Player(InventoryHolder, FightingEntity):
|
||||
entity.hold(self)
|
||||
return super().check_move(y, x, move_if_possible)
|
||||
|
||||
def recalculate_paths(self, max_distance: int = 8) -> None:
|
||||
"""
|
||||
Use Dijkstra algorithm to calculate best paths for monsters to go to
|
||||
the player. Actually, the paths are computed for each tile adjacent to
|
||||
the player then for each step the monsters use the best path avaliable.
|
||||
"""
|
||||
distances = []
|
||||
predecessors = []
|
||||
# four Dijkstras, one for each adjacent tile
|
||||
for dir_y, dir_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
|
||||
queue = PriorityQueue()
|
||||
new_y, new_x = self.y + dir_y, self.x + dir_x
|
||||
if not 0 <= new_y < self.map.height or \
|
||||
not 0 <= new_x < self.map.width or \
|
||||
not self.map.tiles[new_y][new_x].can_walk():
|
||||
continue
|
||||
queue.put(((1, 0), (new_y, new_x)))
|
||||
visited = [(self.y, self.x)]
|
||||
distances.append({(self.y, self.x): (0, 0), (new_y, new_x): (1, 0)})
|
||||
predecessors.append({(new_y, new_x): (self.y, self.x)})
|
||||
while not queue.empty():
|
||||
dist, (y, x) = queue.get()
|
||||
if dist[0] >= max_distance or (y, x) in visited:
|
||||
continue
|
||||
visited.append((y, x))
|
||||
for diff_y, diff_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
|
||||
new_y, new_x = y + diff_y, x + diff_x
|
||||
if not 0 <= new_y < self.map.height or \
|
||||
not 0 <= new_x < self.map.width or \
|
||||
not self.map.tiles[new_y][new_x].can_walk():
|
||||
continue
|
||||
new_distance = (dist[0] + 1,
|
||||
dist[1] + (not self.map.is_free(y, x)))
|
||||
if not (new_y, new_x) in distances[-1] or \
|
||||
distances[-1][(new_y, new_x)] > new_distance:
|
||||
predecessors[-1][(new_y, new_x)] = (y, x)
|
||||
distances[-1][(new_y, new_x)] = new_distance
|
||||
queue.put((new_distance, (new_y, new_x)))
|
||||
# For each tile that is reached by at least one Dijkstra, sort the
|
||||
# different paths by distance to the player. For the technical bits :
|
||||
# The reduce function is a fold starting on the first element of the
|
||||
# iterable, and we associate the points to their distance, sort
|
||||
# along the distance, then only keep the points.
|
||||
self.paths = {}
|
||||
for y, x in reduce(set.union,
|
||||
[set(p.keys()) for p in predecessors], set()):
|
||||
self.paths[(y, x)] = [p for d, p in sorted(
|
||||
[(distances[i][(y, x)], predecessors[i][(y, x)])
|
||||
for i in range(len(distances)) if (y, x) in predecessors[i]])]
|
||||
|
||||
def save_state(self) -> dict:
|
||||
"""
|
||||
Saves the state of the entity into a dictionary
|
||||
|
Reference in New Issue
Block a user