diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index ccc3561..8dba478 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -176,7 +176,8 @@ class PlayerInventoryDisplay(MenuDisplay): 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() + + " " + ("[E]" if item.equipped else "") + + item.translated_name.capitalize() + (f" ({item.description})" if item.description else "") + (": " + str(item.price) + " Hazels" if self.store_mode else "")) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index f4587f3..a80f675 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -12,16 +12,19 @@ class Item(Entity): """ A class for items. """ - held: bool held_by: Optional[InventoryHolder] price: int - def __init__(self, held: bool = False, + def __init__(self, equipped: bool = False, held_by: Optional[InventoryHolder] = None, + hold_slot: str = "equipped_secondary", price: int = 2, *args, **kwargs): super().__init__(*args, **kwargs) - self.held = held self.held_by = held_by + self.equipped = equipped + self.hold_slot = hold_slot + if equipped: + self.equip() self.price = price @property @@ -35,11 +38,11 @@ class Item(Entity): """ The item is dropped from the inventory onto the floor. """ - if self.held: + if self.held_by is not None: + self.unequip() self.held_by.remove_from_inventory(self) self.held_by.map.add_entity(self) self.move(self.held_by.y, self.held_by.x) - self.held = False self.held_by = None def use(self) -> None: @@ -52,28 +55,41 @@ class Item(Entity): Indicates what should be done when the item is thrown. """ + def on_equip(self) -> None: + """ + Indicates a special behaviour when equipping + """ + + def on_unequip(self) -> None: + """ + Indicates a special behaviour when unequipping + """ + def equip(self) -> None: """ Indicates what should be done when the item is equipped. """ # 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 + if not self.equipped: + if getattr(self.held_by, self.hold_slot): + getattr(self.held_by, self.hold_slot).unequip() + self.equipped = True + setattr(self.held_by, self.hold_slot, self) + self.on_equip() def unequip(self) -> None: """ Indicates what should be done when the item is unequipped. """ - self.held_by.remove_from_inventory(self) - self.held_by.add_to_inventory(self) + if self.equipped: + setattr(self.held_by, self.hold_slot, None) + self.equipped = False + self.on_unequip() def hold(self, holder: InventoryHolder) -> None: """ The item is taken from the floor and put into the inventory. """ - self.held = True self.held_by = holder self.held_by.map.remove_entity(self) holder.add_to_inventory(self) @@ -83,7 +99,7 @@ class Item(Entity): Saves the state of the item into a dictionary. """ d = super().save_state() - d["held"] = self.held + d["equipped"] = self.equipped return d @staticmethod @@ -103,10 +119,12 @@ class Item(Entity): inventory. """ if for_free: + self.unequip() if self.equipped else None self.hold(buyer) seller.remove_from_inventory(self) return True elif buyer.hazel >= self.price: + self.unequip() if self.equipped else None self.hold(buyer) seller.remove_from_inventory(self) buyer.change_hazel_balance(-self.price) @@ -169,7 +187,7 @@ class Bomb(Item): """ When the bomb is used, it is thrown and then it explodes. """ - if self.held: + if self.held_by is not None: self.owner = self.held_by super().drop() self.exploding = True @@ -236,7 +254,7 @@ class Weapon(Item): damage: int def __init__(self, damage: int = 3, *args, **kwargs): - super().__init__(*args, **kwargs) + super().__init__(hold_slot="equipped_main", *args, **kwargs) self.damage = damage @property @@ -251,20 +269,17 @@ class Weapon(Item): d["damage"] = self.damage return d - def equip(self) -> None: + def on_equip(self) -> None: """ When a weapon is equipped, the player gains strength. """ - self.held_by.remove_from_inventory(self) - self.held_by.equipped_main = self self.held_by.strength += self.damage - def unequip(self) -> None: + def on_unequip(self) -> None: """ Remove the strength earned by the weapon. :return: """ - super().unequip() self.held_by.strength -= self.damage @@ -301,12 +316,10 @@ class Armor(Item): return f"CON+{self.constitution}" if self.constitution \ else super().description - def equip(self) -> None: - super().equip() + def on_equip(self) -> None: self.held_by.constitution += self.constitution - def unequip(self) -> None: - super().unequip() + def on_unequip(self) -> None: self.held_by.constitution -= self.constitution def save_state(self) -> dict: @@ -332,13 +345,7 @@ class Helmet(Armor): def __init__(self, name: str = "helmet", constitution: int = 2, price: int = 18, *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 + hold_slot="equipped_helmet", *args, **kwargs) class Chestplate(Armor): @@ -348,13 +355,7 @@ class Chestplate(Armor): def __init__(self, name: str = "chestplate", constitution: int = 4, price: int = 30, *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 + hold_slot="equipped_armor", *args, **kwargs) class BodySnatchPotion(Item): @@ -426,8 +427,7 @@ class Ring(Item): ("CRI", self.critical), ("XP", self.experience)] return ", ".join(f"{key}+{value}" for key, value in fields if value) - def equip(self) -> None: - super().equip() + def on_equip(self) -> None: self.held_by.maxhealth += self.maxhealth self.held_by.strength += self.strength self.held_by.intelligence += self.intelligence @@ -437,8 +437,7 @@ class Ring(Item): self.held_by.critical += self.critical self.held_by.xp_buff += self.experience - def unequip(self) -> None: - super().unequip() + def on_unequip(self) -> None: self.held_by.maxhealth -= self.maxhealth self.held_by.strength -= self.strength self.held_by.intelligence -= self.intelligence @@ -557,13 +556,6 @@ class LongRangeWeapon(Weapon): self.held_by.map.logs.add_message(line) return (to_kill.y, to_kill.x) if to_kill else None - def equip(self) -> None: - """ - Equip the weapon. - """ - self.held_by.remove_from_inventory(self) - self.held_by.equipped_main = self - @property def stat(self) -> str: """ diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index a241fef..9613618 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -120,21 +120,6 @@ class Player(InventoryHolder, FightingEntity): self.current_xp += int(xp * self.xp_buff) self.level_up() - def remove_from_inventory(self, obj: Item) -> None: - """ - Remove the given item from the inventory, even if the item is equipped. - """ - if obj == self.equipped_main: - self.equipped_main = None - elif obj == self.equipped_armor: - self.equipped_armor = None - elif obj == self.equipped_secondary: - self.equipped_secondary = None - elif obj == self.equipped_helmet: - self.equipped_helmet = None - else: - return super().remove_from_inventory(obj) - # noinspection PyTypeChecker,PyUnresolvedReferences def check_move(self, y: int, x: int, move_if_possible: bool = False) \ -> bool: diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 44c1f48..61ea75c 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -309,7 +309,8 @@ class Game: if key == KeyValues.USE: self.inventory_menu.validate().use() elif key == KeyValues.EQUIP: - self.inventory_menu.validate().equip() + item = self.inventory_menu.validate() + item.unequip() if item.equipped else item.equip() elif key == KeyValues.DROP: self.inventory_menu.validate().drop() diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 54a2d17..8c0a80a 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -839,6 +839,7 @@ class InventoryHolder(Entity): for i in range(len(inventory)): if isinstance(inventory[i], dict): inventory[i] = self.dict_to_item(inventory[i]) + inventory[i].held_by = self return inventory def dict_to_item(self, item_dict: dict) -> Entity: diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index a0e2548..83a74dc 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -155,9 +155,9 @@ class TestEntities(unittest.TestCase): """ item = Item() self.map.add_entity(item) - self.assertFalse(item.held) + self.assertIsNone(item.held_by) item.hold(self.player) - self.assertTrue(item.held) + self.assertEqual(item.held_by, self.player) item.drop() self.assertEqual(item.y, 1) self.assertEqual(item.x, 6) @@ -165,7 +165,6 @@ class TestEntities(unittest.TestCase): # Pick up item self.player.move_left() self.player.move_right() - self.assertTrue(item.held) self.assertEqual(item.held_by, self.player) self.assertIn(item, self.player.inventory) self.assertNotIn(item, self.map.entities) @@ -208,7 +207,7 @@ class TestEntities(unittest.TestCase): # The player can't hold the explosion explosion.hold(self.player) self.assertNotIn(explosion, self.player.inventory) - self.assertFalse(explosion.held) + self.assertIsNone(explosion.held_by) # The explosion disappears after one tick explosion.act(self.map) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 895af12..032ff64 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -49,6 +49,7 @@ class TestGame(unittest.TestCase): # Add items in the inventory to check that it is well loaded bomb.hold(self.game.player) sword.hold(self.game.player) + sword.equip() # Ensure that merchants can be saved merchant = Merchant() @@ -491,10 +492,8 @@ class TestGame(unittest.TestCase): # Drop an item bomb = self.game.player.inventory[-1] self.assertEqual(self.game.inventory_menu.validate(), bomb) - self.assertTrue(bomb.held) self.assertEqual(bomb.held_by, self.game.player) self.game.handle_key_pressed(KeyValues.DROP) - self.assertFalse(bomb.held) self.assertIsNone(bomb.held_by) self.assertIsNone(bomb.owner) self.assertFalse(bomb.exploding) @@ -504,10 +503,8 @@ class TestGame(unittest.TestCase): # Use the bomb bomb = self.game.player.inventory[-1] self.assertEqual(self.game.inventory_menu.validate(), bomb) - self.assertTrue(bomb.held) self.assertEqual(bomb.held_by, self.game.player) self.game.handle_key_pressed(KeyValues.USE) - self.assertFalse(bomb.held) self.assertIsNone(bomb.held_by) self.assertEqual(bomb.owner, self.game.player) self.assertTrue(bomb.exploding) @@ -660,42 +657,37 @@ class TestGame(unittest.TestCase): 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) + shield.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) + helmet.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) + chestplate.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) + bomb.equip() self.assertEqual(self.game.player.equipped_secondary, bomb) - self.assertIn(shield, self.game.player.inventory) + self.assertFalse(shield.equipped) 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) + shield.equip() self.assertEqual(self.game.player.equipped_secondary, shield) - self.assertFalse(self.game.player.inventory) # Reequip, which is useless but covers code sword.equip() @@ -717,6 +709,7 @@ class TestGame(unittest.TestCase): self.assertIn(shield, self.game.player.inventory) self.assertIn(helmet, self.game.player.inventory) self.assertIn(chestplate, self.game.player.inventory) + self.game.display_actions(DisplayActions.REFRESH) # Test rings self.game.player.inventory.clear()