changeset 177:e0573297b17c

Snake that moves (my god that was an epic struggle).
author Simon Cross <hodgestar@gmail.com>
date Wed, 14 Sep 2011 01:45:41 +0200
parents e7f65e737b0a
children 74f8d8cbc51d
files mamba/mutators.py mamba/snake.py
diffstat 2 files changed, 84 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/mamba/mutators.py	Wed Sep 14 01:36:44 2011 +0200
+++ b/mamba/mutators.py	Wed Sep 14 01:45:41 2011 +0200
@@ -1,7 +1,7 @@
 """Mutations to apply to images when they're loaded."""
 
 from pygame.transform import rotate
-from pygame.locals import BLEND_ADD, BLEND_MULT
+from pygame.locals import BLEND_MULT
 
 from mamba.data import load_image
 
@@ -35,9 +35,9 @@
 
 # sprites mutator aliases
 RIGHT = NULL
-DOWN = R90
+UP = R90
 LEFT = R180
-UP = R270
+DOWN = R270
 
 
 # overlays
--- a/mamba/snake.py	Wed Sep 14 01:36:44 2011 +0200
+++ b/mamba/snake.py	Wed Sep 14 01:45:41 2011 +0200
@@ -2,69 +2,60 @@
 
 from pygame.sprite import Group
 from pygame.locals import BLEND_MULT
-#from pygame.draw import lines
-from pygame.draw import rect
 
-from mamba.sprites import BaseSprite, tile_sizify
+from mamba.constants import TILE_SIZE
+from mamba.sprites import BaseSprite
 from mamba import mutators
 
 
 class Snake(object):
 
     UP, DOWN, LEFT, RIGHT = [(0, -1), (0, 1), (-1, 0), (1, 0)]
-    SPEED = 60.0  # pixels / s
 
     def __init__(self, tile_pos, orientation):
         self.segments = self.create_segments(tile_pos, orientation)
         self.segment_group = Group()
         self.segment_group.add(*self.segments)
         self.set_orientation(orientation)
-        self.pos = tile_sizify(tile_pos)
-        self.length = 80
-        self.xs = self.snake_length(self.length)
-        self.ys = self.snake_length(self.length)
+        self.speed = 60.0  # pixel / s
+        self.frac_ds = 0.0
+        self.segments = self.create_segments(tile_pos, orientation)
+        self.segment_group = Group(reversed(self.segments))
 
     head = property(fget=lambda self: self.segments[0])
     tail = property(fget=lambda self: self.segments[-1])
 
-    def snake_length(self, size=10):
-        result = []
-        for i in range(size):
-            result.append(0)
-        return result
-
     def create_segments(self, tile_pos, orientation):
-        x, y = tile_pos
+        tx, ty = tile_pos
         dx, dy = orientation
-        return [Head((x, y)),
-                Body((x + dx, y + dy)),
-                Tail((x + 2 * dx, y + 2 * dy)),
+        return [Head((tx, ty)),
+                Body((tx + dx, ty + dy)),
+                Tail((tx + 2 * dx, ty + 2 * dy)),
                 ]
 
     def draw(self, surface):
-        # self.segment_group.draw(surface)
-        (x, y), l = self.pos, self.length
-        #lines(surface, (0, 0, 255), False, [(x, y), (x + l, y)], 20)
-        for i in range(0, len(self.xs)):
-            rect(surface, (0, 0, 255), [(self.xs[i], self.ys[i]), (20, 20)])
+        self.segment_group.draw(surface)
 
     def update(self, dt):
-        x, y = self.pos
-        ox, oy = self.orientation
-        ds = self.SPEED * dt
-        self.pos = (x + ox * ds, y + oy * ds)
+        ds = dt * self.speed + self.frac_ds
+        ds, self.frac_ds = divmod(ds, 1)
+        ds = int(ds)
+        while True:
+            tile_state = self.head.get_tile_state()
+            shifted, ds = self.head.shift_head(ds)
+            if not shifted:
+                break
+            self.head.set_orientation(self.orientation)
+            for segment in self.segments[1:]:
+                old_tile_state = segment.get_tile_state()
+                segment.shift_tile(tile_state)
+                tile_state = old_tile_state
 
-        for i in reversed(xrange(1, len(self.xs))):
-            self.xs[i] = self.xs[i - 1]
-            self.ys[i] = self.ys[i - 1]
-        self.xs[0] = self.pos[0]
-        self.ys[0] = self.pos[1]
+        for segment in self.segments:
+            segment.shift_pixels(ds)
 
     def set_orientation(self, orientation):
         self.orientation = orientation
-        #self.orientation = orientation
-        #for segment in self.segments:
-        #    segment.set_orientation(self)
 
 
 class Segment(BaseSprite):
@@ -80,7 +71,7 @@
         super(Segment, self).__init__()
         self.set_base_image(image_name)
         self._colour_overlay = self.GREEN
-        self._orientation = Snake.UP
+        self.orientation = Snake.UP
 
         self.make_images()
         self.update_image()
@@ -102,10 +93,10 @@
                     all_muts)
 
     def update_image(self):
-        self.image = self._images[self._orientation]
+        self.image = self._images[self.orientation]
 
     def set_orientation(self, orientation):
-        self._orientation = orientation
+        self.orientation = orientation
         self.update_image()
 
     def set_colour(self, colour_overlay):
@@ -113,6 +104,24 @@
         self.make_images()
         self.update_image()
 
+    def get_tile_state(self):
+        return self.tile_pos, self.orientation
+
+    def shift_tile(self, tile_state):
+        """Shift this segment to the tile the other one was on.
+
+        Also reset the position to be the center of the tile.
+        """
+        tile_pos, orientation = tile_state
+        self.set_tile_pos(tile_pos)
+        self.orientation = orientation
+
+    def shift_pixels(self, distance):
+        """Shift this segment a number of pixels."""
+        dx, dy = self.orientation
+        dx, dy = distance * dx, distance * dy
+        self.rect = self.rect.move(dx, dy)
+
 
 class Head(Segment):
     CLOSED = "snake-head"
@@ -144,6 +153,40 @@
         self.make_images()
         self.update_image()
 
+    def shift_head(self, ds):
+        """Shift the head a number of pixels in the direction of it
+        orientation.
+        """
+        dx, dy = self.orientation
+        TX, TY = TILE_SIZE
+        rx, ry = self.rect.left, self.rect.top
+        tx, ty = self.tile_pos
+        # WARNING: Tri-state logic ahead
+        # (Tri-state logic is the mamba's natural habitat)
+        if dx != 0:
+            newdx = rx + ds * dx
+            newtx = newdx / TX
+            if newtx > tx:
+                self.set_tile_pos((tx + 1, ty))
+                ds = newdx - self.rect.left
+                return True, ds
+            elif newtx < tx - 1:
+                self.set_tile_pos((tx - 1, ty))
+                ds = self.rect.left - newdx
+                return True, ds
+        else:
+            newdy = ry + ds * dy
+            newty = newdy / TY
+            if newty > ty:
+                self.set_tile_pos((tx, ty + 1))
+                ds = newdy - self.rect.top
+                return True, ds
+            elif newty < ty - 1:
+                self.set_tile_pos((tx, ty - 1))
+                ds = self.rect.top - newdy
+                return True, ds
+        return False, ds
+
 
 class Body(Segment):
     def __init__(self, tile_pos):