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