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)
42 return self.get_thing(self.player_id)
44 def new_thing_id(self):
45 if len(self.things) == 0:
47 return self.things[-1].id_ + 1
49 def new_map(self, yx):
50 self.map_ = self.game.map_type(yx)
52 def proceed_to_next_player_turn(self):
53 """Run game world turns until player can decide their next step.
55 Iterates through all non-player things, on each step
56 furthering them in their tasks (and letting them decide new
57 ones if they finish). The iteration order is: first all things
58 that come after the player in the world things list, then
59 (after incrementing the world turn) all that come before the
60 player; then the player's .proceed() is run, and if it does
61 not finish his task, the loop starts at the beginning. Once
62 the player's task is finished, the loop breaks.
65 player_i = self.things.index(self.player)
66 for thing in self.things[player_i+1:]:
69 for thing in self.things[:player_i]:
71 self.player.proceed(is_AI=False)
72 if self.player.task is None:
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.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.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 if len(self.world.player.inventory) > 0:
152 self.io.send('PLAYER_INVENTORY %s' %
153 ','.join([str(i) for i in self.world.player.inventory]))
155 self.io.send('PLAYER_INVENTORY ,')
156 for id_ in self.world.player.inventory:
157 thing = self.world.get_thing(id_)
158 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
159 self.io.send('THING_POS %s %s' % (thing.id_,
160 stringify_yx(thing.position)))
161 self.io.send('GAME_STATE_COMPLETE')
164 """Send turn finish signal, run game world, send new world data.
166 First sends 'TURN_FINISHED' message, then runs game world
167 until new player input is needed, then sends game state.
169 self.io.send('TURN_FINISHED ' + str(self.world.turn))
170 self.world.proceed_to_next_player_turn()
171 msg = str(self.world.player._last_task_result)
172 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
173 self.send_gamestate()
175 def get_command(self, command_name):
177 def partial_with_attrs(f, *args, **kwargs):
178 from functools import partial
179 p = partial(f, *args, **kwargs)
180 p.__dict__.update(f.__dict__)
183 def cmd_TASK_colon(task_name, game, *args):
184 game.world.player.set_task(task_name, args)
187 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
188 t = game.world.get_thing(thing_id, False)
190 raise ArgError('No such Thing.')
191 task_class = game.tasks[task_name]
192 t.task = task_class(t, args)
195 def task_prefixed(command_name, task_prefix, task_command,
196 argtypes_prefix=None):
197 if command_name[:len(task_prefix)] == task_prefix:
198 task_name = command_name[len(task_prefix):]
199 if task_name in self.tasks:
200 f = partial_with_attrs(task_command, task_name, self)
201 task = self.tasks[task_name]
203 f.argtypes = argtypes_prefix + ' ' + task.argtypes
205 f.argtypes = task.argtypes
209 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
212 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
213 'int:nonneg int:nonneg ')
216 if command_name in self.commands:
217 f = partial_with_attrs(self.commands[command_name], self)