4 from data import load_image, load, filepath
16 def __init__(self, levelfile):
24 # Because of how kivy's coordinate system works,
25 # we reverse the lines so things match up between
26 # the file and the display (top of file == top of display)
27 for line in reversed(levelfile.readlines()):
28 self._data.append(list(line.strip('\n')))
31 """Load the list of tiles for the level"""
38 for j, line in enumerate(self._data):
40 for i, c in enumerate(line):
41 tile_image = self._get_tile_image((i, j), c)
42 tile_line.append(tile_image)
43 self._tiles.append(tile_line)
45 def _get_tile_image(self, pos, c):
48 image = load_image('tiles/floor.png')
50 image = self._get_wall_tile(pos)
53 image = load_image('tiles/entry.png')
55 self.exit_pos.append(pos)
56 image = load_image('tiles/door.png')
58 if pos not in self._gates:
59 self._gates[pos] = -1 # down
60 image = load_image('tiles/gate_down.png')
62 state = self._gates[pos]
64 image = load_image('tiles/gate_down.png')
67 image = load_image('tiles/floor.png')
70 image = load_image('tiles/gate_dented.png')
73 image = load_image('tiles/gate_bent.png')
76 image = load_image('tiles/gate_up.png')
78 image = load_image('tiles/button.png')
79 self._buttons[pos] = 'active'
81 raise RuntimeError('Unknown tile type %s at %s' % (c, pos))
87 for line in self._data:
89 entry_points += line.count(ENTRY)
91 exit_points += line.count(EXIT)
93 raise RuntimeError('No entry point')
95 raise RuntimeError('Multiple entry points')
97 raise RuntimeError('No exit')
102 def get_single_tile(self, pos):
103 return self._tiles[pos[1]][pos[0]]
105 def get_tile_type(self, pos):
106 return self._data[pos[1]][pos[0]]
108 def set_tile_type(self, pos, new_type):
109 # Setting the type resets any state anyway, so
110 if pos in self._gates:
112 if pos in self._buttons:
113 del self._buttons[pos]
114 self._data[pos[1]][pos[0]] = new_type
115 new_tile = self._get_tile_image(pos, new_type)
116 self._tiles[pos[1]][pos[0]] = new_tile
117 self._changed.append((pos, new_tile))
118 # Also update neighbourhood for wall types, etc.
119 for new_pos in [(pos[0] - 1, pos[1]), (pos[0] + 1, pos[1]),
120 (pos[0] - 1, pos[1] - 1), (pos[0] + 1, pos[1] + 1),
121 (pos[0], pos[1] - 1), (pos[0], pos[1] + 1),
122 (pos[0] - 1, pos[1] + 1), (pos[0] + 1, pos[1] - 1)]:
123 if not self._in_limits(new_pos):
125 tile = self._data[new_pos[1]][new_pos[0]]
126 new_tile = self._get_tile_image(new_pos, tile)
127 self._tiles[new_pos[1]][new_pos[0]] = new_tile
128 self._changed.append((new_pos, new_tile))
131 return len(self._tiles[0]), len(self._tiles)
133 def at_exit(self, pos):
134 return pos in self.exit_pos
136 def get_level_data(self):
137 return '\n'.join(reversed([''.join(x) for x in self._data]))
139 def _get_wall_tile(self, pos):
140 # Is the neighbour in this direction also a wall?
142 left = right = top = bottom = False
145 elif self._data[y][x - 1] == WALL:
147 if x == len(self._data[0]) - 1:
149 elif self._data[y][x + 1] == WALL:
153 elif self._data[y - 1][x] == WALL:
155 if y == len(self._data) - 1:
157 elif self._data[y + 1][x] == WALL:
159 if top and bottom and left and right:
160 return load_image('tiles/cwall.png')
161 elif bottom and left and right:
162 return load_image('tiles/bottom_wall.png')
163 elif top and left and right:
164 return load_image('tiles/top_wall.png')
165 elif top and bottom and right:
166 return load_image('tiles/left_wall.png')
167 elif top and bottom and left:
168 return load_image('tiles/right_wall.png')
170 return load_image('tiles/vert_wall.png')
172 return load_image('tiles/horiz_wall.png')
174 return load_image('tiles/corner_lt.png')
175 elif left and bottom:
176 return load_image('tiles/corner_lb.png')
178 return load_image('tiles/corner_rt.png')
179 elif right and bottom:
180 return load_image('tiles/corner_rb.png')
182 return load_image('tiles/end_top.png')
184 return load_image('tiles/end_bottom.png')
186 return load_image('tiles/end_right.png')
188 return load_image('tiles/end_left.png')
189 return load_image('tiles/pillar.png')
191 def _in_limits(self, pos):
197 self._data[pos[1]][pos[0]]
202 def blocked(self, pos):
208 tile = self._data[pos[1]][pos[0]]
211 if tile == WALL or tile == ENTRY:
214 if self._gates[pos] != -1:
218 def is_gate(self, pos):
219 return self._data[pos[1]][pos[0]] == GATE
221 def is_button(self, pos):
222 return self._data[pos[1]][pos[0]] == BUTTON
224 def trigger_button(self, pos):
225 if not self.is_button(pos):
227 if not self._buttons[pos] == 'active':
229 # Find the closest gate down gate and trigger it
232 self._changed.append((pos, self.get_single_tile(pos)))
233 self._changed.append((gate_pos, self.get_single_tile(pos)))
235 def damage_gate(self, pos):
236 if not self.is_gate(pos):
238 if self._gates[pos] == -1 or self._gates[pos] == 0:
240 self._gates[pos] = self._gates[pos] - 1
241 self._fix_gate_tile(pos)
242 self._changed.append((pos, self.get_single_tile(pos)))
245 def get_changed_tiles(self):
246 ret = self._changed[:]
251 class LevelList(object):
253 LEVELS = 'level_list'
258 level_list = load(self.LEVELS)
259 for line in level_list:
261 if os.path.exists(filepath(line)):
262 level_file = load(line)
263 level = Level(level_file)
267 self.levels.append(level)
268 except RuntimeError as err:
270 'Invalid level %s in level_list: %s' % (line, err))
273 'Level list includes non-existant level %s' % line)
277 def get_current_level(self):
278 if self._cur_level < len(self.levels):
279 return self.levels[self._cur_level]
283 def get_errors(self):
286 def advance_to_next_level(self):
288 return self.get_current_level()