1import pygame
2import pygame
3import math
4import random
5import sys
6import os
7
8import PixelPerfect
9
10from pygame.locals import *
11
12from shark import Shark
13from health import Health
14from cannonball import Cannonball
15from steamboat import Steamboat
16from particles import Particles
17from mine import Mine
18from score import Score
19from water import Water
20from pirateboat import Pirateboat
21from seagull import Seagull
22from level import Level
23from titanic import Titanic
24from powerup import Powerup
25
26import cloud
27
28import util
29
30from locals import *
31
32rr=random.random
33
34def rrr(left,right):
35    return left+rr()*(right-left)
36
37class Game:
38    sky = None
39
40
41
42
43
44    def __init__(self, screen, endless = False):
45        self.screen = screen
46
47        self.sharks = []
48        self.shark_sprites = pygame.sprite.Group()
49
50        self.player = Steamboat()
51        self.player_sprite = pygame.sprite.Group()
52        self.player_sprite.add(self.player)
53
54        self.health = Health()
55        self.health_sprite = pygame.sprite.Group()
56        self.health_sprite.add(self.health)
57
58        self.damage_count = 0
59
60        self.t = 0
61
62        self.water = Water.global_water
63        #Water.global_water = self.water
64        self.water_sprite = pygame.sprite.Group()
65        self.water_sprite.add(self.water)
66
67        if not Game.sky:
68            Game.sky = util.load_image("taivas")
69            Game.sky = pygame.transform.scale(self.sky, (SCREEN_WIDTH, SCREEN_HEIGHT))
70
71        self.cannonballs = []
72        self.cannonball_sprites = pygame.sprite.Group()
73
74        self.pirates = []
75        self.pirate_sprites = pygame.sprite.Group()
76
77        self.titanic = None
78        self.titanic_sprite = pygame.sprite.Group()
79
80        self.seagulls = []
81        self.seagull_sprites = pygame.sprite.Group()
82
83        self.particles = Particles()
84        self.particle_sprite = pygame.sprite.Group()
85        self.particle_sprite.add(self.particles)
86
87        self.mines = []
88        self.mine_sprites = pygame.sprite.Group()
89
90        self.score = Score()
91        self.score_sprite = pygame.sprite.Group()
92        self.score_sprite.add(self.score)
93
94        self.powerups = []
95        self.powerup_sprites = pygame.sprite.Group()
96
97        self.level = Level(endless)
98
99        self.lastshot = MIN_FIRE_DELAY + 1
100
101        self.gameover = False
102        self.gameover_image = None
103        self.gameover_rect = None
104        self.done = False
105
106        self.pause = False
107        self.pause_image = util.bigfont.render("Pause", Variables.alpha, (0,0,0))
108        self.pause_rect = self.pause_image.get_rect()
109        self.pause_rect.center = self.screen.get_rect().center
110
111        self.spacepressed = None
112
113    def run(self):
114        while not self.done:
115            if not self.pause:
116                if not self.gameover:
117                    self.spawn_enemies()
118
119                self.update_enemies()
120                self.player.update()
121                self.health.update()
122                cloud.update()
123                for cb in self.cannonballs:
124                    if not cb.underwater:
125                        #~ particle_point=cb.rect.left, cb.rect.top
126                        particle_point = cb.tail_point()
127                        self.particles.add_trace_particle(particle_point)
128                        particle_point = [particle_point[i] + cb.vect[i]/2 for i in (0,1)]
129                        self.particles.add_trace_particle(particle_point)
130                    if cb.special and (not cb.underwater or rr()>0.6) :
131                        particle_point = cb.tail_point()
132                        self.particles.add_explosion_particle(particle_point)
133                    und_old=cb.underwater
134                    cb.update()
135                    if cb.underwater and not und_old:
136                        for i in range(5):
137                            particle_point=cb.rect.right-4.0+rr()*8.0, cb.rect.top+rr()*2.0
138                            self.particles.add_water_particle(particle_point)
139                    if (cb.rect.right < 0 and cb.vect[0] < 0) or (cb.rect.left > SCREEN_WIDTH and cb.vect[0] > 0) or (cb.rect.top > SCREEN_HEIGHT):
140                        self.cannonballs.remove(cb)
141                        self.cannonball_sprites.remove(cb)
142                self.score.update()
143
144                # Add steam particles
145                if Variables.particles:
146                    particle_point = self.player.get_point((5.0 + rr() * 9.0, 0))
147                    particle_point[0] += self.player.rect.centerx
148                    particle_point[1] += self.player.rect.centery
149                    if self.spacepressed and self.t > self.spacepressed + FPS * 3:
150                        pass
151                        #~ self.particles.add_fire_steam_particle(particle_point)
152                    else:
153                        self.particles.add_steam_particle(particle_point)
154
155                    particle_point = self.player.get_point((19.0 + rr() * 7.0, 5.0))
156                    particle_point[0] += self.player.rect.centerx
157                    particle_point[1] += self.player.rect.centery
158                    if self.spacepressed and self.t > self.spacepressed + FPS * 3:
159                        pass
160                        #~ self.particles.add_fire_steam_particle(particle_point)
161                    else:
162                        self.particles.add_steam_particle(particle_point)
163
164                    if self.titanic:
165                        for j in range(4):
166                            particle_point = self.titanic.get_point((49 + rr() * 9.0 + 28 * j, 25))
167                            particle_point[0] += self.titanic.rect.centerx
168                            particle_point[1] += self.titanic.rect.centery
169                            self.particles.add_steam_particle(particle_point)
170
171                    self.particles.update()
172
173                self.water.update()
174
175                if self.player.splash:
176                    if Variables.particles:
177                        for i in range(10):
178                            r = rr()
179                            x = int(r * self.player.rect.left + (1.0-r) * self.player.rect.right)
180                            point = (x, self.water.get_water_level(x))
181                            self.particles.add_water_particle(point)
182
183                for powerup in self.powerups:
184                    powerup.update()
185                    if powerup.picked:
186                        self.powerups.remove(powerup)
187                        self.powerup_sprites.remove(powerup)
188
189                if not self.gameover:
190                    self.check_collisions()
191
192                if self.health.hearts_left == 0 and not self.player.dying:
193                    self.player.die()
194
195                if self.player.dying and not self.player.dead:
196                    if Variables.particles:
197                        self.particles.add_explosion_particle((self.player.rect.centerx, self.player.rect.centery))
198                        self.particles.add_debris_particle((self.player.rect.centerx, self.player.rect.centery))
199
200                if self.player.dead:
201                    #self.done = True
202                    self.set_gameover()
203
204                if self.damage_count > 0:
205                    self.damage_count -= 1
206                self.lastshot += 1
207                self.t += 1
208
209            self.draw()
210
211            self.handle_events()
212
213        return self.score.get_score()
214
215    def damage_player(self):
216        self.health.damage()
217        for i in range(10):
218            particle_point = self.player.get_point((rr() * 26.0, rr() * 10.0))
219            particle_point[0] += self.player.rect.centerx
220            particle_point[1] += self.player.rect.centery
221            self.particles.add_debris_particle(particle_point)
222        self.player.blinks+=12
223
224
225    def set_gameover(self, message = "Game Over"):
226        self.gameover = True
227        images = []
228        height = 0
229        width = 0
230        for text in message.split("\n"):
231            images.append(util.bigfont.render(text, Variables.alpha, (0,0,0)))
232            height += images[-1].get_height()
233            if images[-1].get_width() > width:
234                width = images[-1].get_width()
235        self.gameover_image = pygame.Surface((width, height), SRCALPHA, 32)
236        self.gameover_image.fill((0,0,0,0))
237        for i in range(len(images)):
238            rect = images[i].get_rect()
239            rect.top = i * images[i].get_height()
240            rect.centerx = width / 2
241            self.gameover_image.blit(images[i], rect)
242
243        self.gameover_rect = self.gameover_image.get_rect()
244        self.gameover_rect.center = self.screen.get_rect().center
245
246    def take_screenshot(self):
247        i = 1
248        filename = "sshot.tga"
249        while os.path.exists(filename):
250            i += 1
251            filename = "sshot" + str(i) + ".tga"
252
253        pygame.image.save(self.screen, filename)
254        print("Screenshot saved as " + filename)
255
256    def handle_events(self):
257        nextframe = False
258        framecount = 0
259        while not nextframe:
260          # wait until there's at least one event in the queue
261          #nextframe = True
262          pygame.event.post(pygame.event.wait())
263          for event in pygame.event.get():
264            #event = pygame.event.wait()
265            if event.type == QUIT or \
266               event.type == KEYDOWN and event.key == K_ESCAPE:
267                self.done = True
268                nextframe = True
269            elif event.type == NEXTFRAME:
270                framecount += 1
271                nextframe = True
272            elif self.gameover:
273                if event.type == JOYBUTTONDOWN:
274                    self.done = True
275                    nextframe = True
276                elif event.type == KEYDOWN:
277                    self.done = True
278                    nextframe = True
279                continue
280            elif event.type == JOYAXISMOTION:
281                if event.axis == 0:
282                    if event.value < -0.5:
283                        self.player.move_left(True)
284                    elif event.value > 0.5:
285                        self.player.move_right(True)
286                    else:
287                        self.player.move_left(False)
288                        self.player.move_right(False)
289            elif event.type == JOYBUTTONDOWN:
290                if event.button == 0:
291                    if not self.pause:
292                        self.player.jump()
293                elif event.button == 1:
294                    if not self.pause:
295                        if self.lastshot > MIN_FIRE_DELAY and not self.player.dying:
296                            cb = Cannonball(self.player.rect, self.player.angle)
297                            self.cannonballs.append(cb)
298                            self.cannonball_sprites.add(cb)
299                            particle_point = self.player.get_point((42.0,10.0))
300                            particle_point[0] += self.player.rect.centerx
301                            particle_point[1] += self.player.rect.centery
302                            for i in range(4):
303                                self.particles.add_fire_steam_particle(particle_point)
304                            self.lastshot = 0
305                            self.spacepressed = self.t
306                elif event.button == 5:
307                    self.take_screenshot()
308                elif event.button == 8:
309                    self.set_pause()
310            elif event.type == KEYDOWN:
311                if event.key == K_LEFT:
312                    self.player.move_left(True)
313                elif event.key == K_RIGHT:
314                    self.player.move_right(True)
315                elif event.key == K_SPACE:
316                    if not self.pause:
317                    # Only 3 cannonballs at once
318                    # Maximum firing rate set at the top
319                        if self.lastshot > MIN_FIRE_DELAY and not self.player.dying:
320                            cb = Cannonball(self.player.rect, self.player.angle)
321                            self.cannonballs.append(cb)
322                            self.cannonball_sprites.add(cb)
323                            particle_point = self.player.get_point((42.0,10.0))
324                            particle_point[0] += self.player.rect.centerx
325                            particle_point[1] += self.player.rect.centery
326                            for i in range(4):
327                                self.particles.add_fire_steam_particle(particle_point)
328                            self.lastshot = 0
329                            self.spacepressed = self.t
330                elif event.key == K_UP:
331                    if not self.pause:
332                        self.player.jump()
333                elif event.key == K_s:
334                    self.take_screenshot()
335                elif event.key == K_p:
336                    self.set_pause()
337            elif event.type == KEYUP:
338                if event.key == K_LEFT:
339                    self.player.move_left(False)
340                elif event.key == K_RIGHT:
341                    self.player.move_right(False)
342                elif event.key == K_SPACE:
343                    if not self.pause:
344                        if self.spacepressed and self.t > self.spacepressed + FPS * 3 and not self.player.dying:
345                            cb = Cannonball(self.player.rect, self.player.angle, special=True)
346                            self.cannonballs.append(cb)
347                            self.cannonball_sprites.add(cb)
348                            particle_point = self.player.get_point((42.0,10.0))
349                            particle_point[0] += self.player.rect.centerx
350                            particle_point[1] += self.player.rect.centery
351                            for i in range(30):
352                                self.particles.add_fire_steam_particle((particle_point[0]+rrr(-4,4),particle_point[1]+rrr(-3,3)))
353                            self.lastshot = 0
354                        self.spacepressed = None
355            elif event.type == JOYBUTTONUP:
356                if event.button == 1:
357                    if not self.pause:
358                        if self.spacepressed and self.t > self.spacepressed + FPS * 3 and not self.player.dying:
359                            cb = Cannonball(self.player.rect, self.player.angle, special=True)
360                            self.cannonballs.append(cb)
361                            self.cannonball_sprites.add(cb)
362                            particle_point = self.player.get_point((42.0,10.0))
363                            particle_point[0] += self.player.rect.centerx
364                            particle_point[1] += self.player.rect.centery
365                            for i in range(30):
366                                self.particles.add_fire_steam_particle((particle_point[0]+rrr(-4,4),particle_point[1]+rrr(-3,3)))
367                            self.lastshot = 0
368                        self.spacepressed = None
369
370        #if framecount > 1:
371        #    print str(self.t) + ": missed " + str(framecount - 1) + " frames!"
372
373    def set_pause(self):
374        self.pause = not self.pause
375
376        #if framecount > 1:
377        #    print str(self.t) + ": missed " + str(framecount - 1) + " frames!"
378
379    def set_pause(self):
380        self.pause = not self.pause
381
382    def draw(self):
383        self.screen.blit(Game.sky, self.screen.get_rect())
384        self.health_sprite.draw(self.screen)
385        self.score_sprite.draw(self.screen)
386        self.player_sprite.draw(self.screen)
387        self.powerup_sprites.draw(self.screen)
388        self.pirate_sprites.draw(self.screen)
389        if self.titanic:
390            self.titanic_sprite.draw(self.screen)
391        self.seagull_sprites.draw(self.screen)
392        cloud.draw(self.screen)
393        self.shark_sprites.draw(self.screen)
394        self.mine_sprites.draw(self.screen)
395        self.cannonball_sprites.draw(self.screen)
396        self.water_sprite.draw(self.screen)
397        if Variables.particles:
398            self.particle_sprite.draw(self.screen)
399
400        if self.pause:
401            self.screen.blit(self.pause_image, self.pause_rect)
402
403        if self.gameover:
404            self.screen.blit(self.gameover_image, self.gameover_rect)
405
406        if self.level.t < 120:
407            image = None
408            i = 0
409            if self.level.phase < len(self.level.phase_messages):
410              for text in self.level.phase_messages[self.level.phase].split("\n"):
411                image = util.smallfont.render(text, Variables.alpha, (0,0,0))
412                rect = image.get_rect()
413                rect.centerx = self.screen.get_rect().centerx
414                rect.top = 100 + rect.height * i
415                blit_image = pygame.Surface((image.get_width(), image.get_height()))
416                blit_image.fill((166,183,250))
417                blit_image.set_colorkey((166,183,250))
418                blit_image.blit(image, image.get_rect())
419                if self.level.t > 60:
420                    blit_image.set_alpha(255 - (self.level.t - 60) * 255 / 60)
421                self.screen.blit(blit_image, rect)
422                i += 1
423
424        pygame.display.flip()
425
426
427
428    def check_collisions(self):
429        collisions = PixelPerfect.spritecollide_pp(self.player, self.powerup_sprites, 0)
430        for powerup in collisions:
431            if not powerup.fading:
432                if not self.player.dying:
433                    self.health.add()
434                    powerup.pickup()
435
436        collisions = PixelPerfect.spritecollide_pp(self.player, self.mine_sprites, 0)
437
438        for mine in collisions:
439            if not mine.exploding:
440                if not self.player.dying:
441                    self.damage_player()
442                    mine.explode()
443
444        collisions = PixelPerfect.spritecollide_pp(self.player, self.shark_sprites, 0)
445
446        for shark in collisions:
447            if not shark.dying:
448                if not self.player.dying:
449                    self.damage_player()
450                    shark.die()
451
452        collisions = PixelPerfect.spritecollide_pp(self.player, self.cannonball_sprites, 0)
453
454        for cb in collisions:
455            if not self.player.dying:
456                self.damage_player()
457                self.cannonballs.remove(cb)
458                self.cannonball_sprites.remove(cb)
459                if (Variables.sound):
460                   Mine.sound.play() # Umm... the mine has a nice explosion sound.
461
462        collisions = PixelPerfect.groupcollide_pp(self.cannonball_sprites, self.shark_sprites, 0, 0)
463
464        for cb in dict.keys(collisions):
465            for shark in collisions[cb]:
466                # The test on cb.vect is a rude hack preventing cannonballs from pirate ships from killing sharks.
467                if not shark.dying and cb.vect[0] > 0:
468                    self.score.add(15)
469                    shark.die()
470                    # give her a part of ball's momentum:
471                    shark.dx+=cb.vect[0]*0.6
472                    shark.dy+=cb.vect[1]*0.4
473                    if not cb.special:
474                        self.cannonballs.remove(cb)
475                        self.cannonball_sprites.remove(cb)
476                    break
477
478        collisions = PixelPerfect.groupcollide_pp(self.cannonball_sprites, self.seagull_sprites, 0, 0)
479
480        for cb in dict.keys(collisions):
481            for seagull in collisions[cb]:
482                # cb.vect test is a rude hack preventing pirates from killing seagulls
483                if not seagull.dying and cb.vect[0] > 0:
484                    self.score.add(75)
485                    seagull.die()
486                    # give her a part of ball's momentum:
487                    seagull.vect[0]+=cb.vect[0]*0.4
488                    seagull.vect[1]+=cb.vect[1]*0.4
489                    if not cb.special:
490                        self.cannonballs.remove(cb)
491                        self.cannonball_sprites.remove(cb)
492                    break
493
494        collisions = PixelPerfect.groupcollide_pp(self.cannonball_sprites, self.pirate_sprites, 0, 0)
495
496        for cb in dict.keys(collisions):
497            for pirate in collisions[cb]:
498                # cb.vect hack for preventing pirates from killing each other
499                if not pirate.dying and cb.vect[0] > 0:
500                    if (Variables.sound):
501                       Mine.sound.play() # Umm... the mine has a nice sound.
502                    self.score.add(25)
503                    pirate.damage()
504                    for i in range(6):
505                        self.particles.add_wood_particle((pirate.rect.centerx+rrr(-0,15), pirate.rect.centery+rrr(-10,20)))
506                    if not cb.special:
507                        self.cannonballs.remove(cb)
508                        self.cannonball_sprites.remove(cb)
509                    break
510
511        if self.titanic:
512            collisions = PixelPerfect.spritecollide_pp(self.titanic, self.cannonball_sprites, 0)
513            for cb in collisions:
514                if not self.titanic.dying and cb.vect[0] > 0:
515                    if (Variables.sound):
516                       Mine.sound.play()
517                    if cb.special:
518                        #special round is hard to fire, so lets reward our crafty player
519                        self.titanic.damage(12)
520                        self.score.add(100)
521                    else:
522                        self.score.add(7)
523                        self.titanic.damage()
524                    self.cannonballs.remove(cb)
525                    self.cannonball_sprites.remove(cb)
526                    break
527
528    def update_enemies(self):
529        self.mine_sprites.update()
530        for mine in self.mines:
531            if mine.exploding:
532                if mine.explode_frames == 0:
533                    self.mines.remove(mine)
534                    self.mine_sprites.remove(mine)
535                # this should really be done in the Mine class, but oh well, here's some explosion effects:
536                if Variables.particles:
537                    self.particles.add_explosion_particle((mine.rect.centerx, mine.rect.top + mine.image.get_rect().centerx))
538                    self.particles.add_debris_particle((mine.rect.centerx, mine.rect.top + mine.image.get_rect().centerx))
539            if mine.rect.right < self.screen.get_rect().left:
540                self.mines.remove(mine)
541                self.mine_sprites.remove(mine)
542
543        self.shark_sprites.update()
544        for shark in self.sharks:
545            if shark.dying:
546                if Variables.particles:
547                    self.particles.add_blood_particle(shark.rect.center)
548            if shark.rect.right < self.screen.get_rect().left or shark.dead:
549                self.sharks.remove(shark)
550                self.shark_sprites.remove(shark)
551
552        self.pirate_sprites.update()
553        for pirate in self.pirates:
554            if pirate.t % 50 == 0 and not pirate.dying:
555                # Pirate shoots, this should probably be handled by the Pirateboat class
556                cb = Cannonball(pirate.rect, pirate.angle, left = True)
557                self.cannonballs.append(cb)
558                self.cannonball_sprites.add(cb)
559                particle_point = pirate.get_point((0.0,10.0))
560                particle_point[0] += pirate.rect.centerx
561                particle_point[1] += pirate.rect.centery
562                for i in range(4):
563                    self.particles.add_fire_steam_particle(particle_point)
564
565            if pirate.rect.right < self.screen.get_rect().left or pirate.dead:
566                self.pirates.remove(pirate)
567                self.pirate_sprites.remove(pirate)
568            elif pirate.dying:
569                if Variables.particles:
570                    self.particles.add_explosion_particle((pirate.rect.centerx, pirate.rect.centery))
571                    self.particles.add_wood_particle((pirate.rect.centerx, pirate.rect.centery))
572
573        if self.titanic:
574            self.titanic.update()
575            if self.titanic.t % 100 == 0 and not self.titanic.dying:
576                for i in range(3):
577                    cb = Cannonball(self.titanic.rect, self.titanic.angle + (i-1)*10 - 50, left = True)
578                    self.cannonballs.append(cb)
579                    self.cannonball_sprites.add(cb)
580            elif self.titanic.t % 100 == 50 and not self.titanic.dying:
581                for i in range(3):
582                    cb = Cannonball(self.titanic.rect, self.titanic.angle + (i-1)*10 - 52.5, left = True)
583                    self.cannonballs.append(cb)
584                    self.cannonball_sprites.add(cb)
585            if self.titanic.dead:
586                self.set_gameover("Congratulations!\nYou sunk Titanic!")
587                self.titanic = None
588
589        self.seagull_sprites.update()
590        for seagull in self.seagulls:
591            if seagull.rect.right < 0 or seagull.dead:
592                self.seagulls.remove(seagull)
593                self.seagull_sprites.remove(seagull)
594
595    def spawn_enemies(self):
596        spawns = self.level.get_spawns()
597        if spawns[Level.SHARKS]:
598            # Make a new shark
599            self.sharks.append(Shark())
600            self.shark_sprites.add(self.sharks[-1])
601
602        if spawns[Level.PIRATES]:
603            # Make a new pirate ship
604            self.pirates.append(Pirateboat())
605            self.pirate_sprites.add(self.pirates[-1])
606
607        if spawns[Level.MINES]:
608            self.mines.append(Mine())
609            self.mine_sprites.add(self.mines[-1])
610
611        if spawns[Level.SEAGULLS]:
612            self.seagulls.append(Seagull())
613            self.seagull_sprites.add(self.seagulls[-1])
614
615        if spawns[Level.TITANIC]:
616            self.titanic = Titanic()
617            self.titanic_sprite.add(self.titanic)
618
619        if spawns[Level.POWERUPS]:
620            self.powerups.append(Powerup())
621            self.powerup_sprites.add(self.powerups[-1])
622
623