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        )