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 if len(player.inventory) > 0:
154 self.io.send('PLAYER_INVENTORY %s' % ','.join([str(i) for i in
157 self.io.send('PLAYER_INVENTORY ,')
158 for id_ in player.inventory:
159 thing = self.world.get_thing(id_)
160 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
161 self.io.send('THING_POS %s %s' % (thing.id_,
162 stringify_yx(thing.position)))
163 self.io.send('GAME_STATE_COMPLETE')
166 """Send turn finish signal, run game world, send new world data.
168 First sends 'TURN_FINISHED' message, then runs game world
169 until new player input is needed, then sends game state.
171 self.io.send('TURN_FINISHED ' + str(self.world.turn))
172 self.world.proceed_to_next_player_turn()
173 msg = str(self.world.get_player()._last_task_result)
174 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
175 self.send_gamestate()
177 def get_command(self, command_name):
179 def partial_with_attrs(f, *args, **kwargs):
180 from functools import partial
181 p = partial(f, *args, **kwargs)
182 p.__dict__.update(f.__dict__)
185 def cmd_TASK_colon(task_name, game, *args):
186 game.world.get_player().set_task(task_name, args)
189 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
190 t = game.world.get_thing(thing_id, False)
192 raise ArgError('No such Thing.')
193 task_class = game.tasks[task_name]
194 t.task = task_class(t, args)
197 def task_prefixed(command_name, task_prefix, task_command,
198 argtypes_prefix=None):
199 if command_name[:len(task_prefix)] == task_prefix:
200 task_name = command_name[len(task_prefix):]
201 if task_name in self.tasks:
202 f = partial_with_attrs(task_command, task_name, self)
203 task = self.tasks[task_name]
205 f.argtypes = argtypes_prefix + ' ' + task.argtypes
207 f.argtypes = task.argtypes
211 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
214 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
215 'int:nonneg int:nonneg ')
218 if command_name in self.commands:
219 f = partial_with_attrs(self.commands[command_name], self)