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_THING_INVENTORY, cmd_GET_PICKABLE_ITEMS,
6 cmd_TERRAIN_LINE, cmd_PLAYER_ID, cmd_TURN,
7 cmd_SWITCH_PLAYER, cmd_SAVE)
8 from plomrogue.mapping import MapHex
9 from plomrogue.parser import Parser
10 from plomrogue.io import GameIO
11 from plomrogue.misc import quote, stringify_yx
12 from plomrogue.things import Thing, ThingMonster, ThingHuman, ThingItem
18 def __init__(self, game):
23 def get_thing(self, id_, create_unfound=True):
24 for thing in self.things:
28 t = self.game.thing_type(self, id_)
35 class World(WorldBase):
37 def __init__(self, *args, **kwargs):
38 super().__init__(*args, **kwargs)
43 return self.get_thing(self.player_id)
45 def new_thing_id(self):
46 if len(self.things) == 0:
48 return self.things[-1].id_ + 1
50 def new_map(self, yx):
51 self.map_ = self.game.map_type(yx)
53 def proceed_to_next_player_turn(self):
54 """Run game world turns until player can decide their next step.
56 Iterates through all non-player things, on each step
57 furthering them in their tasks (and letting them decide new
58 ones if they finish). The iteration order is: first all things
59 that come after the player in the world things list, then
60 (after incrementing the world turn) all that come before the
61 player; then the player's .proceed() is run, and if it does
62 not finish his task, the loop starts at the beginning. Once
63 the player's task is finished, the loop breaks.
66 player_i = self.things.index(self.player)
67 for thing in self.things[player_i+1:]:
70 for thing in self.things[:player_i]:
72 self.player.proceed(is_AI=False)
73 if self.player.task is None:
76 def make_new(self, yx, seed):
80 t = self.game.thing_types[type_](self)
81 t.position = (random.randint(0, yx[0] -1),
82 random.randint(0, yx[1] - 1))
91 if 0 in pos or (yx[0] - 1) == pos[0] or (yx[1] - 1) == pos[1]:
94 self.map_[pos] = random.choice(('.', '.', '.', '.', 'x'))
96 player = add_thing('human')
97 self.player_id = player.id_
108 def __init__(self, game_file_name):
109 self.io = GameIO(game_file_name, self)
110 self.map_type = MapHex
111 self.tasks = {'WAIT': Task_WAIT,
113 'PICKUP': Task_PICKUP,
115 self.commands = {'GEN_WORLD': cmd_GEN_WORLD,
116 'GET_GAMESTATE': cmd_GET_GAMESTATE,
118 'THING_TYPE': cmd_THING_TYPE,
119 'THING_POS': cmd_THING_POS,
120 'THING_INVENTORY': cmd_THING_INVENTORY,
121 'TERRAIN_LINE': cmd_TERRAIN_LINE,
122 'GET_PICKABLE_ITEMS': cmd_GET_PICKABLE_ITEMS,
123 'PLAYER_ID': cmd_PLAYER_ID,
125 'SWITCH_PLAYER': cmd_SWITCH_PLAYER,
127 self.world_type = World
128 self.world = self.world_type(self)
129 self.thing_type = Thing
130 self.thing_types = {'human': ThingHuman,
131 'monster': ThingMonster,
134 def get_string_options(self, string_option_type):
135 if string_option_type == 'direction':
136 return self.world.map_.get_directions()
137 elif string_option_type == 'thingtype':
138 return list(self.thing_types.keys())
141 def send_gamestate(self, connection_id=None):
142 """Send out game state data relevant to clients."""
144 self.io.send('TURN ' + str(self.world.turn))
145 self.io.send('MAP ' + stringify_yx(self.world.map_.size))
146 visible_map = self.world.player.get_visible_map()
147 for y, line in visible_map.lines():
148 self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, quote(line)))
149 visible_things = self.world.player.get_visible_things()
150 for thing in visible_things:
151 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
152 self.io.send('THING_POS %s %s' % (thing.id_,
153 stringify_yx(thing.position)))
154 if len(self.world.player.inventory) > 0:
155 self.io.send('PLAYER_INVENTORY %s' %
156 ','.join([str(i) for i in self.world.player.inventory]))
158 self.io.send('PLAYER_INVENTORY ,')
159 for id_ in self.world.player.inventory:
160 thing = self.world.get_thing(id_)
161 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
162 self.io.send('THING_POS %s %s' % (thing.id_,
163 stringify_yx(thing.position)))
164 self.io.send('GAME_STATE_COMPLETE')
167 """Send turn finish signal, run game world, send new world data.
169 First sends 'TURN_FINISHED' message, then runs game world
170 until new player input is needed, then sends game state.
172 self.io.send('TURN_FINISHED ' + str(self.world.turn))
173 self.world.proceed_to_next_player_turn()
174 msg = str(self.world.player._last_task_result)
175 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
176 self.send_gamestate()
178 def get_command(self, command_name):
180 def partial_with_attrs(f, *args, **kwargs):
181 from functools import partial
182 p = partial(f, *args, **kwargs)
183 p.__dict__.update(f.__dict__)
186 def cmd_TASK_colon(task_name, game, *args):
187 game.world.player.set_task(task_name, args)
190 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
191 t = game.world.get_thing(thing_id, False)
193 raise ArgError('No such Thing.')
194 task_class = game.tasks[task_name]
195 t.task = task_class(t, args)
198 def task_prefixed(command_name, task_prefix, task_command,
199 argtypes_prefix=None):
200 if command_name[:len(task_prefix)] == task_prefix:
201 task_name = command_name[len(task_prefix):]
202 if task_name in self.tasks:
203 f = partial_with_attrs(task_command, task_name, self)
204 task = self.tasks[task_name]
206 f.argtypes = argtypes_prefix + ' ' + task.argtypes
208 f.argtypes = task.argtypes
212 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
215 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
216 'int:nonneg int:nonneg ')
219 if command_name in self.commands:
220 f = partial_with_attrs(self.commands[command_name], self)