raise GameError('need to be logged in for this')
speaker = game.get_thing(game.sessions[connection_id])
largest_audible_distance = 20
- dijkstra_map_class = game.map_geometry.dijkstra_map_class
- dijkstra_map = dijkstra_map_class(game.map, speaker.position,
- largest_audible_distance)
+ dijkstra_map = DijkstraMap(game.maps, speaker.position,
+ largest_audible_distance, game.get_map)
for c_id in game.sessions:
listener = game.get_thing(game.sessions[c_id])
- target_yx = dijkstra_map.target_yx(listener.position, True)
+ target_yx = dijkstra_map.target_yx(*listener.position, True)
if not target_yx:
continue
listener_distance = dijkstra_map[target_yx]
if connection_id in game.sessions:
raise GameError('cannot log in twice')
t = game.thing_types['Player'](game)
- t.position = YX(game.map.size.y // 2, game.map.size.x // 2)
+ t.position = (YX(0,0),
+ YX(game.map_geometry.size.y // 2, game.map_geometry.size.x // 2))
game.things += [t] # TODO refactor into Thing.__init__?
t.player_char = game.get_next_player_char()
game.sessions[connection_id] = t.id_
def cmd_ANNOTATE(game, yx, msg, pw, connection_id):
player = game.get_thing(game.sessions[connection_id])
- source_yx = player.fov_stencil.source_yx(yx)
- if not player.fov_test(source_yx):
+ big_yx, little_yx = player.fov_stencil.source_yxyx(yx)
+ if not player.fov_test(big_yx, little_yx):
raise GameError('cannot annotate tile outside field of view')
- if not game.can_do_tile_with_pw(source_yx, pw):
+ if not game.can_do_tile_with_pw(big_yx, little_yx, pw):
raise GameError('wrong password for tile')
if msg == ' ':
- if source_yx in game.annotations:
- del game.annotations[source_yx]
+ if big_yx in game.annotations:
+ if little_yx in game.annotations[big_yx]:
+ del game.annotations[big_yx][little_yx]
else:
- game.annotations[source_yx] = msg
+ if not big_yx in game.annotations:
+ game.annotations[big_yx] = {}
+ game.annotations[big_yx][little_yx] = msg
game.changed = True
cmd_ANNOTATE.argtypes = 'yx_tuple:nonneg string string'
def cmd_PORTAL(game, yx, msg, pw, connection_id):
player = game.get_thing(game.sessions[connection_id])
- source_yx = player.fov_stencil.source_yx(yx)
- if not player.fov_test(source_yx):
+ big_yx, little_yx = player.fov_stencil.source_yxyx(yx)
+ if not player.fov_test(big_yx, little_yx):
raise GameError('cannot edit portal on tile outside field of view')
- if not game.can_do_tile_with_pw(source_yx, pw):
+ if not game.can_do_tile_with_pw(big_yx, little_yx, pw):
raise GameError('wrong password for tile')
if msg == ' ':
- if source_yx in game.portals:
- del game.portals[source_yx]
+ if big_yx in game.portals:
+ if little_yx in game.portals[big_yx]:
+ del game.portals[big_yx][little_xy]
else:
- game.portals[source_yx] = msg
+ if not big_yx in game.portals:
+ game.portals[big_yx] = {}
+ game.portals[big_yx][little_yx] = msg
game.changed = True
cmd_PORTAL.argtypes = 'yx_tuple:nonneg string string'
-def cmd_GOD_ANNOTATE(game, yx, msg):
- game.annotations[yx] = msg
+def cmd_GOD_ANNOTATE(game, big_yx, little_yx, msg):
+ if not big_yx in game.annotations:
+ game.annotations[big_yx] = {}
+ game.annotations[big_yx][little_yx] = msg
game.changed = True
-cmd_GOD_ANNOTATE.argtypes = 'yx_tuple:nonneg string'
+cmd_GOD_ANNOTATE.argtypes = 'yx_tuple yx_tuple:nonneg string'
-def cmd_GOD_PORTAL(game, yx, msg):
- game.portals[yx] = msg
+def cmd_GOD_PORTAL(game, big_yx, little_yx, msg):
+ if not big_yx in game.portals:
+ game.portals[big_yx] = {}
+ game.portals[big_yx][little_yx] = msg
game.changed = True
-cmd_GOD_PORTAL.argtypes = 'yx_tuple:nonneg string'
+cmd_GOD_PORTAL.argtypes = 'yx_tuple yx_tuple:nonneg string'
def cmd_GET_ANNOTATION(game, yx, connection_id):
player = game.get_thing(game.sessions[connection_id])
- source_yx = player.fov_stencil.source_yx(yx)
+ big_yx, little_yx = player.fov_stencil.source_yxyx(yx)
annotation = '(unknown)';
- if player.fov_test(source_yx):
+ if player.fov_test(big_yx, little_yx):
annotation = '(none)';
- if source_yx in game.annotations:
- annotation = game.annotations[source_yx]
+ if big_yx in game.annotations:
+ if little_yx in game.annotations[big_yx]:
+ annotation = game.annotations[big_yx][little_yx]
game.io.send('ANNOTATION %s %s' % (yx, quote(annotation)))
cmd_GET_ANNOTATION.argtypes = 'yx_tuple:nonneg'
-def cmd_MAP_LINE(game, y, line):
- game.map.set_line(y, line)
-cmd_MAP_LINE.argtypes = 'int:nonneg string'
+def cmd_MAP_LINE(game, big_yx, y, line):
+ map_ = game.get_map(big_yx)
+ map_.set_line(y, line)
+cmd_MAP_LINE.argtypes = 'yx_tuple int:nonneg string'
def cmd_MAP(game, geometry, size):
map_geometry_class = globals()['MapGeometry' + geometry]
game.new_world(map_geometry_class(size))
cmd_MAP.argtypes = 'string:map_geometry yx_tuple:pos'
-def cmd_MAP_CONTROL_LINE(game, y, line):
- game.map_control.set_line(y, line)
-cmd_MAP_CONTROL_LINE.argtypes = 'int:nonneg string'
+def cmd_MAP_CONTROL_LINE(game, big_yx, y, line):
+ map_control = game.get_map(big_yx, 'control')
+ map_control.set_line(y, line)
+cmd_MAP_CONTROL_LINE.argtypes = 'yx_tuple int:nonneg string'
def cmd_MAP_CONTROL_PW(game, tile_class, password):
game.map_control_passwords[tile_class] = password
cmd_MAP_CONTROL_PW.argtypes = 'char string'
-def cmd_THING(game, yx, thing_type, thing_id):
+def cmd_THING(game, big_yx, little_yx, thing_type, thing_id):
if not thing_type in game.thing_types:
raise GameError('illegal thing type %s' % thing_type)
- if not game.map.inside(yx):
- raise GameError('illegal position %s' % yx)
+ map_ = game.get_map(big_yx)
t_old = None
if thing_id > 0:
t_old = game.get_thing(thing_id)
- t_new = game.thing_types[thing_type](game, id_=thing_id, position=yx)
+ t_new = game.thing_types[thing_type](game, id_=thing_id, position=(big_yx,
+ little_yx))
if t_old:
game.things[game.things.index(t_old)] = t_new
else:
game.things += [t_new]
game.changed = True
-cmd_THING.argtypes = 'yx_tuple:nonneg string:thing_type int:nonneg'
+cmd_THING.argtypes = 'yx_tuple yx_tuple:nonneg string:thing_type int:nonneg'
def cmd_THING_NAME(game, thing_id, name):
t = game.get_thing(thing_id)
self.tasks = {}
self.thing_types = {}
self.sessions = {}
- self.map = Map(self.map_geometry.size)
- self.map_control = Map(self.map_geometry.size)
+ self.maps = {}
+ self.map_controls = {}
self.map_control_passwords = {}
self.annotations = {}
self.portals = {}
'o': 'sink',
'O': 'toilet'
}
+ self.new_world(self.map_geometry)
if os.path.exists(self.io.save_file):
if not os.path.isfile(self.io.save_file):
raise GameError('save file path refers to non-file')
print("FILE INPUT LINE %5s: %s" % (i, line), end='')
self.io.handle_input(line, god_mode=True)
- def can_do_tile_with_pw(self, yx, pw):
- tile_class = self.map_control[yx]
+ def can_do_tile_with_pw(self, big_yx, little_yx, pw):
+ map_control = self.get_map(big_yx)
+ tile_class = map_control[little_yx]
if tile_class in self.map_control_passwords:
tile_pw = self.map_control_passwords[tile_class]
if pw != tile_pw:
self.io.send('TURN ' + str(self.turn))
for c_id in self.sessions:
player = self.get_thing(self.sessions[c_id])
- visible_terrain = player.fov_stencil_map(self.map)
+ visible_terrain = player.fov_stencil_map()
self.io.send('FOV %s' % quote(player.fov_stencil.terrain), c_id)
self.io.send('MAP %s %s %s' % (self.get_map_geometry_shape(),
- player.fov_stencil.size,
+ player.fov_stencil.geometry.size,
quote(visible_terrain)), c_id)
- visible_control = player.fov_stencil_map(self.map_control)
+ visible_control = player.fov_stencil_map('control')
self.io.send('MAP_CONTROL %s' % quote(visible_control), c_id)
- for t in [t for t in self.things if player.fov_test(t.position)]:
- target_yx = player.fov_stencil.target_yx(t.position)
+ for t in [t for t in self.things if player.fov_test(*t.position)]:
+ target_yx = player.fov_stencil.target_yx(*t.position)
self.io.send('THING %s %s %s' % (target_yx, t.type_, t.id_), c_id)
if hasattr(t, 'name'):
self.io.send('THING_NAME %s %s' % (t.id_, quote(t.name)), c_id)
if hasattr(t, 'player_char'):
self.io.send('THING_CHAR %s %s' % (t.id_,
quote(t.player_char)), c_id)
- for yx in [yx for yx in self.portals if player.fov_test(yx)]:
- self.io.send('PORTAL %s %s' % (player.fov_stencil.target_yx(yx),
- quote(self.portals[yx])), c_id)
+ for big_yx in self.portals:
+ for little_yx in [little_yx for little_yx in self.portals[big_yx]
+ if player.fov_test(big_yx, little_yx)]:
+ target_yx = player.fov_stencil.target_yx(big_yx, little_yx)
+ portal = self.portals[big_yx][little_yx]
+ self.io.send('PORTAL %s %s' % (target_yx, quote(portal)), c_id)
self.io.send('GAME_STATE_COMPLETE')
def run_tick(self):
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 y, line in self.map.lines():
- write(f, 'MAP_LINE %5s %s' % (y, quote(line)))
- for yx in self.annotations:
- write(f, 'GOD_ANNOTATE %s %s' % (yx, quote(self.annotations[yx])))
- for yx in self.portals:
- write(f, 'GOD_PORTAL %s %s' % (yx, quote(self.portals[yx])))
- for y, line in self.map_control.lines():
- write(f, 'MAP_CONTROL_LINE %5s %s' % (y, quote(line)))
+ for yx in self.maps:
+ for y, line in self.maps[yx].lines():
+ write(f, 'MAP_LINE %s %5s %s' % (yx, y, quote(line)))
+ for big_yx in self.annotations:
+ for little_yx in self.annotations[big_yx]:
+ write(f, 'GOD_ANNOTATE %s %s %s' %
+ (big_yx, little_yx, quote(self.annotations[big_yx][little_yx])))
+ for big_yx in self.portals:
+ for little_yx in self.portals[big_yx]:
+ write(f, 'GOD_PORTAL %s %s %s' % (big_yx, little_yx,
+ quote(self.portals[big_yx][little_yx])))
+ for yx in self.map_controls:
+ for y, line in self.map_controls[yx].lines():
+ write(f, 'MAP_CONTROL_LINE %s %5s %s' % (yx, y, quote(line)))
for tile_class in self.map_control_passwords:
write(f, 'MAP_CONTROL_PW %s %s' % (tile_class,
self.map_control_passwords[tile_class]))
for t in [t for t in self.things if not t.type_ == 'Player']:
- write(f, 'THING %s %s %s' % (t.position, t.type_, t.id_))
+ write(f, 'THING %s %s %s %s' % (t.position[0],
+ t.position[1], t.type_, t.id_))
if hasattr(t, 'name'):
write(f, 'THING_NAME %s %s' % (t.id_, quote(t.name)))
+ def get_map(self, big_yx, type_='normal'):
+ if type_ == 'normal':
+ maps = self.maps
+ elif type_ == 'control':
+ maps = self.map_controls
+ if not big_yx in maps:
+ maps[big_yx] = Map(self.map_geometry)
+ return maps[big_yx]
+
def new_world(self, map_geometry):
self.map_geometry = map_geometry
- self.map = Map(self.map_geometry.size)
- self.map_control = Map(self.map_geometry.size)
+ self.maps[YX(0,0)] = Map(self.map_geometry)
+ self.map_controls[YX(0,0)] = Map(self.map_geometry)
self.annotations = {}
def get_directions(self):
directions = []
+ prefix = 'move__'
for name in dir(self):
- if name[:5] == 'move_':
- directions += [name[5:]]
+ if name[:len(prefix)] == prefix:
+ directions += [name[len(prefix):]]
return directions
- def get_neighbors(self, pos):
+ def get_neighbors_yxyx(self, yxyx):
neighbors = {}
for direction in self.get_directions():
- neighbors[direction] = self.move(pos, direction)
+ neighbors[direction] = self.move_yxyx(yxyx, direction)
+ return neighbors
+
+ def get_neighbors_yx(self, pos):
+ neighbors = {}
+ for direction in self.get_directions():
+ neighbors[direction] = self.move_yx(pos, direction)
return neighbors
def get_neighbors_i(self, i):
if i in self.neighbors_i:
return self.neighbors_i[i]
pos = YX(i // self.size.x, i % self.size.x)
- neighbors_pos = self.get_neighbors(pos)
+ neighbors_pos = self.get_neighbors_yx(pos)
neighbors_i = {}
for direction in neighbors_pos:
pos = neighbors_pos[direction]
self.neighbors_i[i] = neighbors_i
return self.neighbors_i[i]
- def move(self, start_pos, direction):
- mover = getattr(self, 'move_' + direction)
- target = mover(start_pos)
+ def move_yx(self, start_yx, direction, check=True):
+ mover = getattr(self, 'move__' + direction)
+ target = mover(start_yx)
+ # TODO refactor with SourcedMap.inside?
if target.y < 0 or target.x < 0 or \
- target.y >= self.size.y or target.x >= self.size.x:
+ target.y >= self.size.y or target.x >= self.size.x:
return None
return target
+ def move_yxyx(self, start_yxyx, direction):
+ mover = getattr(self, 'move__' + direction)
+ start_yx = self.undouble_yxyx(*start_yxyx)
+ target_yx = mover(start_yx)
+ return self.double_yx(target_yx)
+
+ def double_yx(self, absolute_yx):
+ big_y = absolute_yx.y // self.size.y
+ little_y = absolute_yx.y % self.size.y
+ big_x = absolute_yx.x // self.size.x
+ little_x = absolute_yx.x % self.size.x
+ return YX(big_y, big_x), YX(little_y, little_x)
+
+ def undouble_yxyx(self, big_yx, little_yx):
+ y = big_yx.y * self.size.y + little_yx.y
+ x = big_yx.x * self.size.x + little_yx.x
+ return YX(y, x)
+
class MapGeometryWithLeftRightMoves(MapGeometry):
- def move_LEFT(self, start_pos):
+ def move__LEFT(self, start_pos):
return YX(start_pos.y, start_pos.x - 1)
- def move_RIGHT(self, start_pos):
+ def move__RIGHT(self, start_pos):
return YX(start_pos.y, start_pos.x + 1)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fov_map_class = FovMapSquare
- self.dijkstra_map_class = DijkstraMapSquare
def define_segment(self, source_center, radius):
+ source_center = self.undouble_yxyx(*source_center)
size = YX(2 * radius + 1, 2 * radius + 1)
offset = YX(source_center.y - radius, source_center.x - radius)
center = YX(radius, radius)
return size, offset, center
- def move_UP(self, start_pos):
+ def move__UP(self, start_pos):
return YX(start_pos.y - 1, start_pos.x)
- def move_DOWN(self, start_pos):
+ def move__DOWN(self, start_pos):
return YX(start_pos.y + 1, start_pos.x)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fov_map_class = FovMapHex
- self.dijkstra_map_class = DijkstraMapHex
def define_segment(self, source_center, radius):
+ source_center = self.undouble_yxyx(*source_center)
indent = 1 if (source_center.y % 2) else 0
size = YX(2 * radius + 1 + indent, 2 * radius + 1)
offset = YX(source_center.y - radius - indent, source_center.x - radius)
center = YX(radius + indent, radius)
return size, offset, center
- def move_UPLEFT(self, start_pos):
+ def move__UPLEFT(self, start_pos):
start_indented = start_pos.y % 2
if start_indented:
return YX(start_pos.y - 1, start_pos.x)
else:
return YX(start_pos.y - 1, start_pos.x - 1)
- def move_UPRIGHT(self, start_pos):
+ def move__UPRIGHT(self, start_pos):
start_indented = start_pos.y % 2
if start_indented:
return YX(start_pos.y - 1, start_pos.x + 1)
else:
return YX(start_pos.y - 1, start_pos.x)
- def move_DOWNLEFT(self, start_pos):
+ def move__DOWNLEFT(self, start_pos):
start_indented = start_pos.y % 2
if start_indented:
return YX(start_pos.y + 1, start_pos.x)
else:
return YX(start_pos.y + 1, start_pos.x - 1)
- def move_DOWNRIGHT(self, start_pos):
+ def move__DOWNRIGHT(self, start_pos):
start_indented = start_pos.y % 2
if start_indented:
return YX(start_pos.y + 1, start_pos.x + 1)
class Map():
- def __init__(self, map_size):
- self.size = map_size
+ def __init__(self, map_geometry):
+ self.geometry = map_geometry
self.terrain = '.' * self.size_i
def __getitem__(self, yx):
def __iter__(self):
"""Iterate over YX position coordinates."""
- for y in range(self.size.y):
- for x in range(self.size.x):
+ for y in range(self.geometry.size.y):
+ for x in range(self.geometry.size.x):
yield YX(y, x)
- # TODO: use this for more refactoring
- def inside(self, yx):
- if yx.y < 0 or yx.x < 0 or yx.y >= self.size.y or yx.x >= self.size.x:
- return False
- return True
-
@property
def size_i(self):
- return self.size.y * self.size.x
+ return self.geometry.size.y * self.geometry.size.x
def set_line(self, y, line):
- height_map = self.size.y
- width_map = self.size.x
+ height_map = self.geometry.size.y
+ width_map = self.geometry.size.x
if y >= height_map:
raise ArgError('too large row number %s' % y)
width_line = len(line)
self.terrain[(y + 1) * width_map:]
def get_position_index(self, yx):
- return yx.y * self.size.x + yx.x
+ return yx.y * self.geometry.size.x + yx.x
def lines(self):
- width = self.size.x
- for y in range(self.size.y):
+ width = self.geometry.size.x
+ for y in range(self.geometry.size.y):
yield (y, self.terrain[y * width:(y + 1) * width])
-
class SourcedMap(Map):
- def __init__(self, source_map, source_center, radius):
- self.source_map = source_map
+ def __init__(self, source_maps, source_center, radius, get_map):
+ self.source_maps = source_maps
self.radius = radius
- self.size, self.offset, self.center = \
- self.geometry_class.define_segment(None, source_center, radius)
- self.geometry = self.geometry_class(self.size)
+ example_map = get_map(YX(0,0))
+ self.source_geometry = example_map.geometry
+ size, self.offset, self.center = \
+ self.source_geometry.define_segment(source_center, radius)
+ self.geometry = self.source_geometry.__class__(size)
+ for yx in self:
+ big_yx, _ = self.source_yxyx(yx)
+ get_map(big_yx)
- def source_yx(self, yx, check=False):
- source_yx = yx + self.offset
- if check and not self.source_map.inside(source_yx):
- return False
- return source_yx
+ def source_yxyx(self, yx):
+ absolute_yx = yx + self.offset
+ big_yx, little_yx = self.source_geometry.double_yx(absolute_yx)
+ return big_yx, little_yx
- def target_yx(self, yx, check=False):
- target_yx = yx - self.offset
+ def target_yx(self, big_yx, little_yx, check=False):
+ target_yx = self.source_geometry.undouble_yxyx(big_yx, little_yx) - self.offset
if check and not self.inside(target_yx):
return False
return target_yx
+ def inside(self, yx):
+ if yx.y < 0 or yx.x < 0 or \
+ yx.y >= self.geometry.size.y or yx.x >= self.geometry.size.x:
+ return False
+ return True
+
class DijkstraMap(SourcedMap):
shrunk = True
source_map_segment = ''
for yx in self:
- yx_in_source = self.source_yx(yx, True)
- if yx_in_source:
- source_map_segment += self.source_map[yx_in_source]
- else:
- source_map_segment += 'X'
+ big_yx, little_yx = self.source_yxyx(yx)
+ source_map_segment += self.source_maps[big_yx][little_yx]
while shrunk:
shrunk = False
for i in range(self.size_i):
-class DijkstraMapHex(DijkstraMap):
- geometry_class = MapGeometryHex
-
-
-
-class DijkstraMapSquare(DijkstraMap):
- geometry_class = MapGeometrySquare
-
-
-
class FovMap(SourcedMap):
# TODO: player visibility asymmetrical (A can see B when B can't see A):
# does this make sense, or not?
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.terrain = '?' * self.size.y * self.size.x
+ self.terrain = '?' * self.size_i #self.size.y * self.size.x
self[self.center] = '.'
self.shadow_cones = []
self.circle_out(self.center, self.shadow_process)
- def throws_shadow(self, source_yx):
- return self.source_map[source_yx] == 'X'
+ def throws_shadow(self, big_yx, little_yx):
+ return self.source_maps[big_yx][little_yx] == 'X'
- def shadow_process(self, yx, source_yx, distance_to_center, dir_i, dir_progress):
+ def shadow_process(self, yx, source_yxyx, distance_to_center, dir_i, dir_progress):
# Possible optimization: If no shadow_cones yet and self[yx] == '.',
# skip all.
CIRCLE = 360 # Since we'll float anyways, number is actually arbitrary.
if in_shadow_cone(cone):
return
self[yx] = '.'
- if self.throws_shadow(source_yx):
+ if self.throws_shadow(*source_yxyx):
unmerged = True
while merge_cone(cone):
unmerged = False
eval_cone([left_arm, right_arm])
def basic_circle_out_move(self, pos, direction):
- #"""Move position pos into direction. Return whether still in map."""
- mover = getattr(self.geometry, 'move_' + direction)
+ mover = getattr(self.geometry, 'move__' + direction)
return mover(pos)
def circle_out(self, yx, f):
for dir_progress in range(distance):
direction = self.circle_out_directions[dir_i]
yx = self.circle_out_move(yx, direction)
- source_yx = self.source_yx(yx, True)
- if source_yx:
- f(yx, source_yx, distance, dir_i, dir_progress)
+ source_yxyx = self.source_yxyx(yx)
+ f(yx, source_yxyx, distance, dir_i, dir_progress)
distance += 1
class FovMapHex(FovMap):
circle_out_directions = ('DOWNLEFT', 'LEFT', 'UPLEFT',
'UPRIGHT', 'RIGHT', 'DOWNRIGHT')
- geometry_class = MapGeometryHex
def circle_out_move(self, yx, direction):
return self.basic_circle_out_move(yx, direction)
class FovMapSquare(FovMap):
circle_out_directions = (('DOWN', 'LEFT'), ('LEFT', 'UP'),
('UP', 'RIGHT'), ('RIGHT', 'DOWN'))
- geometry_class = MapGeometrySquare
def circle_out_move(self, yx, direction):
yx = self.basic_circle_out_move(yx, direction[0])
"""Parse yx_string as yx_tuple, return result.
The range_ argument may be 'nonneg' (non-negative, including
- 0) or 'pos' (positive, excluding 0).
+ 0) or 'pos' (positive, excluding 0) or 'all'.
"""
def get_axis_position_from_argument(axis, token):
- if len(token) < 3 or token[:2] != axis + ':' or \
- not (token[2:].isdigit() or token[2] == '-'):
- raise ArgError('Non-int arg for ' + axis + ' position.')
- n = int(token[2:])
- if n < 1 and range_ == 'pos':
+ if token[:2] != axis + ':':
+ raise ArgError('invalid YX tuple formatting')
+ n_string = token[2:]
+ if n_string.strip() != n_string:
+ raise ArgError('invalid YX tuple formatting')
+ try:
+ n = int(n_string)
+ except ValueError:
+ raise ArgError('non-int value for ' + axis + ' position')
+ if range_ == 'all':
+ return n
+ if n < 1 and range == 'pos':
raise ArgError('Arg for ' + axis + ' position < 1.')
elif n < 0 and range_ == 'nonneg':
raise ArgError('Arg for ' + axis + ' position < 0.')
args += [self.parse_yx_tuple(arg, 'nonneg')]
elif tmpl == 'yx_tuple:pos':
args += [self.parse_yx_tuple(arg, 'pos')]
+ elif tmpl == 'yx_tuple':
+ args += [self.parse_yx_tuple(arg, 'all')]
elif tmpl == string_string:
args += [arg]
elif tmpl[:len(string_string) + 1] == string_string + ':':
argtypes = 'string:direction'
def get_move_target(self):
- return self.thing.game.map_geometry.move(self.thing.position,
- self.args[0])
+ return self.thing.game.map_geometry.move_yxyx(self.thing.position,
+ self.args[0])
def check(self):
- test_pos = self.get_move_target()
- if test_pos is None:
- raise PlayError('would move out of map')
- elif test_pos in [t.position for t in self.thing.game.things
- if t.blocking]:
+ test_yxyx = self.get_move_target()
+ 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.map[test_pos] != '.':
+ elif self.thing.game.maps[test_yxyx[0]][test_yxyx[1]] != '.':
raise PlayError('blocked by impassable tile')
def do(self):
argtypes = 'string:char string'
def check(self):
- if not self.thing.game.can_do_tile_with_pw(self.thing.position,
+ if not self.thing.game.can_do_tile_with_pw(*self.thing.position,
self.args[1]):
raise GameError('wrong password for tile')
def do(self):
- self.thing.game.map[self.thing.position] = self.args[0]
+ big_yx = self.thing.position[0]
+ little_yx = self.thing.position[1]
+ self.thing.game.maps[big_yx][little_yx] = self.args[0]
pass
def do(self):
- for yx in[self.thing.position] + \
- list(self.thing.game.map_geometry.get_neighbors(self.thing.position).values()):
- if yx is not None:
- if not self.thing.game.can_do_tile_with_pw(yx, self.args[0]):
- continue
- self.thing.game.map[yx] = '.'
+ for yxyx in[self.thing.position] + \
+ list(self.thing.game.map_geometry.get_neighbors_yxyx(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]] = '.'
class ThingBase:
type_ = '?'
- def __init__(self, game, id_=0, position=(YX(0,0))):
+ def __init__(self, game, id_=0, position=(YX(0,0),YX(0,0))):
self.game = game
if id_ == 0:
self.id_ = self.game.new_thing_id()
if self._fov:
return self._fov
fov_map_class = self.game.map_geometry.fov_map_class
- self._fov = fov_map_class(self.game.map, self.position, 12)
+ self._fov = fov_map_class(self.game.maps, self.position, 12,
+ self.game.get_map)
return self._fov
- def fov_test(self, yx):
- test_position = self.fov_stencil.target_yx(yx)
+ def fov_test(self, big_yx, little_yx):
+ test_position = self.fov_stencil.target_yx(big_yx, little_yx)
if self.fov_stencil.inside(test_position):
if self.fov_stencil[test_position] == '.':
return True
return False
- def fov_stencil_map(self, map):
+ def fov_stencil_map(self, map_type='normal'):
visible_terrain = ''
for yx in self.fov_stencil:
if self.fov_stencil[yx] == '.':
- visible_terrain += map[self.fov_stencil.source_yx(yx)]
+ big_yx, little_yx = self.fov_stencil.source_yxyx(yx)
+ map_ = self.game.get_map(big_yx, map_type)
+ visible_terrain += map_[little_yx]
else:
visible_terrain += ' '
return visible_terrain