Skip to content

Commit 27f9b5e

Browse files
committed
Merge PR #38: 'multiplayer'
2 parents 0ffb963 + 79079d7 commit 27f9b5e

18 files changed

Lines changed: 426 additions & 374 deletions

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dependencies = [
1818

1919
[project.scripts]
2020
fantasy-forge = "fantasy_forge.main:main"
21+
fantasy-forge-server = "fantasy_forge.multiplayer:main"
2122

2223
[build-system]
2324
requires = ["hatchling"]

‎src/fantasy_forge/area.py‎

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,20 @@ class Area(Entity):
1414
__important_attributes__ = ("name",)
1515
contents: dict[str, Entity]
1616

17-
def __init__(self: Self, world: World, config_dict: dict[str, Any]):
18-
super().__init__(world, config_dict)
17+
def __init__(self: Self, messages: Messages, config_dict: dict[str, Any]):
18+
super().__init__(messages, config_dict)
1919
self.contents: dict = {}
2020

2121
def __iter__(self: Self) -> Iterator:
2222
for obj in self.contents:
2323
yield obj
2424

25-
def on_look(self: Self) -> str:
26-
return self.world.l10n.format_value(
25+
def on_look(self: Self, actor: Player):
26+
self.messages.to(
27+
[actor],
2728
"look-around-begin",
28-
{
29-
"area-name": self.name,
30-
"area-description": self.description,
31-
},
29+
area_name=self.name,
30+
area_description=self.description,
3231
)
3332

3433
def to_dict(self: Self) -> dict:
@@ -37,60 +36,61 @@ def to_dict(self: Self) -> dict:
3736
return area_dict
3837

3938
@staticmethod
40-
def from_dict(world: World, area_dict: dict) -> Area:
39+
def from_dict(messages: Messages, area_dict: dict) -> Area:
4140
contents_list: list[Entity] = []
4241
for entity_dict in area_dict.get("contents", []):
4342
match entity_dict.get("kind", "entity"):
4443
case "item":
4544
from fantasy_forge.item import Item
4645

47-
contents_list.append(Item(world, entity_dict))
46+
contents_list.append(Item(messages, entity_dict))
4847
case "gateway":
4948
from fantasy_forge.gateway import Gateway
5049

51-
contents_list.append(Gateway(world, entity_dict))
50+
contents_list.append(Gateway(messages, entity_dict))
5251
case "key":
5352
from fantasy_forge.key import Key
5453

55-
contents_list.append(Key(world, entity_dict))
54+
contents_list.append(Key(messages, entity_dict))
5655
case "enemy":
5756
from fantasy_forge.enemy import Enemy
5857

59-
contents_list.append(Enemy(world, entity_dict))
58+
contents_list.append(Enemy(messages, entity_dict))
6059
case "weapon":
6160
from fantasy_forge.weapon import Weapon
6261

63-
contents_list.append(Weapon(world, entity_dict))
62+
contents_list.append(Weapon(messages, entity_dict))
6463
case "armour":
6564
from fantasy_forge.armour import Armour
6665

67-
contents_list.append(Armour(world, entity_dict))
66+
contents_list.append(Armour(messages, entity_dict))
6867

6968
case default:
70-
contents_list.append(Entity(world, entity_dict))
69+
contents_list.append(Entity(messages, entity_dict))
7170
contents = {entity.name: entity for entity in contents_list}
72-
area = Area(world, area_dict)
71+
area = Area(messages, area_dict)
7372
area.contents = contents
7473
return area
7574

7675
@staticmethod
77-
def load(world, root_path: Path, name: str):
76+
def load(messages: Messages, root_path: Path, name: str):
7877
path = root_path / "areas" / f"{name}.toml"
7978
with path.open() as area_file:
8079
area_toml = toml.load(area_file)
81-
return Area.from_dict(world, area_toml)
80+
return Area.from_dict(messages, area_toml)
8281

8382
@staticmethod
84-
def empty(world: World) -> Area:
83+
def empty(messages: Messages) -> Area:
8584
"""Return an empty area, this is a placeholder."""
8685
return Area(
87-
world,
86+
messages,
8887
dict(
89-
name=world.l10n.format_value("void-name"),
90-
description=world.l10n.format_value("void-description"),
88+
name=messages.l10n.format_value("void-name"),
89+
description=messages.l10n.format_value("void-description"),
9190
),
9291
)
9392

9493

9594
if TYPE_CHECKING:
96-
from fantasy_forge.world import World
95+
from fantasy_forge.messages import Messages
96+
from fantasy_forge.player import Player

‎src/fantasy_forge/armour.py‎

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

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

55
from fantasy_forge.item import Item
6-
from fantasy_forge.world import World
76

87
# Armour types:
98
# 'head': for caps, hats, helmets
@@ -19,12 +18,12 @@ class Armour(Item):
1918

2019
__important_attributes__ = ("name", "armour_type", "defense")
2120

22-
def __init__(self, world, config_dict):
21+
def __init__(self, messages: Messages, config_dict):
2322
a_type: str = config_dict.pop("armour_type")
2423
assert a_type in ARMOUR_TYPES
2524
self.armour_type = a_type
2625
self.defense = config_dict.pop("defense")
27-
super().__init__(world, config_dict)
26+
super().__init__(messages, config_dict)
2827

2928
def to_dict(self: Self) -> dict:
3029
armour_dict: dict = super().to_dict()
@@ -33,6 +32,10 @@ def to_dict(self: Self) -> dict:
3332
return armour_dict
3433

3534
@staticmethod
36-
def from_dict(world: World, armour_dict: dict) -> Armour:
37-
armour: Armour = Armour(world, armour_dict)
35+
def from_dict(messages: Messages, armour_dict: dict) -> Armour:
36+
armour: Armour = Armour(messages, armour_dict)
3837
return armour
38+
39+
40+
if TYPE_CHECKING:
41+
from fantasy_forge.messages import Messages

‎src/fantasy_forge/character.py‎

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

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

55
from fantasy_forge.entity import Entity
66
from fantasy_forge.inventory import Inventory
7+
from fantasy_forge.messages import Messages
78
from fantasy_forge.weapon import Weapon
8-
from fantasy_forge.world import World
99

1010
BASE_INVENTORY_CAPACITY = 10
1111
BASE_DAMAGE = 1
1212

1313

14-
def bare_hands(world: World):
14+
def bare_hands(messages: Messages):
1515
return Weapon(
16-
world,
16+
messages,
1717
{
18-
"name": world.l10n.format_value("bare-hands-name"),
19-
"description": world.l10n.format_value("bare-hands-description"),
18+
"name": messages.l10n.format_value("bare-hands-name"),
19+
"description": messages.l10n.format_value("bare-hands-description"),
2020
"damage": BASE_DAMAGE,
2121
},
2222
)
@@ -32,11 +32,11 @@ class Character(Entity):
3232
main_hand: Weapon | None
3333
_alive: bool
3434

35-
def __init__(self: Self, world: World, config_dict: dict[str, Any]) -> None:
35+
def __init__(self: Self, messages: Messages, config_dict: dict[str, Any]) -> None:
3636
self.health = config_dict.pop("health")
37-
self.inventory = Inventory(world, BASE_INVENTORY_CAPACITY)
37+
self.inventory = Inventory(messages, BASE_INVENTORY_CAPACITY)
3838
self.main_hand = None
39-
super().__init__(world, config_dict)
39+
super().__init__(messages, config_dict)
4040

4141
@property
4242
def alive(self: Self) -> bool:
@@ -45,19 +45,16 @@ def alive(self: Self) -> bool:
4545

4646
def attack(self: Self, target: Character) -> None:
4747
if self.main_hand is None:
48-
weapon = bare_hands(self.world)
48+
weapon = bare_hands(self.messages)
4949
else:
5050
weapon = self.main_hand
5151
target.on_attack(weapon)
52-
print(
53-
self.world.l10n.format_value(
54-
"attack-character-message",
55-
{
56-
"source": self.name,
57-
"target": target.name,
58-
"weapon": getattr(weapon, "name", None),
59-
},
60-
)
52+
self.messages.to(
53+
[self, target],
54+
"attack-character-message",
55+
source=self.name,
56+
target=target.name,
57+
weapon=getattr(weapon, "name", None),
6158
)
6259

6360
def on_attack(self: Self, weapon: Weapon):
@@ -68,35 +65,32 @@ def _on_death(self: Self, player: Player):
6865
Automatic on death call.
6966
"""
7067
assert not self.alive, "On_death called while entity is alive"
71-
print(
72-
self.world.l10n.format_value(
73-
"attack-character-dead-message",
74-
{
75-
"target": self.name,
76-
},
77-
)
68+
self.messages.to(
69+
[player],
70+
"attack-character-dead-message",
71+
target=self.name,
7872
)
7973
# Populate area with loot
80-
print(
81-
self.world.l10n.format_value(
82-
"attack-drop-begin",
83-
{
84-
"target": self.name,
85-
"loot_count": len(self.inventory),
86-
},
87-
)
74+
self.messages.to(
75+
[player],
76+
"attack-drop-begin",
77+
target=self.name,
78+
loot_count=len(self.inventory),
8879
)
8980
for loot_item in self.inventory:
9081
player.area.contents[loot_item.name] = loot_item
9182
player.seen_entities[loot_item.name] = loot_item
92-
print(
93-
self.world.l10n.format_value(
94-
"attack-drop-single",
95-
{"item": loot_item.name},
96-
)
83+
self.messages.to(
84+
[player],
85+
"attack-drop-single",
86+
item=loot_item.name,
9787
)
9888
# if the target is dead, remove it from the area and drop their inventory
9989
del player.area.contents[self.name]
10090
del player.seen_entities[self.name]
10191

10292
# remove entity from area
93+
94+
95+
if TYPE_CHECKING:
96+
from fantasy_forge.player import Player

‎src/fantasy_forge/enemy.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
from fantasy_forge.character import Character, bare_hands
44
from fantasy_forge.item import Item
5-
from fantasy_forge.world import World
5+
from fantasy_forge.messages import Messages
66

77

88
class Enemy(Character):
99
"""An enemy is a person which will fight back."""
1010

11-
def __init__(self: Self, world: World, config_dict: dict[str, Any]):
12-
super().__init__(world, config_dict)
11+
def __init__(self: Self, messages: Messages, config_dict: dict[str, Any]):
12+
super().__init__(messages, config_dict)
1313
for item_dict in config_dict.get("loot", []):
14-
self.inventory.add(Item(world, item_dict))
14+
self.inventory.add(Item(messages, item_dict))
1515

1616
def __str__(self: Self) -> str:
1717
return self.name

‎src/fantasy_forge/entity.py‎

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,30 @@ class Entity:
88

99
__important_attributes__ = ("name",)
1010

11-
world: World
11+
messages: Messages
1212
name: str
1313
description: str
1414
obvious: bool # obvious entities are seen when entering the room
1515

1616
def __init__(
1717
self: Self,
18-
world: World,
18+
messages: Messages,
1919
config_dict: dict[str, Any],
2020
) -> None:
21-
self.world = world
21+
self.messages = messages
2222
self.name = config_dict.pop("name")
2323
self.description = config_dict.pop("description", "")
2424
self.obvious = config_dict.pop("obvious", False)
2525

26-
def on_look(self: Self) -> str:
27-
return self.description
28-
29-
def on_use(self: Self, other: Entity | None = None):
30-
print(
31-
self.world.l10n.format_value(
32-
"cannot-use-message",
33-
{
34-
"self": self.name,
35-
"other": getattr(other, "name", None),
36-
},
37-
)
26+
def on_look(self: Self, actor: Player):
27+
actor.shell.stdout.write(self.description + "\n")
28+
29+
def on_use(self: Self, actor: Player, other: Entity | None = None):
30+
self.messages.to(
31+
[actor],
32+
"cannot-use-message",
33+
self=self.name,
34+
other=getattr(other, "name", None),
3835
)
3936

4037
def __repr__(self: Self) -> str:
@@ -54,9 +51,11 @@ def to_dict(self: Self) -> dict:
5451
entity_dict: dict = {"name": self.name, "description": self.description}
5552
return entity_dict
5653

57-
def resolve(self):
54+
def resolve(self, world: World):
5855
pass
5956

6057

6158
if TYPE_CHECKING:
59+
from fantasy_forge.messages import Messages
60+
from fantasy_forge.player import Player
6261
from fantasy_forge.world import World

0 commit comments

Comments
 (0)