nodes/town.pyΒΆ
Part of Dungeon Explorer.
1"""Town hub: safe zone between dungeon runs with NPCs."""
2
3from simvx.core import Input, Node2D, Vec2
4
5from .town_npc import TownNPC
6
7# Town layout constants
8TOWN_W = 640
9TOWN_H = 480
10TILE = 16
11
12
13class TownScene(Node2D):
14 """Town hub with NPCs at fixed positions.
15
16 NPCs: Shopkeeper, Innkeeper, Blacksmith, Well Keeper.
17 Player can move around and interact with E key.
18 """
19
20 def __init__(self, player, on_interact=None, **kwargs):
21 super().__init__(name="TownScene", **kwargs)
22 self._player = player
23 self._on_interact = on_interact
24 self._npcs: list[TownNPC] = []
25
26 def on_ready(self):
27 # Place NPCs
28 npc_defs = [
29 ("shopkeeper", "Shopkeeper", Vec2(200, 200), (0.2, 0.7, 0.3, 1.0)),
30 ("innkeeper", "Innkeeper", Vec2(440, 200), (0.7, 0.4, 0.2, 1.0)),
31 ("blacksmith", "Blacksmith", Vec2(200, 350), (0.5, 0.5, 0.6, 1.0)),
32 ("well_keeper", "Well Keeper", Vec2(440, 350), (0.3, 0.5, 0.8, 1.0)),
33 ]
34 for npc_id, name, pos, colour in npc_defs:
35 npc = TownNPC(npc_id=npc_id, name=name)
36 npc.display_name = name
37 npc.position = pos
38 npc._colour = colour
39 npc.interact_requested.connect(self._npc_interact)
40 self.add_child(npc)
41 self._npcs.append(npc)
42
43 def _npc_interact(self, npc_id: str):
44 if self._on_interact:
45 self._on_interact(npc_id)
46
47 def check_interactions(self):
48 """Check if player is near an NPC and E is pressed."""
49 if not Input.is_action_just_pressed("interact"):
50 return
51 for npc in self._npcs:
52 if npc.can_interact(self._player.position):
53 npc.interact()
54 break
55
56 def on_draw(self, renderer):
57 # Town floor
58 for y in range(0, TOWN_H, TILE):
59 for x in range(0, TOWN_W, TILE):
60 renderer.draw_rect((x, y), (TILE, TILE), colour=(0.25, 0.2, 0.15, 1.0), filled=True)
61 # Border
62 if x == 0 or y == 0 or x >= TOWN_W - TILE or y >= TOWN_H - TILE:
63 renderer.draw_rect((x, y), (TILE, TILE), colour=(0.35, 0.3, 0.25, 1.0), filled=True)
64
65 # Town label
66 renderer.draw_text("TOWN", (TOWN_W // 2 - 30, 20), scale=2.0, colour=(0.9, 0.8, 0.5, 1.0))
67 renderer.draw_text(
68 "Press E to interact with NPCs | Press N to enter dungeon",
69 (80, TOWN_H - 25),
70 scale=0.9,
71 colour=(0.5, 0.5, 0.5, 1.0),
72 )