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