3234d1b2dab8242b954c5517729ccbcc83762797
[erdslangetjie.git] / erdslangetjie / __main__.py
1 import sys
2 import kivy
3 import pygame
4
5 kivy.require('1.6.0')
6
7 from kivy.app import App
8 from kivy.core.window import Window
9 from kivy.uix.widget import Widget
10 from kivy.uix.relativelayout import RelativeLayout
11 from kivy.uix.scrollview import ScrollView
12 from kivy.graphics import Color, Rectangle
13 from kivy.utils import platform
14
15 from erdslangetjie.level import LevelList
16 from erdslangetjie.player import ThePlayer, Nemesis
17
18 TILE_SIZE = 40
19
20
21 class GameWindow(RelativeLayout):
22
23     def __init__(self, level_list, view):
24         self.level_list = level_list
25         self.level_obj = self.level_list.get_current_level()
26         self.level_obj.load_tiles()
27         self.tiles = {}
28         self.view = view
29
30         rows, cols = self.level_obj.get_size()
31
32         super(GameWindow, self).__init__(
33                 size=(cols*TILE_SIZE, rows*TILE_SIZE),
34                 size_hint=(None, None))
35
36         self.mouse_move = False
37
38         self.player = ThePlayer()
39         self.nemesis = Nemesis()
40         if not self.level_obj.enter_pos:
41             raise RuntimeError('No entry point')
42         self.player_tile = None
43         self.nemesis_tile = None
44
45         self.player.pos = self.level_obj.enter_pos
46         if platform() != 'android':
47             # Very hack'ish
48             self.keyboard = Window.request_keyboard(self._closed, self)
49             self.keyboard.bind(on_key_down=self._on_key_down)
50
51     def build(self):
52         self.clear_widgets()
53         self.tiles = {}
54         tiles = self.level_obj.get_tiles()
55         bx, by = 0, 0
56         for tile_line in tiles:
57             bx = 0
58             for tile in tile_line:
59                 node = Widget(size=(TILE_SIZE, TILE_SIZE),
60                         pos=(bx, by),
61                         size_hint=(None, None))
62                 self.add_widget(node)
63                 with node.canvas:
64                     Color(1, 1, 1)
65                     Rectangle(pos=node.pos, size=node.size,
66                             texture=tile.texture)
67                 self.tiles[(bx, by)] = node
68                 bx += TILE_SIZE
69             by += TILE_SIZE
70
71         self.draw_player()
72         self.draw_nemesis()
73
74     def draw_player(self):
75         if self.player_tile:
76             self.remove_widget(self.player_tile)
77         sprite_pos = (self.player.pos[0] * TILE_SIZE,
78                 self.player.pos[1] * TILE_SIZE)
79         self.player_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
80                 pos=sprite_pos)
81         with self.player_tile.canvas:
82             Color(1, 1, 1)
83             Rectangle(pos=sprite_pos, size=self.player_tile.size,
84                     texture=self.player.get_texture())
85         self.add_widget(self.player_tile)
86         # Player position in viewpoint coordinates
87         check_point = (sprite_pos[0] + TILE_SIZE / 2,
88                 sprite_pos[1] + TILE_SIZE / 2)
89         true_point = self.to_parent(*check_point)
90         if not self.included(true_point):
91             # Scroll ourselves
92             while true_point[0] >= self.view.size[0] - TILE_SIZE:
93                 self.pos = (self.pos[0] - 1.5 * TILE_SIZE, self.pos[1])
94                 true_point = self.to_parent(*check_point)
95             while true_point[0] < TILE_SIZE:
96                 self.pos = (self.pos[0] + 1.5 * TILE_SIZE, self.pos[1])
97                 true_point = self.to_parent(*check_point)
98             while true_point[1] >= self.view.size[1] - TILE_SIZE:
99                 self.pos = (self.pos[0], self.pos[1] - 1.5 * TILE_SIZE)
100                 true_point = self.to_parent(*check_point)
101             while true_point[1] < TILE_SIZE:
102                 self.pos = (self.pos[0], self.pos[1] + 1.5 * TILE_SIZE)
103                 true_point = self.to_parent(*check_point)
104
105     def included(self, point):
106         if point[0] < TILE_SIZE:
107             return False
108         if point[0] >= self.view.pos[0] - TILE_SIZE:
109             return False
110         if point[1] < TILE_SIZE:
111             return False
112         if point[1] >= self.view.pos[1] - TILE_SIZE:
113             return False
114
115     def draw_nemesis(self):
116         if not self.nemesis.on_board():
117             return
118         if self.nemesis_tile:
119             self.remove_widget(self.nemesis_tile)
120         sprite_pos = (self.nemesis.pos[0] * TILE_SIZE,
121                 self.nemesis.pos[1] * TILE_SIZE)
122         self.nemesis_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
123                 pos=sprite_pos)
124         with self.nemesis_tile.canvas:
125             Color(1, 1, 1)
126             Rectangle(pos=sprite_pos, size=self.nemesis_tile.size,
127                     texture=self.nemesis.get_texture())
128         self.add_widget(self.nemesis_tile)
129
130     def _closed(self):
131         self.keyboard.unbind(on_key_down=self._on_key_down)
132
133     def _on_key_down(self, keyboard, keycode, text, modifiers):
134         # FIXME - likely portablity issues
135         direction = None
136         if keycode[0] == pygame.K_UP:
137             direction = (0, 1)
138         elif keycode[0] == pygame.K_DOWN:
139             direction = (0, -1)
140         elif keycode[0] == pygame.K_LEFT:
141             direction = (-1, 0)
142         elif keycode[0] == pygame.K_RIGHT:
143             direction = (1, 0)
144         if direction:
145             self.do_move(direction)
146
147     def do_move(self, direction):
148         self.nemesis.move(self.level_obj)
149         self.draw_nemesis()
150         self.player.move(direction, self.level_obj)
151         self.draw_player()
152         self.check_state()
153
154     def check_state(self):
155         if self.level_obj.at_exit(self.player.pos):
156             # Jump to next level
157             self.level_obj = self.level_list.advance_to_next_level()
158             self.nemesis.reset_pos()
159             if self.level_obj:
160                 self.level_obj.load_tiles()
161                 self.player.pos = self.level_obj.enter_pos
162                 self.remove_widget(self.player_tile)
163                 self.build()
164             else:
165                 print 'You won!'
166                 sys.exit(1)
167         elif self.nemesis.pos == self.player.pos:
168             # Caught
169             print 'You lost!'
170             sys.exit(1)
171
172     def _calc_mouse_pos(self, pos):
173         pos = self.to_local(*pos)
174         return (int(pos[0] / TILE_SIZE), int(pos[1] / TILE_SIZE))
175
176     def on_touch_down(self, touch):
177         pos = self._calc_mouse_pos(touch.pos)
178         if pos == self.player.pos:
179             self.mouse_move = True
180             self.mouse_start = pos
181
182     def on_touch_up(self, touch):
183         self.mouse_move = False
184
185     def on_touch_move(self, touch):
186         if self.mouse_move:
187             pos = self._calc_mouse_pos(touch.pos)
188             if (pos[0] - self.mouse_start[0] != 0) or (
189                     pos[1] - self.mouse_start[1] != 0):
190                 direction = (pos[0] - self.mouse_start[0],
191                         pos[1] - self.mouse_start[1])
192                 self.do_move(direction)
193                 self.mouse_start = pos
194
195
196 class GameApp(App):
197
198     def __init__(self):
199         self.levels = LevelList()
200         super(GameApp, self).__init__()
201
202     def build(self):
203         root = ScrollView(size=(640, 480), size_hint=(None, None))
204         game = GameWindow(self.levels, root)
205         game.build()
206         root.add_widget(game)
207         # Ensure the player is visible
208         game.draw_player()
209         return root
210
211
212 def main():
213     """ Erdslangetjie, a maze game of eluding nemesis
214     """
215     GameApp().run()