blink
[sso] / shaken_and_spit_out / SSO.py
1 from os import listdir
2 from glob import glob
3 from random import choice
4 from collections import deque
5
6 from pygame import cursors, PixelArray, Surface, Color
7 from pygame.mouse import set_cursor, get_pos
8 from pygame.font import Font
9 from pygame.image import load
10 from pygame.draw import polygon, aalines
11 from pygame.locals import *
12
13 from shaken_and_spit_out.pgfw.Game import Game
14 from shaken_and_spit_out.pgfw.GameChild import GameChild
15 from shaken_and_spit_out.pgfw.Sprite import Sprite
16 from shaken_and_spit_out.pgfw.Animation import Animation
17
18 class SSO(Game):
19
20 def __init__(self):
21 Game.__init__(self)
22 set_cursor(*cursors.tri_left)
23 # string = u"\u2250\u2254\u2258"
24 # font = Font(self.get_resource("display", "font"), 24)
25 # self.message = font.render(string, True, (0, 255, 0))
26 self.backgrounds = [load(path).convert() for path in \
27 glob(self.get_resource("image", "background") + \
28 "*.png")]
29 self.used_backgrounds = []
30 self.set_random_background()
31 self.subscribe(self.respond)
32 self.title.activate()
33
34 def set_children(self):
35 Game.set_children(self)
36 self.floor = Floor(self)
37 self.title = Title(self)
38 self.cloud = Cloud(self)
39
40 def set_random_background(self):
41 index = choice(list(set(range(len(self.backgrounds))).\
42 difference(self.used_backgrounds)))
43 self.used_backgrounds.append(index)
44 if len(self.used_backgrounds) > 3:
45 self.used_backgrounds.pop(0)
46 self.background = self.backgrounds[index]
47
48 def respond(self, event):
49 if self.delegate.compare(event, "reset-game"):
50 self.set_random_background()
51
52 def update(self):
53 self.get_screen().blit(self.background, (0, 0))
54 self.cloud.update()
55 self.title.update()
56 self.floor.update()
57 # self.get_screen().fill((0, 0, 0))
58 # self.get_screen().blit(self.message, (100, 100))
59
60
61 class Title(GameChild):
62
63 def __init__(self, parent):
64 GameChild.__init__(self, parent)
65 self.delegate = self.get_delegate()
66 self.preface = Preface(self)
67 self.preamble = Preamble(self)
68 self.initializer = Initializer(self)
69 self.subscribe(self.respond)
70 self.subscribe(self.respond, MOUSEBUTTONDOWN)
71 self.deactivate()
72
73 def respond(self, event):
74 if self.delegate.is_command(event) and \
75 self.delegate.compare(event, "reset-game"):
76 self.activate()
77 elif self.active:
78 if event.type == MOUSEBUTTONDOWN and event.button == 1:
79 print "left-click"
80
81 def deactivate(self):
82 self.active = True
83
84 def activate(self):
85 self.active = True
86 self.preface.reset()
87 self.preamble.reset()
88
89 def update(self):
90 if self.active:
91 self.preface.update()
92 self.preamble.update()
93 self.initializer.update()
94
95
96 class Initializer(Sprite):
97
98 def __init__(self, parent):
99 Sprite.__init__(self, parent)
100 self.load_from_path(self.get_resource("image", "initializer"),
101 query="[0-9]*.png", omit=True)
102 self.add_frameset(0, name="waiting")
103 self.add_frameset((0, 2), (1000, 100), "blink")
104 self.set_frameset("waiting")
105 self.location.center = 380, 500
106
107 def update(self):
108 if self.parent.active:
109 mp = get_pos()
110 name = self.get_current_frameset().name
111 if name == "waiting" and self.location.collidepoint(mp):
112 self.set_frameset("blink")
113 elif name == "blink" and not self.location.collidepoint(mp):
114 self.set_frameset("waiting")
115 Sprite.update(self)
116
117
118
119 class Preface(GameChild, Surface):
120
121 def __init__(self, parent):
122 GameChild.__init__(self, parent)
123 ds = self.ds = self.get_display_surface()
124 w, h = ds.get_width(), int(round(ds.get_width() / 3))
125 Surface.__init__(self, (w, h), SRCALPHA)
126 self.background = Surface((w, h))
127 self.background.fill((0, 0, 0))
128 mask = self.mask = Surface((w, h), SRCALPHA)
129 pixels = PixelArray(mask)
130 for y in xrange(h):
131 alpha = int(round(255 - (float(y) / h) * 255))
132 for x in xrange(w):
133 pixels[x][y] = Color(255, 255, 255, alpha)
134 del pixels
135 self.set_text()
136 self.reset()
137
138 def set_text(self):
139 lines = self.get_configuration("text", "preface",
140 False).upper().split("/")
141 cc = 0
142 for line in lines:
143 if len(line) > cc:
144 cc = len(line)
145 cw = int(round((self.get_width() - 80) / float(cc)))
146 font = Font(self.get_resource("display", "font"), 12)
147 font.set_bold(True)
148 ch = font.metrics(lines[0][0])[0][3]
149 margin = 24
150 surface = Surface((cw * cc,
151 ch * len(lines) + margin * (len(lines) - 1)),
152 SRCALPHA)
153 text = self.text = Sprite(self, 200)
154 text.display_surface = self
155 magic_word = "PSPPIT"
156 magic_colors = deque(((255, 128, 128), (255, 255, 128), (128, 255, 128),
157 (128, 255, 255), (128, 128, 255),
158 (255, 128, 255)))
159 box_rect = Rect(0, 0, cw, ch)
160 for _ in xrange(len(magic_word)):
161 frame = surface.copy()
162 box_rect.top = 0
163 for line in lines:
164 box_rect.left = 0
165 extra = cc - len(line)
166 space_count = 0
167 for char in line:
168 if char == " ":
169 space_count += 1
170 space_widths = [1 + extra / space_count] * space_count
171 for ii in xrange(len(space_widths)):
172 if ii >= (extra % space_count):
173 break
174 space_widths[ii] += 1
175 magic_word_ii = 0
176 for ii in xrange(len(line)):
177 if line != lines[-1] and line[ii] == " ":
178 box_rect.left += cw * space_widths.pop(-1)
179 else:
180 if (magic_word_ii and \
181 magic_word_ii < len(magic_word)) \
182 or line[ii:ii + len(magic_word)] == magic_word:
183 color = magic_colors[magic_word_ii]
184 magic_word_ii += 1
185 else:
186 color = (255, 255, 255)
187 left = font.render(line[ii], True, (255, 0, 0))
188 frame.blit(left, self.get_glyph_rect(left, box_rect, 0))
189 right = font.render(line[ii], True, (0, 0, 255))
190 frame.blit(right, self.get_glyph_rect(right, box_rect,
191 4))
192 glyph = font.render(line[ii], True, color)
193 frame.blit(glyph, self.get_glyph_rect(glyph, box_rect,
194 2))
195 box_rect.left += cw
196 box_rect.top += ch + margin
197 text.add_frame(frame)
198 magic_colors.rotate(-1)
199 text.location.centerx = self.get_width() / 2
200
201 def get_glyph_rect(self, glyph, box_rect, offset):
202 gr = glyph.get_rect()
203 gr.center = box_rect.center
204 gr.left += offset
205 return gr
206
207 def reset(self):
208 self.text.location.top = self.get_height()
209
210 def update(self):
211 self.text.move(dy=-.5)
212 if self.text.location.bottom < 0:
213 self.reset()
214 self.blit(self.background, (0, 0))
215 self.text.update()
216 self.blit(self.mask, (0, 0), None, BLEND_RGBA_MIN)
217 self.ds.blit(self, (0, 0))
218
219
220 class Preamble(Animation):
221
222 def __init__(self, parent):
223 Animation.__init__(self, parent, self.increment_plate_index, 4000)
224 plates = self.plates = []
225 font = Font(self.get_resource("display", "font"), 12)
226 font.set_bold(True)
227 for line in self.get_configuration("text", "preamble",
228 False).upper().split("/"):
229 plates.append([])
230 spaced = str(line[0])
231 for character in line[1:]:
232 spaced += " " + character
233 for ii, color in enumerate(((0, 255, 255), (0, 255, 255),
234 (255, 12, 64))):
235 text = Sprite(self)
236 text.add_frame(font.render(spaced, True, color))
237 fr = self.get_game().floor.location
238 text.location.midtop = fr.centerx + (-1, 1, 0)[ii], \
239 fr.bottom + 10
240 plates[-1].append(text)
241 self.reset()
242 self.play()
243
244 def increment_plate_index(self):
245 index = self.plate_index + 1
246 if index >= len(self.plates):
247 index = 0
248 self.plate_index = index
249
250 def reset(self):
251 self.plate_index = 0
252
253 def update(self):
254 Animation.update(self)
255 for text in self.plates[self.plate_index]:
256 text.update()
257
258
259 class Arrow(Sprite):
260
261 def __init__(self, parent):
262 Sprite.__init__(self, parent, 360)
263 w, h = 56, 39
264 surface = Surface((w, h), SRCALPHA)
265 for color in ((255, 255, 0, 200), (0, 255, 255, 200), (255, 0, 255, 200)):
266 frame = surface.copy()
267 for offset in (0, 5):
268 points = [[int(round(val)) for val in coordinate] for
269 coordinate in self.get_coordinates(w - 10, h, offset)]
270 if offset == 5:
271 color = Color(*color)
272 hue, s, l, a = color.hsla
273 color.hsla = (hue + 180) % 360, 50, 100, 100
274 polygon(frame, color, points)
275 self.add_frame(frame)
276 floor = self.get_game().floor.location
277 self.location.midbottom = int(floor.w * .75), floor.top - 8
278
279 def get_coordinates(self, w, h, offset):
280 return (w * .25 + offset, 0), (w * .75 + offset, 0), \
281 (w * .75 + offset, h * .62), (w - 1 + offset, h * .62), \
282 (w * .5 + offset, h - 1), (0 + offset, h * .62), \
283 (w * .25 + offset, h * .62)
284
285
286 class Floor(Sprite):
287
288 def __init__(self, parent):
289 Sprite.__init__(self, parent, 340)
290 base = load(self.get_resource("image", "brick")).convert()
291 ds = self.ds = self.get_display_surface()
292 surface = Surface((ds.get_width(), 17))
293 for swap in False, True:
294 frame = surface.copy()
295 tile = base.copy()
296 if swap:
297 pixels = PixelArray(tile)
298 foreground, background = pixels[1][1], pixels[0][0]
299 for x in xrange(len(pixels)):
300 for y in xrange(len(pixels[0])):
301 if pixels[x][y] == foreground:
302 pixels[x][y] = background
303 else:
304 pixels[x][y] = foreground
305 del pixels
306 for x in xrange(0, frame.get_width(), tile.get_width()):
307 for y in xrange(0, frame.get_height(), tile.get_height()):
308 frame.blit(tile, (x, y))
309 self.add_frame(frame)
310 self.location.bottom = self.ds.get_height() - 34
311
312
313 class Cloud(GameChild):
314
315 def __init__(self, parent):
316 GameChild.__init__(self, parent)
317 self.ds = self.get_display_surface()
318 layers = self.layers = []
319 base = load(self.get_resource("image", "cloud")).convert_alpha()
320 for ii in xrange(3):
321 layer = Sprite(self)
322 frame = base.copy()
323 pixels = PixelArray(frame)
324 for x in xrange(len(pixels)):
325 for y in xrange(len(pixels[0])):
326 color = Color(*base.unmap_rgb(pixels[x][y]))
327 h, s, l, a = color.hsla
328 color.hsla = int((h + (0, 90, 180)[ii]) % 360), int(s), \
329 int(l), int(a * (.95 * (1 - ii * .15)))
330 pixels[x][y] = color
331 del pixels
332 layer.add_frame(frame)
333 layer.move((0, -5, 5)[ii])
334 layer.location.bottom = self.ds.get_height()
335 layer.add_location(offset=(0, -layer.location.h))
336 layers.append(layer)
337
338 def update(self):
339 fr = self.get_game().floor.location
340 ds = self.ds
341 ds.set_clip(fr.topleft, (ds.get_width(), ds.get_height() - fr.top))
342 for ii, layer in enumerate(self.layers):
343 layer.move(dy=1 + (ii * .5))
344 if layer.location.top > ds.get_height():
345 layer.move(dy=-layer.location.h)
346 layer.update()
347 ds.set_clip(None)