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.label import Label
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 if not self.level_obj:
178 self.nemesis.move(self.level_obj)
180 self.player.move(direction, self.level_obj)
185 def timed_move(self):
186 if not self.level_obj:
188 self.nemesis.move(self.level_obj)
193 def reset_timer(self):
196 def check_state(self):
197 if self.level_obj.at_exit(self.player.pos):
199 self.level_obj = self.level_list.advance_to_next_level()
200 self.remove_widget(self.nemesis_tile)
201 self.nemesis.reset_pos()
203 self.level_obj.load_tiles()
204 self.player.pos = self.level_obj.enter_pos
205 self.remove_widget(self.player_tile)
206 self.view.scroll_x = 0
207 self.view.scroll_y = 0
212 self.app.game_over(True)
213 elif self.nemesis.pos == self.player.pos:
215 self.app.game_over(False)
217 def _calc_mouse_pos(self, pos):
218 pos = self.to_local(*pos)
219 return (int(pos[0] / TILE_SIZE), int(pos[1] / TILE_SIZE))
221 def on_touch_down(self, touch):
222 pos = self._calc_mouse_pos(touch.pos)
223 if pos == self.player.pos:
224 self.mouse_move = True
225 self.mouse_start = pos
227 def on_touch_up(self, touch):
228 self.mouse_move = False
230 def on_touch_move(self, touch):
232 pos = self._calc_mouse_pos(touch.pos)
233 if (pos[0] - self.mouse_start[0] != 0) or (
234 pos[1] - self.mouse_start[1] != 0):
235 direction = (pos[0] - self.mouse_start[0],
236 pos[1] - self.mouse_start[1])
237 self.do_move(direction)
238 self.mouse_start = pos
241 class Screen(Widget):
246 def __init__(self, app):
247 super(Screen, self).__init__()
248 self.image = load_image(self.BACKGROUND)
251 Rectangle(pos=(0, 0), size=(1026, 760),
252 texture=self.image.texture)
254 self.stop_button = Label(
255 text='[ref=quit][color=ff0066]Quit[/color][/ref]',
259 pos=((1026 - 200) / 2 - 100, 100))
260 self.stop_button.bind(on_ref_press=self.app.stop_app)
261 self.start_button = Label(
262 text="[ref=start][color=00ff66]" + self.START + "[/color][/ref]",
264 markup = True, size=(200, 40),
265 pos=((1026 - 200) / 2 + 100, 100))
266 self.start_button.bind(on_ref_press=self.app.start_game)
267 self.add_widget(self.stop_button)
268 self.add_widget(self.start_button)
271 class IntroScreen(Screen):
273 BACKGROUND = 'screens/intro_screen.png'
274 START = 'Start the Game'
277 class WonScreen(Screen):
279 BACKGROUND = 'screens/won.png'
280 START = 'Play again?'
283 class LostScreen(Screen):
285 BACKGROUND = 'screens/lost.png'
291 title = "Peter's thread snake"
294 self.levels = LevelList()
295 super(GameApp, self).__init__()
298 root = ScrollView(size_hint=(None, None))
302 from kivy.base import EventLoop
303 window = EventLoop.window
304 if platform() == 'android':
305 window.fullscreen = True
306 self.root.size = window.size
309 def make_intro(self):
310 self.root.clear_widgets()
311 screen = IntroScreen(self)
312 self.root.add_widget(screen)
314 def stop_app(self, label, ref):
317 def start_game(self, label, ref):
319 game = GameWindow(self.levels, self)
321 self.root.clear_widgets()
322 self.root.add_widget(game)
323 # Ensure the player is visible
324 self.root.scroll_x = 0
325 self.root.scroll_y = 0
329 def game_over(self, won):
331 screen = WonScreen(self)
334 screen = LostScreen(self)
335 self.root.clear_widgets()
336 self.root.add_widget(screen)
340 """ Erdslangetjie, a maze game of eluding nemesis
343 for hdlr in Logger.handlers[:]:
344 if not isinstance(hdlr, LoggerHistory):
345 Logger.removeHandler(hdlr)