Skip to content

Commit f0b05a3

Browse files
committed
make containers functional (fixes #12)
1 parent 3afa092 commit f0b05a3

6 files changed

Lines changed: 117 additions & 69 deletions

File tree

data/worlds/chaosdorf/areas/hackcenter.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ description = "a large couch. It seems quite comfortable..."
2424
obvious = true
2525

2626
[[contents]]
27+
kind = "container"
2728
name = "fridge"
2829
description = "a fridge full of magic potions"
2930
obvious = true
31+
[[contents.contents]]
32+
name = "mate"
33+
kind = "item"
3034

3135
[[contents]]
3236
name = "printer"

src/fantasy_forge/area.py

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import toml
1010

1111
from fantasy_forge.entity import Entity
12-
from fantasy_forge.utils import UniqueDict
12+
from fantasy_forge.utils import UniqueDict, inflate_contents
1313

1414
logger = logging.getLogger(__name__)
1515

@@ -59,39 +59,8 @@ def to_dict(self: Self) -> dict:
5959

6060
@staticmethod
6161
def from_dict(messages: Messages, area_dict: dict) -> Area:
62-
contents_list: list[Entity] = []
63-
for entity_dict in area_dict.get("contents", []):
64-
match entity_dict.get("kind", "entity"):
65-
case "item":
66-
from fantasy_forge.item import Item
67-
68-
contents_list.append(Item(messages, entity_dict))
69-
case "gateway":
70-
from fantasy_forge.gateway import Gateway
71-
72-
contents_list.append(Gateway(messages, entity_dict))
73-
case "key":
74-
from fantasy_forge.key import Key
75-
76-
contents_list.append(Key(messages, entity_dict))
77-
case "enemy":
78-
from fantasy_forge.enemy import Enemy
79-
80-
contents_list.append(Enemy(messages, entity_dict))
81-
case "weapon":
82-
from fantasy_forge.weapon import Weapon
83-
84-
contents_list.append(Weapon(messages, entity_dict))
85-
case "armour":
86-
from fantasy_forge.armour import Armour
87-
88-
contents_list.append(Armour(messages, entity_dict))
89-
90-
case default:
91-
contents_list.append(Entity(messages, entity_dict))
9262
area = Area(messages, area_dict)
93-
for entity in contents_list:
94-
area.contents[entity.name] = entity
63+
inflate_contents(messages, area_dict.get("contents", []), area)
9564
return area
9665

9766
@staticmethod

src/fantasy_forge/container.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
1+
from __future__ import annotations
2+
13
"""Container class
24
35
A container is an item in the world which holds an inventory.
46
"""
57

6-
from typing import Any, Self
8+
from typing import TYPE_CHECKING, Any, Iterator, Self
79

8-
from fantasy_forge.inventory import Inventory
910
from fantasy_forge.item import Item
11+
from fantasy_forge.localization import highlight_interactive
12+
from fantasy_forge.messages import Messages
13+
from fantasy_forge.utils import UniqueDict, inflate_contents
1014

1115

12-
class Container(Inventory, Item):
16+
class Container(Item):
1317
"""Container object."""
1418

19+
contents: UniqueDict[str, Item]
20+
capacity: int
1521
__important_attributes__ = (*Item.__important_attributes__, "capacity")
16-
__attributes__ = {**Inventory.__attributes__, **Item.__attributes__}
22+
__attributes__ = {**Item.__attributes__, "capacity": int}
1723

18-
def __init__(
19-
self: Self, config_dict: dict[str, Any], l10n: FluentLocalization
20-
) -> None:
24+
def __init__(self: Self, messages: Messages, config_dict: dict[str, Any]) -> None:
2125
"""
2226
config_dict contents
23-
inherited from Inventory
24-
'capacity' (int): maximum capacity of the inventory
27+
'capacity' (int): maximum capacity of the container
28+
'contents' (list): contents of the container
2529
2630
inherited from Item
2731
'moveable' (bool): can the item be moved by the player (default: True)
@@ -32,9 +36,43 @@ def __init__(
3236
'description' (str): description of the entity (default: "")
3337
'obvious'(bool): whether the entity will be spotted immediately (default: False)
3438
"""
35-
Inventory.__init__(self, config_dict, l10n)
36-
Item.__init__(self, config_dict, l10n)
39+
super().__init__(messages, config_dict)
40+
self.capacity = config_dict.get("capacity", 10)
41+
self.contents = UniqueDict()
42+
inflate_contents(messages, config_dict.get("contents", []), self)
43+
44+
def __len__(self: Self) -> int:
45+
"""Returns current capacity."""
46+
return len(self.contents)
47+
48+
def __iter__(self: Self) -> Iterator[Item]:
49+
"""Iterates over items in container."""
50+
yield from self.contents.values()
51+
52+
def __contains__(self: Self, other: str) -> bool:
53+
"""Returns if entity is in container."""
54+
return other in self.contents
55+
56+
def on_look(self: Self, player: Player):
57+
super().on_look(player)
58+
if not self.contents:
59+
self.messages.to(
60+
[player], "container-look-empty-message", container=self.name
61+
)
62+
else:
63+
self.messages.to(
64+
[player],
65+
"container-look-message",
66+
container=self.name,
67+
contents=", ".join(
68+
[
69+
highlight_interactive(str(item)).format(None)
70+
+ f" (weight: {item.weight})"
71+
for item in self
72+
]
73+
),
74+
)
3775

3876

3977
if TYPE_CHECKING:
40-
from fluent.runtime import FluentLocalization
78+
from fantasy_forge.player import Player

src/fantasy_forge/inventory.py

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING, Iterator, Self
3+
from typing import TYPE_CHECKING, Self
44

5+
from fantasy_forge.container import Container
56
from fantasy_forge.entity import Entity
67
from fantasy_forge.item import Item
8+
from fantasy_forge.localization import highlight_interactive
79
from fantasy_forge.utils import UniqueDict
810

911

@@ -15,36 +17,14 @@ class InventoryTooSmall(Exception):
1517
pass
1618

1719

18-
class Inventory(Entity):
20+
class Inventory(Container):
1921
"""An Inventory contains multiple entities."""
2022

21-
messages: Messages
22-
capacity: int
23-
contents: UniqueDict[str, Item]
24-
25-
__important_attributes__ = ("name", "capacity")
26-
__attributes__ = {**Entity.__attributes__, "capacity": int}
27-
2823
def __init__(self: Self, messages: Messages, capacity: int):
2924
self.messages = messages
3025
self.capacity = capacity
3126
self.contents = UniqueDict()
3227

33-
def __len__(self: Self) -> int:
34-
"""Returns current capacity."""
35-
return len(self.contents)
36-
37-
def __iter__(self: Self) -> Iterator[Item]:
38-
"""Iterates over items in inventory."""
39-
yield from self.contents.values()
40-
41-
def __len__(self: Self) -> int:
42-
return len(self.contents)
43-
44-
def __contains__(self: Self, other: str) -> bool:
45-
"""Returns if entity is in inventory."""
46-
return other in self.contents
47-
4828
def calculate_weight(self: Self) -> int:
4929
weight = 0
5030
for item in self.contents.values():

src/fantasy_forge/l10n/en/main.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ dropped = You dropped { INTER($item) }.
5252
enter-area-message = You are now in { $area }. You see:
5353
inventory-look-message = In the inventory you find { $items }.
5454
inventory-look-empty-message = Your inventory is empty.
55+
container-look-message = In the { $container } you find { $contents }.
56+
container-look-empty-message = The { $container } is empty.
5557
inventory-capacity-message = Maximum capacity ({ NUM($capacity) }) reached.
5658
inventory-too-small-message = Inventory isn't big enough to fit this item. (Capacity: ({ NUM($capacity) }), Item weight: ({ NUM($weight) })).
5759
inventory-item-not-found-message = Item { $item } couldn't be found.

src/fantasy_forge/utils.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1+
from __future__ import annotations
2+
13
import ctypes
24
from pathlib import Path
35
from string import whitespace
6+
from typing import TYPE_CHECKING, Any
7+
8+
from fantasy_forge.entity import Entity
9+
from fantasy_forge.messages import Messages
410

511
SOURCE_FOLDER: Path = Path(__file__).parent.resolve() # fantasy_forge/src/fantasy_forge
612
ROOT_FOLDER: Path = SOURCE_FOLDER.parent.parent.resolve() # fantasy_forge
713

814
DATA_FOLDER: Path = ROOT_FOLDER / "data" # fantasy_forge/data
915
WORLDS_FOLDER: Path = DATA_FOLDER / "worlds" # fantasy_forge/data/worlds
1016

17+
1118
# taken from https://stackoverflow.com/a/5948050/2192464
1219
class UniqueDict[K, V](dict[K, V]):
1320
def __setitem__(self, key: K, value: V):
@@ -17,6 +24,48 @@ def __setitem__(self, key: K, value: V):
1724
raise KeyError("Key already exists")
1825

1926

27+
def inflate_contents(
28+
messages: Messages, contents: list[dict[str, Any]], target: Area | Container
29+
):
30+
contents_list: list[Entity] = []
31+
for entity_dict in contents:
32+
match entity_dict.get("kind", "entity"):
33+
case "item":
34+
from fantasy_forge.item import Item
35+
36+
contents_list.append(Item(messages, entity_dict))
37+
case "gateway":
38+
from fantasy_forge.gateway import Gateway
39+
40+
contents_list.append(Gateway(messages, entity_dict))
41+
case "key":
42+
from fantasy_forge.key import Key
43+
44+
contents_list.append(Key(messages, entity_dict))
45+
case "enemy":
46+
from fantasy_forge.enemy import Enemy
47+
48+
contents_list.append(Enemy(messages, entity_dict))
49+
case "weapon":
50+
from fantasy_forge.weapon import Weapon
51+
52+
contents_list.append(Weapon(messages, entity_dict))
53+
case "armour":
54+
from fantasy_forge.armour import Armour
55+
56+
contents_list.append(Armour(messages, entity_dict))
57+
58+
case "container":
59+
from fantasy_forge.container import Container
60+
61+
contents_list.append(Container(messages, entity_dict))
62+
63+
case _:
64+
contents_list.append(Entity(messages, entity_dict))
65+
for entity in contents_list:
66+
target.contents[entity.name] = entity
67+
68+
2069
# taken from https://stackoverflow.com/a/15274929/2192464
2170
def terminate_thread(thread):
2271
"""Terminates a python thread from another thread.
@@ -36,6 +85,7 @@ def terminate_thread(thread):
3685
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
3786
raise SystemError("PyThreadState_SetAsyncExc failed")
3887

88+
3989
def clean_filename(filename: str) -> str:
4090
result = []
4191
for char in filename.casefold():
@@ -46,3 +96,8 @@ def clean_filename(filename: str) -> str:
4696
else:
4797
continue
4898
return "".join(result)
99+
100+
101+
if TYPE_CHECKING:
102+
from fantasy_forge.area import Area
103+
from fantasy_forge.container import Container

0 commit comments

Comments
 (0)