1 from plomrogue.tasks import Task_WAIT, Task_MOVE, Task_PICKUP, Task_DROP
2 from plomrogue.errors import ArgError
3 from plomrogue.commands import (cmd_GEN_WORLD, cmd_GET_GAMESTATE, cmd_MAP,
4 cmd_MAP, cmd_THING_TYPE, cmd_THING_POS,
5 cmd_TERRAIN_LINE, cmd_PLAYER_ID, cmd_TURN,
6 cmd_SWITCH_PLAYER, cmd_SAVE)
7 from plomrogue.mapping import MapHex
8 from plomrogue.parser import Parser
9 from plomrogue.io import GameIO
10 from plomrogue.misc import quote, stringify_yx
11 from plomrogue.things import Thing, ThingMonster, ThingHuman, ThingItem
17 def __init__(self, game):
22 def get_thing(self, id_, create_unfound=True):
23 for thing in self.things:
27 t = self.game.thing_type(self, id_)
34 class World(WorldBase):
36 def __init__(self, *args, **kwargs):
37 super().__init__(*args, **kwargs)
40 def new_thing_id(self):
41 if len(self.things) == 0:
43 return self.things[-1].id_ + 1
45 def new_map(self, yx):
46 self.map_ = self.game.map_type(yx)
48 def proceed_to_next_player_turn(self):
49 """Run game world turns until player can decide their next step.
51 Iterates through all non-player things, on each step
52 furthering them in their tasks (and letting them decide new
53 ones if they finish). The iteration order is: first all things
54 that come after the player in the world things list, then
55 (after incrementing the world turn) all that come before the
56 player; then the player's .proceed() is run, and if it does
57 not finish his task, the loop starts at the beginning. Once
58 the player's task is finished, the loop breaks.
61 player = self.get_player()
62 player_i = self.things.index(player)
63 for thing in self.things[player_i+1:]:
66 for thing in self.things[:player_i]:
68 player.proceed(is_AI=False)
69 if player.task is None:
73 return self.get_thing(self.player_id)
75 def make_new(self, yx, seed):
79 t = self.game.thing_types[type_](self)
80 t.position = [random.randint(0, yx[0] -1),
81 random.randint(0, yx[1] - 1)]
90 if 0 in pos or (yx[0] - 1) == pos[0] or (yx[1] - 1) == pos[1]:
93 self.map_[pos] = random.choice(('.', '.', '.', '.', 'x'))
95 player = add_thing('human')
96 self.player_id = player.id_
107 def __init__(self, game_file_name):
108 self.io = GameIO(game_file_name, self)
109 self.map_type = MapHex
110 self.tasks = {'WAIT': Task_WAIT,
112 'PICKUP': Task_PICKUP,
114 self.commands = {'GEN_WORLD': cmd_GEN_WORLD,
115 'GET_GAMESTATE': cmd_GET_GAMESTATE,
117 'THING_TYPE': cmd_THING_TYPE,
118 'THING_POS': cmd_THING_POS,
119 'TERRAIN_LINE': cmd_TERRAIN_LINE,
120 'PLAYER_ID': cmd_PLAYER_ID,
122 'SWITCH_PLAYER': cmd_SWITCH_PLAYER,
124 self.world_type = World
125 self.world = self.world_type(self)
126 self.thing_type = Thing
127 self.thing_types = {'human': ThingHuman,
128 'monster': ThingMonster,
131 def get_string_options(self, string_option_type):
132 if string_option_type == 'direction':
133 return self.world.map_.get_directions()
134 elif string_option_type == 'thingtype':
135 return list(self.thing_types.keys())
138 def send_gamestate(self, connection_id=None):
139 """Send out game state data relevant to clients."""
141 self.io.send('TURN ' + str(self.world.turn))
142 self.io.send('MAP ' + stringify_yx(self.world.map_.size))
143 visible_map = self.world.get_player().get_visible_map()
144 for y, line in visible_map.lines():
145 self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, quote(line)))
146 visible_things = self.world.get_player().get_visible_things()
147 for thing in visible_things:
148 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
149 self.io.send('THING_POS %s %s' % (thing.id_,
150 stringify_yx(thing.position)))
151 player = self.world.get_player()
152 self.io.send('PLAYER_POS %s' % (stringify_yx(player.position)))
153 self.io.send('PLAYER_INVENTORY %s' % ','.join([str(i) for i in
155 for id_ in player.inventory:
156 thing = self.world.get_thing(id_)
157 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
158 self.io.send('THING_POS %s %s' % (thing.id_,
159 stringify_yx(thing.position)))
160 self.io.send('GAME_STATE_COMPLETE')
163 """Send turn finish signal, run game world, send new world data.
165 First sends 'TURN_FINISHED' message, then runs game world
166 until new player input is needed, then sends game state.
168 self.io.send('TURN_FINISHED ' + str(self.world.turn))
169 self.world.proceed_to_next_player_turn()
170 msg = str(self.world.get_player()._last_task_result)
171 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
172 self.send_gamestate()
174 def get_command(self, command_name):
176 def partial_with_attrs(f, *args, **kwargs):
177 from functools import partial
178 p = partial(f, *args, **kwargs)
179 p.__dict__.update(f.__dict__)
182 def cmd_TASK_colon(task_name, game, *args):
183 game.world.get_player().set_task(task_name, args)
186 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
187 t = game.world.get_thing(thing_id, False)
189 raise ArgError('No such Thing.')
190 task_class = game.tasks[task_name]
191 t.task = task_class(t, args)
194 def task_prefixed(command_name, task_prefix, task_command,
195 argtypes_prefix=None):
196 if command_name[:len(task_prefix)] == task_prefix:
197 task_name = command_name[len(task_prefix):]
198 if task_name in self.tasks:
199 f = partial_with_attrs(task_command, task_name, self)
200 task = self.tasks[task_name]
202 f.argtypes = argtypes_prefix + ' ' + task.argtypes
204 f.argtypes = task.argtypes
208 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
211 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
212 'int:nonneg int:nonneg ')
215 if command_name in self.commands:
216 f = partial_with_attrs(self.commands[command_name], self)