Cleanup more state - hopefully fix some stale game running bugs
[erdslangetjie.git] / erdslangetjie / __main__.py
index aa261e447d0ae52feb4ba678befc5960aa5bbda2..935d212b98ccb6b246859c91b24efee773d63122 100644 (file)
@@ -1,20 +1,26 @@
-import pygame
-
-from erdslangetjie.constants import TILE_SIZE
+from erdslangetjie.constants import TILE_SIZE, LEFT, RIGHT, UP, DOWN
 
 from kivy.app import App
 from kivy.uix.widget import Widget
 from kivy.uix.relativelayout import RelativeLayout
 from kivy.uix.scrollview import ScrollView
 from kivy.uix.label import Label
 
 from kivy.app import App
 from kivy.uix.widget import Widget
 from kivy.uix.relativelayout import RelativeLayout
 from kivy.uix.scrollview import ScrollView
 from kivy.uix.label import Label
+from kivy.uix.popup import Popup
 from kivy.graphics import Color, Rectangle
 from kivy.utils import platform
 from kivy.graphics import Color, Rectangle
 from kivy.utils import platform
+from kivy.clock import Clock
+from kivy.config import Config
 
 from erdslangetjie.level import LevelList
 from erdslangetjie.data import load_image
 from erdslangetjie.player import ThePlayer, Nemesis
 
 
 
 from erdslangetjie.level import LevelList
 from erdslangetjie.data import load_image
 from erdslangetjie.player import ThePlayer, Nemesis
 
 
+if platform() != 'android':
+    Config.set('graphics', 'width', '1026')
+    Config.set('graphics', 'height', '760')
+
+
 class GameWindow(RelativeLayout):
 
     def __init__(self, level_list, app):
 class GameWindow(RelativeLayout):
 
     def __init__(self, level_list, app):
@@ -42,6 +48,8 @@ class GameWindow(RelativeLayout):
             raise RuntimeError('No entry point')
         self.player_tile = None
         self.nemesis_tile = None
             raise RuntimeError('No entry point')
         self.player_tile = None
         self.nemesis_tile = None
+        self.timer_set = False
+        self.move_counter = 0
 
         self.player.pos = self.level_obj.enter_pos
         if platform() != 'android':
 
         self.player.pos = self.level_obj.enter_pos
         if platform() != 'android':
@@ -60,18 +68,22 @@ class GameWindow(RelativeLayout):
         for tile_line in tiles:
             bx = 0
             for tile in tile_line:
         for tile_line in tiles:
             bx = 0
             for tile in tile_line:
-                node = Widget(size=(TILE_SIZE, TILE_SIZE),
-                        pos=(bx, by),
-                        size_hint=(None, None))
-                self.add_widget(node)
-                with node.canvas:
-                    Color(1, 1, 1)
-                    Rectangle(pos=node.pos, size=node.size,
-                            texture=tile.texture)
-                self.tiles[(bx, by)] = node
+                self.draw_tile((bx, by), tile)
                 bx += TILE_SIZE
             by += TILE_SIZE
 
                 bx += TILE_SIZE
             by += TILE_SIZE
 
+    def draw_tile(self, pos, tile):
+        if pos in self.tiles:
+            self.remove_widget(self.tiles[pos])
+        node = Widget(size=(TILE_SIZE, TILE_SIZE),
+                pos=pos, size_hint=(None, None))
+        self.add_widget(node)
+        with node.canvas:
+            Color(1, 1, 1)
+            Rectangle(pos=node.pos, size=node.size,
+                    texture=tile.texture)
+        self.tiles[pos] = node
+
     def draw_player(self):
         if self.player_tile:
             self.remove_widget(self.player_tile)
     def draw_player(self):
         if self.player_tile:
             self.remove_widget(self.player_tile)
@@ -111,20 +123,15 @@ class GameWindow(RelativeLayout):
                 if true_point[0] >= self.view.size[0]:
                     self.view.scroll_x += self.x_scroll_margin
                     true_point = self.to_parent(*check_point)
                 if true_point[0] >= self.view.size[0]:
                     self.view.scroll_x += self.x_scroll_margin
                     true_point = self.to_parent(*check_point)
-                    #print '-x', self.view.scroll_x, self.view.scroll_y
                 elif true_point[0] < 0:
                     self.view.scroll_x -= self.x_scroll_margin
                     true_point = self.to_parent(*check_point)
                 elif true_point[0] < 0:
                     self.view.scroll_x -= self.x_scroll_margin
                     true_point = self.to_parent(*check_point)
