source: nagslang/render.py @ 388:8a65fd894f73

Last change on this file since 388:8a65fd894f73 was 388:8a65fd894f73, checked in by Neil Muller <drnlmuller@…>, 7 years ago

remove unneeded fill

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