1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - 2004 The Freeciv Project Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
18 #include <math.h> /* sqrt, HUGE_VAL */
32 /* server/generator */
33 #include "mapgen_topology.h"
34 #include "mapgen_utils.h"
36 #include "temperature_map.h"
38 struct islands_data_type
{
45 static struct islands_data_type
*islands
;
46 static int *islands_index
;
48 /****************************************************************************
49 Return an approximation of the goodness of a tile to a civilization.
50 ****************************************************************************/
51 static int get_tile_value(struct tile
*ptile
)
57 struct extra_type
*nextra
;
59 /* Give one point for each food / shield / trade produced. */
61 output_type_iterate(o
) {
62 value
+= city_tile_output(NULL
, ptile
, FALSE
, o
);
63 } output_type_iterate_end
;
65 roaded
= tile_virtual_new(ptile
);
67 if (num_role_units(L_SETTLERS
) > 0) {
68 struct unit_type
*start_worker
= get_role_unit(L_SETTLERS
, 0);
70 extra_type_by_cause_iterate(EC_ROAD
, pextra
) {
71 struct road_type
*proad
= extra_road_get(pextra
);
73 if (road_can_be_built(proad
, roaded
)
74 && are_reqs_active(NULL
, NULL
, NULL
, NULL
, roaded
,
75 NULL
, start_worker
, NULL
, NULL
, NULL
,
76 &pextra
->reqs
, RPT_CERTAIN
)) {
77 tile_add_extra(roaded
, pextra
);
79 } extra_type_by_cause_iterate_end
;
82 nextra
= next_extra_for_tile(roaded
, EC_IRRIGATION
, NULL
, NULL
);
87 vtile
= tile_virtual_new(roaded
);
88 tile_apply_activity(vtile
, ACTIVITY_IRRIGATE
, nextra
);
90 output_type_iterate(o
) {
91 irrig_bonus
+= city_tile_output(NULL
, vtile
, FALSE
, o
);
92 } output_type_iterate_end
;
93 tile_virtual_destroy(vtile
);
96 nextra
= next_extra_for_tile(roaded
, EC_MINE
, NULL
, NULL
);
98 /* Same set of roads used with mine as with irrigation. */
102 vtile
= tile_virtual_new(roaded
);
103 tile_apply_activity(vtile
, ACTIVITY_MINE
, nextra
);
105 output_type_iterate(o
) {
106 mine_bonus
+= city_tile_output(NULL
, vtile
, FALSE
, o
);
107 } output_type_iterate_end
;
108 tile_virtual_destroy(vtile
);
111 tile_virtual_destroy(roaded
);
113 value
+= MAX(0, MAX(mine_bonus
, irrig_bonus
)) / 2;
118 struct start_filter_data
{
120 struct unit_type
*initial_unit
;
124 /****************************************************************************
125 Check if number of reachable native tiles is sufficient.
126 Initially given tile is assumed to be native (not checked by this function)
127 ****************************************************************************/
128 static bool check_native_area(const struct unit_type
*utype
,
129 const struct tile
*ptile
,
132 int tiles
= 1; /* There's the central tile already. */
133 struct tile_list
*tlist
= tile_list_new();
134 struct tile
*central
= tile_virtual_new(ptile
); /* Non-const virtual tile */
137 dbv_init(&handled
, MAP_INDEX_SIZE
);
139 tile_list_append(tlist
, central
);
141 while (tile_list_size(tlist
) > 0 && tiles
< min_area
) {
142 tile_list_iterate(tlist
, ptile2
) {
143 adjc_iterate(&(wld
.map
), ptile2
, ptile3
) {
144 int idx
= tile_index(ptile3
);
146 if (!dbv_isset(&handled
, idx
) && is_native_tile(utype
, ptile3
)) {
148 tile_list_append(tlist
, ptile3
);
149 dbv_set(&handled
, idx
);
150 if (tiles
>= min_area
) {
151 /* Break out when we already know that area is sufficient. */
157 tile_list_remove(tlist
, ptile2
);
159 if (tiles
>= min_area
) {
160 /* Break out when we already know that area is sufficient. */
163 } tile_list_iterate_end
;
166 tile_list_destroy(tlist
);
170 tile_virtual_destroy(central
);
172 return tiles
>= min_area
;
175 /****************************************************************************
176 Return TRUE if (x,y) is a good starting position.
179 - Islands with no room.
180 - Non-suitable terrain;
182 - Too close to another starter on the same continent:
183 'dist' is too close (real_map_distance)
184 'nr' is the number of other start positions to check for too closeness.
185 ****************************************************************************/
186 static bool is_valid_start_pos(const struct tile
*ptile
, const void *dataptr
)
188 const struct start_filter_data
*pdata
= dataptr
;
189 struct islands_data_type
*island
;
190 int cont_size
, cont
= tile_continent(ptile
);
192 /* Only start on certain terrain types. */
193 if (pdata
->value
[tile_index(ptile
)] < pdata
->min_value
) {
197 fc_assert_ret_val(cont
> 0, FALSE
);
198 if (islands
[islands_index
[cont
]].starters
== 0) {
202 /* Don't start on a hut. */
203 if (tile_has_cause_extra(ptile
, EC_HUT
)) {
207 /* Has to be native tile for initial unit */
208 if (!is_native_tile(pdata
->initial_unit
, ptile
)) {
212 /* Check native area size. */
213 if (!check_native_area(pdata
->initial_unit
, ptile
,
214 terrain_control
.min_start_native_area
)) {
218 if (game
.server
.start_city
&& terrain_has_flag(tile_terrain(ptile
), TER_NO_CITIES
)) {
222 /* A longstanding bug allowed starting positions to exist on poles,
223 * sometimes. This hack prevents it by setting a fixed distance from
224 * the pole (dependent on map temperature) that a start pos must be.
225 * Cold and frozen tiles are not allowed for start pos placement. */
226 if (tmap_is(ptile
, TT_NHOT
)) {
230 /* Don't start too close to someone else. */
231 cont_size
= get_continent_size(cont
);
232 island
= islands
+ islands_index
[cont
];
233 map_startpos_iterate(psp
) {
234 struct tile
*tile1
= startpos_tile(psp
);
236 if ((tile_continent(ptile
) == tile_continent(tile1
)
237 && (real_map_distance(ptile
, tile1
) * 1000 / pdata
->min_value
238 <= (sqrt(cont_size
/ island
->total
))))
239 || (real_map_distance(ptile
, tile1
) * 1000 / pdata
->min_value
< 5)) {
242 } map_startpos_iterate_end
;
246 /*************************************************************************
247 help function for qsort
248 *************************************************************************/
249 static int compare_islands(const void *A_
, const void *B_
)
251 const struct islands_data_type
*A
= A_
, *B
= B_
;
253 return B
->goodies
- A
->goodies
;
256 /****************************************************************************
257 Initialize islands data.
258 ****************************************************************************/
259 static void initialize_isle_data(void)
263 islands
= fc_malloc((wld
.map
.num_continents
+ 1) * sizeof(*islands
));
264 islands_index
= fc_malloc((wld
.map
.num_continents
+ 1)
265 * sizeof(*islands_index
));
267 /* islands[0] is unused. */
268 for (nr
= 1; nr
<= wld
.map
.num_continents
; nr
++) {
270 islands
[nr
].size
= get_continent_size(nr
);
271 islands
[nr
].goodies
= 0;
272 islands
[nr
].starters
= 0;
273 islands
[nr
].total
= 0;
277 /****************************************************************************
278 A function that filters for TER_STARTER tiles.
279 ****************************************************************************/
280 static bool filter_starters(const struct tile
*ptile
, const void *data
)
282 return terrain_has_flag(tile_terrain(ptile
), TER_STARTER
);
285 /****************************************************************************
286 where do the different nations start on the map? well this function tries
287 to spread them out on the different islands.
289 MAPSTARTPOS_SINGLE: one player per isle.
290 MAPSTARTPOS_2or3: 2 players per isle (maybe one isle with 3).
291 MAPSTARTPOS_ALL: all players in asingle isle.
292 MAPSTARTPOS_VARIABLE: at least 2 player per isle.
294 Assumes assign_continent_numbers() has already been done!
295 Returns true on success
296 ****************************************************************************/
297 bool create_start_positions(enum map_startpos mode
,
298 struct unit_type
*initial_unit
)
302 struct start_filter_data data
;
303 int *tile_value_aux
= NULL
;
304 int *tile_value
= NULL
;
305 int min_goodies_per_player
= 1500;
306 int total_goodies
= 0;
307 /* this is factor is used to maximize land used in extreme little maps */
308 float efactor
= player_count() / map_size_checked() / 4;
309 bool failure
= FALSE
;
310 bool is_tmap
= temperature_is_initialized();
312 if (wld
.map
.num_continents
< 1) {
313 /* Currently we can only place starters on land terrain, so fail
314 * immediately if there isn't any on the map. */
315 log_verbose("Map has no land, so cannot assign start positions!");
320 /* The temperature map has already been destroyed by the time start
321 * positions have been placed. We check for this and then create a
322 * false temperature map. This is used in the tmap_is() call above.
323 * We don't create a "real" map here because that requires the height
324 * map and other information which has already been destroyed. */
328 /* If the default is given, just use MAPSTARTPOS_VARIABLE. */
329 if (MAPSTARTPOS_DEFAULT
== mode
) {
330 log_verbose("Using startpos=VARIABLE");
331 mode
= MAPSTARTPOS_VARIABLE
;
334 tile_value_aux
= fc_calloc(MAP_INDEX_SIZE
, sizeof(*tile_value_aux
));
335 tile_value
= fc_calloc(MAP_INDEX_SIZE
, sizeof(*tile_value
));
337 /* get the tile value */
338 whole_map_iterate(&(wld
.map
), value_tile
) {
339 tile_value_aux
[tile_index(value_tile
)] = get_tile_value(value_tile
);
340 } whole_map_iterate_end
;
342 /* select the best tiles */
343 whole_map_iterate(&(wld
.map
), value_tile
) {
344 int this_tile_value
= tile_value_aux
[tile_index(value_tile
)];
345 int lcount
= 0, bcount
= 0;
347 /* check all tiles within the default city radius */
348 city_tile_iterate(CITY_MAP_DEFAULT_RADIUS_SQ
, value_tile
, ptile1
) {
349 if (this_tile_value
> tile_value_aux
[tile_index(ptile1
)]) {
351 } else if (this_tile_value
< tile_value_aux
[tile_index(ptile1
)]) {
354 } city_tile_iterate_end
;
356 if (lcount
<= bcount
) {
359 tile_value
[tile_index(value_tile
)] = 100 * this_tile_value
;
360 } whole_map_iterate_end
;
361 /* get an average value */
362 smooth_int_map(tile_value
, TRUE
);
364 initialize_isle_data();
366 /* Only consider tiles marked as 'starter terrains' by ruleset */
367 whole_map_iterate(&(wld
.map
), starter_tile
) {
368 if (!filter_starters(starter_tile
, NULL
)) {
369 tile_value
[tile_index(starter_tile
)] = 0;
371 /* Oceanic terrain cannot be starter terrain currently */
372 fc_assert_action(tile_continent(starter_tile
) > 0, continue);
373 islands
[tile_continent(starter_tile
)].goodies
+= tile_value
[tile_index(starter_tile
)];
374 total_goodies
+= tile_value
[tile_index(starter_tile
)];
376 } whole_map_iterate_end
;
378 /* evaluate the best places on the map */
379 adjust_int_map_filtered(tile_value
, 1000, NULL
, filter_starters
);
381 /* Sort the islands so the best ones come first. Note that islands[0] is
382 * unused so we just skip it. */
383 qsort(islands
+ 1, wld
.map
.num_continents
,
384 sizeof(*islands
), compare_islands
);
386 /* If we can't place starters according to the first choice, change the
388 if (MAPSTARTPOS_SINGLE
== mode
389 && wld
.map
.num_continents
< player_count() + 3) {
390 log_verbose("Not enough continents; falling back to startpos=2or3");
391 mode
= MAPSTARTPOS_2or3
;
394 if (MAPSTARTPOS_2or3
== mode
395 && wld
.map
.num_continents
< player_count() / 2 + 4) {
396 log_verbose("Not enough continents; falling back to startpos=VARIABLE");
397 mode
= MAPSTARTPOS_VARIABLE
;
400 if (MAPSTARTPOS_ALL
== mode
401 && (islands
[1].goodies
< player_count() * min_goodies_per_player
402 || islands
[1].goodies
< total_goodies
* (0.5 + 0.8 * efactor
)
404 log_verbose("No good enough island; falling back to startpos=VARIABLE");
405 mode
= MAPSTARTPOS_VARIABLE
;
408 /* the variable way is the last possibility */
409 if (MAPSTARTPOS_VARIABLE
== mode
) {
410 min_goodies_per_player
= total_goodies
* (0.65 + 0.8 * efactor
)
411 / (1 + efactor
) / player_count();
415 int nr
, to_place
= player_count(), first
= 1;
417 /* inizialize islands_index */
418 for (nr
= 1; nr
<= wld
.map
.num_continents
; nr
++) {
419 islands_index
[islands
[nr
].id
] = nr
;
422 /* When placing a fixed number of players per island, for fairness, try
423 * to avoid sets of populated islands where there is more than 10%
424 * variation in "goodness" within the entire set. (Fallback if that fails:
425 * place players on the worst available islands.) */
426 if (MAPSTARTPOS_SINGLE
== mode
|| MAPSTARTPOS_2or3
== mode
) {
427 float var_goodies
, best
= HUGE_VAL
;
428 int num_islands
= (MAPSTARTPOS_SINGLE
== mode
429 ? player_count() : player_count() / 2);
431 for (nr
= 1; nr
<= 1 + wld
.map
.num_continents
- num_islands
; nr
++) {
432 if (islands
[nr
+ num_islands
- 1].goodies
< min_goodies_per_player
) {
436 = (islands
[nr
].goodies
- islands
[nr
+ num_islands
- 1].goodies
)
437 / (islands
[nr
+ num_islands
- 1].goodies
);
439 if (var_goodies
< best
* 0.9) {
446 /* set starters per isle */
447 if (MAPSTARTPOS_ALL
== mode
) {
448 islands
[1].starters
= to_place
;
449 islands
[1].total
= to_place
;
452 for (nr
= 1; nr
<= wld
.map
.num_continents
; nr
++) {
453 if (MAPSTARTPOS_SINGLE
== mode
&& 0 < to_place
&& nr
>= first
) {
454 islands
[nr
].starters
= 1;
455 islands
[nr
].total
= 1;
458 if (MAPSTARTPOS_2or3
== mode
&& 0 < to_place
&& nr
>= first
) {
459 islands
[nr
].starters
= 2 + (nr
== 1 ? (player_count() % 2) : 0);
460 to_place
-= islands
[nr
].total
= islands
[nr
].starters
;
463 if (MAPSTARTPOS_VARIABLE
== mode
&& 0 < to_place
) {
464 islands
[nr
].starters
= MAX(1, islands
[nr
].goodies
465 / MAX(1, min_goodies_per_player
));
466 to_place
-= islands
[nr
].total
= islands
[nr
].starters
;
471 data
.value
= tile_value
;
472 data
.min_value
= 900;
473 data
.initial_unit
= initial_unit
;
475 for (k
= 1; k
<= wld
.map
.num_continents
; k
++) {
476 sum
+= islands
[islands_index
[k
]].starters
;
477 if (islands
[islands_index
[k
]].starters
!= 0) {
478 log_verbose("starters on isle %i", k
);
481 fc_assert_ret_val(player_count() <= sum
, FALSE
);
483 /* now search for the best place and set start_positions */
484 while (map_startpos_count() < player_count()) {
485 if ((ptile
= rand_map_pos_filtered(&(wld
.map
), &data
,
486 is_valid_start_pos
))) {
487 islands
[islands_index
[(int) tile_continent(ptile
)]].starters
--;
488 log_debug("Adding (%d, %d) as starting position %d, %d goodies on "
489 "islands.", TILE_XY(ptile
), map_startpos_count(),
490 islands
[islands_index
[(int) tile_continent(ptile
)]].goodies
);
491 (void) map_startpos_new(ptile
);
493 data
.min_value
*= 0.95;
494 if (data
.min_value
<= 10) {
495 log_normal(_("The server appears to have gotten into an infinite "
496 "loop in the allocation of starting positions.\nMaybe "
497 "the number of players is too high for this map."));
507 islands_index
= NULL
;
513 FC_FREE(tile_value_aux
);