-                    #print '+x', self.view.scroll_x, self.view.scroll_y
                 elif true_point[1] >= self.view.size[1]:
                     self.view.scroll_y += self.y_scroll_margin
                     true_point = self.to_parent(*check_point)
                 elif true_point[1] >= self.view.size[1]:
                     self.view.scroll_y += self.y_scroll_margin
                     true_point = self.to_parent(*check_point)
-                    #print '+y', self.view.scroll_x, self.view.scroll_y
                 elif true_point[1] < 0:
                     self.view.scroll_y -= self.y_scroll_margin
                     true_point = self.to_parent(*check_point)
                 elif true_point[1] < 0:
                     self.view.scroll_y -= self.y_scroll_margin
                     true_point = self.to_parent(*check_point)
-                    #print '-y', self.view.scroll_x, self.view.scroll_y
-                #print true_point, self.view.size
 
     def included(self, point, margin):
         if point[0] < margin:
 
     def included(self, point, margin):
         if point[0] < margin:
@@ -156,15 +163,15 @@ class GameWindow(RelativeLayout):
         self.keyboard.unbind(on_key_down=self._on_key_down)
 
     def _on_key_down(self, keyboard, keycode, text, modifiers):
         self.keyboard.unbind(on_key_down=self._on_key_down)
 
     def _on_key_down(self, keyboard, keycode, text, modifiers):
-        # FIXME - likely portablity issues
         direction = None
         direction = None
-        if keycode[0] == pygame.K_UP:
+        letter = keycode[1].lower()
+        if letter in UP:
             direction = (0, 1)
             direction = (0, 1)
-        elif keycode[0] == pygame.K_DOWN:
+        elif letter in DOWN:
             direction = (0, -1)
             direction = (0, -1)
-        elif keycode[0] == pygame.K_LEFT:
+        elif letter in LEFT:
             direction = (-1, 0)
             direction = (-1, 0)
-        elif keycode[0] == pygame.K_RIGHT:
+        elif letter in RIGHT:
             direction = (1, 0)
         if direction:
             self.do_move(direction)
             direction = (1, 0)
         if direction:
             self.do_move(direction)
@@ -172,44 +179,88 @@ class GameWindow(RelativeLayout):
     def do_move(self, direction):
         if not self.level_obj:
             return
     def do_move(self, direction):
         if not self.level_obj:
             return
-        self.nemesis.move(self.level_obj)
+        # Do nothing on null moves
+        if not self.player.move(direction, self.level_obj):
+            return
+        self.check_state()
+        self.do_nemesis_move()
+
+    def do_nemesis_move(self):
+        self.nemesis.move(self.level_obj, self.player.pos)
+        self.check_state()
+        if self.move_counter > 4:
+            self.move_counter = 0
+            self.draw_nemesis()
+            self.nemesis.move(self.level_obj, self.player.pos)
+            self.check_state()
+        else:
+            self.move_counter += 1
         self.draw_nemesis()
         self.draw_nemesis()
-        self.player.move(direction, self.level_obj)
         self.draw_player()
         self.draw_player()
-        self.check_state()
         self.reset_timer()
 
         self.reset_timer()
 
-    def timed_move(self):
+    def timed_move(self, event):
         if not self.level_obj:
             return
         if not self.level_obj:
             return
-        self.nemesis.move(self.level_obj)
-        self.draw_nemesis()
-        self.check_state()
-        self.reset_timer()
+        self.do_nemesis_move()
 
     def reset_timer(self):
 
     def reset_timer(self):
-        pass
+        self.timer_set = True
+        Clock.unschedule(self.timed_move)
+        Clock.schedule_once(self.timed_move, 3)
+
+    def check_caught(self):
+        return self.nemesis.pos == self.player.pos
+
+    def reset_level(self):
+        Clock.unschedule(self.timed_move)
+        self.timer_set = False
+        self.move_counter = 0
+        if self.nemesis_tile:
+            self.remove_widget(self.nemesis_tile)
+        self.nemesis.reset_pos()
+
+    def load_level(self):
+        if self.level_obj:
+            self.level_obj.load_tiles()
+            self.player.pos = self.level_obj.enter_pos
+            self.remove_widget(self.player_tile)
+            self.view.scroll_x = 0
+            self.view.scroll_y = 0
+            self.build()
+            self.draw_nemesis()
+            self.draw_player()
+            return True
+        return False
 
     def check_state(self):
 
     def check_state(self):
