Package horizons :: Package editor :: Module intermediatemap
[hide private]
[frames] | no frames]

Source Code for Module horizons.editor.intermediatemap

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  from horizons.constants import GROUND 
 23   
 24  DEEP_WATER = 0 
 25  SHALLOW_WATER = 1 
 26  SAND = 2 
 27  GRASS = 3 
 28   
 29   
30 -class IntermediateMap:
31
32 - def __init__(self, world):
33 self.world = world 34 self.session = world.session 35 self._init_map()
36
37 - def _get_tile_repr(self, coords):
38 if coords not in self.world.full_map: 39 return 0 40 41 tile = self.world.full_map[coords] 42 if tile.id <= 0: 43 return DEEP_WATER 44 elif tile.id == 1: 45 return SHALLOW_WATER 46 elif tile.id == 6: 47 return SAND 48 elif tile.id == 3: 49 return GRASS 50 else: 51 offset = 0 if tile.id == 2 else (1 if tile.id == 5 else 2) 52 rotation = tile.rotation // 90 53 if tile.shape == 'straight': 54 return offset + (1, 0, 0, 1)[rotation] # 2 low, 2 high 55 elif tile.shape == 'curve_in': 56 return offset + (1, 1, 0, 1)[rotation] # 1 low, 3 high 57 else: 58 return offset + (1, 0, 0, 0)[rotation] # 3 low, 1 high
59
60 - def _init_map(self):
61 self._map = {} 62 width = self.world.max_x - self.world.min_x + 1 63 height = self.world.max_y - self.world.min_y + 1 64 for y in range(height + 2): 65 orig_y = y + self.world.min_y - 1 66 for x in range(width + 2): 67 orig_x = x + self.world.min_x - 1 68 self._map[(x, y)] = self._get_tile_repr((orig_x, orig_y)) 69 70 self.max_x = width - 1 71 self.max_y = height - 1
72
73 - def _get_intermediate_coords(self, coords):
74 return (coords[0] - self.world.min_x, coords[1] - self.world.min_y)
75
76 - def distance_from_edge(self, coords):
77 x, y = coords 78 return min(min(x, self.max_x - x), min(y, self.max_y - y))
79
80 - def _update_intermediate_coords(self, coords, new_type):
81 if self._map[coords] == new_type: 82 return 83 self._map[coords] = min(new_type, self.distance_from_edge(coords))
84
85 - def _fix_map(self, coords_list, new_type):
86 changes = True 87 while changes: 88 changes = False 89 for x, y in coords_list: 90 top_left = (x, y) 91 if top_left not in self._map: 92 continue 93 bottom_right = (x + 1, y + 1) 94 if bottom_right not in self._map: 95 continue 96 if self._map[top_left] != self._map[bottom_right]: 97 continue 98 bottom_left = (x, y + 1) 99 top_right = (x + 1, y) 100 if self._map[bottom_left] != self._map[top_right]: 101 continue 102 diff = self._map[top_left] - self._map[top_right] 103 if diff == 0: 104 continue 105 106 lower_corner = top_right if diff == 1 else top_left 107 higher_corner = top_left if diff == 1 else top_right 108 mi = self._map[lower_corner] 109 if new_type <= mi: 110 self._set_tiles([higher_corner], mi) 111 else: 112 self._set_tiles([lower_corner], mi + 1) 113 changes = True
114
115 - def set_south_east_corner(self, raw_coords_list, tile_details):
116 new_type = tile_details[0] if tile_details[0] != 6 else 2 117 coords_list = [] 118 for coords in raw_coords_list: 119 if coords not in self.world.fake_tile_map: 120 continue 121 122 coords2 = self._get_intermediate_coords(coords) 123 assert coords2 in self._map 124 if self._map[coords2] != new_type: 125 coords_list.append(coords2) 126 127 if coords_list: 128 self._set_tiles(coords_list, new_type)
129
130 - def _get_surrounding_coords(self, current_coords_list):
131 all_neighbors = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] 132 current_coords_set = set(current_coords_list) 133 result = set() 134 for x, y in current_coords_list: 135 for dx, dy in all_neighbors: 136 coords2 = (x + dx, y + dy) 137 if coords2 in self._map and coords2 not in current_coords_set: 138 result.add(coords2) 139 return sorted(result)
140
141 - def _set_tiles(self, initial_coords_list, new_type):
142 last_coords_list = [] 143 for coords in initial_coords_list: 144 last_coords_list.append(coords) 145 self._update_intermediate_coords(coords, new_type) 146 147 for _ in range(3): 148 surrounding_coords_list = self._get_surrounding_coords(last_coords_list) 149 for coords2 in surrounding_coords_list: 150 if coords2 not in self._map: 151 continue 152 153 cur_type = self._map[coords2] 154 best_new_type = cur_type 155 best_dist = 10 156 for new_type2 in range(4): 157 if best_dist <= abs(new_type2 - cur_type): 158 continue 159 160 suitable = True 161 for updated_coords in last_coords_list: 162 if abs(updated_coords[0] - coords2[0]) > 1 or abs(updated_coords[1] - coords2[1]) > 1: 163 continue 164 if abs(self._map[updated_coords] - new_type2) > 1: 165 suitable = False 166 break 167 if not suitable: 168 continue 169 170 best_new_type = new_type2 171 best_dist = abs(new_type2 - cur_type) 172 self._update_intermediate_coords(coords2, best_new_type) 173 last_coords_list.append(coords2) 174 175 self._fix_map(last_coords_list, new_type) 176 for coords in last_coords_list: 177 self._update_tile(*coords)
178
179 - def _update_tile(self, x, y):
180 if (x, y) not in self._map: 181 return 182 if (x + 1, y + 1) not in self._map: 183 return 184 185 data = [] 186 for dy in range(2): 187 for dx in range(2): 188 data.append(self._map[(x + dx, y + dy)]) 189 coords = (x + self.world.min_x, y + self.world.min_y) 190 191 minimum = min(data) 192 for i in range(4): 193 data[i] -= minimum 194 195 if max(data) == 0: 196 ground_class = { 197 DEEP_WATER: GROUND.WATER, 198 SHALLOW_WATER: GROUND.SHALLOW_WATER, 199 SAND: GROUND.SAND, 200 GRASS: GROUND.DEFAULT_LAND, 201 }[minimum] 202 self.session.world_editor.set_tile(coords, ground_class) 203 else: 204 assert max(data) == 1, 'This should never happen' 205 tile_type = 2 if minimum == 0 else (5 if minimum == 1 else 4) 206 tile_def = { 207 (0, 1, 0, 1): (tile_type, 'straight', 45), 208 (1, 1, 0, 0): (tile_type, 'straight', 135), 209 (1, 0, 1, 0): (tile_type, 'straight', 225), 210 (0, 0, 1, 1): (tile_type, 'straight', 315), 211 (0, 1, 1, 1): (tile_type, 'curve_in', 45), 212 (1, 1, 0, 1): (tile_type, 'curve_in', 135), 213 (1, 1, 1, 0): (tile_type, 'curve_in', 225), 214 (1, 0, 1, 1): (tile_type, 'curve_in', 315), 215 (0, 0, 0, 1): (tile_type, 'curve_out', 45), 216 (0, 1, 0, 0): (tile_type, 'curve_out', 135), 217 (1, 0, 0, 0): (tile_type, 'curve_out', 225), 218 (0, 0, 1, 0): (tile_type, 'curve_out', 315), 219 }[tuple(data)] 220 self.session.world_editor.set_tile(coords, tile_def)
221
222 - def __str__(self):
223 res = '' 224 width = self.world.max_x - self.world.min_x + 1 225 height = self.world.max_y - self.world.min_y + 1 226 for y in range(height + 2): 227 for x in range(width + 2): 228 res += str(self._map[(x, y)]) 229 res += '\n' 230 return res
231