File: | map_generator.cc |
Location: | line 778, column 2 |
Description: | Declared variable-length array (VLA) has zero size |
1 | /* | |||
2 | * Copyright (C) 2002-2004, 2006-2010, 2013 by the Widelands Development Team | |||
3 | * | |||
4 | * This program is free software; you can redistribute it and/or | |||
5 | * modify it under the terms of the GNU General Public License | |||
6 | * as published by the Free Software Foundation; either version 2 | |||
7 | * of the License, or (at your option) any later version. | |||
8 | * | |||
9 | * This program is distributed in the hope that it will be useful, | |||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
12 | * GNU General Public License for more details. | |||
13 | * | |||
14 | * You should have received a copy of the GNU General Public License | |||
15 | * along with this program; if not, write to the Free Software | |||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
17 | * | |||
18 | */ | |||
19 | ||||
20 | #include "map_generator.h" | |||
21 | #include "log.h" | |||
22 | #include "logic/findnode.h" | |||
23 | #include "logic/map.h" | |||
24 | #include "logic/editor_game_base.h" | |||
25 | #include "editor/tools/editor_increase_resources_tool.h" | |||
26 | ||||
27 | #include <boost/scoped_array.hpp> | |||
28 | ||||
29 | #define AVG_ELEVATION(0x80000000) (0x80000000) | |||
30 | #define MAX_ELEVATION(0xffffffff) (0xffffffff) | |||
31 | #define MAP_ID_DIGITS24 24 | |||
32 | #define ISLAND_BORDER10 10 | |||
33 | #define MAX_ELEVATION_HALF(0x80000000) (0x80000000) | |||
34 | ||||
35 | using boost::scoped_array; | |||
36 | ||||
37 | namespace Widelands | |||
38 | { | |||
39 | ||||
40 | MapGenerator::MapGenerator | |||
41 | (Map & map, const UniqueRandomMapInfo & mapInfo, | |||
42 | Editor_Game_Base & egbase) | |||
43 | : m_map(map), m_mapInfo(mapInfo), m_egbase(egbase) | |||
44 | { | |||
45 | ||||
46 | } | |||
47 | ||||
48 | void MapGenerator::generate_bobs | |||
49 | (scoped_array<uint32_t> const * random_bobs, | |||
50 | Coords const fc, | |||
51 | RNG & rng, | |||
52 | MapGenAreaInfo::MapGenTerrainType const terrType) | |||
53 | { | |||
54 | // Figure out which bob area is due here... | |||
55 | ||||
56 | MapGenInfo & mapGenInfo = m_map.world().getMapGenInfo(); | |||
57 | ||||
58 | size_t num = mapGenInfo.getNumBobAreas(); | |||
59 | size_t found = num; | |||
60 | uint32_t sum_weight = mapGenInfo.getSumBobAreaWeight(); | |||
61 | uint32_t max_val = 0; | |||
62 | for (size_t ix = 0; ix < num; ++ix) { | |||
63 | uint32_t val = random_bobs[ix][fc.x + m_mapInfo.w * fc.y]; | |||
64 | val = (val / sum_weight) * mapGenInfo.getBobArea(ix).getWeight(); | |||
65 | if (val >= max_val) { | |||
66 | found = ix; | |||
67 | max_val = val; | |||
68 | } | |||
69 | } | |||
70 | if (found >= num) | |||
71 | return; | |||
72 | ||||
73 | // Figure out if we really need to set a bob here... | |||
74 | ||||
75 | const MapGenBobArea & bobArea = mapGenInfo.getBobArea(found); | |||
76 | ||||
77 | const MapGenBobKind * bobKind = bobArea.getBobKind(terrType); | |||
78 | ||||
79 | if (not bobKind) // no bobs defined here... | |||
80 | return; | |||
81 | ||||
82 | uint32_t immovDens = bobArea.getImmovableDensity(); | |||
83 | uint32_t movDens = bobArea.getMoveableDensity(); | |||
84 | ||||
85 | immovDens *= max_val / 100; | |||
86 | movDens *= max_val / 100; | |||
87 | ||||
88 | immovDens = immovDens >= MAX_ELEVATION_HALF(0x80000000) ? MAX_ELEVATION(0xffffffff) : immovDens * 2; | |||
89 | movDens = movDens >= MAX_ELEVATION_HALF(0x80000000) ? MAX_ELEVATION(0xffffffff) : movDens * 2; | |||
90 | ||||
91 | uint32_t val = rng.rand(); | |||
92 | bool set_immovable = (val <= immovDens); | |||
93 | val = rng.rand(); | |||
94 | bool set_moveable = (val <= movDens); | |||
95 | ||||
96 | ||||
97 | // Set bob according to bob area | |||
98 | ||||
99 | if (set_immovable and (num = bobKind->getNumImmovableBobs())) | |||
100 | m_egbase.create_immovable | |||
101 | (fc, | |||
102 | bobKind->getImmovableBob | |||
103 | (static_cast<size_t>(rng.rand() / (MAX_ELEVATION(0xffffffff) / num))), | |||
104 | 0); | |||
105 | ||||
106 | if (set_moveable and (num = bobKind->getNumMoveableBobs())) | |||
107 | m_egbase.create_bob | |||
108 | (fc, | |||
109 | m_map.world().get_bob | |||
110 | (bobKind->getMoveableBob | |||
111 | (static_cast<size_t>(rng.rand() / (MAX_ELEVATION(0xffffffff) / num))) | |||
112 | .c_str()), | |||
113 | 0); | |||
114 | } | |||
115 | ||||
116 | #define set_resource_helper(rnd1, res){ Resource_Index const res_idx = terr.get_valid_resource(res) ; uint32_t const max_amount = m_map.world().get_resource(res_idx )->get_max_amount(); uint8_t res_val = static_cast<uint8_t >(rnd1 / ((0xffffffff) / max_amount)); res_val *= static_cast <uint8_t>(m_mapInfo.resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback(fc, &m_map, res_idx )) { fc.field->set_resources(res_idx, res_val); fc.field-> set_starting_res_amount(res_val); } } \ | |||
117 | { \ | |||
118 | Resource_Index const res_idx = terr.get_valid_resource(res); \ | |||
119 | uint32_t const max_amount = \ | |||
120 | m_map.world().get_resource(res_idx)->get_max_amount(); \ | |||
121 | uint8_t res_val = \ | |||
122 | static_cast<uint8_t>(rnd1 / (MAX_ELEVATION(0xffffffff) / max_amount)); \ | |||
123 | res_val *= static_cast<uint8_t>(m_mapInfo.resource_amount) + 1; \ | |||
124 | res_val /= 3; \ | |||
125 | if (Editor_Change_Resource_Tool_Callback(fc, &m_map, res_idx)) { \ | |||
126 | fc.field->set_resources(res_idx, res_val); \ | |||
127 | fc.field->set_starting_res_amount(res_val); \ | |||
128 | } \ | |||
129 | } \ | |||
130 | ||||
131 | void MapGenerator::generate_resources | |||
132 | (uint32_t const * const random1, | |||
133 | uint32_t const * const random2, | |||
134 | uint32_t const * const random3, | |||
135 | uint32_t const * const random4, | |||
136 | FCoords const fc) | |||
137 | { | |||
138 | // We'll take the "D" terrain at first... | |||
139 | // TODO: Check how the editor handles this... | |||
140 | ||||
141 | Terrain_Index const tix = fc.field->get_terrains().d; | |||
142 | const Terrain_Descr & terr = m_map.world().get_ter(tix); | |||
143 | switch (terr.get_num_valid_resources()) { | |||
144 | case 1: { | |||
145 | uint32_t const rnd1 = random1[fc.x + m_mapInfo.w * fc.y]; | |||
146 | set_resource_helper(rnd1, 0){ Resource_Index const res_idx = terr.get_valid_resource(0); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd1 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
147 | break; | |||
148 | } | |||
149 | case 2: { | |||
150 | uint32_t const rnd1 = random1[fc.x + m_mapInfo.w * fc.y]; | |||
151 | uint32_t const rnd2 = random2[fc.x + m_mapInfo.w * fc.y]; | |||
152 | if (rnd1 > rnd2) { | |||
153 | set_resource_helper(rnd1, 0){ Resource_Index const res_idx = terr.get_valid_resource(0); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd1 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } } | |||
154 | } else | |||
155 | set_resource_helper(rnd2, 1){ Resource_Index const res_idx = terr.get_valid_resource(1); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd2 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
156 | break; | |||
157 | } | |||
158 | case 3: { | |||
159 | uint32_t const rnd1 = random1[fc.x + m_mapInfo.w * fc.y]; | |||
160 | uint32_t const rnd2 = random2[fc.x + m_mapInfo.w * fc.y]; | |||
161 | uint32_t const rnd3 = random3[fc.x + m_mapInfo.w * fc.y]; | |||
162 | if (rnd1 > rnd2 && rnd1 > rnd3) { | |||
163 | set_resource_helper(rnd1, 0){ Resource_Index const res_idx = terr.get_valid_resource(0); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd1 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
164 | } else if (rnd2 > rnd1 && rnd2 > rnd3) { | |||
165 | set_resource_helper(rnd2, 1){ Resource_Index const res_idx = terr.get_valid_resource(1); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd2 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
166 | } else | |||
167 | set_resource_helper(rnd3, 2){ Resource_Index const res_idx = terr.get_valid_resource(2); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd3 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
168 | break; | |||
169 | } | |||
170 | case 4: { | |||
171 | uint32_t const rnd1 = random1[fc.x + m_mapInfo.w * fc.y]; | |||
172 | uint32_t const rnd2 = random2[fc.x + m_mapInfo.w * fc.y]; | |||
173 | uint32_t const rnd3 = random3[fc.x + m_mapInfo.w * fc.y]; | |||
174 | uint32_t const rnd4 = random4[fc.x + m_mapInfo.w * fc.y]; | |||
175 | if (rnd1 > rnd2 && rnd1 > rnd3 && rnd1 > rnd4) { | |||
176 | set_resource_helper(rnd1, 0){ Resource_Index const res_idx = terr.get_valid_resource(0); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd1 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
177 | } else if (rnd2 > rnd1 && rnd2 > rnd3 && rnd2 > rnd4) { | |||
178 | set_resource_helper(rnd2, 1){ Resource_Index const res_idx = terr.get_valid_resource(1); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd2 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
179 | } else if (rnd3 > rnd1 && rnd3 > rnd2 && rnd3 > rnd4) { | |||
180 | set_resource_helper(rnd3, 2){ Resource_Index const res_idx = terr.get_valid_resource(2); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd3 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
181 | } else | |||
182 | set_resource_helper(rnd4, 3){ Resource_Index const res_idx = terr.get_valid_resource(3); uint32_t const max_amount = m_map.world().get_resource(res_idx)->get_max_amount (); uint8_t res_val = static_cast<uint8_t>(rnd4 / ((0xffffffff ) / max_amount)); res_val *= static_cast<uint8_t>(m_mapInfo .resource_amount) + 1; res_val /= 3; if (Editor_Change_Resource_Tool_Callback (fc, &m_map, res_idx)) { fc.field->set_resources(res_idx , res_val); fc.field->set_starting_res_amount(res_val); } }; | |||
183 | break; | |||
184 | } | |||
185 | default: | |||
186 | break; // currently mountains have the maximum of allowed resources, which is 4 | |||
187 | } | |||
188 | } | |||
189 | ||||
190 | /// Translates a random value into a map node height. This method is used | |||
191 | /// within the random map generation methods. | |||
192 | /// | |||
193 | /// \param elevation Random value. | |||
194 | /// \param mapGenInfo Map generator information used to translate random values | |||
195 | /// to height information (world specific info). | |||
196 | /// \param c position within map | |||
197 | /// \param mapInfo Information about the random map currently begin created | |||
198 | /// (map specific info). | |||
199 | /// | |||
200 | /// \returns A map height value corresponding to elevation. | |||
201 | uint8_t MapGenerator::make_node_elevation | |||
202 | (double const elevation, | |||
203 | Coords const c) | |||
204 | { | |||
205 | MapGenInfo & mapGenInfo = m_map.world().getMapGenInfo(); | |||
206 | ||||
207 | int32_t const water_h = mapGenInfo.getWaterShallowHeight(); | |||
208 | int32_t const mount_h = mapGenInfo.getMountainFootHeight(); | |||
209 | int32_t const summit_h = mapGenInfo.getSummitHeight (); | |||
210 | ||||
211 | double const water_fac = m_mapInfo.waterRatio; | |||
212 | double const land_fac = m_mapInfo.landRatio; | |||
213 | ||||
214 | uint8_t res_h = | |||
215 | elevation < water_fac ? water_h : | |||
216 | elevation < water_fac + land_fac ? | |||
217 | water_h + 1 + | |||
218 | ((elevation - water_fac) / land_fac) * (mount_h - water_h) | |||
219 | : | |||
220 | mount_h + | |||
221 | ((elevation - water_fac - land_fac) / (1 - water_fac - land_fac)) | |||
222 | * | |||
223 | (summit_h - mount_h); | |||
224 | ||||
225 | // Handle Map Border in island mode | |||
226 | if (m_mapInfo.islandMode) { | |||
227 | int32_t const border_dist = | |||
228 | std::min | |||
229 | (std::min<X_Coordinate>(c.x, m_mapInfo.w - c.x), | |||
230 | std::min<Y_Coordinate>(c.y, m_mapInfo.h - c.y)); | |||
231 | if (border_dist <= ISLAND_BORDER10) { | |||
232 | res_h = | |||
233 | static_cast<uint8_t> | |||
234 | (static_cast<double>(res_h) * border_dist / | |||
235 | static_cast<double>(ISLAND_BORDER10)); | |||
236 | if (res_h < water_h) | |||
237 | res_h = water_h; | |||
238 | } | |||
239 | } | |||
240 | ||||
241 | return res_h; | |||
242 | } | |||
243 | ||||
244 | /** | |||
245 | =============== | |||
246 | Generate a "continuous" array of "reasonable" random values. | |||
247 | The array generated is in fact organized in a two-dimensional | |||
248 | way. "Reasonable" means that the values are not purely random. | |||
249 | Neighboring values (in a two-dimensional way) are fitting | |||
250 | together so that such an array can be used to directly generate | |||
251 | height information for mountains, wasteland, resources etc. | |||
252 | "Continuous" means that also value of the left border fit to | |||
253 | the right border values and values of the top border fit to the | |||
254 | bottom border values. This means we have some kind of "endlessly" | |||
255 | repeating set of random values. | |||
256 | What is more, the different heights are weighed so that the | |||
257 | random distribution of all random values in the array is linear. | |||
258 | The minimum valu will be 0, the maximum value will be MAX_ELEVATION, | |||
259 | the average will be AVG_ELEVATION. | |||
260 | ||||
261 | w, h: are width and height of the two-dimensional array | |||
262 | produced. Thus, the array has w * h entries. To access a certain | |||
263 | "coordinate" in the array, use array[x + w * y] to retrieve the entry. | |||
264 | ||||
265 | rng: is the random number generator to be used. | |||
266 | This will mostly be the current rng of the random map currently being | |||
267 | created. | |||
268 | =============== | |||
269 | */ | |||
270 | uint32_t * MapGenerator::generate_random_value_map | |||
271 | (uint32_t const w, uint32_t const h, RNG & rng) | |||
272 | { | |||
273 | uint32_t const numFields = h * w; // Size of the resulting array | |||
274 | ||||
275 | uint32_t * const values = new uint32_t[numFields]; // Array to be filled | |||
276 | ||||
277 | try { | |||
278 | // We will do some initing here... | |||
279 | ||||
280 | for (uint32_t ix = 0; ix < numFields; ++ix) | |||
281 | values[ix] = AVG_ELEVATION(0x80000000); | |||
282 | ||||
283 | // This will be the first starting random values... | |||
284 | ||||
285 | for (uint32_t x = 0; x < w; x += 16) | |||
286 | for (uint32_t y = 0; y < h; y += 16) { | |||
287 | values[x + y * w] = rng.rand(); | |||
288 | if (x % 32 or y % 32) { | |||
289 | values[x + y * w] += AVG_ELEVATION(0x80000000); | |||
290 | values[x + y * w] /= 2; | |||
291 | } | |||
292 | } | |||
293 | ||||
294 | // randomize the values | |||
295 | ||||
296 | uint32_t step_x = std::min(16U, w), step_y = std::min(16U, h); | |||
297 | uint32_t max = AVG_ELEVATION(0x80000000), min = AVG_ELEVATION(0x80000000); | |||
298 | double ele_fac = 0.15; | |||
299 | ||||
300 | bool end = false; | |||
301 | ||||
302 | while (!end) { | |||
303 | for (uint32_t x = 0; x < w; x += step_x) { | |||
304 | for (uint32_t y = 0; y < h; y += step_y) { | |||
305 | // Calculate coordinates of left and bottom left neighbours of | |||
306 | // the current node. | |||
307 | ||||
308 | uint32_t right_x = x + step_x; | |||
309 | uint32_t lower_y = y + step_y; | |||
310 | if (right_x >= w) | |||
311 | right_x -= w; | |||
312 | if (lower_y >= h) | |||
313 | lower_y -= h; | |||
314 | ||||
315 | // Get the current values of my neighbor nodes and of my node. | |||
316 | ||||
317 | uint32_t const x_0_y_0 = values[x + w * y]; | |||
318 | uint32_t const x_1_y_0 = values[right_x + w * y]; | |||
319 | uint32_t const x_0_y_1 = values[x + w * lower_y]; | |||
320 | uint32_t const x_1_y_1 = values[right_x + w * lower_y]; | |||
321 | ||||
322 | // calculate the in-between values | |||
323 | ||||
324 | uint32_t x_new = | |||
325 | x_0_y_0 / 2 + x_1_y_0 / 2 + | |||
326 | static_cast<uint32_t> | |||
327 | (ele_fac * rng.rand() - ele_fac * AVG_ELEVATION(0x80000000)); | |||
328 | ||||
329 | uint32_t y_new = | |||
330 | x_0_y_0 / 2 + x_0_y_1 / 2 + | |||
331 | static_cast<uint32_t> | |||
332 | (ele_fac * rng.rand() - ele_fac * AVG_ELEVATION(0x80000000)); | |||
333 | ||||
334 | uint32_t xy_new = | |||
335 | x_0_y_0 / 4 + x_1_y_1 / 4 + x_1_y_0 / 4 + x_0_y_1 / 4 + | |||
336 | static_cast<uint32_t> | |||
337 | (ele_fac * rng.rand() - ele_fac * AVG_ELEVATION(0x80000000)); | |||
338 | ||||
339 | values[x + step_x / 2 + w * (y)] = x_new; | |||
340 | values[x + step_x / 2 + w * (y + step_y / 2)] = xy_new; | |||
341 | values[x + w * (y + step_y / 2)] = y_new; | |||
342 | ||||
343 | // see if we have got a new min or max value | |||
344 | ||||
345 | if (x_new > max) | |||
346 | max = x_new; | |||
347 | if (y_new > max) | |||
348 | max = y_new; | |||
349 | if (xy_new > max) | |||
350 | max = xy_new; | |||
351 | ||||
352 | if (x_new < min) | |||
353 | min = x_new; | |||
354 | if (y_new < min) | |||
355 | min = y_new; | |||
356 | if (xy_new < min) | |||
357 | min = xy_new; | |||
358 | } | |||
359 | } | |||
360 | ||||
361 | // preparations for the next iteration | |||
362 | if (step_y == 2 && step_x == 2) | |||
363 | end = true; | |||
364 | step_x /= 2; | |||
365 | step_y /= 2; | |||
366 | if (step_x <= 1) | |||
367 | step_x = 2; | |||
368 | if (step_y <= 1) | |||
369 | step_y = 2; | |||
370 | ele_fac *= 0.9; | |||
371 | } | |||
372 | ||||
373 | // make a histogram of the heights | |||
374 | ||||
375 | uint32_t histo[1024]; | |||
376 | ||||
377 | for (uint32_t x = 0; x < 1024; ++x) | |||
378 | histo[x] = 0; | |||
379 | ||||
380 | for (uint32_t x = 0; x < w; ++x) | |||
381 | for (uint32_t y = 0; y < h; ++y) { | |||
382 | values[x + y * w] = | |||
383 | ((static_cast<double>(values[x + y * w] - min)) | |||
384 | / | |||
385 | static_cast<double>(max - min)) | |||
386 | * | |||
387 | MAX_ELEVATION(0xffffffff); | |||
388 | ++histo[values[x + y * w] >> 22]; | |||
389 | } | |||
390 | ||||
391 | // sort the histo out | |||
392 | ||||
393 | double minVals[1024]; | |||
394 | ||||
395 | double currVal = 0.0; | |||
396 | ||||
397 | for (uint32_t x = 0; x < 1024; ++x) { | |||
398 | minVals[x] = currVal; | |||
399 | currVal += | |||
400 | static_cast<double>(histo[x]) / static_cast<double>(numFields); | |||
401 | } | |||
402 | ||||
403 | // Adjust the heights so that all height values are equal of density. | |||
404 | // This is done to have reliable water/land ratio later on. | |||
405 | for (uint32_t x = 0; x < w; ++x) | |||
406 | for (uint32_t y = 0; y < h; ++y) | |||
407 | values[x + y * w] = | |||
408 | minVals[values[x + y * w] >> 22] | |||
409 | * | |||
410 | static_cast<double>(MAX_ELEVATION(0xffffffff)); | |||
411 | return values; | |||
412 | } catch (...) { | |||
413 | delete[] values; | |||
414 | throw; | |||
415 | } | |||
416 | ||||
417 | return 0; // Should not be reached | |||
418 | } | |||
419 | ||||
420 | ||||
421 | /** | |||
422 | =============== | |||
423 | Figures out terrain info for a field in a random map. | |||
424 | ||||
425 | mapGenInfo: Map generator information used to translate | |||
426 | Random values to height information (world- | |||
427 | specific info) | |||
428 | x, y: first coordinate of the current triangle | |||
429 | x1, y1: second coordinate of the current triangle | |||
430 | x2, y2: third coordinate of the current triangle | |||
431 | random2: Random array for generating different | |||
432 | terrain types on land | |||
433 | random3: Random array for generating different | |||
434 | terrain types on land | |||
435 | random4: Random array for wasteland generation | |||
436 | h1, h2, h3: Map height information for the three triangle coords | |||
437 | mapInfo: Information about the random map currently | |||
438 | begin created (map specific info) | |||
439 | rng: is the random number generator to be used. | |||
440 | This will mostly be the current rng of the random map | |||
441 | currently being created. | |||
442 | terrType: Returns the terrain-Type fpor this triangle | |||
443 | =============== | |||
444 | */ | |||
445 | Terrain_Index MapGenerator::figure_out_terrain | |||
446 | (uint32_t * const random2, | |||
447 | uint32_t * const random3, | |||
448 | uint32_t * const random4, | |||
449 | Coords const c0, Coords const c1, Coords const c2, | |||
450 | uint32_t const h1, uint32_t const h2, uint32_t const h3, | |||
451 | RNG & rng, | |||
452 | MapGenAreaInfo::MapGenTerrainType & terrType) | |||
453 | { | |||
454 | MapGenInfo & mapGenInfo = m_map.world().getMapGenInfo(); | |||
455 | ||||
456 | uint32_t numLandAreas = | |||
457 | mapGenInfo.getNumAreas(MapGenAreaInfo::atLand); | |||
458 | uint32_t const numWasteLandAreas = | |||
459 | mapGenInfo.getNumAreas(MapGenAreaInfo::atWasteland); | |||
460 | ||||
461 | bool isDesert = false; | |||
462 | bool isDesertOuter = false; | |||
463 | uint32_t landAreaIndex = 0; | |||
464 | MapGenAreaInfo::MapGenAreaType landType = MapGenAreaInfo::atLand; | |||
465 | ||||
466 | uint32_t rand2 = | |||
467 | random2[c0.x + m_mapInfo.w * c0.y] / 3 + | |||
468 | random2[c1.x + m_mapInfo.w * c1.y] / 3 + | |||
469 | random2[c2.x + m_mapInfo.w * c2.y] / 3; | |||
470 | uint32_t rand3 = | |||
471 | random3[c0.x + m_mapInfo.w * c0.y] / 3 + | |||
472 | random3[c1.x + m_mapInfo.w * c1.y] / 3 + | |||
473 | random3[c2.x + m_mapInfo.w * c2.y] / 3; | |||
474 | uint32_t rand4 = | |||
475 | random4[c0.x + m_mapInfo.w * c0.y] / 3 + | |||
476 | random4[c1.x + m_mapInfo.w * c1.y] / 3 + | |||
477 | random4[c2.x + m_mapInfo.w * c2.y] / 3; | |||
478 | ||||
479 | // At first we figure out if it is wasteland or not. | |||
480 | ||||
481 | if (numWasteLandAreas == 0) { | |||
482 | } else if (numWasteLandAreas == 1) { | |||
483 | if (rand4 < (AVG_ELEVATION(0x80000000) * m_mapInfo.wastelandRatio)) { | |||
484 | numLandAreas = numWasteLandAreas; | |||
485 | isDesert = true; | |||
486 | isDesertOuter = | |||
487 | rand4 > (AVG_ELEVATION(0x80000000) * m_mapInfo.wastelandRatio / 4) * 3; | |||
488 | landAreaIndex = 0; | |||
489 | } | |||
490 | } else { | |||
491 | if (rand4<(AVG_ELEVATION(0x80000000) * m_mapInfo.wastelandRatio * 0.5)) { | |||
492 | numLandAreas = numWasteLandAreas; | |||
493 | isDesert = true; | |||
494 | isDesertOuter = | |||
495 | rand4 > (AVG_ELEVATION(0x80000000) * m_mapInfo.wastelandRatio * 0.5 / 4) * 3; | |||
496 | landAreaIndex = 0; | |||
497 | } | |||
498 | else if | |||
499 | (rand4 | |||
500 | > | |||
501 | (MAX_ELEVATION(0xffffffff) - AVG_ELEVATION(0x80000000) * m_mapInfo.wastelandRatio * 0.5)) | |||
502 | { | |||
503 | numLandAreas = numWasteLandAreas; | |||
504 | isDesert = true; | |||
505 | isDesertOuter = | |||
506 | rand4 | |||
507 | < | |||
508 | 1 - AVG_ELEVATION(0x80000000) * m_mapInfo.wastelandRatio * 0.5 / 4 * 3; | |||
509 | landAreaIndex = 1; | |||
510 | } | |||
511 | } | |||
512 | ||||
513 | MapGenAreaInfo::MapGenAreaType atp = MapGenAreaInfo::atLand; | |||
514 | MapGenAreaInfo::MapGenTerrainType ttp = MapGenAreaInfo::ttLandLand; | |||
515 | ||||
516 | if (!isDesert) { // see what kind of land it is | |||
517 | ||||
518 | if (numLandAreas == 1) | |||
519 | landAreaIndex = 0; | |||
520 | else if (numLandAreas == 2) { | |||
521 | uint32_t const weight1 = | |||
522 | mapGenInfo.getArea(MapGenAreaInfo::atLand, 0).getWeight(); | |||
523 | uint32_t const weight2 = | |||
524 | mapGenInfo.getArea(MapGenAreaInfo::atLand, 1).getWeight(); | |||
525 | uint32_t const sum = mapGenInfo.getSumLandWeight(); | |||
526 | if | |||
527 | (weight1 * (random2[c0.x + m_mapInfo.w * c0.y] / sum) | |||
528 | >= | |||
529 | weight2 * (AVG_ELEVATION(0x80000000) / sum)) | |||
530 | landAreaIndex = 0; | |||
531 | else | |||
532 | landAreaIndex = 1; | |||
533 | } else { | |||
534 | uint32_t const weight1 = | |||
535 | mapGenInfo.getArea(MapGenAreaInfo::atLand, 0).getWeight(); | |||
536 | uint32_t const weight2 = | |||
537 | mapGenInfo.getArea(MapGenAreaInfo::atLand, 1).getWeight(); | |||
538 | uint32_t const weight3 = | |||
539 | mapGenInfo.getArea(MapGenAreaInfo::atLand, 2).getWeight(); | |||
540 | uint32_t const sum = mapGenInfo.getSumLandWeight(); | |||
541 | uint32_t const randomX = (rand2 + rand3) / 2; | |||
542 | if | |||
543 | (weight1 * (rand2 / sum) > weight2 * (rand3 / sum) && | |||
544 | weight1 * (rand2 / sum) > weight3 * (randomX / sum)) | |||
545 | landAreaIndex = 0; | |||
546 | else if | |||
547 | (weight2 * (rand3 / sum) > weight1 * (rand2 / sum) && | |||
548 | weight2 * (rand3 / sum) > weight3 * (randomX / sum)) | |||
549 | landAreaIndex = 1; | |||
550 | else | |||
551 | landAreaIndex = 2; | |||
552 | } | |||
553 | ||||
554 | atp = MapGenAreaInfo::atLand; | |||
555 | ttp = MapGenAreaInfo::ttLandLand; | |||
556 | } else { | |||
557 | atp = MapGenAreaInfo::atWasteland; | |||
558 | ttp = MapGenAreaInfo::ttWastelandInner; | |||
559 | } | |||
560 | ||||
561 | // see whether it is water | |||
562 | ||||
563 | uint32_t const coast_h = mapGenInfo.getLandCoastHeight(); | |||
564 | if (h1 <= coast_h && h2 <= coast_h && h3 <= coast_h) { // water or coast... | |||
565 | atp = landType; | |||
566 | ttp = MapGenAreaInfo::ttLandCoast; | |||
567 | ||||
568 | uint32_t const ocean_h = mapGenInfo.getWaterOceanHeight(); | |||
569 | uint32_t const shelf_h = mapGenInfo.getWaterShelfHeight(); | |||
570 | uint32_t const shallow_h = mapGenInfo.getWaterShallowHeight(); | |||
571 | ||||
572 | // TODO: The heights can not be lower than water-Shallow -- | |||
573 | // TODO: there will never be an ocean yet | |||
574 | ||||
575 | if (h1 <= ocean_h && h2 <= ocean_h && h3 <= ocean_h) { | |||
576 | atp = MapGenAreaInfo::atWater; | |||
577 | ttp = MapGenAreaInfo::ttWaterOcean; | |||
578 | } else if (h1 <= shelf_h && h2 <= shelf_h && h3 <= shelf_h) { | |||
579 | atp = MapGenAreaInfo::atWater; | |||
580 | ttp = MapGenAreaInfo::ttWaterShelf; | |||
581 | } else if (h1 <= shallow_h && h2 <= shallow_h && h3 <= shallow_h) { | |||
582 | atp = MapGenAreaInfo::atWater; | |||
583 | ttp = MapGenAreaInfo::ttWaterShallow; | |||
584 | } | |||
585 | } else { // it is not water | |||
586 | uint32_t const upper_h = mapGenInfo.getLandUpperHeight (); | |||
587 | uint32_t const foot_h = mapGenInfo.getMountainFootHeight(); | |||
588 | uint32_t const mount_h = mapGenInfo.getMountainHeight (); | |||
589 | uint32_t const snow_h = mapGenInfo.getSnowHeight (); | |||
590 | if (h1 >= snow_h && h2 >= snow_h && h3 >= snow_h) { | |||
591 | atp = MapGenAreaInfo::atMountains; | |||
592 | ttp = MapGenAreaInfo::ttMountainsSnow; | |||
593 | } else if (h1 >= mount_h && h2 >= mount_h && h3 >= mount_h) { | |||
594 | atp = MapGenAreaInfo::atMountains; | |||
595 | ttp = MapGenAreaInfo::ttMountainsMountain; | |||
596 | } else if (h1 >= foot_h && h2 >= foot_h && h3 >= foot_h) { | |||
597 | atp = MapGenAreaInfo::atMountains; | |||
598 | ttp = MapGenAreaInfo::ttMountainsFoot; | |||
599 | } else if (h1 >= upper_h && h2 >= upper_h && h3 >= upper_h) { | |||
600 | atp = MapGenAreaInfo::atLand; | |||
601 | ttp = MapGenAreaInfo::ttLandUpper; | |||
602 | } | |||
603 | } | |||
604 | ||||
605 | // Aftermath for land/Wasteland. | |||
606 | ||||
607 | uint32_t usedLandIndex = landAreaIndex; | |||
608 | if (atp != MapGenAreaInfo::atLand && atp != MapGenAreaInfo::atWasteland) | |||
609 | usedLandIndex = 0; | |||
610 | else if (isDesert) { | |||
611 | atp = MapGenAreaInfo::atWasteland; | |||
612 | ttp = | |||
613 | ttp == MapGenAreaInfo::ttLandCoast || isDesertOuter ? | |||
614 | MapGenAreaInfo::ttWastelandOuter : MapGenAreaInfo::ttWastelandInner; | |||
615 | } | |||
616 | ||||
617 | // Return terrain type | |||
618 | terrType = ttp; | |||
619 | ||||
620 | // Figure out which terrain to use at this point in the map... | |||
621 | return | |||
622 | mapGenInfo.getArea(atp, usedLandIndex).getTerrain | |||
623 | (ttp, | |||
624 | rng.rand() | |||
625 | % | |||
626 | mapGenInfo.getArea(atp, usedLandIndex).getNumTerrains(ttp)); | |||
627 | ||||
628 | } | |||
629 | ||||
630 | ||||
631 | void MapGenerator::create_random_map() | |||
632 | { | |||
633 | // Init random number generator with map number | |||
634 | ||||
635 | // We will use our own random number generator here so we do not influence | |||
636 | // someone else... | |||
637 | RNG rng; | |||
638 | ||||
639 | rng.seed(m_mapInfo.mapNumber); | |||
640 | ||||
641 | // get world generator info | |||
642 | MapGenInfo & mapGenInfo = m_map.world().getMapGenInfo(); | |||
643 | ||||
644 | // Create a "raw" random elevation matrix. | |||
645 | // We will transform this into reasonable elevations and terrains later on. | |||
646 | ||||
647 | scoped_array<uint32_t> elevations | |||
648 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
649 | ||||
650 | // for land stuff | |||
651 | scoped_array<uint32_t> random2 | |||
652 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
653 | scoped_array<uint32_t> random3 | |||
654 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
655 | ||||
656 | // for desert/land | |||
657 | scoped_array<uint32_t> random4 | |||
658 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
659 | ||||
660 | // for resources | |||
661 | scoped_array<uint32_t> random_rsrc_1 | |||
662 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
663 | scoped_array<uint32_t> random_rsrc_2 | |||
664 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
665 | scoped_array<uint32_t> random_rsrc_3 | |||
666 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
667 | scoped_array<uint32_t> random_rsrc_4 | |||
668 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
669 | ||||
670 | // for bobs | |||
671 | scoped_array<scoped_array<uint32_t> > random_bobs | |||
672 | (new scoped_array<uint32_t> [mapGenInfo.getNumBobAreas()]); | |||
673 | ||||
674 | for (size_t ix = 0; ix < mapGenInfo.getNumBobAreas(); ++ix) | |||
| ||||
675 | random_bobs[ix].reset | |||
676 | (generate_random_value_map(m_mapInfo.w, m_mapInfo.h, rng)); | |||
677 | ||||
678 | // Now we have generated a lot of random data!! | |||
679 | // Lets use it !!! | |||
680 | iterate_Map_FCoords(m_map, m_mapInfo, fc)for (Widelands::FCoords fc = (m_map).get_fcoords(Widelands::Coords (0, 0)); fc.y < static_cast<Widelands::Y_Coordinate> (m_mapInfo.h); ++fc.y) for (fc.x = 0; fc.x < static_cast< Widelands::X_Coordinate>(m_mapInfo.w); ++fc.x, ++fc.field) | |||
681 | fc.field->set_height | |||
682 | (make_node_elevation | |||
683 | (static_cast<double>(elevations[fc.x + m_mapInfo.w * fc.y]) | |||
684 | / | |||
685 | static_cast<double>(MAX_ELEVATION(0xffffffff)), | |||
686 | fc)); | |||
687 | ||||
688 | // Now lets set the terrain right according to the heights. | |||
689 | ||||
690 | iterate_Map_FCoords(m_map, m_mapInfo, fc)for (Widelands::FCoords fc = (m_map).get_fcoords(Widelands::Coords (0, 0)); fc.y < static_cast<Widelands::Y_Coordinate> (m_mapInfo.h); ++fc.y) for (fc.x = 0; fc.x < static_cast< Widelands::X_Coordinate>(m_mapInfo.w); ++fc.x, ++fc.field) { | |||
691 | // Calculate coordinates of left and bottom left neighbours of the | |||
692 | // current node. | |||
693 | ||||
694 | // ... Treat "even" and "uneven" row numbers differently | |||
695 | uint32_t const x_dec = fc.y % 2 == 0; | |||
696 | ||||
697 | uint32_t right_x = fc.x + 1; | |||
698 | uint32_t lower_y = fc.y + 1; | |||
699 | uint32_t lower_x = fc.x - x_dec; | |||
700 | uint32_t lower_right_x = fc.x - x_dec + 1; | |||
701 | ||||
702 | if (lower_x > m_mapInfo.w) lower_x += m_mapInfo.w; | |||
703 | if (right_x >= m_mapInfo.w) right_x -= m_mapInfo.w; | |||
704 | if (lower_x >= m_mapInfo.w) lower_x -= m_mapInfo.w; | |||
705 | if (lower_right_x >= m_mapInfo.w) lower_right_x -= m_mapInfo.w; | |||
706 | if (lower_y >= m_mapInfo.h) lower_y -= m_mapInfo.h; | |||
707 | ||||
708 | // get the heights of my neighbour nodes and of my current node | |||
709 | ||||
710 | uint8_t height_x0_y0 = | |||
711 | fc.field ->get_height(); | |||
712 | uint8_t height_x1_y0 = | |||
713 | m_map[Coords(right_x, fc.y)].get_height(); | |||
714 | uint8_t height_x0_y1 = | |||
715 | m_map[Coords(lower_x, lower_y)].get_height(); | |||
716 | uint8_t height_x1_y1 = | |||
717 | m_map[Coords(lower_right_x, lower_y)].get_height(); | |||
718 | ||||
719 | MapGenAreaInfo::MapGenTerrainType terrType; | |||
720 | ||||
721 | fc.field->set_terrain_d | |||
722 | (figure_out_terrain | |||
723 | (random2.get(), random3.get(), random4.get(), | |||
724 | fc, Coords(lower_x, lower_y), Coords(lower_right_x, lower_y), | |||
725 | height_x0_y0, height_x0_y1, height_x1_y1, | |||
726 | rng, terrType)); | |||
727 | ||||
728 | fc.field->set_terrain_r | |||
729 | (figure_out_terrain | |||
730 | (random2.get(), random3.get(), random4.get(), | |||
731 | fc, Coords(right_x, fc.y), Coords(lower_right_x, lower_y), | |||
732 | height_x0_y0, height_x1_y0, height_x1_y1, | |||
733 | rng, terrType)); | |||
734 | ||||
735 | // set resources for this field | |||
736 | generate_resources | |||
737 | (random_rsrc_1.get(), random_rsrc_2.get(), | |||
738 | random_rsrc_3.get(), random_rsrc_4.get(), | |||
739 | fc); | |||
740 | ||||
741 | // set bobs and immovables for this field | |||
742 | generate_bobs(random_bobs.get(), fc, rng, terrType); | |||
743 | } | |||
744 | ||||
745 | // Aftermaths... | |||
746 | m_map.recalc_whole_map(); | |||
747 | ||||
748 | // Care about players and place their start positions | |||
749 | const std::string tribe = m_map.get_scenario_player_tribe(1); | |||
750 | const std::string ai = m_map.get_scenario_player_ai(1); | |||
751 | m_map.set_nrplayers(m_mapInfo.numPlayers); | |||
752 | FindNodeSize functor(FindNodeSize::sizeBig); | |||
753 | Coords playerstart; | |||
754 | ||||
755 | // Build a basic structure how player start positions are placed | |||
756 | uint8_t line[3]; | |||
757 | uint8_t rows = 1, lines = 1; | |||
758 | if (m_mapInfo.numPlayers > 1) { | |||
759 | ++lines; | |||
760 | if (m_mapInfo.numPlayers > 2) { | |||
761 | ++rows; | |||
762 | if (m_mapInfo.numPlayers > 4) { | |||
763 | ++lines; | |||
764 | if (m_mapInfo.numPlayers > 6) { | |||
765 | ++rows; | |||
766 | } | |||
767 | } | |||
768 | } | |||
769 | } | |||
770 | line[0] = line[1] = line[2] = rows; | |||
771 | if (rows * lines > m_mapInfo.numPlayers) { | |||
772 | --line[1]; | |||
773 | if (rows * lines - 1 > m_mapInfo.numPlayers) | |||
774 | --line[2]; | |||
775 | } | |||
776 | ||||
777 | // Random placement of starting positions | |||
778 | Player_Number pn[m_mapInfo.numPlayers]; | |||
| ||||
779 | for (Player_Number n = 1; n <= m_mapInfo.numPlayers; ++n) { | |||
780 | bool okay = false; | |||
781 | // This is a kinda dump algorithm -> we generate a random number and increase it until it fits. | |||
782 | // However it's working and simple ;) - if you've got a better idea, feel free to fix it. | |||
783 | Player_Number x = rng.rand() % m_mapInfo.numPlayers; | |||
784 | while (!okay) { | |||
785 | okay = true; | |||
786 | ++x; // Player_Number begins at 1 not at 0 | |||
787 | for (Player_Number p = 1; p < n; ++p) { | |||
788 | if (pn[p - 1] == x) { | |||
789 | okay = false; | |||
790 | x = x % m_mapInfo.numPlayers; | |||
791 | break; | |||
792 | } | |||
793 | } | |||
794 | } | |||
795 | pn[n - 1] = x; | |||
796 | } | |||
797 | ||||
798 | for (Player_Number n = 1; n <= m_mapInfo.numPlayers; ++n) { | |||
799 | // Set scenario information - needed even if it's not a scenario | |||
800 | m_map.set_scenario_player_name(n, "Random Player"); | |||
801 | m_map.set_scenario_player_tribe(n, tribe); | |||
802 | m_map.set_scenario_player_ai(n, ai); | |||
803 | m_map.set_scenario_player_closeable(n, false); | |||
804 | ||||
805 | // Calculate wished coords for player starting position | |||
806 | if (line[0] + 1 > pn[n - 1]) { | |||
807 | // X-Coordinates | |||
808 | playerstart.x = m_mapInfo.w * (line[0] * line[0] + 1 - pn[n - 1] * pn[n - 1]); | |||
809 | playerstart.x /= line[0] * line[0] + 1; | |||
810 | // Y-Coordinates | |||
811 | if (lines == 1) | |||
812 | playerstart.y = m_mapInfo.h / 2; | |||
813 | else | |||
814 | playerstart.y = m_mapInfo.h / 7 + ISLAND_BORDER10; | |||
815 | } else if (line[0] + line[1] + 1 > pn[n - 1]) { | |||
816 | // X-Coordinates | |||
817 | uint8_t pos = pn[n - 1] - line[0]; | |||
818 | playerstart.x = m_mapInfo.w; | |||
819 | playerstart.x *= line[1] * line[1] + 1 - pos * pos; | |||
820 | playerstart.x /= line[1] * line[1] + 1; | |||
821 | // Y-Coordinates | |||
822 | if (lines == 3) | |||
823 | playerstart.y = m_mapInfo.h / 2; | |||
824 | else | |||
825 | playerstart.y = m_mapInfo.h - m_mapInfo.h / 7 - ISLAND_BORDER10; | |||
826 | } else { | |||
827 | // X-Coordinates | |||
828 | uint8_t pos = pn[n - 1] - line[0] - line[1]; | |||
829 | playerstart.x = m_mapInfo.w; | |||
830 | playerstart.x *= line[2] * line[2] + 1 - pos * pos; | |||
831 | playerstart.x /= line[2] * line[2] + 1; | |||
832 | // Y-Coordinates | |||
833 | playerstart.y = m_mapInfo.h - m_mapInfo.h / 7 - ISLAND_BORDER10; | |||
834 | } | |||
835 | ||||
836 | // Now try to find a place as near as possible to the wished | |||
837 | // starting position | |||
838 | std::vector<Coords> coords; | |||
839 | m_map.find_fields | |||
840 | (Area<FCoords>(m_map.get_fcoords(playerstart), 20), | |||
841 | &coords, functor); | |||
842 | ||||
843 | // Take the nearest ones | |||
844 | uint32_t min_distance = -1; | |||
845 | Coords coords2; | |||
846 | for (uint16_t i = 0; i < coords.size(); ++i) { | |||
847 | uint32_t test = m_map.calc_distance(coords[i], playerstart); | |||
848 | if (test < min_distance) { | |||
849 | min_distance = test; | |||
850 | coords2 = coords[i]; | |||
851 | } | |||
852 | } | |||
853 | ||||
854 | if (coords.empty()) { | |||
855 | // TODO inform players via popup | |||
856 | log("WARNING: Could not find a suitable place for player %u\n", n); | |||
857 | // Let's hope that one is at least on dry ground. | |||
858 | coords2 = playerstart; | |||
859 | } | |||
860 | ||||
861 | // Finally set the found starting position | |||
862 | m_map.set_starting_pos(n, coords2); | |||
863 | } | |||
864 | } | |||
865 | ||||
866 | /** | |||
867 | =============== | |||
868 | Converts a character out of a mapId-String into an integer value. | |||
869 | Valid characters are 'a'-'z' (or 'A'-'Z') and '2'-'9'. 'i' and 'o' | |||
870 | (or 'I' and 'O') are not valid. | |||
871 | The character is treated case-insensitive. | |||
872 | ||||
873 | num: Number to convert | |||
874 | Return value: The resulting number (0-31) or -1 if the character | |||
875 | was no legal character. | |||
876 | =============== | |||
877 | */ | |||
878 | ||||
879 | int UniqueRandomMapInfo::mapIdCharToNumber(char ch) | |||
880 | { | |||
881 | if ((ch == '0') || (ch == 'o') || (ch == 'O')) | |||
882 | return 22; | |||
883 | else if | |||
884 | ((ch == '1') || (ch == 'l') || (ch == 'L') || | |||
885 | (ch == 'I') || (ch == 'i') || (ch == 'J') || (ch == 'j')) | |||
886 | return 23; | |||
887 | else if (ch >= 'A' && ch < 'O') { | |||
888 | char res = ch - 'A'; | |||
889 | if (ch > 'I') | |||
890 | --res; | |||
891 | if (ch > 'J') | |||
892 | --res; | |||
893 | if (ch > 'L') | |||
894 | --res; | |||
895 | if (ch > 'O') | |||
896 | --res; | |||
897 | return res; | |||
898 | } | |||
899 | else if (ch >= 'a' && ch <= 'z') { | |||
900 | char res = ch - 'a'; | |||
901 | if (ch > 'i') | |||
902 | --res; | |||
903 | if (ch > 'j') | |||
904 | --res; | |||
905 | if (ch > 'l') | |||
906 | --res; | |||
907 | if (ch > 'o') | |||
908 | --res; | |||
909 | return res; | |||
910 | } | |||
911 | else if (ch >= '2' && ch <= '9') | |||
912 | return 24 + ch - '2'; | |||
913 | return -1; | |||
914 | } | |||
915 | ||||
916 | /** | |||
917 | =============== | |||
918 | Converts an integer number (0-31) to a characted usable in | |||
919 | a map id string. | |||
920 | ||||
921 | num: Number to convert | |||
922 | Return value: The converted value as a character | |||
923 | =============== | |||
924 | */ | |||
925 | char UniqueRandomMapInfo::mapIdNumberToChar(int32_t const num) | |||
926 | { | |||
927 | if (num == 22) | |||
928 | return '0'; | |||
929 | else if (num == 23) | |||
930 | return '1'; | |||
931 | else if ((0 <= num) && (num < 22)) { | |||
932 | char result = num + 'a'; | |||
933 | if (result >= 'i') | |||
934 | ++result; | |||
935 | if (result >= 'j') | |||
936 | ++result; | |||
937 | if (result >= 'l') | |||
938 | ++result; | |||
939 | if (result >= 'o') | |||
940 | ++result; | |||
941 | return result; | |||
942 | } else if ((24 <= num) && (num < 32)) | |||
943 | return (num - 24) + '2'; | |||
944 | else | |||
945 | return '?'; | |||
946 | } | |||
947 | ||||
948 | /** | |||
949 | =============== | |||
950 | Fills a UniqueRandomMapInfo structure from a given Map-id-string. | |||
951 | ||||
952 | mapIdString: Map-Id-String | |||
953 | mapInfo_out: UniqueRandomMapInfo-Structure to be filled | |||
954 | Return value: true if the map-id-string was valid, false otherwise | |||
955 | =============== | |||
956 | */ | |||
957 | ||||
958 | bool UniqueRandomMapInfo::setFromIdString | |||
959 | (UniqueRandomMapInfo & mapInfo_out, const std::string & mapIdString, | |||
960 | const std::vector<std::string> & worlds) | |||
961 | { | |||
962 | // check string | |||
963 | ||||
964 | if (mapIdString.length() != MAP_ID_DIGITS24 + MAP_ID_DIGITS24 / 4 - 1) | |||
965 | return false; | |||
966 | ||||
967 | for (uint32_t ix = 4; ix < MAP_ID_DIGITS24; ix += 5) | |||
968 | if (mapIdString[ix] != '-') | |||
969 | return false; | |||
970 | ||||
971 | // convert digits to values | |||
972 | ||||
973 | int32_t nums[MAP_ID_DIGITS24]; | |||
974 | ||||
975 | for (uint32_t ix = 0; ix < MAP_ID_DIGITS24; ++ix) { | |||
976 | int const num = mapIdCharToNumber(mapIdString[ix + (ix / 4)]); | |||
977 | if (num < 0) | |||
978 | return false; | |||
979 | nums[ix] = num; | |||
980 | } | |||
981 | ||||
982 | // get xxor start value | |||
983 | ||||
984 | int32_t xorr = nums[MAP_ID_DIGITS24 - 1]; | |||
985 | ||||
986 | for (int32_t ix = MAP_ID_DIGITS24 - 1; ix >= 0; --ix) { | |||
987 | nums[ix] = nums[ix] ^ xorr; | |||
988 | xorr -= 7; | |||
989 | xorr -= ix; | |||
990 | if (xorr < 0) | |||
991 | xorr &= 0x0000001f; | |||
992 | } | |||
993 | ||||
994 | // check if xxor was right | |||
995 | if (nums[MAP_ID_DIGITS24 - 1]) | |||
996 | return false; | |||
997 | ||||
998 | // check if version number is 1 | |||
999 | if (nums[MAP_ID_DIGITS24 - 2] != 1) | |||
1000 | return false; | |||
1001 | ||||
1002 | // check if csm is right | |||
1003 | if (nums[MAP_ID_DIGITS24 - 3] != 0x15) | |||
1004 | return false; | |||
1005 | ||||
1006 | ||||
1007 | // convert map number | |||
1008 | mapInfo_out.mapNumber = | |||
1009 | (nums[0]) | | |||
1010 | (nums[1] << 5) | | |||
1011 | (nums[2] << 10) | | |||
1012 | (nums[3] << 15) | | |||
1013 | (nums[4] << 20) | | |||
1014 | (nums[5] << 25) | | |||
1015 | ((nums[6] & 3) << 30); | |||
1016 | ||||
1017 | // Convert amount of resources | |||
1018 | mapInfo_out.resource_amount = | |||
1019 | static_cast<Widelands::UniqueRandomMapInfo::Resource_Amount> | |||
1020 | ((nums[6] & 0xc) >> 2); | |||
1021 | ||||
1022 | if | |||
1023 | (mapInfo_out.resource_amount > | |||
1024 | Widelands::UniqueRandomMapInfo::raHigh) | |||
1025 | return false; | |||
1026 | ||||
1027 | // Convert map size | |||
1028 | mapInfo_out.w = nums[7] * 16 + 64; | |||
1029 | mapInfo_out.h = nums[8] * 16 + 64; | |||
1030 | ||||
1031 | // Convert water percent | |||
1032 | mapInfo_out.waterRatio = static_cast<double>(nums[9]) / 20.0; | |||
1033 | // Convert land percent | |||
1034 | mapInfo_out.landRatio = static_cast<double>(nums[10]) / 20.0; | |||
1035 | // Convert wasteland percent | |||
1036 | mapInfo_out.wastelandRatio = static_cast<double>(nums[11]) / 20.0; | |||
1037 | // Number of players | |||
1038 | mapInfo_out.numPlayers = nums[12]; | |||
1039 | // Island mode | |||
1040 | mapInfo_out.islandMode = (nums[13] == 1) ? true : false; | |||
1041 | ||||
1042 | // World name hash | |||
1043 | uint16_t nameHash = nums[14]; | |||
1044 | nameHash |= nums[15] << 5; | |||
1045 | nameHash |= nums[16] << 10; | |||
1046 | nameHash |= nums[17] << 15; | |||
1047 | ||||
1048 | for (size_t idx = 0; idx<worlds.size(); idx++) | |||
1049 | if (generateWorldNameHash(worlds[idx]) == nameHash) { | |||
1050 | mapInfo_out.worldName = worlds[idx]; | |||
1051 | return true; | |||
1052 | } | |||
1053 | ||||
1054 | return false; // No valid world name found | |||
1055 | } | |||
1056 | ||||
1057 | /** | |||
1058 | =============== | |||
1059 | Generates an ID-String for map generation. | |||
1060 | The ID-String is an encoded version of the | |||
1061 | information in a UniqueMapInfo structure. | |||
1062 | Thus, the ID_String will contain all info | |||
1063 | necessary to re-create a given random map. | |||
1064 | ||||
1065 | mapIdsString_out: Output buffer for the resulting | |||
1066 | Map-ID-String | |||
1067 | mapInfo: Information about the random map currently | |||
1068 | begin created (map specific info) | |||
1069 | =============== | |||
1070 | */ | |||
1071 | void UniqueRandomMapInfo::generateIdString | |||
1072 | (std::string & mapIdsString_out, const UniqueRandomMapInfo & mapInfo) | |||
1073 | { | |||
1074 | // Init | |||
1075 | assert(mapInfo.w <= 560)((mapInfo.w <= 560) ? static_cast<void> (0) : __assert_fail ("mapInfo.w <= 560", "/home/arch/widelands/src/map_generator.cc" , 1075, __PRETTY_FUNCTION__)); | |||
1076 | assert(mapInfo.h <= 560)((mapInfo.h <= 560) ? static_cast<void> (0) : __assert_fail ("mapInfo.h <= 560", "/home/arch/widelands/src/map_generator.cc" , 1076, __PRETTY_FUNCTION__)); | |||
1077 | assert(mapInfo.waterRatio >= 0.0)((mapInfo.waterRatio >= 0.0) ? static_cast<void> (0) : __assert_fail ("mapInfo.waterRatio >= 0.0", "/home/arch/widelands/src/map_generator.cc" , 1077, __PRETTY_FUNCTION__)); | |||
1078 | assert(mapInfo.waterRatio <= 1.0)((mapInfo.waterRatio <= 1.0) ? static_cast<void> (0) : __assert_fail ("mapInfo.waterRatio <= 1.0", "/home/arch/widelands/src/map_generator.cc" , 1078, __PRETTY_FUNCTION__)); | |||
1079 | assert(mapInfo.landRatio >= 0.0)((mapInfo.landRatio >= 0.0) ? static_cast<void> (0) : __assert_fail ("mapInfo.landRatio >= 0.0", "/home/arch/widelands/src/map_generator.cc" , 1079, __PRETTY_FUNCTION__)); | |||
1080 | assert(mapInfo.landRatio <= 1.0)((mapInfo.landRatio <= 1.0) ? static_cast<void> (0) : __assert_fail ("mapInfo.landRatio <= 1.0", "/home/arch/widelands/src/map_generator.cc" , 1080, __PRETTY_FUNCTION__)); | |||
1081 | assert(mapInfo.wastelandRatio >= 0.0)((mapInfo.wastelandRatio >= 0.0) ? static_cast<void> (0) : __assert_fail ("mapInfo.wastelandRatio >= 0.0", "/home/arch/widelands/src/map_generator.cc" , 1081, __PRETTY_FUNCTION__)); | |||
1082 | assert(mapInfo.wastelandRatio <= 1.0)((mapInfo.wastelandRatio <= 1.0) ? static_cast<void> (0) : __assert_fail ("mapInfo.wastelandRatio <= 1.0", "/home/arch/widelands/src/map_generator.cc" , 1082, __PRETTY_FUNCTION__)); | |||
1083 | assert(mapInfo.resource_amount <= Widelands::UniqueRandomMapInfo::raHigh)((mapInfo.resource_amount <= Widelands::UniqueRandomMapInfo ::raHigh) ? static_cast<void> (0) : __assert_fail ("mapInfo.resource_amount <= Widelands::UniqueRandomMapInfo::raHigh" , "/home/arch/widelands/src/map_generator.cc", 1083, __PRETTY_FUNCTION__ )); | |||
1084 | ||||
1085 | mapIdsString_out = ""; | |||
1086 | int32_t nums[MAP_ID_DIGITS24]; | |||
1087 | for (uint32_t ix = 0; ix < MAP_ID_DIGITS24; ++ix) | |||
1088 | nums[ix] = 0; | |||
1089 | ||||
1090 | // Generate world name hash | |||
1091 | uint16_t nameHash = generateWorldNameHash(mapInfo.worldName); | |||
1092 | ||||
1093 | // Convert map random number | |||
1094 | nums [0] = mapInfo.mapNumber & 31; | |||
1095 | nums [1] = (mapInfo.mapNumber >> 5) & 31; | |||
1096 | nums [2] = (mapInfo.mapNumber >> 10) & 31; | |||
1097 | nums [3] = (mapInfo.mapNumber >> 15) & 31; | |||
1098 | ||||
1099 | nums [4] = (mapInfo.mapNumber >> 20) & 31; | |||
1100 | nums [5] = (mapInfo.mapNumber >> 25) & 31; | |||
1101 | nums [6] = (mapInfo.mapNumber >> 30) & 3; | |||
1102 | ||||
1103 | // Convert amount of resources | |||
1104 | nums [6] |= (mapInfo.resource_amount & 3) << 2; | |||
1105 | // Convert width | |||
1106 | nums [7] = (mapInfo.w - 64) / 16; | |||
1107 | ||||
1108 | ||||
1109 | // Convert height | |||
1110 | nums [8] = (mapInfo.h - 64) / 16; | |||
1111 | // Convert water percent | |||
1112 | nums [9] = (mapInfo.waterRatio + 0.025) * 20.0; | |||
1113 | // Convert land percent | |||
1114 | nums[10] = (mapInfo.landRatio + 0.025) * 20.0; | |||
1115 | // Convert wasteland percent | |||
1116 | nums[11] = (mapInfo.wastelandRatio + 0.025) * 20.0; | |||
1117 | ||||
1118 | // Set number of islands | |||
1119 | nums[12] = mapInfo.numPlayers; | |||
1120 | // Island mode | |||
1121 | nums[13] = mapInfo.islandMode ? 1 : 0; | |||
1122 | // World name hash (16 bit) | |||
1123 | nums[14] = nameHash & 31; | |||
1124 | nums[15] = (nameHash >> 5) & 31; | |||
1125 | ||||
1126 | nums[16] = (nameHash >> 10) & 31; | |||
1127 | nums[17] = (nameHash >> 15) & 1; | |||
1128 | ||||
1129 | // Set id csm | |||
1130 | nums[MAP_ID_DIGITS24 - 3] = 0x15; | |||
1131 | // Set id version number | |||
1132 | nums[MAP_ID_DIGITS24 - 2] = 0x01; | |||
1133 | // Last number intentionally left blank | |||
1134 | nums[MAP_ID_DIGITS24 - 1] = 0x00; | |||
1135 | ||||
1136 | ||||
1137 | // Nox xor everything | |||
1138 | // This lets it look better | |||
1139 | // Every change in a digit will result in a complete id change | |||
1140 | ||||
1141 | int32_t xorr = 0x0a; | |||
1142 | for (uint32_t ix = 0; ix < MAP_ID_DIGITS24; ++ix) | |||
1143 | xorr = xorr ^ nums[ix]; | |||
1144 | ||||
1145 | for (int32_t ix = MAP_ID_DIGITS24 - 1; ix >= 0; --ix) { | |||
1146 | nums[ix] = nums[ix] ^ xorr; | |||
1147 | xorr -= 7; | |||
1148 | xorr -= ix; | |||
1149 | if (xorr < 0) | |||
1150 | xorr &= 0x0000001f; | |||
1151 | } | |||
1152 | ||||
1153 | // translate it to ASCII | |||
1154 | for (uint32_t ix = 0; ix < MAP_ID_DIGITS24; ++ix) { | |||
1155 | mapIdsString_out += mapIdNumberToChar(nums[ix]); | |||
1156 | if (ix % 4 == 3 && ix != MAP_ID_DIGITS24 - 1) | |||
1157 | mapIdsString_out += "-"; | |||
1158 | } | |||
1159 | } | |||
1160 | ||||
1161 | ||||
1162 | uint16_t Widelands::UniqueRandomMapInfo::generateWorldNameHash | |||
1163 | (const std::string & name) | |||
1164 | { | |||
1165 | // This is only a simple digest algorithm. Thats enough for our purposes. | |||
1166 | ||||
1167 | uint16_t hash = 0xa5a5; | |||
1168 | int32_t posInHash = 0; | |||
1169 | ||||
1170 | for (size_t idx = 0; idx<name.size(); idx++) { | |||
1171 | hash ^= static_cast<uint8_t>(name[idx] & 0xff) << posInHash; | |||
1172 | posInHash ^= 8; | |||
1173 | } | |||
1174 | ||||
1175 | hash ^= (name.size() & 0xff) << 4; | |||
1176 | ||||
1177 | return hash; | |||
1178 | } | |||
1179 | ||||
1180 | // TODO: Also take mountain and water areas into bob generation | |||
1181 | // (we have ducks and chamois) | |||
1182 | // TODO: Move other map generation functions from Map to MapGenerator | |||
1183 | // TODO: Define the "none"-bob to weigh other bobs lower within BobKinds... | |||
1184 | // TODO: Clean up code | |||
1185 | // TODO: Improve mapgenconf files for nicer generated worlds | |||
1186 | // TODO: MapGen: Bob generation, configurable in mapgenconf | |||
1187 | // TODO: MapGen: How to handle "Bob layers" ??? | |||
1188 | // TODO: MapGen: Resource generation, configurable in mapgenconf | |||
1189 | // TODO: MapGen: Check out sample map | |||
1190 | // TODO: MapGen: Generate Start positions | |||
1191 | // TODO: MapGen: How to handle height profile in make_blah... | |||
1192 | // TODO: MapGen: Display something else than | |||
1193 | // TODO: "Preparing..." when generating map... | |||
1194 | // TODO: MapGen: Allow up to 3 different water areas | |||
1195 | ||||
1196 | }; |