import os
from data import load_image, load, filepath
+
+from kivy.logger import Logger
+
WALL = '.'
FLOOR = ' '
ENTRY = 'E'
class Level(object):
- def __init__(self, levelfile):
+ def __init__(self, levelfile, name):
self._data = []
self.exit_pos = []
self.enter_pos = None
self._changed = []
self._gates = {}
self._buttons = {}
+ self._name = name
# Because of how kivy's coordinate system works,
# we reverse the lines so things match up between
# the file and the display (top of file == top of display)
def load_tiles(self):
"""Load the list of tiles for the level"""
+ Logger.info('%s: load tiles' % self._name)
self._tiles = []
self._gates = {}
self._buttons = {}
self._tiles.append(tile_line)
def _get_tile_image(self, pos, c):
- if pos in self._gates:
- del self._gates[pos]
- if pos in self._buttons:
- del self._buttons[pos]
image = None
if c == FLOOR:
image = load_image('tiles/floor.png')
self.exit_pos.append(pos)
image = load_image('tiles/door.png')
elif c == GATE:
- image = load_image('tiles/gate_down.png')
- self._gates[pos] = -1 # down
+ if pos not in self._gates:
+ self._gates[pos] = -1 # down
+ image = load_image('tiles/gate_down.png')
+ else:
+ state = self._gates[pos]
+ if state == -1:
+ image = load_image('tiles/gate_down.png')
+ elif state == 0:
+ # destroyed
+ image = load_image('tiles/floor.png')
+ elif state == 1:
+ # badly damaged
+ image = load_image('tiles/gate_dented.png')
+ elif state == 2:
+ # lightly damaged
+ image = load_image('tiles/gate_bent.png')
+ else:
+ # gate up
+ image = load_image('tiles/gate_up.png')
elif c == BUTTON:
- image = load_image('tiles/button.png')
- self._buttons[pos] = 'active'
+ if not pos in self._buttons:
+ image = load_image('tiles/button.png')
+ self._buttons[pos] = 'active'
+ elif self._buttons[pos] == 'active':
+ image = load_image('tiles/button.png')
+ else:
+ image = load_image('tiles/floor.png')
if image is None:
raise RuntimeError('Unknown tile type %s at %s' % (c, pos))
return image
def validate(self):
entry_points = 0
exit_points = 0
+ gates = 0
+ buttons = 0
for line in self._data:
if ENTRY in line:
entry_points += line.count(ENTRY)
if EXIT in line:
exit_points += line.count(EXIT)
+ if BUTTON in line:
+ buttons += line.count(BUTTON)
+ if GATE in line:
+ gates += line.count(GATE)
if entry_points == 0:
raise RuntimeError('No entry point')
if entry_points > 1:
raise RuntimeError('Multiple entry points')
if exit_points == 0:
raise RuntimeError('No exit')
+ if gates != buttons:
+ raise RuntimeError('The number of buttons and gates differ')
def get_tiles(self):
return self._tiles
return self._data[pos[1]][pos[0]]
def set_tile_type(self, pos, new_type):
+ # Setting the type resets any state anyway, so
+ if pos in self._gates:
+ del self._gates[pos]
+ if pos in self._buttons:
+ del self._buttons[pos]
self._data[pos[1]][pos[0]] = new_type
new_tile = self._get_tile_image(pos, new_type)
self._tiles[pos[1]][pos[0]] = new_tile
(pos[0] - 1, pos[1] + 1), (pos[0] + 1, pos[1] - 1)]:
if not self._in_limits(new_pos):
continue
- tile = self._data[new_pos[1]][new_pos[0]]
- new_tile = self._get_tile_image(new_pos, tile)
- self._tiles[new_pos[1]][new_pos[0]] = new_tile
- self._changed.append((new_pos, new_tile))
+ # Update display to changed status
+ self._fix_tile(new_pos)
+
+ def _fix_tile(self, pos):
+ tile = self._data[pos[1]][pos[0]]
+ new_tile = self._get_tile_image(pos, tile)
+ self._tiles[pos[1]][pos[0]] = new_tile
+ self._changed.append((pos, new_tile))
def get_size(self):
return len(self._tiles[0]), len(self._tiles)
if tile == WALL or tile == ENTRY:
return True
if tile == GATE:
- if self._gates[pos] != 'down':
+ if self._gates[pos] > 0:
return True
return False
+ def calc_dist(self, pos1, pos2):
+ return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1])
+
def is_gate(self, pos):
+ if not self._in_limits(pos):
+ return False
return self._data[pos[1]][pos[0]] == GATE
def is_button(self, pos):
+ if not self._in_limits(pos):
+ return False
return self._data[pos[1]][pos[0]] == BUTTON
+ def is_wall(self, pos):
+ if not self._in_limits(pos):
+ return True
+ return self._data[pos[1]][pos[0]] == WALL
+
def trigger_button(self, pos):
if not self.is_button(pos):
return False
if not self._buttons[pos] == 'active':
return False
# Find the closest gate down gate and trigger it
- gate_pos = pos
-
- self._changed.append((pos, self.get_single_tile(pos)))
- self._changed.append((gate_pos, self.get_single_tile(pos)))
+ mindist = 99999
+ gate_pos = None
+ for cand in self._gates:
+ dist = self.calc_dist(pos, cand)
+ if dist < mindist:
+ gate_pos = cand
+ mindist = dist
+ if gate_pos:
+ self._buttons[pos] = 'pressed'
+ self._gates[gate_pos] = 3 # Raise gate
+ self._fix_tile(pos)
+ self._fix_tile(gate_pos)
def damage_gate(self, pos):
if not self.is_gate(pos):
- return False
+ return
if self._gates[pos] == -1 or self._gates[pos] == 0:
- return False
+ return
self._gates[pos] = self._gates[pos] - 1
- self._fix_gate_tile(pos)
- self._changed.append((pos, self.get_single_tile(pos)))
- return True
+ self._fix_tile(pos)
def get_changed_tiles(self):
ret = self._changed[:]
LEVELS = 'level_list'
def __init__(self):
- self.levels = []
+ self._levels = []
+ self._level_names = []
+ self._errors = []
level_list = load(self.LEVELS)
for line in level_list:
line = line.strip()
if os.path.exists(filepath(line)):
level_file = load(line)
- level = Level(level_file)
+ level = Level(level_file, line)
level_file.close()
try:
level.validate()
+ self._levels.append(level)
+ self._level_names.append(line)
except RuntimeError as err:
- raise RuntimeError(
+ self._errors.append(
'Invalid level %s in level_list: %s' % (line, err))
- self.levels.append(level)
else:
- raise RuntimeError('Level list includes non-existant level %s' % line)
+ self._errors.append(
+ 'Level list includes non-existant level %s' % line)
level_list.close()
self._cur_level = 0
def get_current_level(self):
- if self._cur_level < len(self.levels):
- return self.levels[self._cur_level]
+ if self._cur_level < len(self._levels):
+ return self._levels[self._cur_level]
else:
return None
+ def get_errors(self):
+ return self._errors
+
+ def get_level_names(self):
+ return self._level_names
+
+ def set_level_to(self, level_name):
+ if level_name in self._level_names:
+ self._cur_level = self._level_names.index(level_name)
+
def advance_to_next_level(self):
self._cur_level += 1
return self.get_current_level()