1 | import math |
---|
2 | |
---|
3 | import pygame |
---|
4 | import pymunk |
---|
5 | |
---|
6 | from nagslang.options import options |
---|
7 | from nagslang.widgets.text import LabelWidget |
---|
8 | |
---|
9 | |
---|
10 | class 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 | |
---|
41 | def image_pos(image, pos): |
---|
42 | return (pos[0] - image.get_width() / 2, |
---|
43 | pos[1] - image.get_height() / 2) |
---|
44 | |
---|
45 | |
---|
46 | class ImageRenderer(Renderer): |
---|
47 | def __init__(self, image): |
---|
48 | self._image = image |
---|
49 | |
---|
50 | def get_image(self): |
---|
51 | return self._image |
---|
52 | |
---|
53 | def rotate_image(self, image): |
---|
54 | angle = self.game_object.get_render_angle() * 180 / math.pi |
---|
55 | return pygame.transform.rotate(image, angle) |
---|
56 | |
---|
57 | def render_image(self, surface, image): |
---|
58 | image = self.rotate_image(image) |
---|
59 | pos = self.game_object.get_render_position(surface) |
---|
60 | surface.blit(image, image_pos(image, pos)) |
---|
61 | |
---|
62 | def render(self, surface): |
---|
63 | self.render_image(surface, self.get_image()) |
---|
64 | super(ImageRenderer, self).render(surface) |
---|
65 | |
---|
66 | |
---|
67 | class ImageStateRenderer(ImageRenderer): |
---|
68 | def __init__(self, state_images): |
---|
69 | self._state_images = state_images |
---|
70 | |
---|
71 | def get_image(self): |
---|
72 | return self._state_images[self.game_object.puzzler.get_state()] |
---|
73 | |
---|
74 | |
---|
75 | class TimedAnimatedRenderer(ImageRenderer): |
---|
76 | def __init__(self, images, frame_ticks=1): |
---|
77 | self._images = images |
---|
78 | self._frame_ticks = frame_ticks |
---|
79 | self._frame_tick = 0 |
---|
80 | self._frame = 0 |
---|
81 | |
---|
82 | def advance_tick(self): |
---|
83 | self._frame_tick += 1 |
---|
84 | if self._frame_tick > self._frame_ticks: |
---|
85 | self._frame_tick = 0 |
---|
86 | self._frame += 1 |
---|
87 | if self._frame >= len(self._images): |
---|
88 | self._frame = 0 |
---|
89 | |
---|
90 | def reset(self): |
---|
91 | self._frame_tick = 0 |
---|
92 | self._frame = 0 |
---|
93 | |
---|
94 | def get_image(self): |
---|
95 | return self._images[self._frame] |
---|
96 | |
---|
97 | def animate(self): |
---|
98 | self.advance_tick() |
---|
99 | |
---|
100 | |
---|
101 | class MovementAnimatedRenderer(TimedAnimatedRenderer): |
---|
102 | def animate(self): |
---|
103 | if self.game_object.is_moving: |
---|
104 | self.advance_tick() |
---|
105 | else: |
---|
106 | self.reset() |
---|
107 | |
---|
108 | |
---|
109 | class RendererSelectionRenderer(Renderer): |
---|
110 | def __init__(self, renderers): |
---|
111 | self._renderers = renderers |
---|
112 | |
---|
113 | def set_game_object(self, game_object): |
---|
114 | self.game_object = game_object |
---|
115 | for renderer in self._renderers.values(): |
---|
116 | renderer.set_game_object(game_object) |
---|
117 | |
---|
118 | @property |
---|
119 | def renderer(self): |
---|
120 | return self._renderers[self.select_renderer()] |
---|
121 | |
---|
122 | def render(self, surface): |
---|
123 | return self.renderer.render(surface) |
---|
124 | |
---|
125 | def animate(self): |
---|
126 | return self.renderer.animate() |
---|
127 | |
---|
128 | def select_renderer(self): |
---|
129 | raise NotImplementedError() |
---|
130 | |
---|
131 | |
---|
132 | class FacingSelectionRenderer(RendererSelectionRenderer): |
---|
133 | def select_renderer(self): |
---|
134 | return self.game_object.get_facing_direction() |
---|
135 | |
---|
136 | |
---|
137 | class ShapeRenderer(Renderer): |
---|
138 | def render(self, surface): |
---|
139 | self._render_shape(surface) |
---|
140 | super(ShapeRenderer, self).render(surface) |
---|
141 | |
---|
142 | |
---|
143 | class ShapeStateRenderer(ShapeRenderer): |
---|
144 | """Renders the shape in a different colour depending on the state. |
---|
145 | |
---|
146 | Requires the game object it's attached to to have a puzzler. |
---|
147 | """ |
---|
148 | def render(self, surface): |
---|
149 | if self.game_object.puzzler.get_state(): |
---|
150 | color = pygame.color.THECOLORS['green'] |
---|
151 | else: |
---|
152 | color = pygame.color.THECOLORS['red'] |
---|
153 | |
---|
154 | self.game_object.get_shape().color = color |
---|
155 | super(ShapeStateRenderer, self).render(surface) |
---|
156 | |
---|
157 | |
---|
158 | class Overlay(object): |
---|
159 | def set_game_object(self, game_object): |
---|
160 | self.game_object = game_object |
---|
161 | |
---|
162 | def render(self, surface, display_offset): |
---|
163 | pass |
---|
164 | |
---|
165 | def is_visible(self): |
---|
166 | return self.game_object.puzzler.get_state() |
---|
167 | |
---|
168 | |
---|
169 | class TextOverlay(Overlay): |
---|
170 | def __init__(self, text): |
---|
171 | self.text = text |
---|
172 | self.widget = LabelWidget((20, 20), self.text) |
---|
173 | |
---|
174 | def render(self, surface, display_offset): |
---|
175 | x, y = 20, 20 |
---|
176 | if display_offset[0] < 0: |
---|
177 | x += abs(display_offset[0]) |
---|
178 | if display_offset[1] < 0: |
---|
179 | y += abs(display_offset[1]) |
---|
180 | self.widget.rect.topleft = (x, y) |
---|
181 | self.widget.draw(surface) |
---|