Less buggy Kivy 1.7 hackery
[erdslangetjie.git] / erdslangetjie / player.py
1 # Player and Player-like objects
2 from erdslangetjie.data import load_image, load_sound
3
4
5 class GameSprite(object):
6
7     def __init__(self):
8         self.pos = (0, 0)
9         self.sprite = None
10
11     def get_image(self):
12         return self.sprite
13
14     def get_texture(self):
15         return self.sprite.texture
16
17
18 class FigureSprite(GameSprite):
19
20     def can_move(self, direction, level):
21         '''Check if we can move in the given direction'''
22         cand_pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
23         return not level.blocked(cand_pos)
24
25
26 class ThePlayer(FigureSprite):
27
28     def __init__(self):
29         super(ThePlayer, self).__init__()
30         self.sprite = load_image('sprites/player.png')
31
32     def move(self, direction, level):
33         if self.can_move(direction, level):
34             self.pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
35             return True
36         return False
37
38
39 class Nemesis(FigureSprite):
40
41     def __init__(self, config):
42         super(Nemesis, self).__init__()
43         self.sprite = load_image('sprites/nemesis.png')
44         self.reset_pos()
45         self._config = config
46         self._deadends = set([])
47         self._been = {}
48         self._bend = load_sound('sounds/bend.ogg')
49
50     def move(self, level, player_pos):
51         if not self.on_board():
52             # Advance towards the map a step at a time
53             self.pos = (self.pos[0] + 1, self.pos[1])
54             if self.on_board():
55                 self.pos = level.enter_pos
56             return
57         # AI goes here
58         # First, if we're standing next to a gate, we attack it
59         neighbours = [(self.pos[0] + 1, self.pos[1]),
60                 (self.pos[0] - 1, self.pos[1]),
61                 (self.pos[0], self.pos[1] + 1),
62                 (self.pos[0], self.pos[1] - 1)]
63         for cand in neighbours:
64             if level.is_gate(cand) and level.blocked(cand):
65                 level.damage_gate(cand)
66                 if self._config.getdefault('bane', 'sound', '0') != '0':
67                     self._bend.play()
68                 return
69         # check for dead-ends
70         if self._in_dead_end(level):
71             self._deadends.add(self.pos)
72         self._been.setdefault(self.pos, 0)
73         mindist = 999999
74         best_pos = None
75         for direction in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
76             new_pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
77             if new_pos in self._deadends:
78                 continue
79             if self.can_move(direction, level):
80                 # We weigh stuff by the number of times we've stepped there, to
81                 # avoid certain types of loop
82                 dist = (level.calc_dist(new_pos, player_pos)
83                         + self._been.get(new_pos, 0))
84                 if dist < mindist:
85                     mindist = dist
86                     best_pos = new_pos
87         if best_pos:
88             self._been[self.pos] += 1
89             self.pos = best_pos
90
91     def reset_pos(self):
92         self.pos = (-7, 0)
93         self._deadends = set([])
94         self._been = {}
95
96     def on_board(self):
97         return self.pos[0] >= 0
98
99     def _in_dead_end(self, level):
100         # Check if this is a dead end
101         blocked = 0
102         for direction in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
103             new_pos = (self.pos[0] + direction[0], self.pos[1] + direction[1])
104             if level.is_wall(new_pos) or new_pos in self._deadends:
105                 blocked += 1
106         # A dead end has only 1 exit, and recurse back from there
107         return blocked >= 3