nodes/grass.pyΒΆ

Part of HeartBeast Action RPG.

 1"""Grass patches: destructible world decoration.
 2
 3Mirrors the upstream HeartBeast Grass scene: a tuft with a hurtbox that the
 4player's sword can cut. Cutting it emits ``destroyed`` (the World turns that
 5into a burst of leaves) and removes the node.
 6"""
 7
 8from __future__ import annotations
 9
10from settings import COLOUR_GRASS_BLADE, COLOUR_SHADOW, GRASS_HURTBOX_RADIUS
11
12from simvx.core import Node2D, Signal, Vec2
13
14
15class Grass(Node2D):
16    """Destructible grass tuft."""
17
18    destroyed = Signal()  # (position: Vec2)
19
20    def __init__(self, position: Vec2 | None = None, **kwargs):
21        super().__init__(position=position if position is not None else Vec2(0.0, 0.0), **kwargs)
22        self._alive = True
23
24    @property
25    def is_alive(self) -> bool:
26        """False once the tuft has been cut."""
27        return self._alive
28
29    def on_draw(self, renderer):
30        if not self._alive:
31            return
32        px, py = self.position
33        renderer.draw_circle((px, py + 1), 4.0, colour=COLOUR_SHADOW, filled=True)
34        blades = [
35            [(px - 5, py + 1), (px - 3, py - 8), (px - 1, py + 1)],
36            [(px - 1, py + 1), (px + 0.5, py - 11), (px + 2, py + 1)],
37            [(px + 2, py + 1), (px + 4.5, py - 7), (px + 6, py + 1)],
38        ]
39        for blade in blades:
40            renderer.draw_polygon(blade, colour=COLOUR_GRASS_BLADE)
41
42    def take_damage(self) -> bool:
43        """Cut this tuft down. Returns True if it was still standing."""
44        if not self._alive:
45            return False
46        self._alive = False
47        # ``on_draw`` reads a plain attribute, so the retained 2D cache is told
48        # by hand that this node's output changed before the node goes away.
49        self.queue_redraw()
50        self.destroyed.emit(self.position)
51        self.destroy()
52        return True
53
54    def get_hurtbox(self) -> tuple[Vec2, float]:
55        """``(centre, radius)`` of the tuft's hurtbox."""
56        return (self.position, GRASS_HURTBOX_RADIUS)