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
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 self.things = [player, npc]
93 def __init__(self, game_file_name):
94 self.io = GameIO(game_file_name, self)
95 self.map_type = MapHex
96 self.tasks = {'WAIT': Task_WAIT, 'MOVE': Task_MOVE}
97 self.commands = {'GEN_WORLD': cmd_GEN_WORLD,
98 'GET_GAMESTATE': cmd_GET_GAMESTATE,
100 'THING_TYPE': cmd_THING_TYPE,
101 'THING_POS': cmd_THING_POS,
102 'TERRAIN_LINE': cmd_TERRAIN_LINE,
103 'PLAYER_ID': cmd_PLAYER_ID,
105 'SWITCH_PLAYER': cmd_SWITCH_PLAYER,
107 self.world_type = World
108 self.world = self.world_type(self)
109 self.thing_type = Thing
110 self.thing_types = {'human': ThingHuman, 'monster': ThingMonster}
112 def get_string_options(self, string_option_type):
113 if string_option_type == 'direction':
114 return self.world.map_.get_directions()
115 elif string_option_type == 'thingtype':
116 return list(self.thing_types.keys())
119 def send_gamestate(self, connection_id=None):
120 """Send out game state data relevant to clients."""
122 self.io.send('TURN ' + str(self.world.turn))
123 self.io.send('MAP ' + stringify_yx(self.world.map_.size))
124 visible_map = self.world.get_player().get_visible_map()
125 for y, line in visible_map.lines():
126 self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, quote(line)))
127 visible_things = self.world.get_player().get_visible_things()
128 for thing in visible_things:
129 self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
130 self.io.send('THING_POS %s %s' % (thing.id_,
131 stringify_yx(thing.position)))
132 player = self.world.get_player()
133 self.io.send('PLAYER_POS %s' % (stringify_yx(player.position)))
134 self.io.send('GAME_STATE_COMPLETE')
137 """Send turn finish signal, run game world, send new world data.
139 First sends 'TURN_FINISHED' message, then runs game world
140 until new player input is needed, then sends game state.
142 self.io.send('TURN_FINISHED ' + str(self.world.turn))
143 self.world.proceed_to_next_player_turn()
144 msg = str(self.world.get_player()._last_task_result)
145 self.io.send('LAST_PLAYER_TASK_RESULT ' + quote(msg))
146 self.send_gamestate()
148 def get_command(self, command_name):
150 def partial_with_attrs(f, *args, **kwargs):
151 from functools import partial
152 p = partial(f, *args, **kwargs)
153 p.__dict__.update(f.__dict__)
156 def cmd_TASK_colon(task_name, game, *args):
157 game.world.get_player().set_task(task_name, args)
160 def cmd_SET_TASK_colon(task_name, game, thing_id, todo, *args):
161 t = game.world.get_thing(thing_id, False)
163 raise ArgError('No such Thing.')
164 task_class = game.tasks[task_name]
165 t.task = task_class(t, args)
168 def task_prefixed(command_name, task_prefix, task_command,
169 argtypes_prefix=None):
170 if command_name[:len(task_prefix)] == task_prefix:
171 task_name = command_name[len(task_prefix):]
172 if task_name in self.tasks:
173 f = partial_with_attrs(task_command, task_name, self)
174 task = self.tasks[task_name]
176 f.argtypes = argtypes_prefix + ' ' + task.argtypes
178 f.argtypes = task.argtypes
182 command = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
185 command = task_prefixed(command_name, 'SET_TASK:', cmd_SET_TASK_colon,
186 'int:nonneg int:nonneg ')
189 if command_name in self.commands:
190 f = partial_with_attrs(self.commands[command_name], self)