3 from erdslangetjie.constants import TILE_SIZE
5 from kivy.app import App
6 from kivy.uix.widget import Widget
7 from kivy.uix.relativelayout import RelativeLayout
8 from kivy.uix.scrollview import ScrollView
9 from kivy.uix.label import Label
10 from kivy.graphics import Color, Rectangle
11 from kivy.utils import platform
12 from kivy.clock import Clock
13 from kivy.config import Config
15 from erdslangetjie.level import LevelList
16 from erdslangetjie.data import load_image
17 from erdslangetjie.player import ThePlayer, Nemesis
20 if platform() != 'android':
21 Config.set('graphics', 'width', '1026')
22 Config.set('graphics', 'height', '760')
25 class GameWindow(RelativeLayout):
27 def __init__(self, level_list, app):
28 self.level_list = level_list
29 self.level_obj = self.level_list.get_current_level()
30 self.level_obj.load_tiles()
35 cols, rows = self.level_obj.get_size()
37 super(GameWindow, self).__init__(
38 size=(cols * TILE_SIZE, rows * TILE_SIZE),
39 size_hint=(None, None))
41 self.x_scroll_margin = float(TILE_SIZE) / self.view.size[0]
42 self.y_scroll_margin = float(TILE_SIZE) / self.view.size[1]
44 self.mouse_move = False
46 self.player = ThePlayer()
47 self.nemesis = Nemesis()
48 if not self.level_obj.enter_pos:
49 raise RuntimeError('No entry point')
50 self.player_tile = None
51 self.nemesis_tile = None
52 self.timer_set = False
54 self.player.pos = self.level_obj.enter_pos
55 if platform() != 'android':
57 # We need to delay this import until after the window creation by
58 # the app, else our size config doesn't work
59 from kivy.core.window import Window
60 self.keyboard = Window.request_keyboard(self._closed, self)
61 self.keyboard.bind(on_key_down=self._on_key_down)
66 tiles = self.level_obj.get_tiles()
68 for tile_line in tiles:
70 for tile in tile_line:
71 node = Widget(size=(TILE_SIZE, TILE_SIZE),
73 size_hint=(None, None))
77 Rectangle(pos=node.pos, size=node.size,
79 self.tiles[(bx, by)] = node
83 def draw_player(self):
85 self.remove_widget(self.player_tile)
86 sprite_pos = (self.player.pos[0] * TILE_SIZE,
87 self.player.pos[1] * TILE_SIZE)
88 self.player_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
90 with self.player_tile.canvas:
92 Rectangle(pos=sprite_pos, size=self.player_tile.size,
93 texture=self.player.get_texture())
94 self.add_widget(self.player_tile)
95 for offset in [(TILE_SIZE - 1, TILE_SIZE - 1),
96 (-TILE_SIZE + 1, TILE_SIZE - 1),
97 (TILE_SIZE - 1, -TILE_SIZE + 1),
98 (-TILE_SIZE + 1, -TILE_SIZE + 1),
99 (0, 2 * TILE_SIZE - 2),
100 (-2 * TILE_SIZE + 2, 0),
101 (2 * TILE_SIZE - 2, 0),
102 (0, -2 * TILE_SIZE + 2),
104 # Aim is to ensure a 'neighbourhood' around the player
105 # is visible if possible
106 check_point = (sprite_pos[0] + offset[0] + TILE_SIZE / 2,
107 sprite_pos[1] + offset[1] + TILE_SIZE / 2)
108 true_point = self.to_parent(*check_point)
109 if check_point[0] < 0:
111 if check_point[1] < 0:
113 if check_point[0] >= self.size[0]:
115 if check_point[1] >= self.size[1]:
117 while not self.included(true_point, 0):
119 if true_point[0] >= self.view.size[0]:
120 self.view.scroll_x += self.x_scroll_margin
121 true_point = self.to_parent(*check_point)
122 #print '-x', self.view.scroll_x, self.view.scroll_y
123 elif true_point[0] < 0:
124 self.view.scroll_x -= self.x_scroll_margin
125 true_point = self.to_parent(*check_point)
126 #print '+x', self.view.scroll_x, self.view.scroll_y
127 elif true_point[1] >= self.view.size[1]:
128 self.view.scroll_y += self.y_scroll_margin
129 true_point = self.to_parent(*check_point)
130 #print '+y', self.view.scroll_x, self.view.scroll_y
131 elif true_point[1] < 0:
132 self.view.scroll_y -= self.y_scroll_margin
133 true_point = self.to_parent(*check_point)
134 #print '-y', self.view.scroll_x, self.view.scroll_y
135 #print true_point, self.view.size
137 def included(self, point, margin):
138 if point[0] < margin:
140 if point[0] >= self.view.size[0] - margin:
142 if point[1] < margin:
144 if point[1] >= self.view.size[1] - margin:
148 def draw_nemesis(self):
149 if not self.nemesis.on_board():
151 if self.nemesis_tile:
152 self.remove_widget(self.nemesis_tile)
153 sprite_pos = (self.nemesis.pos[0] * TILE_SIZE,
154 self.nemesis.pos[1] * TILE_SIZE)
155 self.nemesis_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
157 with self.nemesis_tile.canvas:
159 Rectangle(pos=sprite_pos, size=self.nemesis_tile.size,
160 texture=self.nemesis.get_texture())
161 self.add_widget(self.nemesis_tile)
164 self.keyboard.unbind(on_key_down=self._on_key_down)
166 def _on_key_down(self, keyboard, keycode, text, modifiers):
167 # FIXME - likely portablity issues
169 if keycode[0] == pygame.K_UP:
171 elif keycode[0] == pygame.K_DOWN:
173 elif keycode[0] == pygame.K_LEFT:
175 elif keycode[0] == pygame.K_RIGHT:
178 self.do_move(direction)
180 def do_move(self, direction):
181 if not self.level_obj:
183 self.player.move(direction, self.level_obj)
186 if not self.timer_set:
189 def timed_move(self, event):
190 if not self.level_obj:
192 self.nemesis.move(self.level_obj, self.check_caught)
197 def reset_timer(self):
198 self.timer_set = True
199 Clock.unschedule(self.timed_move)
200 Clock.schedule_once(self.timed_move, 0.5)
202 def check_caught(self):
203 return self.nemesis.pos == self.player.pos
205 def reset_level(self):
206 Clock.unschedule(self.timed_move)
207 self.timer_set = False
208 self.remove_widget(self.nemesis_tile)
209 self.nemesis.reset_pos()
211 self.level_obj.load_tiles()
212 self.player.pos = self.level_obj.enter_pos
213 self.remove_widget(self.player_tile)
214 self.view.scroll_x = 0
215 self.view.scroll_y = 0
222 def check_state(self):
223 if self.level_obj.at_exit(self.player.pos):
225 self.level_obj = self.level_list.advance_to_next_level()
226 if not self.reset_level():
227 self.app.game_over(True)
228 elif self.check_caught():
230 self.app.game_over(False)
232 def _calc_mouse_pos(self, pos):
233 pos = self.to_local(*pos)
234 return (int(pos[0] / TILE_SIZE), int(pos[1] / TILE_SIZE))
236 def on_touch_down(self, touch):
237 pos = self._calc_mouse_pos(touch.pos)
238 if pos == self.player.pos:
239 self.mouse_move = True
240 self.mouse_start = pos
242 def on_touch_up(self, touch):
243 self.mouse_move = False
245 def on_touch_move(self, touch):
247 pos = self._calc_mouse_pos(touch.pos)
248 if (pos[0] - self.mouse_start[0] != 0) or (
249 pos[1] - self.mouse_start[1] != 0):
250 direction = (pos[0] - self.mouse_start[0],
251 pos[1] - self.mouse_start[1])
252 self.do_move(direction)
253 self.mouse_start = pos
256 class Screen(Widget):
261 def __init__(self, app):
262 super(Screen, self).__init__()
263 self.image = load_image(self.BACKGROUND)
266 Rectangle(pos=(0, 0), size=(1026, 760),
267 texture=self.image.texture)
269 self.stop_button = Label(
270 text='[ref=quit][color=ff0066]Quit[/color][/ref]',
274 pos=((1026 - 200) / 2 - 100, 100))
275 self.stop_button.bind(on_ref_press=self.app.stop_app)
276 self.start_button = Label(
277 text="[ref=start][color=00ff66]%s[/color][/ref]" % self.START,
279 markup=True, size=(200, 40),
280 pos=((1026 - 200) / 2 + 100, 100))
281 self.start_button.bind(on_ref_press=self.app.start_game)
282 self.add_widget(self.stop_button)
283 self.add_widget(self.start_button)
286 class IntroScreen(Screen):
288 BACKGROUND = 'screens/intro_screen.png'
289 START = 'Start the Game'
292 class WonScreen(Screen):
294 BACKGROUND = 'screens/won.png'
295 START = 'Play again?'
298 class LostScreen(Screen):
300 BACKGROUND = 'screens/lost.png'
306 title = "Peter's thread snake"
309 self.levels = LevelList()
310 super(GameApp, self).__init__()
313 root = ScrollView(size_hint=(None, None))
317 from kivy.base import EventLoop
318 window = EventLoop.window
319 if platform() == 'android':
320 window.fullscreen = True
321 self.root.size = window.size
324 def make_intro(self):
325 self.root.clear_widgets()
326 screen = IntroScreen(self)
327 self.root.add_widget(screen)
329 def stop_app(self, label, ref):
332 def start_game(self, label, ref):
334 game = GameWindow(self.levels, self)
336 self.root.clear_widgets()
337 self.root.add_widget(game)
338 # Ensure the player is visible
339 self.root.scroll_x = 0
340 self.root.scroll_y = 0
344 def game_over(self, won):
346 screen = WonScreen(self)
349 screen = LostScreen(self)
350 self.root.clear_widgets()
351 self.root.add_widget(screen)
355 """ Erdslangetjie, a maze game of eluding nemesis