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,
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 'PLAYER_ID': cmd_PLAYER_ID,
124 'SWITCH_PLAYER': cmd_SWITCH_PLAYER,
126 self.world_type = World
127 self.world = self.world_type(self)
128 self.thing_type = Thing
129 self.thing_types = {'human': ThingHuman,
130 'monster': ThingMonster,
133 def get_string_options(self, string_option_type):
134 if string_option_type == 'direction':
135 return self.world.map_.get_directions()
136 elif string_option_type == 'thingtype':
137 return list(self.thing_types.keys())
140 def send_gamestate(self, connection_id=None):
141 """Send out game state data relevant to clients."""
143 self.io.send('TURN ' + str(self.world.turn))
144 self.io.send('MAP ' + stringify_yx(self.world.map_.size))
145 visible_map = self.world.player.get_visible_map()
146 for y, line in visible_map.lines():
147 self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, quote(line)))
148 visible_things = self.world.player.get_visible_things()
149 for thing in visible_things:
150 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
151 self.io.send('THING_POS %s %s' % (thing.id_,
152 stringify_yx(thing.position)))
153 if len(self.world.player.inventory) > 0:
154 self.io.send('PLAYER_INVENTORY %s' %
155 ','.join([str(i) for i in self.world.player.inventory]))
157 self.io.send('PLAYER_INVENTORY ,')
158 for id_ in self.world.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.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.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)