- enemy sprites added
authorFrank DeMarco <if.self.end@gmail.com>
Fri, 18 Oct 2019 08:07:15 +0000 (04:07 -0400)
committerFrank DeMarco <if.self.end@gmail.com>
Fri, 18 Oct 2019 08:07:15 +0000 (04:07 -0400)
- warping transition between levels
- glowing light animation along curve
- scrolling universe background added
- portal added at the end of the line
- cake explodes and leaves ow graphic after collision
- icons implemented by pygame and py2exe for windowing system

22 files changed:
.gitmodules
Cakewalk.py
config
lib/pgfw
resource/background.png
resource/fire/fire-1.png [new file with mode: 0644]
resource/fire/fire-2.png [new file with mode: 0644]
resource/fish/fish-1.png [new file with mode: 0644]
resource/fish/fish-2.png [new file with mode: 0644]
resource/high-scores [new file with mode: 0644]
resource/icon/16x16.png [new file with mode: 0644]
resource/icon/32x32.png [new file with mode: 0644]
resource/icon/cake.ico [new file with mode: 0644]
resource/ow.png [new file with mode: 0644]
resource/projectile/projectile-1.png [new file with mode: 0644]
resource/projectile/projectile-2.png [new file with mode: 0644]
resource/projectile/projectile-3.png [new file with mode: 0644]
resource/projectile/projectile-4.png [new file with mode: 0644]
resource/projector/projector-1.png [new file with mode: 0644]
resource/projector/projector-2.png [new file with mode: 0644]
resource/slicer/slicer-1.png [new file with mode: 0644]
resource/slicer/slicer-2.png [new file with mode: 0644]

index b9cf2a1..bfadec8 100644 (file)
@@ -1,3 +1,3 @@
 [submodule "lib/pgfw"]
        path = lib/pgfw
-       url = http://git.shampoo.ooo/pgfw
+       url = frank@shampoo.ooo:/var/www/git/pgfw
index 3b82743..9aae4b7 100644 (file)
@@ -3,12 +3,14 @@ import math, os
 
 import pygame
 from pygame.locals import *
+from pygame import gfxdraw
 
 from lib.pgfw.pgfw.Game import Game
 from lib.pgfw.pgfw.GameChild import GameChild
-from lib.pgfw.pgfw.Sprite import Sprite
+from lib.pgfw.pgfw.Sprite import Sprite, RainbowSprite
 from lib.pgfw.pgfw.Vector import Vector
 from lib.pgfw.pgfw.Audio import SoundEffect
