X-Git-Url: https://plomlompom.com/repos/?p=plomrogue2-experiments;a=blobdiff_plain;f=new%2Fplomrogue%2Fmapping.py;h=b670938e64658e2ff34306483bdb6cb7adf12d77;hp=0ec953d329befd0dd5e6ec93910e06e78ed69d7a;hb=8f2dc382612c0684fd9a75e60c23561a1859cb8f;hpb=3b7db36664e8989b106d8975d7a115e8a872b473 diff --git a/new/plomrogue/mapping.py b/new/plomrogue/mapping.py index 0ec953d..b670938 100644 --- a/new/plomrogue/mapping.py +++ b/new/plomrogue/mapping.py @@ -1,12 +1,27 @@ from plomrogue.errors import ArgError +import collections + + + +class YX(collections.namedtuple('YX', ('y', 'x'))): + + def __add__(self, other): + return YX(self.y + other.y, self.x + other.x) + + def __sub__(self, other): + return YX(self.y - other.y, self.x - other.x) + + def __str__(self): + return 'Y:%s,X:%s' % (self.y, self.x) class Map: - def __init__(self, size=(0, 0)): + def __init__(self, size=YX(0, 0), init_char = '?', start_indented=True): self.size = size - self.terrain = '?'*self.size_i + self.terrain = init_char * self.size_i + self.start_indented = start_indented def __getitem__(self, yx): return self.terrain[self.get_position_index(yx)] @@ -20,17 +35,17 @@ class Map: def __iter__(self): """Iterate over YX position coordinates.""" - for y in range(self.size[0]): - for x in range(self.size[1]): - yield (y, x) + for y in range(self.size.y): + for x in range(self.size.x): + yield YX(y, x) @property def size_i(self): - return self.size[0] * self.size[1] + return self.size.y * self.size.x def set_line(self, y, line): - height_map = self.size[0] - width_map = self.size[1] + height_map = self.size.y + width_map = self.size.x if y >= height_map: raise ArgError('too large row number %s' % y) width_line = len(line) @@ -40,15 +55,22 @@ class Map: self.terrain[(y + 1) * width_map:] def get_position_index(self, yx): - return yx[0] * self.size[1] + yx[1] + return yx.y * self.size.x + yx.x def lines(self): - width = self.size[1] - for y in range(self.size[0]): + width = self.size.x + for y in range(self.size.y): yield (y, self.terrain[y * width:(y + 1) * width]) - def get_fov_map(self, yx): - return self.fov_map_type(self, yx) + + +class MapChunk(Map): + awake = 0 # asleep if zero + stats = {} + + + +class MapGeometry(): def get_directions(self): directions = [] @@ -57,100 +79,133 @@ class Map: directions += [name[5:]] return directions - def get_neighbors(self, pos): + def get_neighbors(self, pos, map_size, start_indented=True): neighbors = {} - pos = tuple(pos) if not hasattr(self, 'neighbors_to'): self.neighbors_to = {} - if pos in self.neighbors_to: - return self.neighbors_to[pos] + if not map_size in self.neighbors_to: + self.neighbors_to[map_size] = {} + if not start_indented in self.neighbors_to[map_size]: + self.neighbors_to[map_size][start_indented] = {} + if pos in self.neighbors_to[map_size][start_indented]: + return self.neighbors_to[map_size][start_indented][pos] for direction in self.get_directions(): - neighbors[direction] = None - neighbor_pos = self.move(pos, direction) - if neighbor_pos: - neighbors[direction] = neighbor_pos - self.neighbors_to[pos] = neighbors + neighbors[direction] = self.move(pos, direction, map_size, + start_indented) + self.neighbors_to[map_size][start_indented][pos] = neighbors return neighbors - def new_from_shape(self, init_char): - import copy - new_map = copy.deepcopy(self) - for pos in new_map: - new_map[pos] = init_char - return new_map - - def move(self, start_pos, direction): + def undouble_coordinate(self, maps_size, coordinate): + y = maps_size.y * coordinate[0].y + coordinate[1].y + x = maps_size.x * coordinate[0].x + coordinate[1].x + return YX(y, x) + + def get_view_offset(self, maps_size, center, radius): + yx_to_origin = self.undouble_coordinate(maps_size, center) + return yx_to_origin - YX(radius, radius) + + def pos_in_view(self, pos, offset, maps_size): + return self.undouble_coordinate(maps_size, pos) - offset + + def get_view_and_seen_maps(self, maps_size, get_map, radius, view_offset): + m = Map(size=YX(radius*2+1, radius*2+1), + start_indented=(view_offset.y % 2 == 0)) + seen_maps = [] + for pos in m: + seen_pos = self.correct_double_coordinate(maps_size, (0,0), + pos + view_offset) + if seen_pos[0] not in seen_maps: + seen_maps += [seen_pos[0]] + seen_map = get_map(seen_pos[0]) + if seen_map is None: + seen_map = Map(size=maps_size) + m[pos] = seen_map[seen_pos[1]] + return m, seen_maps + + def correct_double_coordinate(self, map_size, big_yx, little_yx): + + def adapt_axis(axis): + maps_crossed = little_yx[axis] // map_size[axis] + new_big = big_yx[axis] + maps_crossed + new_little = little_yx[axis] % map_size[axis] + return new_big, new_little + + new_big_y, new_little_y = adapt_axis(0) + new_big_x, new_little_x = adapt_axis(1) + return YX(new_big_y, new_big_x), YX(new_little_y, new_little_x) + + def move(self, start_pos, direction, map_size, start_indented=True): mover = getattr(self, 'move_' + direction) - new_pos = mover(start_pos) - if new_pos[0] < 0 or new_pos[1] < 0 or \ - new_pos[0] >= self.size[0] or new_pos[1] >= self.size[1]: - return None - return new_pos + big_yx, little_yx = start_pos + uncorrected_target = mover(little_yx, start_indented) + return self.correct_double_coordinate(map_size, big_yx, + uncorrected_target) -class MapWithLeftRightMoves(Map): +class MapGeometryWithLeftRightMoves(MapGeometry): - def move_LEFT(self, start_pos): - return (start_pos[0], start_pos[1] - 1) + def move_LEFT(self, start_pos, _): + return YX(start_pos.y, start_pos.x - 1) - def move_RIGHT(self, start_pos): - return (start_pos[0], start_pos[1] + 1) + def move_RIGHT(self, start_pos, _): + return YX(start_pos.y, start_pos.x + 1) -class MapSquare(MapWithLeftRightMoves): +class MapGeometrySquare(MapGeometryWithLeftRightMoves): - def move_UP(self, start_pos): - return (start_pos[0] - 1, start_pos[1]) + def move_UP(self, start_pos, _): + return YX(start_pos.y - 1, start_pos.x) - def move_DOWN(self, start_pos): - return (start_pos[0] + 1, start_pos[1]) + def move_DOWN(self, start_pos, _): + return YX(start_pos.y + 1, start_pos.x) -class MapHex(MapWithLeftRightMoves): +class MapGeometryHex(MapGeometryWithLeftRightMoves): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fov_map_type = FovMapHex - def move_UPLEFT(self, start_pos): - if start_pos[0] % 2 == 1: - return (start_pos[0] - 1, start_pos[1] - 1) + def move_UPLEFT(self, start_pos, start_indented): + if start_pos.y % 2 == start_indented: + return YX(start_pos.y - 1, start_pos.x - 1) else: - return (start_pos[0] - 1, start_pos[1]) + return YX(start_pos.y - 1, start_pos.x) - def move_UPRIGHT(self, start_pos): - if start_pos[0] % 2 == 1: - return (start_pos[0] - 1, start_pos[1]) + def move_UPRIGHT(self, start_pos, start_indented): + if start_pos.y % 2 == start_indented: + return YX(start_pos.y - 1, start_pos.x) else: - return (start_pos[0] - 1, start_pos[1] + 1) + return YX(start_pos.y - 1, start_pos.x + 1) - def move_DOWNLEFT(self, start_pos): - if start_pos[0] % 2 == 1: - return (start_pos[0] + 1, start_pos[1] - 1) + def move_DOWNLEFT(self, start_pos, start_indented): + if start_pos.y % 2 == start_indented: + return YX(start_pos.y + 1, start_pos.x - 1) else: - return (start_pos[0] + 1, start_pos[1]) + return YX(start_pos.y + 1, start_pos.x) - def move_DOWNRIGHT(self, start_pos): - if start_pos[0] % 2 == 1: - return (start_pos[0] + 1, start_pos[1]) + def move_DOWNRIGHT(self, start_pos, start_indented): + if start_pos.y % 2 == start_indented: + return YX(start_pos.y + 1, start_pos.x) else: - return (start_pos[0] + 1, start_pos[1] + 1) + return YX(start_pos.y + 1, start_pos.x + 1) -class FovMap: +class FovMap(Map): - def __init__(self, source_map, yx): + def __init__(self, source_map, center): self.source_map = source_map self.size = self.source_map.size - self.fov_radius = (self.size[0] / 2) - 0.5 + self.fov_radius = (self.size.y / 2) - 0.5 + self.start_indented = source_map.start_indented self.terrain = '?' * self.size_i - self[yx] = '.' + self[center] = '.' self.shadow_cones = [] - self.circle_out(yx, self.shadow_process_hex) + self.circle_out(center, self.shadow_process_hex) def shadow_process_hex(self, yx, distance_to_center, dir_i, dir_progress): # Possible optimization: If no shadow_cones yet and self[yx] == '.', @@ -221,10 +276,10 @@ class FovMap: def basic_circle_out_move(self, pos, direction): """Move position pos into direction. Return whether still in map.""" - mover = getattr(self, 'move_' + direction) - pos = mover(pos) - if pos[0] < 0 or pos[1] < 0 or \ - pos[0] >= self.size[0] or pos[1] >= self.size[1]: + mover = getattr(self.geometry, 'move_' + direction) + pos = mover(pos, self.start_indented) + if pos.y < 0 or pos.x < 0 or \ + pos.y >= self.size.y or pos.x >= self.size.x: return pos, False return pos, True @@ -240,7 +295,7 @@ class FovMap: # TODO: get rid of circle_in_map logic circle_in_map = True distance = 1 - yx = yx[:] + yx = YX(yx.y, yx.x) #print('DEBUG CIRCLE_OUT', yx) while circle_in_map: if distance > self.fov_radius: @@ -258,19 +313,28 @@ class FovMap: -class FovMapHex(FovMap, MapHex): +class FovMapHex(FovMap): circle_out_directions = ('DOWNLEFT', 'LEFT', 'UPLEFT', 'UPRIGHT', 'RIGHT', 'DOWNRIGHT') + def __init__(self, *args, **kwargs): + self.geometry = MapGeometryHex() + super().__init__(*args, **kwargs) + def circle_out_move(self, yx, direction): return self.basic_circle_out_move(yx, direction) -class FovMapSquare(FovMap, MapSquare): +class FovMapSquare(FovMap): circle_out_directions = (('DOWN', 'LEFT'), ('LEFT', 'UP'), ('UP', 'RIGHT'), ('RIGHT', 'DOWN')) + def __init__(self, *args, **kwargs): + self.geometry = MapGeometrySquare() + super().__init__(*args, **kwargs) + def circle_out_move(self, yx, direction): self.basic_circle_out_move(yx, direction[0]) return self.basic_circle_out_move(yx, direction[1]) +