6 from kivy.app import App
7 from kivy.uix.widget import Widget
8 from kivy.logger import Logger, LoggerHistory
9 from kivy.uix.relativelayout import RelativeLayout
10 from kivy.uix.scrollview import ScrollView
11 from kivy.uix.button import Button
12 from kivy.graphics import Color, Rectangle
13 from kivy.utils import platform
15 from erdslangetjie.level import LevelList
16 from erdslangetjie.data import load_image
17 from erdslangetjie.player import ThePlayer, Nemesis
18 from erdslangetjie.constants import TILE_SIZE, QUIET
21 class GameWindow(RelativeLayout):
23 def __init__(self, level_list, app):
24 self.level_list = level_list
25 self.level_obj = self.level_list.get_current_level()
26 self.level_obj.load_tiles()
31 cols, rows = self.level_obj.get_size()
33 super(GameWindow, self).__init__(
34 size=(cols * TILE_SIZE, rows * TILE_SIZE),
35 size_hint=(None, None))
37 self.x_scroll_margin = float(TILE_SIZE) / self.view.size[0]
38 self.y_scroll_margin = float(TILE_SIZE) / self.view.size[1]
40 self.mouse_move = False
42 self.player = ThePlayer()
43 self.nemesis = Nemesis()
44 if not self.level_obj.enter_pos:
45 raise RuntimeError('No entry point')
46 self.player_tile = None
47 self.nemesis_tile = None
49 self.player.pos = self.level_obj.enter_pos
50 if platform() != 'android':
52 # We need to delay this import until after the window creation by
53 # the app, else our size config doesn't work
54 from kivy.core.window import Window
55 self.keyboard = Window.request_keyboard(self._closed, self)
56 self.keyboard.bind(on_key_down=self._on_key_down)
61 tiles = self.level_obj.get_tiles()
63 for tile_line in tiles:
65 for tile in tile_line:
66 node = Widget(size=(TILE_SIZE, TILE_SIZE),
68 size_hint=(None, None))
72 Rectangle(pos=node.pos, size=node.size,
74 self.tiles[(bx, by)] = node
78 def draw_player(self):
80 self.remove_widget(self.player_tile)
81 sprite_pos = (self.player.pos[0] * TILE_SIZE,
82 self.player.pos[1] * TILE_SIZE)
83 self.player_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
85 with self.player_tile.canvas:
87 Rectangle(pos=sprite_pos, size=self.player_tile.size,
88 texture=self.player.get_texture())
89 self.add_widget(self.player_tile)
90 for offset in [(TILE_SIZE - 1, TILE_SIZE - 1),
91 (-TILE_SIZE + 1, TILE_SIZE - 1),
92 (TILE_SIZE - 1, -TILE_SIZE + 1),
93 (-TILE_SIZE + 1, -TILE_SIZE + 1),
94 (0, 2 * TILE_SIZE - 2),
95 (-2 * TILE_SIZE + 2, 0),
96 (2 * TILE_SIZE - 2, 0),
97 (0, -2 * TILE_SIZE + 2),
99 # Aim is to ensure a 'neighbourhood' around the player
100 # is visible if possible
101 check_point = (sprite_pos[0] + offset[0] + TILE_SIZE / 2,
102 sprite_pos[1] + offset[1] + TILE_SIZE / 2)
103 true_point = self.to_parent(*check_point)
104 if check_point[0] < 0:
106 if check_point[1] < 0:
108 if check_point[0] >= self.size[0]:
110 if check_point[1] >= self.size[1]:
112 while not self.included(true_point, 0):
114 if true_point[0] >= self.view.size[0]:
115 self.view.scroll_x += self.x_scroll_margin
116 true_point = self.to_parent(*check_point)
117 #print '-x', self.view.scroll_x, self.view.scroll_y
118 elif true_point[0] < 0:
119 self.view.scroll_x -= self.x_scroll_margin
120 true_point = self.to_parent(*check_point)
121 #print '+x', self.view.scroll_x, self.view.scroll_y
122 elif true_point[1] >= self.view.size[1]:
123 self.view.scroll_y += self.y_scroll_margin
124 true_point = self.to_parent(*check_point)
125 #print '+y', self.view.scroll_x, self.view.scroll_y
126 elif true_point[1] < 0:
127 self.view.scroll_y -= self.y_scroll_margin
128 true_point = self.to_parent(*check_point)
129 #print '-y', self.view.scroll_x, self.view.scroll_y
130 #print true_point, self.view.size
132 def included(self, point, margin):
133 if point[0] < margin:
135 if point[0] >= self.view.size[0] - margin:
137 if point[1] < margin:
139 if point[1] >= self.view.size[1] - margin:
143 def draw_nemesis(self):
144 if not self.nemesis.on_board():
146 if self.nemesis_tile:
147 self.remove_widget(self.nemesis_tile)
148 sprite_pos = (self.nemesis.pos[0] * TILE_SIZE,
149 self.nemesis.pos[1] * TILE_SIZE)
150 self.nemesis_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
152 with self.nemesis_tile.canvas:
154 Rectangle(pos=sprite_pos, size=self.nemesis_tile.size,
155 texture=self.nemesis.get_texture())
156 self.add_widget(self.nemesis_tile)
159 self.keyboard.unbind(on_key_down=self._on_key_down)
161 def _on_key_down(self, keyboard, keycode, text, modifiers):
162 # FIXME - likely portablity issues
164 if keycode[0] == pygame.K_UP:
166 elif keycode[0] == pygame.K_DOWN:
168 elif keycode[0] == pygame.K_LEFT:
170 elif keycode[0] == pygame.K_RIGHT:
173 self.do_move(direction)
175 def do_move(self, direction):
176 self.nemesis.move(self.level_obj)
178 self.player.move(direction, self.level_obj)
183 def timed_move(self):
184 self.nemesis.move(self.level_obj)
189 def reset_timer(self):
192 def check_state(self):
193 if self.level_obj.at_exit(self.player.pos):
195 self.level_obj = self.level_list.advance_to_next_level()
196 self.remove_widget(self.nemesis_tile)
197 self.nemesis.reset_pos()
199 self.level_obj.load_tiles()
200 self.player.pos = self.level_obj.enter_pos
201 self.remove_widget(self.player_tile)
202 self.view.scroll_x = 0
203 self.view.scroll_y = 0
208 self.app.game_over(True)
209 elif self.nemesis.pos == self.player.pos:
211 self.app.game_over(False)
213 def _calc_mouse_pos(self, pos):
214 pos = self.to_local(*pos)
215 return (int(pos[0] / TILE_SIZE), int(pos[1] / TILE_SIZE))
217 def on_touch_down(self, touch):
218 pos = self._calc_mouse_pos(touch.pos)
219 if pos == self.player.pos:
220 self.mouse_move = True
221 self.mouse_start = pos
223 def on_touch_up(self, touch):
224 self.mouse_move = False
226 def on_touch_move(self, touch):
228 pos = self._calc_mouse_pos(touch.pos)
229 if (pos[0] - self.mouse_start[0] != 0) or (
230 pos[1] - self.mouse_start[1] != 0):
231 direction = (pos[0] - self.mouse_start[0],
232 pos[1] - self.mouse_start[1])
233 self.do_move(direction)
234 self.mouse_start = pos
237 class Screen(Widget):
242 def __init__(self, app):
243 super(Screen, self).__init__()
244 self.image = load_image(self.BACKGROUND)
247 Rectangle(pos=(0, 0), size=(1026, 760),
248 texture=self.image.texture)
250 self.stop_button = Button(
251 text='Quit', size=(200, 40),
252 pos=((1026 - 200) / 2 - 100, 100))
253 self.stop_button.bind(on_press=self.app.stop_app)
254 self.start_button = Button(
255 text=self.START, size=(200, 40),
256 pos=((1026 - 200) / 2 + 100, 100))
257 self.start_button.bind(on_press=self.app.start_game)
258 self.add_widget(self.stop_button)
259 self.add_widget(self.start_button)
262 class IntroScreen(Screen):
264 BACKGROUND = 'screens/intro_screen.png'
265 START = 'Start the Game'
268 class WonScreen(Screen):
270 BACKGROUND = 'screens/won.png'
271 START = 'Play again?'
274 class LostScreen(Screen):
276 BACKGROUND = 'screens/lost.png'
282 title = "Peter's thread snake"
285 self.levels = LevelList()
286 super(GameApp, self).__init__()
289 root = ScrollView(size_hint=(None, None))
293 from kivy.base import EventLoop
294 window = EventLoop.window
295 if platform() == 'android':
296 window.fullscreen = True
297 self.root.size = window.size
300 def make_intro(self):
301 self.root.clear_widgets()
302 screen = IntroScreen(self)
303 self.root.add_widget(screen)
305 def stop_app(self, button):
308 def start_game(self, button):
310 game = GameWindow(self.levels, self)
312 self.root.clear_widgets()
313 self.root.add_widget(game)
314 # Ensure the player is visible
315 self.root.scroll_x = 0
316 self.root.scroll_y = 0
320 def game_over(self, won):
322 screen = WonScreen(self)
325 screen = LostScreen(self)
326 self.root.clear_widgets()
327 self.root.add_widget(screen)
331 """ Erdslangetjie, a maze game of eluding nemesis
334 for hdlr in Logger.handlers[:]:
335 if not isinstance(hdlr, LoggerHistory):
336 Logger.removeHandler(hdlr)