1 from plomrogue.tasks import (Task_WAIT, Task_MOVE, Task_PICKUP,
3 from plomrogue.errors import ArgError, GameError
4 from plomrogue.commands import (cmd_GEN_WORLD, cmd_GET_GAMESTATE,
5 cmd_MAP, cmd_MAP, cmd_THING_TYPE,
6 cmd_THING_POS, cmd_THING_INVENTORY,
7 cmd_THING_HEALTH, cmd_SEED,
8 cmd_GET_PICKABLE_ITEMS, cmd_MAP_SIZE,
9 cmd_TERRAIN_LINE, cmd_PLAYER_ID,
10 cmd_TURN, cmd_SWITCH_PLAYER, cmd_SAVE)
11 from plomrogue.mapping import MapHex
12 from plomrogue.parser import Parser
13 from plomrogue.io import GameIO
14 from plomrogue.misc import quote, stringify_yx
15 from plomrogue.things import Thing, ThingMonster, ThingHuman, ThingFood
20 class PRNGod(random.Random):
23 self.prngod_seed = seed
26 return self.prngod_seed
32 self.prngod_seed = ((self.prngod_seed * 1103515245) + 12345) % 2**32
33 return (self.prngod_seed >> 16) / (2**16 - 1)
39 def __init__(self, game):
44 def get_thing(self, id_, create_unfound=True):
45 for thing in self.things:
49 t = self.game.thing_type(self, id_)
54 def things_at_pos(self, pos):
63 class World(WorldBase):
65 def __init__(self, *args, **kwargs):
66 super().__init__(*args, **kwargs)
68 self.player_is_alive = True
75 return self.get_thing(self.player_id)
77 def new_thing_id(self):
78 if len(self.things) == 0:
80 return self.things[-1].id_ + 1
82 def new_map(self, map_pos):
83 self.maps[map_pos] = self.game.map_type(self.map_size)
85 def proceed_to_next_player_turn(self):
86 """Run game world turns until player can decide their next step.
88 Iterates through all non-player things, on each step
89 furthering them in their tasks (and letting them decide new
90 ones if they finish). The iteration order is: first all things
91 that come after the player in the world things list, then
92 (after incrementing the world turn) all that come before the
93 player; then the player's .proceed() is run, and if it does
94 not finish his task, the loop starts at the beginning. Once
95 the player's task is finished, or the player is dead, the loop
100 player_i = self.things.index(self.player)
101 for thing in self.things[player_i+1:]:
104 for pos in self.maps[(0,0)]:
105 if self.maps[(0,0)][pos] == '.' and \
106 len(self.things_at_pos(((0,0), pos))) == 0 and \
107 self.rand.random() > 0.999:
108 self.add_thing_at('food', ((0,0), pos))
109 for thing in self.things[:player_i]:
111 self.player.proceed(is_AI=False)
112 if self.player.task is None or not self.player_is_alive:
115 def add_thing_at(self, type_, pos):
116 t = self.game.thing_types[type_](self)
121 def make_new(self, yx, seed):
123 def add_thing_at_random(type_):
126 (self.rand.randint(0, yx[0] -1),
127 self.rand.randint(0, yx[1] -1)))
128 if self.maps[new_pos[0]][new_pos[1]] != '.':
130 if len(self.things_at_pos(new_pos)) > 0:
132 return self.add_thing_at(type_, new_pos)
145 self.new_map((-1,-1))
148 for map_pos in self.maps:
149 map_ = self.maps[map_pos]
152 map_[pos] = self.rand.choice(('.', '.', '.', '.', 'x'))
156 player = add_thing_at_random('human')
157 self.player_id = player.id_
158 add_thing_at_random('monster')
159 add_thing_at_random('monster')
160 add_thing_at_random('food')
161 add_thing_at_random('food')
162 add_thing_at_random('food')
163 add_thing_at_random('food')
170 def __init__(self, game_file_name):
171 self.io = GameIO(game_file_name, self)
172 self.map_type = MapHex
173 self.tasks = {'WAIT': Task_WAIT,
175 'PICKUP': Task_PICKUP,
178 self.commands = {'GEN_WORLD': cmd_GEN_WORLD,
179 'GET_GAMESTATE': cmd_GET_GAMESTATE,
181 'MAP_SIZE': cmd_MAP_SIZE,
183 'THING_TYPE': cmd_THING_TYPE,
184 'THING_POS': cmd_THING_POS,
185 'THING_HEALTH': cmd_THING_HEALTH,
186 'THING_INVENTORY': cmd_THING_INVENTORY,
187 'TERRAIN_LINE': cmd_TERRAIN_LINE,
188 'GET_PICKABLE_ITEMS': cmd_GET_PICKABLE_ITEMS,
189 'PLAYER_ID': cmd_PLAYER_ID,
191 'SWITCH_PLAYER': cmd_SWITCH_PLAYER,
193 self.world_type = World
194 self.world = self.world_type(self)
195 self.thing_type = Thing
196 self.thing_types = {'human': ThingHuman,
197 'monster': ThingMonster,
200 def get_string_options(self, string_option_type):
201 if string_option_type == 'direction':
202 return self.world.maps[(0,0)].get_directions()
203 elif string_option_type == 'thingtype':
204 return list(self.thing_types.keys())
207 def send_gamestate(self, connection_id=None):
208 """Send out game state data relevant to clients."""
210 def send_thing(offset, thing):
211 offset_pos = (thing.position[1][0] - offset[0],
212 thing.position[1][1] - offset[1])
213 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
214 self.io.send('THING_POS %s %s' % (thing.id_,
215 stringify_yx(offset_pos)))
217 self.io.send('TURN ' + str(self.world.turn))
218 visible_map = self.world.player.get_visible_map()
219 offset = self.world.player.get_surroundings_offset()
220 self.io.send('VISIBLE_MAP ' + stringify_yx(offset) + ' '
221 + stringify_yx(visible_map.size))
222 for y, line in visible_map.lines():
223 self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, quote(line)))
224 visible_things = self.world.player.get_visible_things()
225 for thing in visible_things:
226 send_thing(offset, thing)
227 if hasattr(thing, 'health'):
228 self.io.send('THING_HEALTH %s %s' % (thing.id_,
230 if len(self.world.player.inventory) > 0:
231 self.io.send('PLAYER_INVENTORY %s' %
232 ','.join([str(i) for i in self.world.player.inventory]))
234 self.io.send('PLAYER_INVENTORY ,')
235 for id_ in self.world.player.inventory:
236 thing = self.world.get_thing(id_)
237 send_thing(offset, thing)
238 self.io.send('GAME_STATE_COMPLETE')
241 """Send turn finish signal, run game world, send new world data.
243 First sends 'TURN_FINISHED' message, then runs game world
244 until new player input is needed, then sends game state.
246 self.io.send('TURN_FINISHED ' + str(self.world.turn))
247 self.world.proceed_to_next_player_turn()
248 msg = str(self.world.player._last_task_result)
249 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
250 self.send_gamestate()
252 def get_command(self, command_name):
254 def partial_with_attrs(f, *args, **kwargs):
255 from functools import partial
256 p = partial(f, *args, **kwargs)
257 p.__dict__.update(f.__dict__)
260 def cmd_TASK_colon(task_name, game, *args):
261 if not game.world.player_is_alive:
262 raise GameError('You are dead.')
263 game.world.player.set_task(task_name, args)
266 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
267 t = game.world.get_thing(thing_id, False)
269 raise ArgError('No such Thing.')
270 task_class = game.tasks[task_name]
271 t.task = task_class(t, args)
274 def task_prefixed(command_name, task_prefix, task_command,
275 argtypes_prefix=None):
276 if command_name[:len(task_prefix)] == task_prefix:
277 task_name = command_name[len(task_prefix):]
278 if task_name in self.tasks:
279 f = partial_with_attrs(task_command, task_name, self)
280 task = self.tasks[task_name]
282 f.argtypes = argtypes_prefix + ' ' + task.argtypes
284 f.argtypes = task.argtypes
288 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
291 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
292 'int:nonneg int:nonneg ')
295 if command_name in self.commands:
296 f = partial_with_attrs(self.commands[command_name], self)