Added chests, they are immortal and contain objects the player can take for free.

This commit is contained in:
eichhornchen 2021-01-08 23:15:48 +01:00
parent 175706b1e4
commit bdbf214d8d
9 changed files with 178 additions and 13 deletions

View File

@ -10,7 +10,8 @@ from squirrelbattle.display.mapdisplay import MapDisplay
from squirrelbattle.display.messagedisplay import MessageDisplay
from squirrelbattle.display.statsdisplay import StatsDisplay
from squirrelbattle.display.menudisplay import MainMenuDisplay, \
PlayerInventoryDisplay, StoreInventoryDisplay, SettingsMenuDisplay
PlayerInventoryDisplay, StoreInventoryDisplay, SettingsMenuDisplay, \
ChestInventoryDisplay
from squirrelbattle.display.logsdisplay import LogsDisplay
from squirrelbattle.display.texturepack import TexturePack
from typing import Any, List
@ -29,6 +30,7 @@ class DisplayManager:
self.logsdisplay = LogsDisplay(screen, pack)
self.playerinventorydisplay = PlayerInventoryDisplay(screen, pack)
self.storeinventorydisplay = StoreInventoryDisplay(screen, pack)
self.chestinventorydisplay = ChestInventoryDisplay(screen, pack)
self.mainmenudisplay = MainMenuDisplay(self.game.main_menu,
screen, pack)
self.settingsmenudisplay = SettingsMenuDisplay(screen, pack)
@ -40,7 +42,8 @@ class DisplayManager:
self.mainmenudisplay, self.settingsmenudisplay,
self.logsdisplay, self.messagedisplay,
self.playerinventorydisplay,
self.storeinventorydisplay, self.creditsdisplay]
self.storeinventorydisplay, self.creditsdisplay,
self.chestinventorydisplay]
self.update_game_components()
def handle_display_action(self, action: DisplayActions, *params) -> None:
@ -87,7 +90,8 @@ class DisplayManager:
if self.game.state == GameMode.PLAY \
or self.game.state == GameMode.INVENTORY \
or self.game.state == GameMode.STORE:
or self.game.state == GameMode.STORE\
or self.game.state == GameMode.CHEST:
# The map pad has already the good size
self.mapdisplay.refresh(0, 0, self.rows * 4 // 5,
self.mapdisplay.pack.tile_width
@ -124,6 +128,19 @@ class DisplayManager:
pack.tile_width * (2 * self.cols // (5 * pack.tile_width)))
displays.append(self.storeinventorydisplay)
displays.append(self.playerinventorydisplay)
elif self.game.state == GameMode.CHEST:
self.chestinventorydisplay.refresh(
self.rows // 10,
pack.tile_width * (self.cols // (2 * pack.tile_width)),
8 * self.rows // 10,
pack.tile_width * (2 * self.cols // (5 * pack.tile_width)))
self.playerinventorydisplay.refresh(
self.rows // 10,
pack.tile_width * (self.cols // (10 * pack.tile_width)),
8 * self.rows // 10,
pack.tile_width * (2 * self.cols // (5 * pack.tile_width)))
displays.append(self.chestinventorydisplay)
displays.append(self.playerinventorydisplay)
elif self.game.state == GameMode.MAINMENU:
self.mainmenudisplay.refresh(0, 0, self.rows, self.cols)
displays.append(self.mainmenudisplay)

View File

@ -5,7 +5,8 @@ import curses
from random import randint
from typing import List
from squirrelbattle.menus import Menu, MainMenu, SettingsMenu, StoreMenu
from squirrelbattle.menus import Menu, MainMenu, SettingsMenu, StoreMenu,\
ChestMenu
from .display import Box, Display
from ..entities.player import Player
from ..enums import KeyValues, GameMode
@ -156,13 +157,16 @@ class PlayerInventoryDisplay(MenuDisplay):
player: Player = None
selected: bool = True
store_mode: bool = False
chest_mode: bool = False
def update(self, game: Game) -> None:
self.player = game.player
self.update_menu(game.inventory_menu)
self.store_mode = game.state == GameMode.STORE
self.chest_mode = game.state == GameMode.CHEST
self.selected = game.state == GameMode.INVENTORY \
or (self.store_mode and not game.is_in_store_menu)
or (self.store_mode and not game.is_in_store_menu)\
or (self.chest_mode and not game.is_in_chest_menu)
def update_pad(self) -> None:
self.menubox.update_title(_("INVENTORY"))
@ -239,3 +243,39 @@ class StoreInventoryDisplay(MenuDisplay):
self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2))
game.is_in_store_menu = True
game.handle_key_pressed(KeyValues.ENTER)
class ChestInventoryDisplay(MenuDisplay):
"""
A class to handle the display of a merchant's inventory.
"""
menu: ChestMenu
selected: bool = False
def update(self, game: Game) -> None:
self.update_menu(game.chest_menu)
self.selected = game.is_in_chest_menu
def update_pad(self) -> None:
self.menubox.update_title(_("CHEST"))
for i, item in enumerate(self.menu.values):
rep = self.pack[item.name.upper()]
selection = f"[{rep}]" if i == self.menu.position \
and self.selected else f" {rep} "
self.addstr(self.pad, i + 1, 0, selection
+ " " + item.translated_name.capitalize())
@property
def truewidth(self) -> int:
return max(1, self.height if hasattr(self, "height") else 10)
@property
def trueheight(self) -> int:
return 2 + super().trueheight
def handle_click(self, y: int, x: int, game: Game) -> None:
"""
We can select a menu item with the mouse.
"""
self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2))
game.is_in_chest_menu = True
game.handle_key_pressed(KeyValues.ENTER)

View File

@ -22,6 +22,7 @@ class TexturePack:
BODY_SNATCH_POTION: str
BOMB: str
BOW: str
CHEST: str
CHESTPLATE: str
EAGLE: str
EMPTY: str
@ -79,6 +80,7 @@ TexturePack.ASCII_PACK = TexturePack(
BODY_SNATCH_POTION='S',
BOMB='ç',
BOW=')',
CHEST='',
CHESTPLATE='(',
EAGLE='µ',
EMPTY=' ',
@ -119,6 +121,7 @@ TexturePack.SQUIRREL_PACK = TexturePack(
BODY_SNATCH_POTION='🔀',
BOMB='💣',
BOW='🏹',
CHEST='🧰',
CHESTPLATE='🦺',
EAGLE='🦅',
EMPTY=' ',

View File

@ -38,6 +38,39 @@ class Merchant(InventoryHolder, FriendlyEntity):
"""
self.hazel += hz
class Chest(InventoryHolder, FriendlyEntity):
"""
A class of chest inanimate entities which contain objects.
"""
def __init__(self, name: str = "chest", inventory: list = None,
hazel: int = 0, *args, **kwargs):
super().__init__(name=name, *args, **kwargs)
self.hazel = hazel
self.inventory = self.translate_inventory(inventory or [])
if not self.inventory:
for i in range(3):
self.inventory.append(choice(Item.get_all_items())())
def talk_to(self, player: Player) -> str:
"""
This function is used to open the chest's inventory in a menu,
and allows the player to take objects.
"""
return _("You have opened the chest")
def take_damage(self, attacker: "Entity", amount: int) -> str:
"""
A chest is not living, it can not take damage
"""
return _("It's not really effective")
@property
def dead(self) -> bool:
"""
Chest can not die
"""
return False
class Sunflower(FriendlyEntity):
"""

View File

@ -88,13 +88,18 @@ class Item(Entity):
Chestplate, Helmet, RingCritical, RingXP,
ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff]
def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool:
def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder,\
for_free: bool = False) -> bool:
"""
Does all necessary actions when an object is to be sold.
Is overwritten by some classes that cannot exist in the player's
inventory.
"""
if buyer.hazel >= self.price:
if for_free:
self.hold(buyer)
seller.remove_from_inventory(self)
return True
elif buyer.hazel >= self.price:
self.hold(buyer)
seller.remove_from_inventory(self)
buyer.change_hazel_balance(-self.price)

View File

@ -28,6 +28,7 @@ class GameMode(Enum):
SETTINGS = auto()
INVENTORY = auto()
STORE = auto()
CHEST = auto()
CREDITS = auto()

View File

@ -37,6 +37,7 @@ class Game:
self.waiting_for_friendly_key = False
self.waiting_for_launch_key = False
self.is_in_store_menu = True
self.is_in_chest_menu = True
self.settings = Settings()
self.settings.load_settings()
self.settings.write_settings()
@ -46,6 +47,7 @@ class Game:
self.settings_menu.update_values(self.settings)
self.inventory_menu = menus.InventoryMenu()
self.store_menu = menus.StoreMenu()
self.chest_menu = menus.ChestMenu()
self.logs = Logs()
self.message = None
@ -131,6 +133,8 @@ class Game:
self.settings_menu.handle_key_pressed(key, raw_key, self)
elif self.state == GameMode.STORE:
self.handle_key_pressed_store(key)
elif self.state == GameMode.CHEST:
self.handle_key_pressed_chest(key)
elif self.state == GameMode.CREDITS:
self.state = GameMode.MAINMENU
self.display_actions(DisplayActions.REFRESH)
@ -253,6 +257,11 @@ class Game:
self.is_in_store_menu = True
self.store_menu.update_merchant(entity)
self.display_actions(DisplayActions.UPDATE)
elif entity.is_chest():
self.state = GameMode.CHEST
self.is_in_chest_menu = True
self.chest_menu.update_chest(entity)
self.display_actions(DisplayActions.UPDATE)
def handle_launch(self, key: KeyValues) -> None:
"""
@ -332,6 +341,36 @@ class Game:
self.display_actions(DisplayActions.UPDATE)
# Ensure that the cursor has a good position
menu.position = min(menu.position, len(menu.values) - 1)
def handle_key_pressed_chest(self, key: KeyValues) -> None:
"""
In a chest menu, we can take or put items or close the menu.
"""
menu = self.chest_menu if self.is_in_chest_menu else self.inventory_menu
if key == KeyValues.SPACE or key == KeyValues.INVENTORY:
self.state = GameMode.PLAY
elif key == KeyValues.UP:
menu.go_up()
elif key == KeyValues.DOWN:
menu.go_down()
elif key == KeyValues.LEFT:
self.is_in_chest_menu = False
self.display_actions(DisplayActions.UPDATE)
elif key == KeyValues.RIGHT:
self.is_in_chest_menu = True
self.display_actions(DisplayActions.UPDATE)
if menu.values and not self.player.dead:
if key == KeyValues.ENTER:
item = menu.validate()
owner = self.chest_menu.chest if self.is_in_chest_menu \
else self.player
buyer = self.player if self.is_in_chest_menu \
else self.chest_menu.chest
flag = item.be_sold(buyer, owner, for_free = True)
self.display_actions(DisplayActions.UPDATE)
# Ensure that the cursor has a good position
menu.position = min(menu.position, len(menu.values) - 1)
def handle_key_pressed_main_menu(self, key: KeyValues) -> None:
"""

View File

@ -589,6 +589,13 @@ class Entity:
from squirrelbattle.entities.friendly import Merchant
return isinstance(self, Merchant)
def is_chest(self) -> bool:
"""
Is this entity a chest?
"""
from squirrelbattle.entities.friendly import Chest
return isinstance(self, Chest)
@property
def translated_name(self) -> str:
"""
@ -605,9 +612,9 @@ class Entity:
from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
Rabbit, TeddyBear, GiantSeaEagle
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
Trumpet
Trumpet, Chest
return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,
Sunflower, Tiger, Merchant, GiantSeaEagle, Trumpet]
Sunflower, Tiger, Merchant, GiantSeaEagle, Trumpet, Chest]
@staticmethod
def get_weights() -> list:
@ -615,8 +622,7 @@ class Entity:
Returns a weigth list associated to the above function, to
be used to spawn random entities with a certain probability.
"""
return [3, 5, 6, 5, 5, 5,
5, 4, 4, 1, 2]
return [3, 5, 6, 5, 5, 5, 5, 4, 3, 1, 2, 4]
@staticmethod
def get_all_entity_classes_in_a_dict() -> dict:
@ -627,7 +633,7 @@ class Entity:
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
TeddyBear, GiantSeaEagle
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
Trumpet
Trumpet, Chest
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \
Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \
ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff
@ -655,6 +661,7 @@ class Entity:
"ScrollofWeakening": ScrollofWeakening,
"Bow": Bow,
"FireBallStaff": FireBallStaff,
"Chest": Chest,
}
def save_state(self) -> dict:

View File

@ -6,7 +6,7 @@ from typing import Any, Optional
from .display.texturepack import TexturePack
from .entities.player import Player
from .entities.friendly import Merchant
from .entities.friendly import Merchant, Chest
from .enums import GameMode, KeyValues, DisplayActions
from .settings import Settings
from .translations import gettext as _, Translator
@ -158,3 +158,23 @@ class StoreMenu(Menu):
Returns the values of the menu.
"""
return self.merchant.inventory if self.merchant else []
class ChestMenu(Menu):
"""
A special instance of a menu : the menu for the inventory of a chest.
"""
chest: Chest = None
def update_chest(self, chest: Chest) -> None:
"""
Updates the player.
"""
self.chest = chest
@property
def values(self) -> list:
"""
Returns the values of the menu.
"""
return self.chest.inventory if self.chest else []