1 from plomrogue.tasks import Task_WAIT, Task_MOVE
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_map(self, yx):
41 self.map_ = self.game.map_type(yx)
43 def proceed_to_next_player_turn(self):
44 """Run game world turns until player can decide their next step.
46 Iterates through all non-player things, on each step
47 furthering them in their tasks (and letting them decide new
48 ones if they finish). The iteration order is: first all things
49 that come after the player in the world things list, then
50 (after incrementing the world turn) all that come before the
51 player; then the player's .proceed() is run, and if it does
52 not finish his task, the loop starts at the beginning. Once
53 the player's task is finished, the loop breaks.
56 player = self.get_player()
57 player_i = self.things.index(player)
58 for thing in self.things[player_i+1:]:
61 for thing in self.things[:player_i]:
63 player.proceed(is_AI=False)
64 if player.task is None:
68 return self.get_thing(self.player_id)
70 def make_new(self, yx, seed):
76 if 0 in pos or (yx[0] - 1) == pos[0] or (yx[1] - 1) == pos[1]:
79 self.map_[pos] = random.choice(('.', '.', '.', '.', 'x'))
80 player = self.game.thing_types['human'](self, 0)
81 player.position = [random.randint(0, yx[0] -1),
82 random.randint(0, yx[1] - 1)]
83 npc = self.game.thing_types['monster'](self, 1)
84 npc.position = [random.randint(0, yx[0] -1),
85 random.randint(0, yx[1] -1)]
86 item = self.game.thing_types['item'](self, 2)
87 item.position = [random.randint(0, yx[0] -1),
88 random.randint(0, yx[1] -1)]
89 self.things = [player, npc, item]
96 def __init__(self, game_file_name):
97 self.io = GameIO(game_file_name, self)
98 self.map_type = MapHex
99 self.tasks = {'WAIT': Task_WAIT, 'MOVE': Task_MOVE}
100 self.commands = {'GEN_WORLD': cmd_GEN_WORLD,
101 'GET_GAMESTATE': cmd_GET_GAMESTATE,
103 'THING_TYPE': cmd_THING_TYPE,
104 'THING_POS': cmd_THING_POS,
105 'TERRAIN_LINE': cmd_TERRAIN_LINE,
106 'PLAYER_ID': cmd_PLAYER_ID,
108 'SWITCH_PLAYER': cmd_SWITCH_PLAYER,
110 self.world_type = World
111 self.world = self.world_type(self)
112 self.thing_type = Thing
113 self.thing_types = {'human': ThingHuman,
114 'monster': ThingMonster,
117 def get_string_options(self, string_option_type):
118 if string_option_type == 'direction':
119 return self.world.map_.get_directions()
120 elif string_option_type == 'thingtype':
121 return list(self.thing_types.keys())
124 def send_gamestate(self, connection_id=None):
125 """Send out game state data relevant to clients."""
127 self.io.send('TURN ' + str(self.world.turn))
128 self.io.send('MAP ' + stringify_yx(self.world.map_.size))
129 visible_map = self.world.get_player().get_visible_map()
130 for y, line in visible_map.lines():
131 self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, quote(line)))
132 visible_things = self.world.get_player().get_visible_things()
133 for thing in visible_things:
134 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
135 self.io.send('THING_POS %s %s' % (thing.id_,
136 stringify_yx(thing.position)))
137 player = self.world.get_player()
138 self.io.send('PLAYER_POS %s' % (stringify_yx(player.position)))
139 self.io.send('GAME_STATE_COMPLETE')
142 """Send turn finish signal, run game world, send new world data.
144 First sends 'TURN_FINISHED' message, then runs game world
145 until new player input is needed, then sends game state.
147 self.io.send('TURN_FINISHED ' + str(self.world.turn))
148 self.world.proceed_to_next_player_turn()
149 msg = str(self.world.get_player()._last_task_result)
150 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
151 self.send_gamestate()
153 def get_command(self, command_name):
155 def partial_with_attrs(f, *args, **kwargs):
156 from functools import partial
157 p = partial(f, *args, **kwargs)
158 p.__dict__.update(f.__dict__)
161 def cmd_TASK_colon(task_name, game, *args):
162 game.world.get_player().set_task(task_name, args)
165 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
166 t = game.world.get_thing(thing_id, False)
168 raise ArgError('No such Thing.')
169 task_class = game.tasks[task_name]
170 t.task = task_class(t, args)
173 def task_prefixed(command_name, task_prefix, task_command,
174 argtypes_prefix=None):
175 if command_name[:len(task_prefix)] == task_prefix:
176 task_name = command_name[len(task_prefix):]
177 if task_name in self.tasks:
178 f = partial_with_attrs(task_command, task_name, self)
179 task = self.tasks[task_name]
181 f.argtypes = argtypes_prefix + ' ' + task.argtypes
183 f.argtypes = task.argtypes
187 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
190 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
191 'int:nonneg int:nonneg ')
194 if command_name in self.commands:
195 f = partial_with_attrs(self.commands[command_name], self)