7 from kivy.app import App
8 from kivy.uix.widget import Widget
9 from kivy.logger import Logger, LoggerHistory
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
15 from erdslangetjie.level import LevelList
16 from erdslangetjie.player import ThePlayer, Nemesis
17 from erdslangetjie.constants import TILE_SIZE, QUIET
20 class GameWindow(RelativeLayout):
22 def __init__(self, level_list, view):
23 self.level_list = level_list
24 self.level_obj = self.level_list.get_current_level()
25 self.level_obj.load_tiles()
29 cols, rows = self.level_obj.get_size()
31 super(GameWindow, self).__init__(
32 size=(cols * TILE_SIZE, rows * TILE_SIZE),
33 size_hint=(None, None))
35 self.x_scroll_margin = float(TILE_SIZE) / self.view.size[0]
36 self.y_scroll_margin = float(TILE_SIZE) / self.view.size[1]
38 self.mouse_move = False
40 self.player = ThePlayer()
41 self.nemesis = Nemesis()
42 if not self.level_obj.enter_pos:
43 raise RuntimeError('No entry point')
44 self.player_tile = None
45 self.nemesis_tile = None
47 self.player.pos = self.level_obj.enter_pos
48 if platform() != 'android':
50 # We need to delay this import until after the window creation by
51 # the app, else our size config doesn't work
52 from kivy.core.window import Window
53 self.keyboard = Window.request_keyboard(self._closed, self)
54 self.keyboard.bind(on_key_down=self._on_key_down)
59 tiles = self.level_obj.get_tiles()
61 for tile_line in tiles:
63 for tile in tile_line:
64 node = Widget(size=(TILE_SIZE, TILE_SIZE),
66 size_hint=(None, None))
70 Rectangle(pos=node.pos, size=node.size,
72 self.tiles[(bx, by)] = node
76 def draw_player(self):
78 self.remove_widget(self.player_tile)
79 sprite_pos = (self.player.pos[0] * TILE_SIZE,
80 self.player.pos[1] * TILE_SIZE)
81 self.player_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
83 with self.player_tile.canvas:
85 Rectangle(pos=sprite_pos, size=self.player_tile.size,
86 texture=self.player.get_texture())
87 self.add_widget(self.player_tile)
88 for offset in [(TILE_SIZE - 1, TILE_SIZE - 1),
89 (-TILE_SIZE + 1, TILE_SIZE - 1),
90 (TILE_SIZE - 1, -TILE_SIZE + 1),
91 (-TILE_SIZE + 1, -TILE_SIZE + 1),
92 (0, 2 * TILE_SIZE - 2),
93 (-2 * TILE_SIZE + 2, 0),
94 (2 * TILE_SIZE - 2, 0),
95 (0, -2 * TILE_SIZE + 2),
97 # Aim is to ensure a 'neighbourhood' around the player
98 # is visible if possible
99 check_point = (sprite_pos[0] + offset[0] + TILE_SIZE / 2,
100 sprite_pos[1] + offset[1] + TILE_SIZE / 2)
101 true_point = self.to_parent(*check_point)
102 if check_point[0] < 0:
104 if check_point[1] < 0:
106 if check_point[0] >= self.size[0]:
108 if check_point[1] >= self.size[1]:
110 while not self.included(true_point, 0):
112 if true_point[0] >= self.view.size[0]:
113 self.view.scroll_x += self.x_scroll_margin
114 true_point = self.to_parent(*check_point)
115 #print '-x', self.view.scroll_x, self.view.scroll_y
116 elif true_point[0] < 0:
117 self.view.scroll_x -= self.x_scroll_margin
118 true_point = self.to_parent(*check_point)
119 #print '+x', self.view.scroll_x, self.view.scroll_y
120 elif true_point[1] >= self.view.size[1]:
121 self.view.scroll_y += self.y_scroll_margin
122 true_point = self.to_parent(*check_point)
123 #print '+y', self.view.scroll_x, self.view.scroll_y
124 elif true_point[1] < 0:
125 self.view.scroll_y -= self.y_scroll_margin
126 true_point = self.to_parent(*check_point)
127 #print '-y', self.view.scroll_x, self.view.scroll_y
128 #print true_point, self.view.size
130 def included(self, point, margin):
131 if point[0] < margin:
133 if point[0] >= self.view.size[0] - margin:
135 if point[1] < margin:
137 if point[1] >= self.view.size[1] - margin:
141 def draw_nemesis(self):
142 if not self.nemesis.on_board():
144 if self.nemesis_tile:
145 self.remove_widget(self.nemesis_tile)
146 sprite_pos = (self.nemesis.pos[0] * TILE_SIZE,
147 self.nemesis.pos[1] * TILE_SIZE)
148 self.nemesis_tile = Widget(size=(TILE_SIZE, TILE_SIZE),
150 with self.nemesis_tile.canvas:
152 Rectangle(pos=sprite_pos, size=self.nemesis_tile.size,
153 texture=self.nemesis.get_texture())
154 self.add_widget(self.nemesis_tile)
157 self.keyboard.unbind(on_key_down=self._on_key_down)
159 def _on_key_down(self, keyboard, keycode, text, modifiers):
160 # FIXME - likely portablity issues
162 if keycode[0] == pygame.K_UP:
164 elif keycode[0] == pygame.K_DOWN:
166 elif keycode[0] == pygame.K_LEFT:
168 elif keycode[0] == pygame.K_RIGHT:
171 self.do_move(direction)
173 def do_move(self, direction):
174 self.nemesis.move(self.level_obj)
176 self.player.move(direction, self.level_obj)
180 def check_state(self):
181 if self.level_obj.at_exit(self.player.pos):
183 self.level_obj = self.level_list.advance_to_next_level()
184 self.remove_widget(self.nemesis_tile)
185 self.nemesis.reset_pos()
187 self.level_obj.load_tiles()
188 self.player.pos = self.level_obj.enter_pos
189 self.remove_widget(self.player_tile)
190 self.view.scroll_x = 0
191 self.view.scroll_y = 0
198 elif self.nemesis.pos == self.player.pos:
203 def _calc_mouse_pos(self, pos):
204 pos = self.to_local(*pos)
205 return (int(pos[0] / TILE_SIZE), int(pos[1] / TILE_SIZE))
207 def on_touch_down(self, touch):
208 pos = self._calc_mouse_pos(touch.pos)
209 if pos == self.player.pos:
210 self.mouse_move = True
211 self.mouse_start = pos
213 def on_touch_up(self, touch):
214 self.mouse_move = False
216 def on_touch_move(self, touch):
218 pos = self._calc_mouse_pos(touch.pos)
219 if (pos[0] - self.mouse_start[0] != 0) or (
220 pos[1] - self.mouse_start[1] != 0):
221 direction = (pos[0] - self.mouse_start[0],
222 pos[1] - self.mouse_start[1])
223 self.do_move(direction)
224 self.mouse_start = pos
229 title = "Peter's thread snake"
232 self.levels = LevelList()
233 super(GameApp, self).__init__()
236 root = ScrollView(size_hint=(None, None))
240 from kivy.base import EventLoop
241 window = EventLoop.window
242 if platform() == 'android':
243 window.fullscreen = True
244 self.root.size = window.size
245 game = GameWindow(self.levels, self.root)
247 self.root.add_widget(game)
248 # Ensure the player is visible
249 self.root.scroll_x = 0
250 self.root.scroll_y = 0
256 """ Erdslangetjie, a maze game of eluding nemesis
259 for hdlr in Logger.handlers[:]:
260 if not isinstance(hdlr, LoggerHistory):
261 Logger.removeHandler(hdlr)