# Player and Player-like objects
+from erdslangetjie.data import load_image, load_sound
-from erdslangetjie.data import load_image
-
-class PlayerSprite(object):
+class GameSprite(object):
def __init__(self):
self.pos = (0, 0)
self.sprite = None
- def move(self, direction):
- self.pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
-
def get_image(self):
return self.sprite
+ def get_texture(self):
+ return self.sprite.texture
+
+
+class FigureSprite(GameSprite):
-class ThePlayer(PlayerSprite):
+ def can_move(self, direction, level):
+ '''Check if we can move in the given direction'''
+ cand_pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
+ return not level.blocked(cand_pos)
+
+
+class ThePlayer(FigureSprite):
def __init__(self):
super(ThePlayer, self).__init__()
self.sprite = load_image('sprites/player.png')
+ def move(self, direction, level):
+ if self.can_move(direction, level):
+ self.pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
+ return True
+ return False
-class Nemesis(PlayerSprite):
- def __init__(self):
- super(ThePlayer, self).__init__()
+class Nemesis(FigureSprite):
+
+ def __init__(self, config):
+ super(Nemesis, self).__init__()
self.sprite = load_image('sprites/nemesis.png')
+ self.reset_pos()
+ self._config = config
+ self._deadends = set([])
+ self._been = {}
+ self._bend = load_sound('sounds/bend.ogg')
+
+ def move(self, level, player_pos):
+ if not self.on_board():
+ # Advance towards the map a step at a time
+ self.pos = (self.pos[0] + 1, self.pos[1])
+ if self.on_board():
+ self.pos = level.enter_pos
+ return
+ # AI goes here
+ # First, if we're standing next to a gate, we attack it
+ neighbours = [(self.pos[0] + 1, self.pos[1]),
+ (self.pos[0] - 1, self.pos[1]),
+ (self.pos[0], self.pos[1] + 1),
+ (self.pos[0], self.pos[1] - 1)]
+ for cand in neighbours:
+ if level.is_gate(cand) and level.blocked(cand):
+ level.damage_gate(cand)
+ if self._config.getdefault('bane', 'sound', '0') != '0':
+ self._bend.play()
+ return
+ # check for dead-ends
+ if self._in_dead_end(level):
+ self._deadends.add(self.pos)
+ self._been.setdefault(self.pos, 0)
+ mindist = 999999
+ best_pos = None
+ for direction in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
+ new_pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
+ if new_pos in self._deadends:
+ continue
+ if self.can_move(direction, level):
+ # We weigh stuff by the number of times we've stepped there, to
+ # avoid certain types of loop
+ dist = (level.calc_dist(new_pos, player_pos)
+ + self._been.get(new_pos, 0))
+ if dist < mindist:
+ mindist = dist
+ best_pos = new_pos
+ if best_pos:
+ self._been[self.pos] += 1
+ self.pos = best_pos
+
+ def reset_pos(self):
+ self.pos = (-7, 0)
+ self._deadends = set([])
+ self._been = {}
+
+ def on_board(self):
+ return self.pos[0] >= 0
+
+ def _in_dead_end(self, level):
+ # Check if this is a dead end
+ blocked = 0
+ for direction in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
+ new_pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
+ if level.is_wall(new_pos) or new_pos in self._deadends:
+ blocked += 1
+ # A dead end has only 1 exit, and recurse back from there
+ return blocked >= 3