source: nagslang/render.py @ 229:329b3044ddef

Last change on this file since 229:329b3044ddef was 229:329b3044ddef, checked in by Jeremy Thurgood <firxen@…>, 7 years ago

Much better facing renderers.

File size: 5.2 KB
Line 
1import math
2
3import pygame
4import pymunk
5
6from nagslang.options import options
7from nagslang.widgets.text import LabelWidget
8
9
10class Renderer(object):
11    def set_game_object(self, game_object):
12        self.game_object = game_object
13
14    def _render_shape(self, surface):
15        shape = self.game_object.get_shape()
16        # Less general that pymunk.pygame_util.draw, but also a lot less noisy.
17        color = getattr(shape, 'color', pygame.color.THECOLORS['lightblue'])
18        # We only explicitly draw Circle and Poly shapes. Everything else we
19        # forward to pymunk.
20        if isinstance(shape, pymunk.Circle):
21            centre = pymunk.pygame_util.to_pygame(shape.body.position, surface)
22            radius = int(shape.radius)
23            pygame.draw.circle(surface, color, centre, radius, 2)
24        elif isinstance(shape, pymunk.Poly):
25            # polygon bounding box
26            points = [pymunk.pygame_util.to_pygame(p, surface)
27                      for p in shape.get_vertices()]
28            pygame.draw.lines(surface, color, True, points, 2)
29        else:
30            pymunk.pygame_util.draw(surface, shape)
31
32    def render(self, surface):
33        if options.debug:
34            self._render_shape(surface)
35
36    def animate(self):
37        # Used by time animatations to advance the clock
38        pass
39
40
41def image_pos(image, pos):
42    return (pos[0] - image.get_width() / 2,
43            pos[1] - image.get_height() / 2)
44
45
46class ImageRenderer(Renderer):
47    rotate = True  # Set to `False` to suppress image rotation.
48
49    def __init__(self, image):
50        self._image = image
51
52    def get_image(self):
53        return self._image
54
55    def rotate_image(self, image):
56        if not self.rotate:
57            return image
58        angle = self.game_object.get_render_angle() * 180 / math.pi
59        return pygame.transform.rotate(image, angle)
60
61    def render_image(self, surface, image):
62        image = self.rotate_image(image)
63        pos = self.game_object.get_render_position(surface)
64        surface.blit(image, image_pos(image, pos))
65
66    def render(self, surface):
67        self.render_image(surface, self.get_image())
68        super(ImageRenderer, self).render(surface)
69
70
71class ImageStateRenderer(ImageRenderer):
72    def __init__(self, state_images):
73        self._state_images = state_images
74
75    def get_image(self):
76        return self._state_images[self.game_object.puzzler.get_state()]
77
78
79class TimedAnimatedRenderer(ImageRenderer):
80    def __init__(self, images, frame_ticks=1):
81        self._images = images
82        self._frame_ticks = frame_ticks
83        self._frame_tick = 0
84        self._frame = 0
85
86    def advance_tick(self):
87        self._frame_tick += 1
88        if self._frame_tick > self._frame_ticks:
89            self._frame_tick = 0
90            self._frame += 1
91        if self._frame >= len(self._images):
92            self._frame = 0
93
94    def reset(self):
95        self._frame_tick = 0
96        self._frame = 0
97
98    def get_image(self):
99        return self._images[self._frame]
100
101    def animate(self):
102        self.advance_tick()
103
104
105class MovementAnimatedRenderer(TimedAnimatedRenderer):
106    def animate(self):
107        if self.game_object.is_moving:
108            self.advance_tick()
109        else:
110            self.reset()
111
112
113class RendererSelectionRenderer(Renderer):
114    def __init__(self, renderers):
115        self._renderers = renderers
116
117    def set_game_object(self, game_object):
118        self.game_object = game_object
119        for renderer in self._renderers.values():
120            renderer.set_game_object(game_object)
121
122    @property
123    def renderer(self):
124        return self._renderers[self.select_renderer()]
125
126    def render(self, surface):
127        return self.renderer.render(surface)
128
129    def animate(self):
130        return self.renderer.animate()
131
132    def select_renderer(self):
133        raise NotImplementedError()
134
135
136class FacingSelectionRenderer(RendererSelectionRenderer):
137    def select_renderer(self):
138        return self.game_object.get_facing_direction()
139
140
141class ShapeRenderer(Renderer):
142    def render(self, surface):
143        self._render_shape(surface)
144        super(ShapeRenderer, self).render(surface)
145
146
147class ShapeStateRenderer(ShapeRenderer):
148    """Renders the shape in a different colour depending on the state.
149
150    Requires the game object it's attached to to have a puzzler.
151    """
152    def render(self, surface):
153        if self.game_object.puzzler.get_state():
154            color = pygame.color.THECOLORS['green']
155        else:
156            color = pygame.color.THECOLORS['red']
157
158        self.game_object.get_shape().color = color
159        super(ShapeStateRenderer, self).render(surface)
160
161
162class Overlay(object):
163    def set_game_object(self, game_object):
164        self.game_object = game_object
165
166    def render(self, surface, display_offset):
167        pass
168
169    def is_visible(self):
170        return self.game_object.puzzler.get_state()
171
172
173class TextOverlay(Overlay):
174    def __init__(self, text):
175        self.text = text
176        self.widget = LabelWidget((20, 20), self.text)
177
178    def render(self, surface, display_offset):
179        x, y = 20, 20
180        if display_offset[0] < 0:
181            x += abs(display_offset[0])
182        if display_offset[1] < 0:
183            y += abs(display_offset[1])
184        self.widget.rect.topleft = (x, y)
185        self.widget.draw(surface)
Note: See TracBrowser for help on using the repository browser.