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, YX
12 from plomrogue.parser import Parser
13 from plomrogue.io import GameIO
14 from plomrogue.misc import quote
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
70 self.map_size = YX(1,1)
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[YX(0,0)]:
105 if self.maps[YX(0,0)][pos] == '.' and \
106 len(self.things_at_pos((YX(0,0), pos))) == 0 and \
107 self.rand.random() > 0.999:
108 self.add_thing_at('food', (YX(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 YX(self.rand.randint(0, yx.y - 1),
127 self.rand.randint(0, yx.x - 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)
139 self.new_map(YX(0,0))
140 self.new_map(YX(0,1))
141 self.new_map(YX(1,1))
142 self.new_map(YX(1,0))
143 self.new_map(YX(1,-1))
144 self.new_map(YX(0,-1))
145 self.new_map(YX(-1,-1))
146 self.new_map(YX(-1,0))
147 self.new_map(YX(-1,1))
148 for map_pos in self.maps:
149 map_ = self.maps[map_pos]
150 if YX(0,0) == 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.map_type().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] - offset)
212 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
213 self.io.send('THING_POS %s %s' % (thing.id_, offset_pos))
215 self.io.send('TURN ' + str(self.world.turn))
216 visible_map = self.world.player.get_visible_map()
217 offset = self.world.player.get_surroundings_offset()
218 self.io.send('VISIBLE_MAP %s %s' % (offset, visible_map.size))
219 for y, line in visible_map.lines():
220 self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, quote(line)))
221 visible_things = self.world.player.get_visible_things()
222 for thing in visible_things:
223 send_thing(offset, thing)
224 if hasattr(thing, 'health'):
225 self.io.send('THING_HEALTH %s %s' % (thing.id_,
227 if len(self.world.player.inventory) > 0:
228 self.io.send('PLAYER_INVENTORY %s' %
229 ','.join([str(i) for i in self.world.player.inventory]))
231 self.io.send('PLAYER_INVENTORY ,')
232 for id_ in self.world.player.inventory:
233 thing = self.world.get_thing(id_)
234 send_thing(offset, thing)
235 self.io.send('GAME_STATE_COMPLETE')
238 """Send turn finish signal, run game world, send new world data.
240 First sends 'TURN_FINISHED' message, then runs game world
241 until new player input is needed, then sends game state.
243 self.io.send('TURN_FINISHED ' + str(self.world.turn))
244 self.world.proceed_to_next_player_turn()
245 msg = str(self.world.player._last_task_result)
246 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
247 self.send_gamestate()
249 def get_command(self, command_name):
251 def partial_with_attrs(f, *args, **kwargs):
252 from functools import partial
253 p = partial(f, *args, **kwargs)
254 p.__dict__.update(f.__dict__)
257 def cmd_TASK_colon(task_name, game, *args):
258 if not game.world.player_is_alive:
259 raise GameError('You are dead.')
260 game.world.player.set_task(task_name, args)
263 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
264 t = game.world.get_thing(thing_id, False)
266 raise ArgError('No such Thing.')
267 task_class = game.tasks[task_name]
268 t.task = task_class(t, args)
271 def task_prefixed(command_name, task_prefix, task_command,
272 argtypes_prefix=None):
273 if command_name[:len(task_prefix)] == task_prefix:
274 task_name = command_name[len(task_prefix):]
275 if task_name in self.tasks:
276 f = partial_with_attrs(task_command, task_name, self)
277 task = self.tasks[task_name]
279 f.argtypes = argtypes_prefix + ' ' + task.argtypes
281 f.argtypes = task.argtypes
285 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
288 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
289 'int:nonneg int:nonneg ')
292 if command_name in self.commands:
293 f = partial_with_attrs(self.commands[command_name], self)