From 2311059cbe06e307eca55bd560f2b71c54c66b69 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:06:39 +0100 Subject: [PATCH 01/14] init Armour class --- src/fantasy_forge/armour.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/fantasy_forge/armour.py diff --git a/src/fantasy_forge/armour.py b/src/fantasy_forge/armour.py new file mode 100644 index 0000000..2d29b42 --- /dev/null +++ b/src/fantasy_forge/armour.py @@ -0,0 +1,4 @@ +from fantasy_forge.item import Item + +class Armour(Item): + pass \ No newline at end of file From c8cde7f66f81e38cf2187594154bda4d71996f51 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:20:44 +0100 Subject: [PATCH 02/14] implements armour types --- src/fantasy_forge/armour.py | 44 ++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/fantasy_forge/armour.py b/src/fantasy_forge/armour.py index 2d29b42..822136c 100644 --- a/src/fantasy_forge/armour.py +++ b/src/fantasy_forge/armour.py @@ -1,4 +1,46 @@ +from __future__ import annotations + +from typing import Self + from fantasy_forge.item import Item +from fantasy_forge.world import World + +# Armour types: +# 'head': for caps, hats, helmets +# 'torso': for shirts, hoodies +# 'legs': for short, trousers, cargos +# 'feet': for shoes +ARMOUR_TYPES = ("head", "torso", "legs", "feet") + class Armour(Item): - pass \ No newline at end of file + armour_type: str + defense: int + + __important_attributes__ = ("name", "armour_type", "defense") + + def __init__(self, world, config_dict): + a_type: str = config_dict.pop("armour_type") + assert a_type in ARMOUR_TYPES + self.armour_type = a_type + self.defense = config_dict.pop("defense") + super().__init__(world, config_dict) + + def to_dict(self: Self) -> dict: + armour_dict: dict = super().to_dict() + armour_dict["armour_type"] = self.armour_type + armour_dict["defense"] = self.defense + return armour_dict + + @staticmethod + def from_dict(world: World, armour_dict: dict) -> Armour: + armour_type: str = armour_dict.get("armour_type", "torso") + defense: int = armour_dict.get("defense", 0) + armour: Armour = Armour( + world, + armour_dict["name"], + armour_dict.get("description", ""), + armour_type, + defense, + ) + return armour From 8da33c49d79aa43ad0c4a3f2b7630ab73a4d69d0 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:29:53 +0100 Subject: [PATCH 03/14] adds armour slots to Player --- src/fantasy_forge/player.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index d9d3a07..b68de3c 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -1,6 +1,7 @@ from typing import Self from fantasy_forge.area import Area +from fantasy_forge.armour import Armour, ARMOUR_TYPES from fantasy_forge.character import Character from fantasy_forge.enemy import BASE_DAMAGE from fantasy_forge.entity import Entity @@ -29,6 +30,7 @@ class Player(Character): area: Area # the area we are currently in seen_entities: dict[str, Entity] + armour_slots: dict[str, Armour] def __init__(self: Self, world: World, name: str, health: int = BASE_PLAYER_HEALTH): super().__init__( @@ -46,6 +48,10 @@ def __init__(self: Self, world: World, name: str, health: int = BASE_PLAYER_HEAL self.area.contents[self.name] = self self.seen_entities = {} + # define armour slots + for armour_type in ARMOUR_TYPES: + self.armour_slots[armour_type] = None + def look_around(self): """Player looks around the current area.""" # clear seen items, but re-add inventory items From ce832c9b8ab21c3d7b8d54ef14b57dcc199d59f3 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:30:55 +0100 Subject: [PATCH 04/14] renames method --- src/fantasy_forge/player.py | 2 +- src/fantasy_forge/shell.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index b68de3c..84c81b9 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -128,7 +128,7 @@ def pick_up(self, item_name: str): else: print(self.world.l10n.format_value("pick-up-failed-message")) - def equip(self, weapon_name: str): + def equip_weapon(self, weapon_name: str): """Puts an item in the main hand.""" weapon = self.seen_entities.get(weapon_name) if weapon is None: diff --git a/src/fantasy_forge/shell.py b/src/fantasy_forge/shell.py index bea99e7..1794b25 100644 --- a/src/fantasy_forge/shell.py +++ b/src/fantasy_forge/shell.py @@ -250,7 +250,7 @@ def complete_attack( def do_equip(self, arg: str) -> None: """Take an item out of the inventory and place it firmly in your hand.""" - self.player.equip(arg) + self.player.equip_weapon(arg) def complete_equip( self, From acaaa88c579d1cd1817024f627814ea2fbcf0adb Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:44:40 +0100 Subject: [PATCH 05/14] creates type specific equip methods --- src/fantasy_forge/player.py | 54 ++++++++++++++++++++++++++----------- src/fantasy_forge/shell.py | 4 +-- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index 84c81b9..94dd5e1 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -1,7 +1,7 @@ from typing import Self from fantasy_forge.area import Area -from fantasy_forge.armour import Armour, ARMOUR_TYPES +from fantasy_forge.armour import ARMOUR_TYPES, Armour from fantasy_forge.character import Character from fantasy_forge.enemy import BASE_DAMAGE from fantasy_forge.entity import Entity @@ -128,40 +128,57 @@ def pick_up(self, item_name: str): else: print(self.world.l10n.format_value("pick-up-failed-message")) - def equip_weapon(self, weapon_name: str): - """Puts an item in the main hand.""" - weapon = self.seen_entities.get(weapon_name) - if weapon is None: + def equip(self, item_name: str): + """Equips item.""" + item = self.seen_entities.get(item_name) + # check if item was already seen + if item is None: print( self.world.l10n.format_value( "entity-does-not-exist", - {"entity": weapon_name}, + {"entity": item_name}, ) ) return - if not isinstance(weapon, Weapon): - print(self.world.l10n.format_value("cannot-equip", {"weapon": weapon_name})) - return + # item must be in the area or the inventory to be equiped if ( - weapon_name not in self.area.contents - and weapon_name not in self.inventory.contents + item_name not in self.area.contents + and item_name not in self.inventory.contents ): print(self.world.l10n.format_value("item-vanished")) - self.seen_entities.pop(weapon_name) + self.seen_entities.pop(item_name) return - if weapon not in self.inventory.contents.values(): + + # by equipping the item is implicitly picked up + if item not in self.inventory.contents.values(): # if it's not already in the inventory, place it there - self.inventory.add(weapon) + self.inventory.add(item) # picking up items keeps them in seen_entities - self.area.contents.pop(weapon_name) + self.area.contents.pop(item_name) print( self.world.l10n.format_value( "pick-up-item-message", { - "item": weapon.name, + "item": item.name, + }, + ) + ) + if isinstance(item, Weapon): + self.equip_weapon(item) + elif isinstance(item, Armour): + self.equip_armour(item) + else: + print( + self.world.l10n.format_value( + "cannot-equip", + { + "weapon": item.name, }, ) ) + + def equip_weapon(self, weapon: Weapon): + """Equips weapon.""" self.main_hand = weapon print( self.world.l10n.format_value( @@ -173,6 +190,11 @@ def equip_weapon(self, weapon_name: str): ) ) + def equip_armour(self, armour: Armour) -> None: + """Equips armour piece.""" + # TODO + pass + # TODO: Refactor def attack(self, target_name: str) -> None: """Player attacks character using their main hand.""" diff --git a/src/fantasy_forge/shell.py b/src/fantasy_forge/shell.py index 1794b25..a387549 100644 --- a/src/fantasy_forge/shell.py +++ b/src/fantasy_forge/shell.py @@ -249,8 +249,8 @@ def complete_attack( return completions def do_equip(self, arg: str) -> None: - """Take an item out of the inventory and place it firmly in your hand.""" - self.player.equip_weapon(arg) + """Take an item out of the inventory and put it on.""" + self.player.equip(arg) def complete_equip( self, From 944fcfde8a9c4d402959e9372d91ac03b6fcd937 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:52:01 +0100 Subject: [PATCH 06/14] implements equip armour --- src/fantasy_forge/player.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index 94dd5e1..7cb370f 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -192,8 +192,21 @@ def equip_weapon(self, weapon: Weapon): def equip_armour(self, armour: Armour) -> None: """Equips armour piece.""" - # TODO - pass + current_armour: Armour = self.armour_slots.pop(armour.armour_type) + # check if armour slot is already filled + if current_armour is not None: + self.inventory.add(current_armour) + + self.armour_slots[armour.armour_type] = armour + print( + self.world.l10n.format_value( + "equip-item-message", + { + "player": self.name, + "item": armour.name, + }, + ) + ) # TODO: Refactor def attack(self, target_name: str) -> None: From fe325839182c1a4209cac7d1d88088ca029fdd45 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:54:12 +0100 Subject: [PATCH 07/14] fixes init of armour slots --- src/fantasy_forge/player.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index 7cb370f..1bf349e 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -49,6 +49,7 @@ def __init__(self: Self, world: World, name: str, health: int = BASE_PLAYER_HEAL self.seen_entities = {} # define armour slots + self.armour_slots: dict[str, Armour] = dict() for armour_type in ARMOUR_TYPES: self.armour_slots[armour_type] = None From c5db4358214ea59903f4f223aea3eabf8de5e54c Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 11:58:03 +0100 Subject: [PATCH 08/14] adss case for armour loading --- src/fantasy_forge/area.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fantasy_forge/area.py b/src/fantasy_forge/area.py index cdd1fb2..2e3b3e3 100644 --- a/src/fantasy_forge/area.py +++ b/src/fantasy_forge/area.py @@ -61,6 +61,10 @@ def from_dict(world: World, area_dict: dict) -> Area: from fantasy_forge.weapon import Weapon contents_list.append(Weapon(world, entity_dict)) + case "armour": + from fantasy_forge.armour import Armour + contents_list.append(Armour(world, entity_dict)) + case default: contents_list.append(Entity(world, entity_dict)) contents = {entity.name: entity for entity in contents_list} From a58a76dbef54729edb9b88da3451185107bd5aa0 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 12:03:15 +0100 Subject: [PATCH 09/14] adds unequip message --- data/l10n/en/main.ftl | 1 + src/fantasy_forge/player.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/data/l10n/en/main.ftl b/data/l10n/en/main.ftl index ab4f8c9..c4ae1fe 100644 --- a/data/l10n/en/main.ftl +++ b/data/l10n/en/main.ftl @@ -14,6 +14,7 @@ item-is-in-inventory = You already picked up this item. pick-up-failed-message = You can't pick up this item. go-failed-message = You can't go there. equip-item-message = You equipped { INTER($item) }. +unequip-item-message = You took of { INTER(§item)} and took it in your inventory. cannot-equip = You can't equip { INTER($weapon) }. attack-character-message = { INTER($source) } attacks { INTER($target) }{ EXISTS($weapon) -> [true] { "" } with { $weapon } diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index 1bf349e..832fcea 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -196,7 +196,15 @@ def equip_armour(self, armour: Armour) -> None: current_armour: Armour = self.armour_slots.pop(armour.armour_type) # check if armour slot is already filled if current_armour is not None: - self.inventory.add(current_armour) + print( + self.world.l10n.format_value( + "unequip-item-message", + { + "player": self.name, + "item": armour.name, + }, + ) + ) self.armour_slots[armour.armour_type] = armour print( From bfbbc4c6d1feb922c75b52b34a39158632270ea8 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 12:07:55 +0100 Subject: [PATCH 10/14] fixes unequip message --- data/l10n/en/main.ftl | 2 +- src/fantasy_forge/player.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/l10n/en/main.ftl b/data/l10n/en/main.ftl index c4ae1fe..d4d6efe 100644 --- a/data/l10n/en/main.ftl +++ b/data/l10n/en/main.ftl @@ -14,7 +14,7 @@ item-is-in-inventory = You already picked up this item. pick-up-failed-message = You can't pick up this item. go-failed-message = You can't go there. equip-item-message = You equipped { INTER($item) }. -unequip-item-message = You took of { INTER(§item)} and took it in your inventory. +unequip-item-message = You took of { INTER($item)} and took it in your inventory. cannot-equip = You can't equip { INTER($weapon) }. attack-character-message = { INTER($source) } attacks { INTER($target) }{ EXISTS($weapon) -> [true] { "" } with { $weapon } diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index 832fcea..338fba2 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -197,14 +197,14 @@ def equip_armour(self, armour: Armour) -> None: # check if armour slot is already filled if current_armour is not None: print( - self.world.l10n.format_value( - "unequip-item-message", - { - "player": self.name, - "item": armour.name, - }, - ) + self.world.l10n.format_value( + "unequip-item-message", + { + "player": self.name, + "item": armour.name, + }, ) + ) self.armour_slots[armour.armour_type] = armour print( From 539e8e8eb4d6b052218294556120ed7b04ec9d09 Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 12:08:04 +0100 Subject: [PATCH 11/14] adds armour items --- data/worlds/chaosdorf/areas/cave.toml | 6 ++++-- data/worlds/chaosdorf/areas/hackcenter.toml | 6 ++++-- data/worlds/chaosdorf/areas/kitchen.toml | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/data/worlds/chaosdorf/areas/cave.toml b/data/worlds/chaosdorf/areas/cave.toml index ca764a1..2fb6791 100644 --- a/data/worlds/chaosdorf/areas/cave.toml +++ b/data/worlds/chaosdorf/areas/cave.toml @@ -13,9 +13,11 @@ name = "tv remote" description = "the batteries seem to have died. Can you point it at the door?" [[contents]] -kind = "item" -description = "an old-school fedora out of fine black fabric" +kind = "armour" name = "black hat" +description = "an old-school fedora out of fine black fabric" +armour_type = "head" +defense = 5 [[contents]] kind = "gateway" diff --git a/data/worlds/chaosdorf/areas/hackcenter.toml b/data/worlds/chaosdorf/areas/hackcenter.toml index f515d5b..4628653 100644 --- a/data/worlds/chaosdorf/areas/hackcenter.toml +++ b/data/worlds/chaosdorf/areas/hackcenter.toml @@ -25,6 +25,8 @@ name = "printer" description = "a big paper disgorging device" [[contents]] -kind = "item" +kind = "armour" name = "grey hat" -description = "a grey cap with some strange stickers on it" \ No newline at end of file +description = "a grey cap with some strange stickers on it" +armour_type = "head" +defense = 5 \ No newline at end of file diff --git a/data/worlds/chaosdorf/areas/kitchen.toml b/data/worlds/chaosdorf/areas/kitchen.toml index 867b0bb..0214e4a 100644 --- a/data/worlds/chaosdorf/areas/kitchen.toml +++ b/data/worlds/chaosdorf/areas/kitchen.toml @@ -14,9 +14,11 @@ target = "hackcenter" obvious = true [[contents]] -kind = "item" +kind = "armour" name = "white hat" description = "a western like hat made out of white leather" +armour_type = "head" +defense = 5 [[contents]] kind = "enemy" From f52018d019421010357c45fc55be0164ba5b592f Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 12:21:58 +0100 Subject: [PATCH 12/14] adds defense property --- src/fantasy_forge/player.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/fantasy_forge/player.py b/src/fantasy_forge/player.py index 338fba2..f4a8d59 100644 --- a/src/fantasy_forge/player.py +++ b/src/fantasy_forge/player.py @@ -53,6 +53,15 @@ def __init__(self: Self, world: World, name: str, health: int = BASE_PLAYER_HEAL for armour_type in ARMOUR_TYPES: self.armour_slots[armour_type] = None + @property + def defense(self) -> int: + defense_sum: int = 0 + armour_item: Armour + for armour_item in self.armour_slots.keys(): + if armour_item is not None: + defense_sum += armour_item.defense + return defense_sum + def look_around(self): """Player looks around the current area.""" # clear seen items, but re-add inventory items @@ -197,14 +206,14 @@ def equip_armour(self, armour: Armour) -> None: # check if armour slot is already filled if current_armour is not None: print( - self.world.l10n.format_value( - "unequip-item-message", - { - "player": self.name, - "item": armour.name, - }, + self.world.l10n.format_value( + "unequip-item-message", + { + "player": self.name, + "item": armour.name, + }, + ) ) - ) self.armour_slots[armour.armour_type] = armour print( From 61a6314b59e50a57953ff2e8af9368a1a1a2b55d Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 12:32:25 +0100 Subject: [PATCH 13/14] adds armour command, fixes equip auto complete --- src/fantasy_forge/shell.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/fantasy_forge/shell.py b/src/fantasy_forge/shell.py index a387549..504a85c 100644 --- a/src/fantasy_forge/shell.py +++ b/src/fantasy_forge/shell.py @@ -4,6 +4,7 @@ from cmd import Cmd from typing import TYPE_CHECKING +from fantasy_forge.armour import Armour from fantasy_forge.character import Character from fantasy_forge.gateway import Gateway from fantasy_forge.item import Item @@ -165,6 +166,16 @@ def do_inventory(self, arg: str): """shows the contents of the players inventory""" print(self.player.inventory.on_look()) + def do_armour(self, arg: str): + """shows the players armour""" + for armour_type, armour_item in self.player.armour_slots.items(): + if armour_item is None: + print(f"{armour_type}: None") + else: + print( + f"{armour_type}: {armour_item.name} ({armour_item.defense} defense)" + ) + def do_use(self, arg: str): """ use [with ] @@ -259,11 +270,11 @@ def complete_equip( begidx: int, endidx: int, ) -> list[str]: - weapon = line.removeprefix("equip ").strip() + item = line.removeprefix("equip ").strip() completions = [ - text + name.removeprefix(weapon).strip() + " " + text + name.removeprefix(item).strip() + " " for name, entity in self.player.seen_entities.items() - if name.startswith(weapon) and isinstance(entity, Weapon) + if name.startswith(item) and isinstance(entity, (Weapon, Armour)) ] if " " in completions: completions.remove(" ") From d0195834e07f1d992df915aa8254d191fcf6a54c Mon Sep 17 00:00:00 2001 From: dodo Date: Sun, 27 Oct 2024 12:35:10 +0100 Subject: [PATCH 14/14] ruff formating --- src/fantasy_forge/area.py | 1 + src/fantasy_forge/enemy.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fantasy_forge/area.py b/src/fantasy_forge/area.py index 2e3b3e3..f80a172 100644 --- a/src/fantasy_forge/area.py +++ b/src/fantasy_forge/area.py @@ -63,6 +63,7 @@ def from_dict(world: World, area_dict: dict) -> Area: contents_list.append(Weapon(world, entity_dict)) case "armour": from fantasy_forge.armour import Armour + contents_list.append(Armour(world, entity_dict)) case default: diff --git a/src/fantasy_forge/enemy.py b/src/fantasy_forge/enemy.py index dd1c780..0c582a4 100644 --- a/src/fantasy_forge/enemy.py +++ b/src/fantasy_forge/enemy.py @@ -2,7 +2,6 @@ from fantasy_forge.character import Character from fantasy_forge.item import Item -from fantasy_forge.weapon import Weapon from fantasy_forge.world import World BASE_DAMAGE = 1