home · contact · privacy
Refactor.
[plomrogue2-experiments] / game.py
1 class GameError(Exception):
2     pass
3
4
5 def move_pos(direction, pos_yx):
6     if direction == 'UP':
7         pos_yx[0] -= 1
8     elif direction == 'DOWN':
9         pos_yx[0] += 1
10     elif direction == 'RIGHT':
11         pos_yx[1] += 1
12     elif direction == 'LEFT':
13         pos_yx[1] -= 1
14
15
16 class World:
17
18     def __init__(self):
19         self.turn = 0
20         self.map_size = (0, 0)
21         self.terrain_map = ''
22         self.things = []
23         self.player_id = 0
24
25     def proceed_to_next_player_turn(self):
26         """Run game world turns until player can decide their next step.
27
28         Iterates through all non-player things, on each step
29         furthering them in their tasks (and letting them decide new
30         ones if they finish). The iteration order is: first all things
31         that come after the player in the world things list, then
32         (after incrementing the world turn) all that come before the
33         player; then the player's .proceed() is run, and if it does
34         not finish his task, the loop starts at the beginning. Once
35         the player's task is finished, the loop breaks.
36         """
37         while True:
38             for thing in self.things[self.player_id+1:]:
39                 thing.proceed()
40             self.turn += 1
41             for thing in self.things[:self.player_id]:
42                 thing.proceed()
43             player = self.get_thing(self.player_id)
44             player.proceed(is_AI=False)
45             if player.task is None:
46                 break
47
48     def set_map_size(self, yx):
49         y, x = yx
50         self.map_size = (y, x)
51         self.terrain_map = ''
52         for y in range(self.map_size[0]):
53             self.terrain_map += '?' * self.map_size[1]
54
55     def set_map_line(self, y, line):
56         width_map = self.map_size[1]
57         if y >= self.map_size[0]:
58             raise ArgError('too large row number %s' % y)
59         width_line = len(line)
60         if width_line > width_map:
61             raise ArgError('too large map line width %s' % width_line)
62         self.terrain_map = self.terrain_map[:y * width_map] + line + \
63                            self.terrain_map[(y + 1) * width_map:]
64
65     def get_thing(self, i):
66         for thing in self.things:
67             if i == thing.id_:
68                 return thing
69         t = Thing(self, i, '?', [0,0])
70         self.things += [t]
71         return t
72
73
74 class Task:
75
76     def __init__(self, thing, name, args=(), kwargs={}):
77         self.name = name
78         self.thing = thing
79         self.args = args
80         self.kwargs = kwargs
81         self.todo = 1
82
83     def check(self):
84         if self.name == 'move':
85             if len(self.args) > 0:
86                 direction = self.args[0]
87             else:
88                 direction = self.kwargs['direction']
89             test_pos = self.thing.position[:]
90             move_pos(direction, test_pos)
91             if test_pos[0] < 0 or test_pos[1] < 0 or \
92                test_pos[0] >= self.thing.world.map_size[0] or \
93                test_pos[1] >= self.thing.world.map_size[1]:
94                 raise GameError('would move outside map bounds')
95             pos_i = test_pos[0] * self.thing.world.map_size[1] + test_pos[1]
96             map_tile = self.thing.world.terrain_map[pos_i]
97             if map_tile != '.':
98                 raise GameError('would move into illegal terrain')
99
100
101 class Thing:
102
103     def __init__(self, world, id_, type_, position):
104         self.world = world
105         self.id_ = id_
106         self.type_ = type_
107         self.position = position
108         self.task = Task(self, 'wait')
109
110     def task_wait(self):
111         pass
112
113     def task_move(self, direction):
114         move_pos(direction, self.position)
115
116     def decide_task(self):
117         if self.position[1] > 1:
118             self.set_task('move', 'LEFT')
119         elif self.position[1] < 3:
120             self.set_task('move', 'RIGHT')
121         else:
122             self.set_task('wait')
123
124     def set_task(self, task_name, *args, **kwargs):
125         self.task = Task(self, task_name, args, kwargs)
126         self.task.check()
127
128     def proceed(self, is_AI=True):
129         """Further the thing in its tasks.
130
131         Decrements .task.todo; if it thus falls to <= 0, enacts method whose
132         name is 'task_' + self.task.name and sets .task = None. If is_AI, calls
133         .decide_task to decide a self.task.
134         """
135         self.task.todo -= 1
136         if self.task.todo <= 0:
137             task = getattr(self, 'task_' + self.task.name)
138             task(*self.task.args, **self.task.kwargs)
139             self.task = None
140         if is_AI and self.task is None:
141             self.decide_task()