+        if not self.level_obj:
+            return
         if self.level_obj.at_exit(self.player.pos):
         if self.level_obj.at_exit(self.player.pos):
+            self.reset_level()
             # Jump to next level
             self.level_obj = self.level_list.advance_to_next_level()
             # Jump to next level
             self.level_obj = self.level_list.advance_to_next_level()
-            self.remove_widget(self.nemesis_tile)
-            self.nemesis.reset_pos()
-            if self.level_obj:
-                self.level_obj.load_tiles()
-                self.player.pos = self.level_obj.enter_pos
-                self.remove_widget(self.player_tile)
-                self.view.scroll_x = 0
-                self.view.scroll_y = 0
-                self.build()
-                self.draw_nemesis()
-                self.draw_player()
-            else:
-                self.app.game_over(True)
-        elif self.nemesis.pos == self.player.pos:
+            if not self.load_level():
+                app = self.app
+                self.app = None
+                self._closed()
+                app.game_over(True)
+            return
+        elif self.check_caught():
             # Caught
             # Caught
-            self.app.game_over(False)
+            self.reset_level()
+            app = self.app
+            self.app = None
+            self._closed()
+            app.game_over(False)
+            return
+        elif self.level_obj.is_button(self.player.pos):
+            self.level_obj.trigger_button(self.player.pos)
+        elif self.level_obj.is_button(self.nemesis.pos):
+            self.level_obj.trigger_button(self.nemesis.pos)
+        for map_pos, new_tile in self.level_obj.get_changed_tiles():
+            pos = (map_pos[0] * TILE_SIZE, map_pos[1] * TILE_SIZE)
+            self.draw_tile(pos, new_tile)
 
     def _calc_mouse_pos(self, pos):
         pos = self.to_local(*pos)
 
     def _calc_mouse_pos(self, pos):
         pos = self.to_local(*pos)
@@ -288,8 +339,9 @@ class GameApp(App):
     title = "Peter's thread snake"
 
     def __init__(self):
     title = "Peter's thread snake"
 
     def __init__(self):
-        self.levels = LevelList()
         super(GameApp, self).__init__()
         super(GameApp, self).__init__()
+        self.levels = LevelList()
+        self.game = None
 
     def build(self):
         root = ScrollView(size_hint=(None, None))
 
     def build(self):
         root = ScrollView(size_hint=(None, None))
@@ -301,6 +353,12 @@ class GameApp(App):
         if platform() == 'android':
             window.fullscreen = True
         self.root.size = window.size
         if platform() == 'android':
             window.fullscreen = True
         self.root.size = window.size
+        errors = self.levels.get_errors()
+        if errors:
+            popup = Popup(title='Levels excluded',
+                    content=Label(text='\n'.join(errors)),
+                    size_hint=(.5, .5))
+            popup.open()
         self.make_intro()
 
     def make_intro(self):
         self.make_intro()
 
     def make_intro(self):
@@ -313,15 +371,15 @@ class GameApp(App):
 
     def start_game(self, label, ref):
         """Start the game"""
 
     def start_game(self, label, ref):
         """Start the game"""
-        game = GameWindow(self.levels, self)
-        game.build()
+        self.game = GameWindow(self.levels, self)
+        self.game.build()
         self.root.clear_widgets()
         self.root.clear_widgets()
-        self.root.add_widget(game)
+        self.root.add_widget(self.game)
         # Ensure the player is visible
         self.root.scroll_x = 0
         self.root.scroll_y = 0
         # Ensure the player is visible
         self.root.scroll_x = 0
         self.root.scroll_y = 0
-        game.draw_player()
-        game.draw_nemesis()
+        self.game.draw_player()
+        self.game.draw_nemesis()
 
     def game_over(self, won):
         if won:
 
     def game_over(self, won):
         if won:
@@ -329,6 +387,8 @@ class GameApp(App):
             self.levels.reset()
         else:
             screen = LostScreen(self)
             self.levels.reset()
         else:
             screen = LostScreen(self)
+        del self.game
+        self.game = None
         self.root.clear_widgets()
         self.root.add_widget(screen)
 
         self.root.clear_widgets()
         self.root.add_widget(screen)