+from lib.pgfw.pgfw.Animation import Animation
 from lib.pgfw.pgfw.extension import *
 
 LEVEL_POINTS = (
@@ -318,10 +320,12 @@ TITLE_POINTS = (
     Vector(805.000000, 272.000000),
 )
 
-class Cakewalk(Game):
+class Cakewalk(Game, Animation):
 
     def __init__(self):
+        pygame.display.set_icon(pygame.image.load("resource/icon/32x32.png"))
         Game.__init__(self)
+        Animation.__init__(self, self)
         self.subscribe(self.respond, MOUSEBUTTONDOWN)
         self.subscribe(self.respond, MOUSEBUTTONUP)
         self.subscribe(self.respond, KEYDOWN)
@@ -331,6 +335,7 @@ class Cakewalk(Game):
         self.title_active = True
         self.end_active = False
         self.curve_color = pygame.Color(255, 128, 128)
+        self.curve_color_offset = 0
         # self.control_points = [
         #     Vector(100, 100), Vector(150, 400), Vector(450, 400), Vector(500, 150)
         # ]
@@ -342,6 +347,22 @@ class Cakewalk(Game):
         self.current_level_index = 0
         # self.control_points = []
         dsr = self.get_display_surface().get_rect()
+        base = pygame.image.load(self.get_resource("background.png")).convert()
+        width = dsr.w + base.get_width() - dsr.w % base.get_width()
+        height = dsr.h + base.get_height() - dsr.h % base.get_height()
+        self.background = Sprite(self)
+        frame = pygame.Surface((width, height))
+        fill_tile(frame, base)
+        self.background = []
+        self.background_size = Vector(self)
+        for x in range(0, width + base.get_width(), base.get_width()):
+            for y in range(0, height + base.get_height(), base.get_height()):
+                tile = Sprite(self)
+                tile.add_frame(base)
+                tile.location.topleft = x, y
+                self.background.append(tile)
+        self.background_size.x = x + base.get_width()
+        self.background_size.y = y + base.get_height()
         # for ii in range(10):
         #     self.control_points.append(Vector(randint(0, dsr.w), randint(0, dsr.h)))
         # field = pygame.Rect(-50, -50, dsr.w + 100, dsr.h + 100)
@@ -386,11 +407,43 @@ class Cakewalk(Game):
         self.controls_diagram = Sprite(self)
         self.controls_diagram.load_from_path(self.get_resource("controls.png"), True)
         self.controls_diagram.location.midbottom = dsr.centerx, dsr.bottom - 100
+        self.ow = RainbowSprite(self, pygame.image.load(self.get_resource("ow.png")), 30)
+        self.warp = Sprite(self)
+        base = pygame.Surface((dsr.w * 2, dsr.h * 2), SRCALPHA)
+        scale = 1
+        angle = 0
+        while scale < 150:
+            surface = pygame.transform.rotozoom(self.character.frames[0], angle, scale)
+            frame = base.copy()
+            rect = surface.get_rect()
+            rect.center = frame.get_rect().center
+            frame.blit(surface, rect)
+            self.warp.add_frame(frame)
+            scale += 3
+            angle += 30
+        self.wipe = Sprite(self)
+        painting = get_blinds_frames(pygame.Surface(dsr.size, SRCALPHA), fill=(0, 0, 0))
+        full  = pygame.Surface(dsr.size, SRCALPHA)
+        full.fill((0, 0, 0))
+        erasing = get_blinds_frames(full)
+        for frame in painting + erasing:
+            self.wipe.add_frame(frame)
+        self.wipe = Sprite(self)
+        blank = pygame.Surface(dsr.size, SRCALPHA)
+        blank.fill((255, 255, 255, 255))
+        for frame in get_blinds_frames(blank, .04, 5):
+            self.wipe.add_frame(frame)
+        self.register(self.explode, self.blow_up)
+        self.get_configuration().type_declarations.add("bool", "effects", "crumbs")
         self.reset()
+        pygame.event.clear()
 
     def reset(self):
         self.title_active = True
         self.end_active = False
+        self.ow.halt(self.ow.toggle_hidden)
+        self.ow.hide()
+        self.crumbs = []
         if self.level_music.get_num_channels() > 0:
             self.level_music.fadeout(3000)
         if self.title_music.get_num_channels() == 0:
@@ -406,10 +459,16 @@ class Cakewalk(Game):
         if os.path.exists(path):
             results = sorted(list(map(int, open(path, "r"))))[:5]
             dsr = self.get_display_surface().get_rect()
-            locations = get_points_on_line((0, 0), dsr.topright, len(results) + 2)[1:-1]
+            locations = get_points_on_line((0, 0), dsr.topright, len(results) + 3)[1:-1]
+            size = 18
+            font = pygame.font.Font(self.get_resource("BPmono.ttf"), size)
+            top = Sprite(self)
+            top.add_frame(render_box(font, " BEST ", True, (0, 0, 0), (200, 200, 200), (0, 0, 0)))
+            top.location.midtop = locations[0].x, locations[0].y - 2
+            self.high_scores.append(top)
             for ii, time in enumerate(results):
-                result = self.get_time_sprite(time, 18, pygame.Color(50, 50, 50))
-                result.location.midtop = locations[ii]
+                result = self.get_time_sprite(time, size, True)
+                result.location.midtop = locations[ii + 1].x, locations[ii + 1].y - 2
                 self.high_scores.append(result)
 
     def return_character_to_beginning(self):
@@ -477,7 +536,7 @@ class Cakewalk(Game):
         elif event.type == KEYDOWN:
             if event.key == K_e and pygame.key.get_mods() & KMOD_CTRL:
                 self.editing = not self.editing
-            elif event.key == K_SPACE:
+            elif event.key == K_SPACE and not self.is_playing(self.blow_up):
                 if self.title_active:
                     self.title_active = False
                     self.increment_level(0)
@@ -493,7 +552,7 @@ class Cakewalk(Game):
                 for point in self.get_control_points():
                     print(point)
                 for enemy in self.enemies:
-                    if type(enemy) != Projectile and type(enemy) != Fire:
+                    if isinstance(enemy, Projectile) and isinstance(enemy, Fire):
                         print(enemy)
             elif event.key == K_RIGHT and pygame.key.get_mods() & KMOD_CTRL:
                 self.increment_level(1)
@@ -504,24 +563,24 @@ class Cakewalk(Game):
                 if closest is not None:
                     mod = 1 if event.key == K_UP else -1
                     if pygame.key.get_mods() & KMOD_ALT:
-                        if type(closest) == Slicer:
+                        if isinstance(closest, Slicer):
                             closest.increase_stray(2.5 * mod)
-                        elif type(closest) == Fish:
+                        elif isinstance(closest, Fish):
                             closest.increase_radius(2 * mod)
-                        elif type(closest) == Projector:
+                        elif isinstance(closest, Projector):
                             closest.increase_frequency(250 * -mod)
                     else:
-                        if type(closest) == Slicer:
+                        if isinstance(closest, Slicer):
                             closest.increase_speed(.25 * mod)
-                        elif type(closest) == Fish:
+                        elif isinstance(closest, Fish):
                             closest.increase_speed(math.pi / 256.0 * mod)
-                        elif type(closest) == Projector:
+                        elif isinstance(closest, Projector):
                             closest.increase_speed(.25 * mod)
                     self.reset_enemies()
             elif event.key == K_t and pygame.key.get_mods() & KMOD_CTRL and self.editing:
                 self.enemy_type_index = (self.enemy_type_index + 1) % len(self.enemy_types)
                 print("currently selected enemy type: %s" % self.enemy_types[self.enemy_type_index])
-        elif event.type == KEYUP and event.key == K_SPACE:
+        elif event.type == KEYUP and event.key == K_SPACE and not self.is_playing(self.blow_up):
             self.character_accelerating = False
         elif event.type == MOUSEBUTTONUP and event.button == 1:
             self.selected = None
@@ -532,7 +591,7 @@ class Cakewalk(Game):
         min_distance = None
         closest = None
         for enemy in self.enemies:
-            if type(enemy) != Projectile and type(enemy) != Fire:
+            if isinstance(enemy, Projectile) and isinstance(enemy, Fire):
                 center = self.translate_point_to_screen(enemy.center)
                 distance = get_distance(center, self.mouse)
                 if min_distance is None or distance < min_distance:
@@ -545,15 +604,53 @@ class Cakewalk(Game):
 
     def set_level(self, index):
         self.current_level_index = index
-        self.set_curve()
+        self.crumbs = []
+        start = self.set_curve()
         self.set_enemies()
         self.return_character_to_beginning()
+        if not self.end_active:
+            self.set_goal(start)
 
     def set_curve(self):
         self.curve = []
         points = self.get_control_points()
         for ii in range(0, len(points) - 3, 3):
             self.curve.extend(compute_bezier_points(points[ii:ii + 4], self.bezier_resolution))
+        self.curve_plate = Sprite(self)
+        light_colors = []
+        start = randint(0, 300)
+        for hue in range(start, start + 60, 4):
+            light_colors.append(get_hsla_color(hue, 100, 50, 100))
+        groups = self.translate_points_to_screen(self.curve)
+        for offset in range(30):
+            frame = pygame.Surface(self.get_display_surface().get_size(), SRCALPHA)
+            self.curve_plate.add_frame(frame)
+            for group in groups:
+                if group:
+                    # pygame.draw.lines(frame, self.curve_color, False, group, 7)
+                    for ii, point in enumerate(group[:-1]):
+                        # angle = get_angle(point, group[ii + 1]) + math.pi * .5
+                        # left_start = [int(round(n)) for n in get_endpoint(point, angle - math.pi * .5, 3, False)]
+                        # left_end = [int(round(n)) for n in get_endpoint(group[ii + 1], angle - math.pi * .5, 3, False)]
+                        if (offset - ii) // 15 % 2:
+                            color = light_colors[(offset - ii) % 15]
+                            # color = self.curve_color
+                        else:
+                            color = pygame.Color("white")
+                        # pygame.draw.line(frame, color, left_start, left_end, 2)
+                        # right_start = [int(round(n)) for n in get_endpoint(point, angle + math.pi * .5, 3, False)]
+                        # right_end = [int(round(n)) for n in get_endpoint(group[ii + 1], angle + math.pi * .5, 3, False)]
+                        # pygame.draw.line(frame, color, right_start, right_end, 2)
+                        pygame.draw.line(frame, color, point, group[ii + 1], 1)
+                        # right = get_endpoint(point, angle + math.pi * .75, 5, False)
+                        # pygame.draw.line(ds, [self.curve_color, pygame.Color("white")][(ii + self.curve_color_offset) % 2],
+                        #                  point, group[ii + 1], 3)
+                        # pygame.draw.lines(ds, colors[ii % 10], False, (left, point, right), 2)
+                    # for ii, point in enumerate(group):
+                    #     pygame.draw.circle(ds, colors[ii % 10], (point.x, point.y), 5)
+                    #     gfxdraw.aacircle(ds, point.x, point.y, 5, colors[ii % 10])
+        self.curve_plate.location.center = self.get_display_surface().get_rect().center
+        return start
 
     def get_curve_index_from_offset(self, offset):
         return int(round(len(self.curve) * offset))
@@ -566,18 +663,102 @@ class Cakewalk(Game):
         seconds = time // 1000 - minutes * 60
         return minutes, seconds
 
-    def get_time_sprite(self, time, size, background=None):
+    def get_time_sprite(self, time, size, box=False):
         minutes, seconds = self.get_minutes_and_seconds(time)
         font = pygame.font.Font(self.get_resource("BPmono.ttf"), size)
-        surface = font.render(" %im %is " % (minutes, seconds), True, self.curve_color, background)
+        text = " %im %is " % (minutes, seconds)
+        if box:
+            surface = render_box(font, text, True, (0, 0, 0), (200, 200, 200), (0, 0, 0))
+        else:
+            surface = font.render(text, True, pygame.Color(255, 255, 255))
         sprite = Sprite(self)
         sprite.add_frame(surface)
         return sprite
 
+    def explode(self, center, radius=1):
+        max_r = 40
+        ring = Sprite(self)
+        frame = pygame.Surface([radius * 2 + 2] * 2, SRCALPHA)
+        ring.add_frame(frame)
+        for ii, sub_radius in enumerate(range(radius, 0, -5)):
+            if ii <= 3:
+                alpha = int((1 - float(sub_radius) / max_r) * 255)
+                color = (
+                    pygame.Color(255, 192, 18, alpha), pygame.Color(255, 128, 128, alpha),
+                    pygame.Color(255, 255, 128, alpha))[ii % 3]
+                pygame.draw.circle(
+                    frame,
+                    color,
+                    ring.location.center, sub_radius, 1)
+        ring.location.center = center
+        ring.update()
+        if radius < max_r:
+            self.play(self.explode, play_once=True, center=center, radius=radius + 1)
+
+    def set_goal(self, start):
+        self.goal = Sprite(self)
+        location = self.translate_point_to_screen(self.curve[-1])
+        start = randint(60, 359)
+        for offset in range(6):
+            frame = pygame.Surface((26, 48), SRCALPHA)
+            for ii, y in enumerate(range(40, 7, -1)):
+                hue = range(start, start - 60, -10)[(ii - offset) % 6]
+                alpha = int(round(y / 40.0 * 100))
+                color = get_hsla_color(hue, 100, 50, alpha)
+                gfxdraw.filled_ellipse(frame, 13, y, 13, 8, color)
+                # gfxdraw.aaellipse(frame, 13, y, 13, 8, color)
+            self.goal.add_frame(frame)
+        self.goal.location.midbottom = location.x, location.y + 8
+
+    def blow_up(self, increment):
+        if not self.warp.is_playing() and not self.wipe.is_playing():
+            self.warp.unhide()
+            self.warp.get_current_frameset().reset()
+            self.warp.play()
+        elif self.warp.is_playing():
+            self.warp.location.center = self.character.location.center
+            self.warp.update()
+            frameset = self.warp.get_current_frameset()
+            if frameset.current_index == frameset.length() - 1:
+                self.warp.halt()
+                self.warp.hide()
+                self.wipe.hide()
+                self.wipe.get_current_frameset().reset()
+                self.wipe.play()
+                if not self.end_active and self.is_final_level():
+                    self.end_active = True
+                    fp = open("resource/high-scores", "a")
+                    fp.write("%i\n" % self.time_elapsed)
+                    fp.close()
+                    dsr = self.get_display_surface().get_rect()
+                    self.character_pos = Vector(*dsr.center)
+                    self.time_result = self.get_time_sprite(self.time_elapsed, 40)
+                    self.time_result.location.midtop = dsr.centerx, 287
+                    self.level_music.fadeout(2000)
+                    self.title_music.play(-1, 0, 500)
+                self.increment_level(increment)
+        else:
+            intermediate = pygame.Surface(self.get_display_surface().get_size(), SRCALPHA)
+            intermediate.blit(self.warp.get_current_frame(), self.warp.location)
+            intermediate.blit(self.wipe.get_current_frame(), (0, 0), None, BLEND_RGBA_MIN)
+            self.get_display_surface().blit(intermediate, (0, 0))
+            self.warp.update()
+            self.wipe.update()
+            frameset = self.wipe.get_current_frameset()
+            if frameset.current_index == frameset.length() - 1:
+                self.wipe.halt()
+                self.halt(self.blow_up)
+
     def update(self):
         ds = self.get_display_surface()
         dsr = ds.get_rect()
-        ds.fill(pygame.Color(100, 100, 100))
+        for tile in self.background:
+            tile.move(-2, -2)
+            if tile.location.right <= 0:
+                tile.move(self.background_size.x, 0)
+            if tile.location.bottom <= 0:
+                tile.move(0, self.background_size.y)
+            tile.update()
         if self.editing:
             mouse_motion = Vector(*pygame.mouse.get_rel())
             self.mouse += mouse_motion
@@ -594,25 +775,39 @@ class Cakewalk(Game):
                 pygame.draw.circle(ds, pygame.Color("green"), self.translate_point_to_screen(self.selected), 10)
                 self.set_curve()
                 self.reset_enemies()
-                # self.set_enemies()
             for p in self.get_control_points():
                 pygame.draw.circle(ds, pygame.Color("blue"), self.translate_point_to_screen(p), 4)
             for group in self.translate_points_to_screen(self.get_control_points()):
                 pygame.draw.lines(ds, pygame.Color(200, 200, 200), False, group)
-        if not self.title_active:
+        if not self.title_active and not self.is_playing(self.blow_up):
             for enemy in self.enemies:
                 if self.character.collide_mask(enemy) and self.next_point_index > 0:
+                    self.play(self.explode, play_once=True, center=self.character.location.center)
                     self.return_character_to_beginning()
+                    self.ow.location.bottomleft = self.translate_point_to_screen(
+                        Vector(*self.character.location.topright))
+                    self.ow.unhide()
+                    self.ow.play(self.ow.toggle_hidden, play_once=True, delay=1800)
+                    pile = Sprite(self)
+                    self.crumbs.append(pile)
+                    frame = pygame.Surface((20, 20), SRCALPHA)
+                    for _ in range(randint(3, 6)):
+                        color = get_hsla_color(randint(50, 70), randint(70, 100), randint(50, 90))
+                        frame.fill(
+                            color, Rect((randint(0, 17), randint(0, 17)), [randint(1, 3)] * 2))
+                    pile.add_frame(frame)
+                    pile.location.center = self.character.location.midbottom
                     choice(self.ow_sfx).play(x=self.character.location.centerx)
-                    if type(enemy) == Projectile:
+                    if isinstance(enemy, Projectile):
                         self.enemies.remove(enemy)
                     break
-        for group in self.translate_points_to_screen(self.curve):
-            if group:
-                pygame.draw.lines(ds, self.curve_color, False, group, 1)
+        self.curve_plate.update()
+        if self.get_configuration("effects", "crumbs"):
+            for pile in self.crumbs:
+                pile.update()
         if self.editing:
             pygame.draw.circle(ds, pygame.Color("yellow"), self.mouse, 3)
-        if not self.title_active:
+        if not self.title_active and not self.is_playing(self.blow_up):
             if self.character_accelerating:
                 self.character_speed += .5 + abs(self.character_speed) * .125
             else:
@@ -624,7 +819,7 @@ class Cakewalk(Game):
         else:
             self.character_speed = 1
         distance_remaining = abs(self.character_speed)
-        while distance_remaining:
+        while distance_remaining and not self.is_playing(self.blow_up) and not self.end_active:
             if self.character_speed < 0 and self.next_point_index == 0:
                 self.character_speed = 0
                 break
@@ -634,19 +829,7 @@ class Cakewalk(Game):
                     increment = 0
                 else:
                     increment = 1
-                if not self.end_active and self.is_final_level():
-                    self.end_active = True
-                    self.character_pos = Vector(*dsr.center)
-                    self.time_result = self.get_time_sprite(self.time_elapsed, 40)
-                    self.time_result.location.midtop = dsr.centerx, 287
-                    self.level_music.fadeout(2000)
-                    self.title_music.play(-1, 0, 500)
-                    self.set_curve()
-                    self.set_enemies()
-                    fp = open("resource/high-scores", "a")
-                    fp.write("%i\n" % self.time_elapsed)
-                    fp.close()
-                self.increment_level(increment)
+                self.play(self.blow_up, increment=increment)
                 self.teleport_sfx.play()
                 break
             if self.character_speed > 0:
@@ -679,14 +862,25 @@ class Cakewalk(Game):
                 framerate = 200
             self.character.set_framerate(framerate)
         self.character.location.midbottom = self.translate_point_to_screen(self.character_pos)
+        if not self.end_active:
+            self.goal.update()
         if not self.title_active:
             self.time_elapsed += self.time_filter.get_last_frame_duration()
-        if not self.title_active:
+            outgoing = []
             for enemy in self.enemies:
                 if self.editing:
                     pygame.draw.circle(ds, pygame.Color("black"),
                                        list(map(int, self.translate_point_to_screen(enemy.center))), 3)
-                enemy.update()
+                if self.is_playing(self.blow_up) and (
+                        isinstance(enemy, Projectile) or isinstance(enemy, Projector)):
+                    move = False
+                else:
+                    move = True
+                enemy.update(move=move)
+                if isinstance(enemy, Projectile) and enemy.marked_to_delete:
+                    outgoing.append(enemy)
+            for enemy in outgoing:
+                self.enemies.remove(enemy)
         else:
             self.controls_diagram.update()
             for score in self.high_scores:
@@ -694,6 +888,8 @@ class Cakewalk(Game):
         if self.end_active:
             self.time_result.update()
         self.character.update()
+        self.ow.update()
+        Animation.update(self)
 
     def translate_points_to_screen(self, points):
         groups = [[]]
@@ -731,15 +927,16 @@ class Cakewalk(Game):
 class Slicer(Sprite):
 
     def __init__(self, parent, offset, speed=5, stray=60):
-        Sprite.__init__(self, parent)
+        Sprite.__init__(self, parent, 500)
         self.offset = offset
         self.speed = speed
         self.stray = stray
-        frame = pygame.Surface((12, 12), SRCALPHA)
-        color = pygame.Color("green")
-        pygame.draw.line(frame, color, (0, 0), frame.get_size())
-        pygame.draw.line(frame, color, (0, frame.get_height()), (frame.get_width(), 0))
-        self.add_frame(frame)
+        # frame = pygame.Surface((12, 12), SRCALPHA)
+        # color = pygame.Color("green")
+        # pygame.draw.line(frame, color, (0, 0), frame.get_size())
+        # pygame.draw.line(frame, color, (0, frame.get_height()), (frame.get_width(), 0))
+        # self.add_frame(frame)
+        self.load_from_path(self.get_resource("slicer"), True)
         ii = self.get_game().get_curve_index_from_offset(self.offset)
         self.center = self.get_game().curve[ii]
         angle = get_angle(self.get_game().curve[ii - 1], self.get_game().curve[ii + 1])
@@ -760,47 +957,44 @@ class Slicer(Sprite):
     def __repr__(self):
         return "<Slicer %.5f %.5f %.5f>" % (self.offset, self.speed, self.stray)
 
-    def update(self):
-        ii = self.get_game().get_curve_index_from_offset(self.offset)
-        self.center = self.get_game().curve[ii]
-        angle = get_angle(self.get_game().curve[ii - 1], self.get_game().curve[ii + 1])
-        self.end = get_endpoint(self.center, angle, self.stray, False)
-        self.start = get_endpoint(self.center, angle + math.pi, self.stray, False)
-        speed = self.speed
-        if self.toward_end:
-            distance = get_distance(self.pos, self.end)
-            if distance < speed:
-                self.pos = self.end.copy()
-                speed -= distance
-                self.toward_end = False
-        else:
-            distance = get_distance(self.pos, self.start)
-            if distance < speed:
-                self.pos = self.start.copy()
-                speed -= distance
-                self.toward_end = True
-        if self.toward_end:
-            step = get_step(self.pos, self.end, speed)
-            self.pos.move(*step)
-        else:
-            step = get_step(self.pos, self.start, speed)
-            self.pos.move(*step)
-        self.location.center = self.get_game().translate_point_to_screen(self.pos)
+    def update(self, move=True):
+        if move:
+            ii = self.get_game().get_curve_index_from_offset(self.offset)
+            self.center = self.get_game().curve[ii]
+            angle = get_angle(self.get_game().curve[ii - 1], self.get_game().curve[ii + 1])
+            self.end = get_endpoint(self.center, angle, self.stray, False)
+            self.start = get_endpoint(self.center, angle + math.pi, self.stray, False)
+            speed = self.speed
+            if self.toward_end:
+                distance = get_distance(self.pos, self.end)
+                if distance < speed:
+                    self.pos = self.end.copy()
+                    speed -= distance
+                    self.toward_end = False
+            else:
+                distance = get_distance(self.pos, self.start)
+                if distance < speed:
+                    self.pos = self.start.copy()
+                    speed -= distance
+                    self.toward_end = True
+            if self.toward_end:
+                step = get_step(self.pos, self.end, speed)
+                self.pos.move(*step)
+            else:
+                step = get_step(self.pos, self.start, speed)
+                self.pos.move(*step)
+            self.location.center = self.get_game().translate_point_to_screen(self.pos)
         Sprite.update(self)
 
 
 class Fish(Sprite):
 
     def __init__(self, parent, offset, speed=math.pi / 32.0, radius=30):
-        Sprite.__init__(self, parent)
+        Sprite.__init__(self, parent, 300)
         self.offset = offset
         self.speed = speed
         self.radius = radius
-        frame = pygame.Surface((12, 12), SRCALPHA)
-        color = pygame.Color("cyan")
-        pygame.draw.line(frame, color, (0, 0), frame.get_size())
-        pygame.draw.line(frame, color, (0, frame.get_height()), (frame.get_width(), 0))
-        self.add_frame(frame)
+        self.load_from_path(self.get_resource("fish"), True)
         ii = self.get_game().get_curve_index_from_offset(self.offset)
         self.center = self.get_game().curve[ii]
         self.reset()
@@ -817,12 +1011,13 @@ class Fish(Sprite):
     def increase_radius(self, increase):
         self.radius += increase
 
-    def update(self):
-        ii = self.get_game().get_curve_index_from_offset(self.offset)
-        self.center = self.get_game().curve[ii]
-        self.angle += self.speed
-        self.location.center = self.get_game().translate_point_to_screen(
-            get_point_on_circle(self.center, self.radius, self.angle, False))
+    def update(self, move=True):
+        if move:
+            ii = self.get_game().get_curve_index_from_offset(self.offset)
+            self.center = self.get_game().curve[ii]
+            self.angle += self.speed
+            self.location.center = self.get_game().translate_point_to_screen(
+                get_point_on_circle(self.center, self.radius, self.angle, False))
         Sprite.update(self)
 
 
@@ -834,18 +1029,22 @@ class Projector(Sprite):
         self.speed = speed
         self.frequency = frequency
         self.center = pos
-        frame = pygame.Surface((12, 12), SRCALPHA)
-        color = pygame.Color("yellow")
-        pygame.draw.line(frame, color, (0, 0), frame.get_size())
-        pygame.draw.line(frame, color, (0, frame.get_height()), (frame.get_width(), 0))
-        self.add_frame(frame)
-        self.register(self.shoot, interval=self.frequency)
-        self.play(self.shoot)
+        self.load_from_path(self.get_resource("projector"), True)
+        self.add_frameset([0], name="released", switch=True)
+        self.add_frameset([1], name="charging")
+        self.register(self.charge, interval=self.frequency)
+        self.register(self.release)
+        self.play(self.charge)
 
     def reset(self):
         self.reset_timer()
 
-    def shoot(self):
+    def charge(self):
+        self.set_frameset("charging")
+        self.play(self.release, play_once=True, delay=300)
+
+    def release(self):
+        self.set_frameset("released")
         self.get_game().enemies.append(Projectile(self))
 
     def __repr__(self):
@@ -857,9 +1056,10 @@ class Projector(Sprite):
     def increase_frequency(self, increase):
         self.frequency += increase
 
-    def update(self):
-        self.location.center = self.get_game().translate_point_to_screen(self.pos)
-        self.register(self.shoot, interval=self.frequency)
+    def update(self, move=True):
+        if move:
+            self.location.center = self.get_game().translate_point_to_screen(self.pos)
+            self.register(self.charge, interval=self.frequency)
         Sprite.update(self)
 
 
@@ -871,23 +1071,21 @@ class Projectile(Sprite):
         self.speed = parent.speed
         self.angle = get_angle(self.pos, self.get_game().character.location.center) + math.pi / 2
         self.center = self.pos
-        frame = pygame.Surface((12, 12), SRCALPHA)
-        color = pygame.Color("yellow")
-        pygame.draw.line(frame, color, (0, 0), frame.get_size())
-        pygame.draw.line(frame, color, (0, frame.get_height()), (frame.get_width(), 0))
-        self.add_frame(frame)
+        self.load_from_path(self.get_resource("projectile"), True)
+        self.marked_to_delete = False
 
     def reset(self):
         self.get_game().enemies.remove(self)
 
-    def update(self):
-        self.pos.move(*get_delta(self.angle, self.speed, False))
-        self.location.center = self.get_game().translate_point_to_screen(self.pos)
-        self.center = self.pos
+    def update(self, move=True):
         dsr = self.get_display_surface().get_rect()
+        if move:
+            self.pos.move(*get_delta(self.angle, self.speed, False))
+            self.location.center = self.get_game().translate_point_to_screen(self.pos)
+            self.center = self.pos
         if self.pos.y < 0 or self.pos.y > dsr.bottom or \
            self.pos.x < 0 or self.pos.x > dsr.right:
-            self.get_game().enemies.remove(self)
+            self.marked_to_delete = True
         else:
             Sprite.update(self)
 
@@ -895,17 +1093,13 @@ class Projectile(Sprite):
 class Fire(Sprite):
 
     def __init__(self, parent, pos, angle, speed, mirroring=False):
-        Sprite.__init__(self, parent)
+        Sprite.__init__(self, parent, 300)
         self.pos = pos
         self.angle = angle
         self.speed = speed
         self.center = self.pos
         self.mirroring = mirroring
-        frame = pygame.Surface((12, 12), SRCALPHA)
-        color = pygame.Color("orange")
-        pygame.draw.line(frame, color, (0, 0), frame.get_size())
-        pygame.draw.line(frame, color, (0, frame.get_height()), (frame.get_width(), 0))
-        self.add_frame(frame)
+        self.load_from_path(self.get_resource("fire"), True)
         self.register(self.mirror, interval=4000)
         if self.mirroring:
             self.play(self.mirror)
@@ -916,19 +1110,10 @@ class Fire(Sprite):
     def mirror(self):
         self.angle += math.pi
 
-    def update(self):
-        self.pos.move(*get_delta(self.angle, self.speed, False))
-        # dsr = self.get_display_surface().get_rect()
-        # if self.pos.x > dsr.right:
-        #     self.pos.move(-dsr.w, 0)
-        # elif self.pos.x < 0:
-        #     self.pos.move(dsr.w, 0)
-        # if self.pos.y > dsr.bottom:
-        #     self.pos.move(0, -dsr.h)
-        # elif self.pos.y < 0:
-        #     self.pos.move(0, dsr.h)
-        self.location.center = self.get_game().translate_point_to_screen(self.pos)
-        # self.location.center = self.pos
+    def update(self, move=True):
+        if move:
+            self.pos.move(*get_delta(self.angle, self.speed, False))
+            self.location.center = self.get_game().translate_point_to_screen(self.pos)
         Sprite.update(self)
 
 
diff --git a/config b/config
index 14ade7a..ed38c02 100644 (file)
--- a/config
+++ b/config
@@ -1,10 +1,11 @@
 [setup]
 title = cakewalk
 url = http://shampoo.ooo/
-version = 0.1
+version = 0.2
 init-script = OPEN-GAME
 additional-packages = lib
-data-exclude = local/, *.pyc, __pycache__, dist/
+data-exclude = local/, *.pyc, __pycache__, dist/, high-scores
+windows-icon-path = resource/icon/cake.ico
 
 [display]
 dimensions = 864, 486
@@ -15,3 +16,6 @@ quit = K_ESCAPE
 
 [mouse]
 visible = no
+
+[effects]
+crumbs = no
index 5403896..869fed1 160000 (submodule)
--- a/lib/pgfw
+++ b/lib/pgfw
@@ -1 +1 @@
-Subproject commit 5403896b79511a2e88718ec15e62089c46c12f13
+Subproject commit 869fed171d55f4665f3b41f18d26a50d8507d3b2
index e97dbea..88e0b58 100644 (file)
Binary files a/resource/background.png and b/resource/background.png differ
diff --git a/resource/fire/fire-1.png b/resource/fire/fire-1.png
new file mode 100644 (file)
index 0000000..1c23599
Binary files /dev/null and b/resource/fire/fire-1.png differ
diff --git a/resource/fire/fire-2.png b/resource/fire/fire-2.png
new file mode 100644 (file)
index 0000000..8e9ef43
Binary files /dev/null and b/resource/fire/fire-2.png differ
diff --git a/resource/fish/fish-1.png b/resource/fish/fish-1.png
new file mode 100644 (file)
index 0000000..caca195
Binary files /dev/null and b/resource/fish/fish-1.png differ
diff --git a/resource/fish/fish-2.png b/resource/fish/fish-2.png
new file mode 100644 (file)
index 0000000..914fbf5
Binary files /dev/null and b/resource/fish/fish-2.png differ
diff --git a/resource/high-scores b/resource/high-scores
new file mode 100644 (file)
index 0000000..c5e6cea
--- /dev/null
@@ -0,0 +1,6 @@
+1537174
+779671
+716765
+962779
+696253
+604939
diff --git a/resource/icon/16x16.png b/resource/icon/16x16.png
new file mode 100644 (file)
index 0000000..19d35ea
Binary files /dev/null and b/resource/icon/16x16.png differ
diff --git a/resource/icon/32x32.png b/resource/icon/32x32.png
new file mode 100644 (file)
index 0000000..28392f3
Binary files /dev/null and b/resource/icon/32x32.png differ
diff --git a/resource/icon/cake.ico b/resource/icon/cake.ico
new file mode 100644 (file)
index 0000000..930f19e
Binary files /dev/null and b/resource/icon/cake.ico differ
diff --git a/resource/ow.png b/resource/ow.png
new file mode 100644 (file)
index 0000000..99f551a
Binary files /dev/null and b/resource/ow.png differ
diff --git a/resource/projectile/projectile-1.png b/resource/projectile/projectile-1.png
new file mode 100644 (file)
index 0000000..dfc3212
Binary files /dev/null and b/resource/projectile/projectile-1.png differ
diff --git a/resource/projectile/projectile-2.png b/resource/projectile/projectile-2.png
new file mode 100644 (file)
index 0000000..d5b31ad
Binary files /dev/null and b/resource/projectile/projectile-2.png differ
diff --git a/resource/projectile/projectile-3.png b/resource/projectile/projectile-3.png
new file mode 100644 (file)
index 0000000..bd34103
Binary files /dev/null and b/resource/projectile/projectile-3.png differ
diff --git a/resource/projectile/projectile-4.png b/resource/projectile/projectile-4.png
new file mode 100644 (file)
index 0000000..4dce427
Binary files /dev/null and b/resource/projectile/projectile-4.png differ
diff --git a/resource/projector/projector-1.png b/resource/projector/projector-1.png
new file mode 100644 (file)
index 0000000..aa41c18
Binary files /dev/null and b/resource/projector/projector-1.png differ
diff --git a/resource/projector/projector-2.png b/resource/projector/projector-2.png
new file mode 100644 (file)
index 0000000..e5d0c02
Binary files /dev/null and b/resource/projector/projector-2.png differ
diff --git a/resource/slicer/slicer-1.png b/resource/slicer/slicer-1.png
new file mode 100644 (file)
index 0000000..b624bac
Binary files /dev/null and b/resource/slicer/slicer-1.png differ
diff --git a/resource/slicer/slicer-2.png b/resource/slicer/slicer-2.png
new file mode 100644 (file)
index 0000000..3d356f3
Binary files /dev/null and b/resource/slicer/slicer-2.png differ