7 class GameError(Exception):
11 def move_pos(direction, pos_yx):
14 elif direction == 'DOWN':
16 elif direction == 'RIGHT':
18 elif direction == 'LEFT':
22 class Map(game_common.Map):
24 def get_line(self, y):
26 return self.terrain[y * width:(y + 1) * width]
29 class World(game_common.World):
33 self.Thing = Thing # use local Thing class instead of game_common's
34 self.map_ = Map() # use extended child class
37 def proceed_to_next_player_turn(self):
38 """Run game world turns until player can decide their next step.
40 Iterates through all non-player things, on each step
41 furthering them in their tasks (and letting them decide new
42 ones if they finish). The iteration order is: first all things
43 that come after the player in the world things list, then
44 (after incrementing the world turn) all that come before the
45 player; then the player's .proceed() is run, and if it does
46 not finish his task, the loop starts at the beginning. Once
47 the player's task is finished, the loop breaks.
50 player = self.get_player()
51 player_i = self.things.index(player)
52 for thing in self.things[player_i+1:]:
55 for thing in self.things[:player_i]:
57 player.proceed(is_AI=False)
58 if player.task is None:
62 return self.get_thing(self.player_id)
67 def __init__(self, thing, name, args=(), kwargs={}):
75 if self.name == 'move':
76 if len(self.args) > 0:
77 direction = self.args[0]
79 direction = self.kwargs['direction']
80 test_pos = self.thing.position[:]
81 move_pos(direction, test_pos)
82 if test_pos[0] < 0 or test_pos[1] < 0 or \
83 test_pos[0] >= self.thing.world.map_.size[0] or \
84 test_pos[1] >= self.thing.world.map_.size[1]:
85 raise GameError('would move outside map bounds')
86 pos_i = test_pos[0] * self.thing.world.map_.size[1] + test_pos[1]
87 map_tile = self.thing.world.map_.terrain[pos_i]
89 raise GameError('would move into illegal terrain')
90 for t in self.thing.world.things:
91 if t.position == test_pos:
92 raise GameError('would move into other thing')
95 class Thing(game_common.Thing):
97 def __init__(self, *args, **kwargs):
98 super().__init__(*args, **kwargs)
99 self.task = Task(self, 'wait')
100 self.last_task_result = None
105 def task_move(self, direction):
106 move_pos(direction, self.position)
109 def decide_task(self):
110 if self.position[1] > 1:
111 self.set_task('move', 'LEFT')
112 elif self.position[1] < 3:
113 self.set_task('move', 'RIGHT')
115 self.set_task('wait')
117 def set_task(self, task_name, *args, **kwargs):
118 self.task = Task(self, task_name, args, kwargs)
121 def proceed(self, is_AI=True):
122 """Further the thing in its tasks.
124 Decrements .task.todo; if it thus falls to <= 0, enacts method whose
125 name is 'task_' + self.task.name and sets .task = None. If is_AI, calls
126 .decide_task to decide a self.task.
128 Before doing anything, checks that task is still possible, and aborts
129 it otherwise (for AI things, decides a new task).
133 except GameError as e:
135 self.last_task_result = e
140 if self.task.todo <= 0:
141 task = getattr(self, 'task_' + self.task.name)
142 self.last_task_result = task(*self.task.args, **self.task.kwargs)
144 if is_AI and self.task is None:
147 def get_visible_map(self):
148 size = self.world.map_.size
149 m = Map(size, '?'*size[0]*size[1])
150 y_me = self.position[0]
151 x_me = self.position[1]
152 for y in range(m.size[0]):
153 if y in (y_me - 1, y_me, y_me + 1):
154 for x in range(m.size[1]):
155 if x in (x_me - 1, x_me, x_me + 1):
156 pos = y * size[1] + x
157 c = self.world.map_.terrain[pos]
158 m.terrain = m.terrain[:pos] + c + m.terrain[pos+1:]
164 def cmd_MOVE(self, direction):
165 """Set player task to 'move' with direction arg, finish player turn."""
166 if direction not in {'UP', 'DOWN', 'RIGHT', 'LEFT'}:
167 raise parser.ArgError('Move argument must be one of: '
168 'UP, DOWN, RIGHT, LEFT')
169 self.world.get_player().set_task('move', direction=direction)
171 cmd_MOVE.argtypes = 'string'
174 """Set player task to 'wait', finish player turn."""
175 self.world.get_player().set_task('wait')
178 def cmd_GET_GAMESTATE(self, connection_id):
179 """Send game state jto caller."""
180 self.send_gamestate(connection_id)
182 def cmd_ECHO(self, msg, connection_id):
183 """Send msg to caller."""
184 self.send(msg, connection_id)
185 cmd_ECHO.argtypes = 'string'
187 def cmd_ALL(self, msg, connection_id):
188 """Send msg to all clients."""
190 cmd_ALL.argtypes = 'string'
192 def cmd_TERRAIN_LINE(self, y, terrain_line):
193 self.world.map_.set_line(y, terrain_line)
194 cmd_TERRAIN_LINE.argtypes = 'int:nonneg string'