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