from plomrogue.misc import quote
from plomrogue.errors import GameError, ArgError
+from plomrogue.misc import Terrain
cmd_THING_TYPES.argtypes = ''
def cmd_TERRAINS(game, connection_id):
- for t in game.terrains.keys():
- game.io.send('TERRAIN %s %s' % (quote(t), quote(game.terrains[t])),
- connection_id)
+ for t in game.terrains.values():
+ game.io.send('TERRAIN %s %s' % (quote(t.character),
+ quote(t.description)), connection_id)
cmd_TERRAINS.argtypes = ''
+def cmd_TERRAIN(game, character, description,
+ blocks_light, blocks_sound, blocks_movement):
+ game.terrains[character] = Terrain(character, description, blocks_light,
+ blocks_sound, blocks_movement)
+cmd_TERRAIN.argtypes = 'char string bool bool bool'
+
def cmd_ALL(game, msg, connection_id):
speaker = game.get_player(connection_id)
if not speaker:
if geometry == 'Hex':
map_geometry_class = MapGeometryHex
game.new_world(map_geometry_class(size))
+ game.terrains = {}
cmd_MAP.argtypes = 'string:map_geometry yx_tuple:pos'
def cmd_MAP_CONTROL_LINE(game, big_yx, y, line):
def __init__(self):
self.turn = 0
self.things = []
- self.map_geometry = MapGeometrySquare(YX(24, 40))
+ self.map_geometry = MapGeometrySquare(YX(32, 32))
self.commands = {}
def get_thing(self, id_):
class Game(GameBase):
def __init__(self, save_file, *args, **kwargs):
+ from plomrogue.misc import Terrain
super().__init__(*args, **kwargs)
self.changed = True
self.changed_tiles = []
self.last_send_gamestate = datetime.datetime.now() -\
self.send_gamestate_interval
self.terrains = {
- '.': 'floor',
- 'X': 'wall',
- '=': 'window',
- '#': 'bed',
- 'T': 'desk',
- '8': 'cupboard',
- '[': 'glass door',
- 'o': 'sink',
- 'O': 'toilet'
+ '.': Terrain('.', 'floor'),
+ 'X': Terrain('X', 'wall', blocks_light=True, blocks_sound=True,
+ blocks_movement=True),
+ '=': Terrain('=', 'glass', blocks_sound=True, blocks_movement=True),
+ 'T': Terrain('T', 'table', blocks_movement=True),
}
if os.path.exists(self.io.save_file):
if not os.path.isfile(self.io.save_file):
self.player_char_i = 0
return self.player_chars[self.player_char_i]
+ def get_foo_blockers(self, foo):
+ foo_blockers = ''
+ for t in self.terrains.values():
+ block_attr = getattr(t, 'blocks_' + foo)
+ if block_attr:
+ foo_blockers += t.character
+ return foo_blockers
+
+ def get_sound_blockers(self):
+ return self.get_foo_blockers('sound')
+
+ def get_light_blockers(self):
+ return self.get_foo_blockers('light')
+
+ def get_movement_blockers(self):
+ return self.get_foo_blockers('movement')
+
+ def get_flatland(self):
+ for t in self.terrains.values:
+ if not t.blocks_movement:
+ return t.character
+
def save(self):
def write(f, msg):
write(f, 'TURN %s' % self.turn)
map_geometry_shape = self.get_map_geometry_shape()
write(f, 'MAP %s %s' % (map_geometry_shape, self.map_geometry.size,))
+ for terrain in self.terrains.values():
+ write(f, 'TERRAIN %s %s %s %s %s' % (quote(terrain.character),
+ quote(terrain.description),
+ int(terrain.blocks_light),
+ int(terrain.blocks_sound),
+ int(terrain.blocks_movement)))
for big_yx in [yx for yx in self.maps if self.maps[yx].modified]:
for y, line in self.maps[big_yx].lines():
write(f, 'MAP_LINE %s %5s %s' % (big_yx, y, quote(line)))
def __init__(self, map_geometry):
self.geometry = map_geometry
- self.terrain = '.' * self.size_i
+ self.terrain = '.' * self.size_i # TODO: use Game.get_flatland()?
def __getitem__(self, yx):
return self.terrain[self.get_position_index(yx)]
class SourcedMap(Map):
- def __init__(self, things, source_maps, source_center, radius, get_map):
+ def __init__(self, block_chars, things, source_maps, source_center, radius,
+ get_map):
+ self.block_chars = block_chars
self.radius = radius
example_map = get_map(YX(0, 0))
self.source_geometry = example_map.geometry
for yx in self: # TODO: iter and source_yxyx expensive, cache earlier?
big_yx, little_yx = self.source_yxyx(yx)
if big_yx in obstacles and little_yx in obstacles[big_yx]:
- self.source_map_segment += 'X'
+ self.source_map_segment += self.block_chars[0]
else:
self.source_map_segment += source_maps[big_yx][little_yx]
while shrunk:
shrunk = False
for i in range(self.size_i):
- if self.source_map_segment[i] in 'X=':
+ if self.source_map_segment[i] in self.block_chars:
continue
neighbors = self.geometry.get_neighbors_i(i)
for direction in [d for d in neighbors if neighbors[d]]:
return self
def throws_shadow(self, yx):
- return self.source_map_segment[self.get_position_index(yx)] == 'X'
+ return self.source_map_segment[self.get_position_index(yx)]\
+ in self.block_chars
def shadow_process(self, yx, distance_to_center, dir_i, dir_progress):
# Possible optimization: If no shadow_cones yet and self[yx] == '.',
return mover(pos)
def circle_out(self, yx, f):
- # Optimization potential: Precalculate movement positions. (How to check
- # circle_in_map then?)
+ # Optimization potential: Precalculate movement positions.
# Optimization potential: Precalculate what tiles are shaded by what tile
# and skip evaluation of already shaded tile. (This only works if tiles
# shading implies they completely lie in existing shades; otherwise we
+class Terrain:
+
+ def __init__(self, character, description, blocks_light=False,
+ blocks_sound=False, blocks_movement=False):
+ self.character = character
+ self.description = description
+ self.blocks_light = blocks_light
+ self.blocks_sound = blocks_sound
+ self.blocks_movement = blocks_movement
+
+
+
def quote(string):
"""Quote & escape string so client interprets it as single token."""
quoted = []
def check(self):
test_yxyx = self._get_move_target()
+ move_blockers = self.thing.game.get_movement_blockers()
if test_yxyx in [t.position for t in self.thing.game.things
if t.blocking]:
raise PlayError('blocked by other thing')
- elif self.thing.game.maps[test_yxyx[0]][test_yxyx[1]] != '.':
+ elif self.thing.game.maps[test_yxyx[0]][test_yxyx[1]] in move_blockers:
raise PlayError('blocked by impassable tile')
def do(self):
self.thing.position).values()):
if not self.thing.game.can_do_tile_with_pw(*yxyx, self.args[0]):
continue
- self.thing.game.maps[yxyx[0]][yxyx[1]] = '.'
+ self.thing.game.maps[yxyx[0]][yxyx[1]] = self.game.get_flatland()
self.thing.game.record_fov_change(yxyx)
largest_audible_distance = 20
# player's don't block sound (or should they?)
things = [t for t in self.game.things if t.type_ != 'Player']
- dijkstra_map = DijkstraMap(things, self.game.maps, self.position,
+ sound_blockers = self.game.get_sound_blockers()
+ dijkstra_map = DijkstraMap(sound_blockers, things, self.game.maps, self.position,
largest_audible_distance, self.game.get_map)
url_limits = []
for m in re.finditer('https?://[^\s]+', msg):
# and ThingPlayer.fov_test
fov_map_class = self.game.map_geometry.fov_map_class
fov_radius = 12
- fov = fov_map_class(self.game.things, self.game.maps,
+ light_blockers = self.game.get_light_blockers()
+ fov = fov_map_class(light_blockers, self.game.things, self.game.maps,
self.position, fov_radius, self.game.get_map)
fov.init_terrain()
visible_players = []
def prepare_multiprocessible_fov_stencil(self):
fov_map_class = self.game.map_geometry.fov_map_class
fov_radius = 3 if self.drunk > 0 else 12
- self._fov = fov_map_class(self.game.things, self.game.maps,
+ light_blockers = self.game.get_light_blockers()
+ self._fov = fov_map_class(light_blockers, self.game.things, self.game.maps,
self.position, fov_radius, self.game.get_map)
def multiprocessible_fov_stencil(self):
cmd_GOD_THING_PROTECTION, cmd_THING_PROTECTION,
cmd_SET_MAP_CONTROL_PASSWORD, cmd_SPAWN_POINT,
cmd_THING_MUSICPLAYER_SETTINGS, cmd_THING_HAT_DESIGN,
- cmd_THING_MUSICPLAYER_PLAYLIST_ITEM,
+ cmd_THING_MUSICPLAYER_PLAYLIST_ITEM, cmd_TERRAIN,
cmd_THING_BOTTLE_EMPTY, cmd_PLAYER_FACE,
cmd_GOD_PLAYER_FACE, cmd_GOD_PLAYER_HAT)
from plomrogue.tasks import (Task_WAIT, Task_MOVE, Task_WRITE, Task_PICK_UP,
game.register_command(cmd_NICK)
game.register_command(cmd_TURN)
game.register_command(cmd_MAP)
+game.register_command(cmd_TERRAIN)
game.register_command(cmd_MAP_LINE)
game.register_command(cmd_MAP_CONTROL_LINE)
game.register_command(cmd_MAP_CONTROL_PW)