[Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain pl
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: |
undisclosed-recipients: ; |
Subject: |
[Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles. |
From: |
"Marcelo Burda" <mburda@xxxxxxxxx> |
Date: |
Sun, 6 Jun 2004 05:29:18 -0700 |
Reply-to: |
rt@xxxxxxxxxxx |
<URL: http://rt.freeciv.org/Ticket/Display.html?id=8624 >
Oops! i forget the patch :-)
diff -ruN -Xfreeciv/diff_ignore freeciv/common/map.c freeciv_/common/map.c
--- freeciv/common/map.c 2004-05-27 04:06:52.000000000 +0200
+++ freeciv_/common/map.c 2004-06-06 13:57:08.725403304 +0200
@@ -192,6 +192,7 @@
map.generator = MAP_DEFAULT_GENERATOR;
map.tinyisles = MAP_DEFAULT_TINYISLES;
map.separatepoles = MAP_DEFAULT_SEPARATE_POLES;
+ map.alltemperate = MAP_DEFAULT_ALLTEMPERATE;
map.tiles = NULL;
map.num_continents = 0;
map.num_start_positions = 0;
@@ -1546,3 +1547,23 @@
map_distance_vector(&diff_x, &diff_y, start_x, start_y, end_x, end_y);
return (diff_x == 0) || (diff_y == 0);
}
+
+/****************************************************************************
+ A "SINGULAR position" is any map position that have a anormal number of
+ tiles in the radius of dist
+ arg need to be normalized_map_pos
+****************************************************************************/
+bool is_singular_map_pos(int map_x, int map_y, int dist)
+{
+ CHECK_MAP_POS(map_x, map_y);
+ do_in_natural_pos(ntl_x, ntl_y, map_x, map_y) {
+ /* in natural coordinates dist from borders are / 2 */
+ dist *= topo_has_flag(TF_ISO) ? 2 : 1;
+
+ return (!topo_has_flag(TF_WRAPX)
+ && (ntl_x < dist || ntl_x >= NATURAL_WIDTH - dist))
+ ||
+ (!topo_has_flag(TF_WRAPY)
+ && (ntl_y < dist || ntl_y >= NATURAL_HEIGHT - dist));
+ } do_in_natural_pos_end;
+}
diff -ruN -Xfreeciv/diff_ignore freeciv/common/map.h freeciv_/common/map.h
--- freeciv/common/map.h 2004-05-27 04:06:52.000000000 +0200
+++ freeciv_/common/map.h 2004-06-06 13:57:08.727403000 +0200
@@ -131,6 +131,7 @@
int generator;
bool tinyisles;
bool separatepoles;
+ bool alltemperate;
int num_start_positions;
bool have_specials;
bool have_huts;
@@ -311,7 +312,7 @@
bool contains_special(enum tile_special_type all,
enum tile_special_type to_test_for);
-
+bool is_singular_map_pos(int map_x, int map_y, int dist);
bool normalize_map_pos(int *x, int *y);
void nearest_real_pos(int *x, int *y);
void map_distance_vector(int *dx, int *dy, int x0, int y0, int x1, int y1);
@@ -697,6 +698,10 @@
#define MAP_MIN_SEPARATE_POLES FALSE
#define MAP_MAX_SEPARATE_POLES TRUE
+#define MAP_DEFAULT_ALLTEMPERATE FALSE
+#define MAP_MIN_ALLTEMPERATE FALSE
+#define MAP_MAX_ALLTEMPERATE TRUE
+
/*
* Inline function definitions. These are at the bottom because they may use
@@ -733,4 +738,5 @@
} do_in_native_pos_end;
}
+
#endif /* FC__MAP_H */
diff -ruN -Xfreeciv/diff_ignore freeciv/common/terrain.h
freeciv_/common/terrain.h
--- freeciv/common/terrain.h 2004-05-19 02:49:31.000000000 +0200
+++ freeciv_/common/terrain.h 2004-06-06 13:57:08.728402848 +0200
@@ -64,7 +64,8 @@
enum tile_terrain_type {
T_ARCTIC, T_DESERT, T_FOREST, T_GRASSLAND, T_HILLS, T_JUNGLE,
T_MOUNTAINS, T_OCEAN, T_PLAINS, T_UNUSED, T_SWAMP, T_TUNDRA, T_UNKNOWN,
- T_LAST
+ T_LAST, /* last terrain type */
+ T_ANY /* used as arg for search for any terrain type */
};
#define T_FIRST (T_ARCTIC)
#define T_COUNT (T_UNKNOWN)
diff -ruN -Xfreeciv/diff_ignore
freeciv/doc/Who_to_create_the_temperature_math.txt
freeciv_/doc/Who_to_create_the_temperature_math.txt
--- freeciv/doc/Who_to_create_the_temperature_math.txt 1970-01-01
01:00:00.000000000 +0100
+++ freeciv_/doc/Who_to_create_the_temperature_math.txt 2004-06-06
14:03:18.409202824 +0200
@@ -0,0 +1,87 @@
+After the map is folded we get a rectangular base map with Cartesian
coordinates are (x, y)
+x and y are Real Range [0, 1].
+
+for a torus topology we choice to place one pole in coordinates (0,0) on these
map, and the other in coordinates (1,1).
+
+the pole are circles of radius 0 the equation of this are
+1)x² + y² = 0
+
+for the second pole the equation are
+2)(x - 1)² + (y - 1)² = 0
+
+We want a function evolution from a pole to a another. this be easy made by
+
+ (x² + y² )
+3)f (x,y) = ---------------------
+ ((x - 1)² + (y - 1)²)
+
+f(x,y) is a Real Range function [0, infinity] it reach 0 value in (0,0) pole
and infinity one for the (1,1) pole.
+
+a isotherm line can be define by
+4)G : f(x,y) = g where g is a Real positive constant.
+
+Solve the equation:
+5)f(x,0) = g
+allow to find the maximal value of x for the isotherm
+ g - sqrt(-g (g - 2))
+6) x(g) = ----------------------
+ -1 + g
+
+Solve the equation:
+6)f(x,y) = g
+and the equation of the isotherm G :
+ g -sqrt (-g² - x² + 2 g - 2 g x + 2 g x² + 2 g² x -
g ² x² )
+7)G : y(x,g)
-------------------------------------------------------------------
+ -1 + g
+
+then the area on the map covered by the isotherm G is
+
+
+ / x(g)
+ |
+ 8) A = | y(x, g) dx
+ |
+ / 0
+
+to calculate it we need some work.
+
+if we see the isotherm pasing equator (1,0)
+9) g(equator) = f(1,0) = 1
+ we verify this is the spected equator solvin
+
+9)f(x,y) = 1
+
+we find the equation of the diagonal of the map
+10)y(x, 1) = 1 - x
+
+then to calculate A we can assume g is only in the Real Range (0 .. 1)
+
+and we get
+
+
+
+ / sqrt(2 g)
sqrt(2 (2 - g)) \
+ - g |arcsin(------------) + sqrt(2 - g) g -
arcsin(-------------------) - g|
+ \ 2
2 /
+ 11) A (g)=
---------------------------------------------------------------------------
+ (g - 1)²
+
+
+we can see at equator A(1) = 0.5
+
+we can create a function going from 0 to 1 from (0,0) pole to equator fomr
equation 11 and 3
+
+12)temperature(x,y) =2A(f(x,y))
+
+the for equator to pole (1,1) we need to fold the map over diagonal y(x, 1) =
1 - x.
+
+For optimize the code we can develop A as a truncated Taylor serie and
factorize
+
+13)2* A(z) := -0.4621961892*(z + 0.06387781764)*z*(z - 2.148260011)*(z*z -
1.658803738*z + 2.440718605)
+
+where i was substitute the g var by
+14) z(x,y)=sqrt(g)=sqrt(f(x,y))
+
+under this form 2*A(z(x,y)) has a error less than 1%
+
+M.Burda
\ No newline at end of file
diff -ruN -Xfreeciv/diff_ignore freeciv/server/mapgen.c freeciv_/server/mapgen.c
--- freeciv/server/mapgen.c 2004-05-31 16:11:16.000000000 +0200
+++ freeciv_/server/mapgen.c 2004-06-06 14:11:53.282930120 +0200
@@ -63,6 +63,8 @@
static void mapgenerator5(void);
static void smooth_map(void);
static void adjust_map(void);
+static void normalize_hmap_poles(void);
+static void renormalize_hmap_poles(void);
static void adjust_terrain_param(void);
#define RIVERS_MAXTRIES 32767
@@ -77,7 +79,6 @@
static int *height_map;
static int maxval=0;
static int forests=0;
-static bool has_poles;
struct isledata {
int goodies;
@@ -87,6 +88,191 @@
/* this is used for generator>1 */
#define MAP_NCONT 255
+/* this is the maximal temperature at equators returned by map_temperature */
+#define MAX_TEMP 1000
+#define SQSIZE MAX(1, sqrt( map.xsize * map.ysize / 1000))
+/* used to create the poles and for separate its */
+#define ICE_BASE_LEVEL (/* if poles are strips */ \
+ (!topo_has_flag(TF_WRAPX) || !topo_has_flag(TF_WRAPY)) \
+ /* 5% for litles maps 2% for bigs ones */ \
+ ? MAX_TEMP * (3 + 2 * SQSIZE) / ( 100 * SQSIZE) \
+ /* else */ \
+ : 5 * MAX_TEMP / 100 /* 5% for all maps */)
+/* **********************************************************************
+ * return 0 for the coolest map_x,map_y tile of the map
+ * and 1000 for the hotest one, this is proportional to area climatic zones
+ *************************************************************************/
+
+static int map_temperature(int map_x, int map_y)
+{
+ double x, y;
+
+ if(map.alltemperate) { return MAX_TEMP / 2; }
+ /* is a all temperate map */
+
+ do_in_natural_pos(ntl_x, ntl_y, map_x, map_y) {
+ /*
+ * Is a FLAT (unwraped) map
+ * I asume this is a partial map of Earth, at top i place a polar zone
+ * at bottom the ecuator one. this is a partial Earth map.
+ * flat as only one pole!
+ */
+ if (!topo_has_flag(TF_WRAPX) && !topo_has_flag(TF_WRAPY)) {
+ return MAX_TEMP * ntl_y / (NATURAL_HEIGHT - 1);
+ }
+
+ /*
+ * Is a global map
+ */
+
+ /* we fold the map to get the base symetries
+ ......
+ :c__c:
+ :____:
+ :____:
+ :c__c:
+ ......
+
+
+ x, y vars form 0.0 to 1.0 map a folded representation of the map
+ were C are all corners
+ ...>x
+ :C_
+ :__
+ V
+ y
+
+ */
+
+ x = ( ntl_x > (NATURAL_WIDTH / 2 - 1)
+ ? NATURAL_WIDTH - 1.0 - (double)ntl_x
+ : (double)ntl_x)
+ / ((NATURAL_WIDTH / 2 - 1));
+ y = ( ntl_y > (NATURAL_HEIGHT / 2 - 1)
+ ? NATURAL_HEIGHT - 1.0 - (double)ntl_y
+ : (double)ntl_y)
+ / (NATURAL_HEIGHT / 2 - 1);
+ } do_in_natural_pos_end;
+
+ /*
+ * classic topo
+ * the polar zone are at N and S
+ */
+ if (topo_has_flag(TF_WRAPX) && !topo_has_flag(TF_WRAPY)) {
+ return MAX_TEMP * y;
+ }
+
+ /* Uranus topo */
+ /* poles at E and W */
+ if (!topo_has_flag(TF_WRAPX) && topo_has_flag(TF_WRAPY)) {
+ return MAX_TEMP * x;
+ }
+
+ /*
+ * torus topo
+ * i make 2 circle polar zone (2 poles)
+ * Equatorial zone as the shape of a square in the map
+ */
+
+
+ /* generator 2 and 5 work best if the center of the map is free */
+ /* poles (N,S) and equator ( /,\)
+ ........
+ :\ NN /:
+ : \ / :
+ :S \/ S:
+ :S /\ S: .....
+ : / \ :
+ :/ NN \:
+ ''''''''
+ */
+ x = 1.0 - x;
+ /* poles (N,S) and equator ( /,\)
+ ........
+ :N /\ N:
+ : / \ :
+ :/ SS \:
+ :\ SS /: .....
+ : \ / :
+ :N \/ N:
+ ''''''''
+ */
+
+ /*
+ after the 4 fold
+ then we can fold it in diagonal:
+ */
+
+ if (x + y > 1.0) {
+ x = 1.0 - x;
+ y = 1.0 - y;
+ }
+
+ /* now the x and y variables work in 1/8 of the maps
+ .....
+ :P /
+ : /
+ :/
+ where P is N and S poles
+ */
+
+ /*
+ these math make poles with a shapes of 1/4circle on "P"
+ and equator as shape of rifht line on "/"
+ see:doc/Who_to_create_the_temperature_math.txt
+ */
+ {
+ float z = sqrt(( x * x + y * y ) / ((x - 1.0) * (x - 1.0) + (y - 1.0) *
(y - 1.0)));
+
+ return MAX_TEMP * -0.4621961892*(z + 0.06387781764)*z*(z -
2.148260011)*(z*z - 1.658803738*z + 2.440718605);
+ }
+}
+
+/* **************************************************************
+ *
+ * return random map coordinates where climate is in selected range
+ * and were terrains choice exept if T_ANY
+ * return false if pos is not finded
+ * **************************************************************/
+struct DataFilter {int T_min, T_max; enum tile_terrain_type t;};
+static bool temperature_filter(int map_x, int map_y, void *D_)
+{
+ struct DataFilter *D;
+ const int T = map_temperature( map_x, map_y);
+ D = D_;
+ return T >= D->T_min && T <= D->T_max
+ && (T_ANY == D->t || map_get_terrain(map_x, map_y) == D->t) ;
+}
+
+static bool rand_map_pos_temperature( int *map_x, int *map_y,
+ int T_min, int T_max,int TT)
+{
+ struct DataFilter D;
+
+ /* no poles, all temperate */
+ if(map.alltemperate
+ && ( T_min > MAX_TEMP / 2
+ || T_max < MAX_TEMP / 2)) {
+ return FALSE;
+ }
+
+ D.T_min = T_min;
+ D.T_max = T_max;
+ D.t = TT;
+ return rand_map_pos_filtered( map_x, map_y, &D, temperature_filter);
+}
+
+
+/* ************************************************************
+ * Return TRUE if the map in a city radius is singular
+ * This need to be best defined in future topologies works
+ * This def is ok for generalised topologies
+ * ************************************************************/
+
+static bool near_singularity(int map_x, int map_y)
+{
+ return is_singular_map_pos(map_x, map_y, CITY_MAP_RADIUS) ;
+}
/**************************************************************************
make_mountains() will convert all squares that are higher than thill to
@@ -113,9 +299,11 @@
whole_map_iterate(x, y) {
if (hmap(x, y) > thill && !is_ocean(map_get_terrain(x,y))) {
- if (myrand(100)>75)
+ if (myrand(100)>75
+ /* T_HILLS are too green for poles */
+ || map_temperature(x, y) <= MAX_TEMP / 10 )
map_set_terrain(x, y, T_MOUNTAINS);
- else if (myrand(100)>25)
+ else if (myrand(100)>25)
map_set_terrain(x, y, T_HILLS);
}
} whole_map_iterate_end;
@@ -123,70 +311,97 @@
/**************************************************************************
add arctic and tundra squares in the arctic zone.
- (that is the top 10%, and low 10% of the map)
-**************************************************************************/
-static void make_polar(void)
+ (that is the coolest 10% of the map)
+ make_polar() we texture the pole (tundra and mount)
+ to be used in generators 2-4
+***************************************************************************/
+static void make_polar()
{
- int xn, yn;
+ struct tile *ptile;
+ int T;
- for (yn = 0; yn < map.ysize / 10; yn++) {
- for (xn = 0; xn < map.xsize; xn++) {
- if ((hnat(xn, yn) + (map.ysize / 10 - yn * 25) > myrand(maxval)
- && nat_get_terrain(xn, yn) == T_GRASSLAND) || yn == 0) {
- if (yn < 2) {
- nat_set_terrain(xn, yn, T_ARCTIC);
- } else {
- nat_set_terrain(xn, yn, T_TUNDRA);
- }
- }
- }
- }
- for (yn = map.ysize * 9 / 10; yn < map.ysize; yn++) {
- for (xn = 0; xn < map.xsize; xn++) {
- if (((hnat(xn, yn) + (map.ysize / 10 - (map.ysize - yn - 1) * 25)
- > myrand(maxval))
- && nat_get_terrain(xn, yn) == T_GRASSLAND)
- || yn == map.ysize - 1) {
- if (yn > map.ysize - 3) {
- nat_set_terrain(xn, yn, T_ARCTIC);
- } else {
- nat_set_terrain(xn, yn, T_TUNDRA);
+ whole_map_iterate(map_x, map_y) {
+ T = map_temperature(map_x, map_y); /* temperature parameter */
+ ptile = map_get_tile(map_x, map_y);
+ if (T < ICE_BASE_LEVEL) { /* get the coldest part of the map */
+ if (ptile->terrain != T_MOUNTAINS)
+ ptile->terrain = T_ARCTIC;
+ } else if ((T <= 1.5 * ICE_BASE_LEVEL)
+ && (ptile->terrain == T_OCEAN) ) {
+ ptile->terrain = T_ARCTIC;
+ } else if (T <= 2 * ICE_BASE_LEVEL) {
+ if (ptile->terrain == T_OCEAN) {
+ if (myrand(10) > 5) {
+ ptile->terrain = T_ARCTIC;
+ } else if (myrand(10) > 6) {
+ ptile->terrain = T_TUNDRA;
+ }
+ } else if (myrand(10) > 0 && ptile->terrain != T_MOUNTAINS) {
+ ptile->terrain = T_TUNDRA;
}
}
- }
- }
+ } whole_map_iterate_end;
+
+}
+/*************************************************************************
+ make extra land at poles
+ used by generatros 1,5
+**************************************************************************/
+static void make_polar_land()
+{
+ struct tile *ptile;
+ int T;
- /* only arctic and tundra allowed at the poles (first and last two lines,
- as defined in make_passable() ), to be consistent with generator>1.
- turn every land tile on the second lines that is not arctic into tundra,
- since the first lines has already been set to all arctic above. */
- for (xn = 0; xn < map.xsize; xn++) {
- if (nat_get_terrain(xn, 1) != T_ARCTIC
- && !is_ocean(nat_get_terrain(xn, 1))) {
- nat_set_terrain(xn, 1, T_TUNDRA);
- }
- if (nat_get_terrain(xn, map.ysize - 2) != T_ARCTIC
- && !is_ocean(nat_get_terrain(xn, map.ysize - 2))) {
- nat_set_terrain(xn, map.ysize - 2, T_TUNDRA);
+ whole_map_iterate(map_x, map_y) {
+ T = map_temperature(map_x, map_y); /* temperature parameter */
+ ptile = map_get_tile(map_x, map_y);
+ if (T < 1.5 *ICE_BASE_LEVEL) {
+ ptile->terrain = T_GRASSLAND;
+ } else if ((T <= 2 * ICE_BASE_LEVEL) && myrand(10) > 4 ) {
+ ptile->terrain = T_GRASSLAND;
}
- }
+ } whole_map_iterate_end;
+}
+/*************************************************************************
+ create tundra and artic in cold zones
+*************************************************************************/
+static void make_tundra(void)
+{
+ whole_map_iterate(x, y) {
+ int T = map_temperature(x, y);
+ if (map_get_terrain(x, y) == T_GRASSLAND
+ && (2 * ICE_BASE_LEVEL > T || myrand(MAX_TEMP/5) > T))
+ map_set_terrain(x, y, T_TUNDRA);
+ } whole_map_iterate_end;
+}
+static void make_arctic(void)
+{
+ whole_map_iterate(x, y) {
+ int T = map_temperature(x, y);
+ if (map_get_terrain(x, y) == T_GRASSLAND
+ && myrand(15 * MAX_TEMP / 100) > T - ICE_BASE_LEVEL
+ && T <= 3 * ICE_BASE_LEVEL )
+ map_set_terrain(x, y, T_ARCTIC);
+ } whole_map_iterate_end;
}
/**************************************************************************
recursively generate deserts, i use the heights of the map, to make the
desert unregulary shaped, diff is the recursion stopper, and will be reduced
- more if desert wants to grow in the y direction, so we end up with
+ more if desert wants to grow in the N-S axes, so we end up with
"wide" deserts.
**************************************************************************/
-static void make_desert(int x, int y, int height, int diff)
+static void make_desert(int x, int y, int height, int diff, int base_T)
{
- if (abs(hmap(x, y)-height)<diff && map_get_terrain(x, y)==T_GRASSLAND) {
+ const int DeltaT = MAX_TEMP / (3 * SQSIZE);
+ if (abs(hmap(x, y) - height) < diff
+ && map_get_terrain(x, y) == T_GRASSLAND) {
map_set_terrain(x, y, T_DESERT);
cartesian_adjacent_iterate(x, y, x1, y1) {
- if (x != x1)
- make_desert(x1, y1, height, diff-1);
- else /* y != y1 */
- make_desert(x1, y1, height, diff-3);
+ make_desert(x1, y1, height,
+ diff - 1 - abs(map_temperature(x1, y1) - base_T) / DeltaT
+ , base_T);
+
} cartesian_adjacent_iterate_end;
}
}
@@ -199,16 +414,13 @@
**************************************************************************/
static void make_forest(int map_x, int map_y, int height, int diff)
{
- int nat_x, nat_y;
+ int nat_x, nat_y,T;
map_to_native_pos(&nat_x, &nat_y, map_x, map_y);
- if (has_poles && (nat_y == 0 || nat_y == map.ysize - 1)) {
- return;
- }
-
+ T = map_temperature(map_x, map_y);
if (map_get_terrain(map_x, map_y) == T_GRASSLAND) {
- if (has_poles && nat_y > map.ysize * 42 / 100
- && nat_y < map.ysize * 58 / 100 && myrand(100) > 50) {
+ if (T > 8 * MAX_TEMP/ 10
+ && myrand(1000) > 500 - 300 * (T * 1000 / MAX_TEMP - 800)) {
map_set_terrain(map_x, map_y, T_JUNGLE);
} else {
map_set_terrain(map_x, map_y, T_FOREST);
@@ -236,19 +448,24 @@
forests = 0;
do {
- rand_map_pos(&x, &y);
- if (map_get_terrain(x, y) == T_GRASSLAND) {
+ if( rand_map_pos_temperature(&x, &y,
+ MAX_TEMP / 10, MAX_TEMP,
+ T_GRASSLAND)) {
make_forest(x, y, hmap(x, y), 25);
+ } else {
+ break;/* if rand_map_pos_temperature is false this never will be true*/
}
- if (has_poles && myrand(100) > 75) {
- int yn = myrand(map.ysize * 2 / 10) + map.ysize * 4 / 10;
- int xn = myrand(map.xsize);
-
- if (nat_get_terrain(xn, yn) == T_GRASSLAND) {
- do_in_map_pos(x, y, xn, yn) {
- make_forest(x, y, hmap(x, y), 25);
- } do_in_map_pos_end;
- }
+ /* some extra tropical forest */
+ if( rand_map_pos_temperature(&x, &y,
+ 7 *MAX_TEMP / 10, MAX_TEMP,
+ T_GRASSLAND) ) {
+ make_forest(x, y, hmap(x, y), 25);
+ }
+ /* some extra cold forest */
+ if( rand_map_pos_temperature(&x, &y,
+ 1 *MAX_TEMP / 10, 3 * MAX_TEMP / 10,
+ T_GRASSLAND) ) {
+ make_forest(x, y, hmap(x, y), 25);
}
} while (forests < forestsize);
}
@@ -270,10 +487,11 @@
if (map_get_terrain(x, y)==T_GRASSLAND && hmap(x, y)<(maxval*60)/100) {
map_set_terrain(x, y, T_SWAMP);
cartesian_adjacent_iterate(x, y, x1, y1) {
- if (myrand(10) > 5 && !is_ocean(map_get_terrain(x1, y1))) {
- map_set_terrain(x1, y1, T_SWAMP);
+ if (myrand(10) > 5 && !is_ocean(map_get_terrain(x1, y1))
+ && map_get_terrain(x1, y1) != T_SWAMP ) {
+ map_set_terrain(x1, y1, T_SWAMP);
+ swamps++;
}
- /* maybe this should increment i too? */
} cartesian_adjacent_iterate_end;
swamps++;
}
@@ -286,33 +504,20 @@
static void make_deserts(void)
{
int x,y,i,j;
- i=map.deserts;
- j=0;
+ i = map.deserts;
+ j = 0;
while (i > 0 && j < 100 * map.deserts) {
j++;
- if (has_poles) {
- /* Choose a random coordinate between 20 and 30 degrees north/south
- * (deserts tend to be between 15 and 35; make_desert will expand
- * them). */
- int xn = myrand(map.xsize);
- int yn = myrand(map.ysize * 10 / 180) + map.ysize * 60 / 180;
-
- if (myrand(2) != 0) {
- yn = map.ysize - 1 - yn;
- }
- native_to_map_pos(&x, &y, xn, yn);
- } else {
- /* If there are no poles we can pick any location to be a desert. */
- rand_map_pos(&x, &y);
- }
-
- /* If it's a grassland square call make_desert here. We loop repeatedly
- * since we may not always find grassland. */
- if (map_get_terrain(x, y)==T_GRASSLAND) {
- make_desert(x,y, hmap(x, y), 50);
+ /* Choose a random coordinate between 20 and 30 degrees north/south
+ * (deserts tend to be between 15 and 35; make_desert will expand
+ * them). */
+ if(rand_map_pos_temperature(&x, &y,
+ 65 * MAX_TEMP / 100, 80 * MAX_TEMP / 100,
+ T_GRASSLAND)){
+ make_desert(x,y, hmap(x, y), 50, map_temperature(x, y));
i--;
- }
+ } else { break; }
}
}
@@ -561,8 +766,12 @@
x, y);
/* Test if the river is done. */
- if (adjacent_river_tiles4(x, y) != 0||
- adjacent_ocean_tiles4(x, y) != 0) {
+ if (adjacent_river_tiles4(x, y) != 0 ||
+ adjacent_ocean_tiles4(x, y) != 0 ||
+ /*rivers end at poles */
+ (map_get_terrain(x, y) == T_ARCTIC
+ && map_temperature(x, y) < 8 * MAX_TEMP / 100)) {
+
freelog(LOG_DEBUG,
"The river ended at (%d, %d).\n", x, y);
return TRUE;
@@ -678,7 +887,7 @@
* 1000 rather than the current 100 */
10 *
/* The size of the map (poles don't count). */
- (map_num_tiles() - 2 * map.xsize) *
+ map_num_tiles() * (map.alltemperate ? 1.0 : 0.90) *
/* Rivers need to be on land only. */
map.landpercent /
/* Adjustment value. Tested by me. Gives no rivers with 'set
@@ -798,41 +1007,6 @@
map_set_terrain(x, y, T_PLAINS);
} whole_map_iterate_end;
}
-
-/**************************************************************************
- we want the map to be sailable east-west at least at north and south pole
- and make it a bit jagged at the edge as well.
- So this procedure converts the second line and the second last line to
- ocean, and 50% of the 3rd and 3rd last line to ocean.
-**************************************************************************/
-static void make_passable(void)
-{
- int x;
-
- for (x=0;x<map.xsize;x++) {
- nat_set_terrain(x, 2, T_OCEAN);
-
- /* Iso-maps need two lines of ocean. */
- if (myrand(2) != 0 || topo_has_flag(TF_ISO)) {
- nat_set_terrain(x, 1, T_OCEAN);
- }
-
- if (myrand(2) != 0) {
- nat_set_terrain(x, 3, T_OCEAN);
- }
-
- nat_set_terrain(x, map.ysize - 3, T_OCEAN);
-
- if (myrand(2) != 0 || topo_has_flag(TF_ISO)) {
- nat_set_terrain(x, map.ysize - 2, T_OCEAN);
- }
- if (myrand(2) != 0) {
- nat_set_terrain(x, map.ysize - 4, T_OCEAN);
- }
- }
-
-}
-
/****************************************************************************
Return TRUE if the terrain at the given map position is "clean". This
means that all the terrain for 2 squares around it is either grassland or
@@ -873,6 +1047,43 @@
}
/**************************************************************************
+ this function flater the map in the polar zone to avoid too much land there
+**************************************************************************/
+static void normalize_hmap_poles(void)
+{
+ whole_map_iterate(x, y) {
+ if(near_singularity(x, y)) {
+ hmap(x, y) = 0;
+ } else if(map_temperature(x, y) < 2 * ICE_BASE_LEVEL) {
+ hmap(x, y) *= map_temperature(x, y) / (2.5 * ICE_BASE_LEVEL);
+ } else if(map.separatepoles
+ && map_temperature(x, y) <= 2.5 * ICE_BASE_LEVEL) {
+ hmap(x, y) *= 0.1;
+ } else if(map_temperature(x, y) <= 2.5 * ICE_BASE_LEVEL) {
+ hmap(x, y) *= map_temperature(x, y) / (2.5 * ICE_BASE_LEVEL);
+ }
+ } whole_map_iterate_end;
+}
+/* ***********************************************************************
+ inverse the effect of normalize_hmap_poles to best place terrains in polar
+ continents
+**************************************************************************/
+static void renormalize_hmap_poles(void)
+{
+ whole_map_iterate(x, y) {
+ if( hmap(x, y) == 0 || map_temperature(x, y) == 0) {
+ } else if(map_temperature(x, y) < 2 * ICE_BASE_LEVEL) {
+ hmap(x, y) *= (2.5 * ICE_BASE_LEVEL) / map_temperature(x, y) ;
+ } else if(map.separatepoles
+ && map_temperature(x, y) <= 2.5 * ICE_BASE_LEVEL) {
+ hmap(x, y) *= 10;
+ } else if(map_temperature(x, y) <= 2.5 * ICE_BASE_LEVEL) {
+ hmap(x, y) *= (2.5 * ICE_BASE_LEVEL) / map_temperature(x, y) ;
+ }
+ } whole_map_iterate_end;
+}
+
+/**************************************************************************
make land simply does it all based on a generated heightmap
1) with map.landpercent it generates a ocean/grassland map
2) it then calls the above functions to generate the different terrains
@@ -884,7 +1095,8 @@
int total = (map_num_tiles() * map.landpercent) / 100;
int forever=0;
- adjust_map();
+ adjust_map();
+ normalize_hmap_poles();
tres = (maxval * map.landpercent ) / 100;
do {
@@ -905,17 +1117,16 @@
tres*=9;
tres/=10;
} while (abs(total-count)> maxval/40);
- if (map.separatepoles && has_poles) {
- make_passable();
- }
+
+ renormalize_hmap_poles();
+ make_polar_land(); /* make extra land at poles*/
make_mountains(maxval*8/10);
+ make_arctic();
+ make_tundra();
make_forests();
make_swamps();
make_deserts();
make_plains();
- if (has_poles) {
- make_polar();
- }
make_fair();
make_rivers();
}
@@ -979,8 +1190,6 @@
/**************************************************************************
Assign continent numbers to all tiles.
- Numbers 1 and 2 are reserved for polar continents if
- map.generator != 0; otherwise are not special.
Also sets map.num_continents (note 0 is ocean, and continents
have numbers 1 to map.num_continents _inclusive_).
Note this is not used by generators 2,3 or 4 at map creation
@@ -988,34 +1197,20 @@
**************************************************************************/
void assign_continent_numbers(void)
{
- int isle = 1;
-
+ map.num_continents = 0;
whole_map_iterate(x, y) {
map_set_continent(x, y, 0);
} whole_map_iterate_end;
- if (map.generator != 0 && has_poles) {
- do_in_map_pos(x, y, 0, 0) {
- assign_continent_flood(x, y, 1);
- } do_in_map_pos_end;
-
- do_in_map_pos(x, y, 0, map.ysize - 1) {
- assign_continent_flood(x, y, 2);
- } do_in_map_pos_end;
- isle = 3;
- }
-
whole_map_iterate(x, y) {
if (map_get_continent(x, y) == 0
&& !is_ocean(map_get_terrain(x, y))) {
- assign_continent_flood(x, y, isle++);
+ assign_continent_flood(x, y, ++map.num_continents);
}
} whole_map_iterate_end;
- map.num_continents = isle-1;
freelog(LOG_VERBOSE, "Map has %d continents", map.num_continents);
}
-
/****************************************************************************
Return an approximation of the goodness of a tile to a civilization.
****************************************************************************/
@@ -1064,20 +1259,13 @@
static void setup_isledata(void)
{
int starters = 0;
- int min, firstcont, i;
+ int min, i;
assert(map.num_continents > 0);
/* allocate + 1 so can use continent number as index */
islands = fc_calloc((map.num_continents + 1), sizeof(struct isledata));
-
- /* the arctic and the antarctic are continents 1 and 2 for generator > 0 */
- if (map.generator > 0 && map.separatepoles && has_poles) {
- firstcont = 3;
- } else {
- firstcont = 1;
- }
-
+
/* add up all the resources of the map */
whole_map_iterate(x, y) {
/* number of different continents seen from (x,y) */
@@ -1091,8 +1279,9 @@
map_city_radius_iterate(x, y, x1, y1) {
/* (x1,y1) is possible location of a future city which will
* be able to get benefit of the tile (x,y) */
- if (map_get_continent(x1, y1) < firstcont) {
- /* (x1, y1) belongs to a non-startable continent */
+ if (map_get_continent(x1, y1) < 1
+ || map_temperature(x1, y1) <= 2 * ICE_BASE_LEVEL) {
+ /* (x1, y1) too cold*/
continue;
}
for (j = 0; j < seen_conts; j++) {
@@ -1122,7 +1311,7 @@
/* set minimum number of resources per starting position to be value of
* the best continent */
min = 0;
- for (i = firstcont; i < map.num_continents + 1; i++) {
+ for (i = 1; i < map.num_continents + 1; i++) {
if (min < islands[i].goodies) {
min = islands[i].goodies;
}
@@ -1134,7 +1323,7 @@
int nextmin = 0;
starters = 0;
- for (i = firstcont; i <= map.num_continents; i++) {
+ for (i = 1; i <= map.num_continents; i++) {
int value = islands[i].goodies;
starters += value / min;
@@ -1163,7 +1352,7 @@
"Please report this bug at " WEBSITE_URL);
abort();
} else {
- for (i = firstcont; i <= map.num_continents; i++) {
+ for (i = 1; i <= map.num_continents; i++) {
islands[i].starters = islands[i].goodies / min;
}
}
@@ -1298,13 +1487,6 @@
mysrand(map.seed);
- /* FIXME: currently the lack of poles is hard-coded for maps that wrap
- * north-south. In the future this could be a server option. It also
- * needs to control the temperature gradient between "poles" and
- * "equator"; e.g., if there are no poles desert and tundra should be
- * equally likely at either end. */
- has_poles = !topo_has_flag(TF_WRAPY);
-
/* don't generate tiles with mapgen==0 as we've loaded them from file */
/* also, don't delete (the handcrafted!) tiny islands in a scenario */
if (map.generator != 0) {
@@ -1397,7 +1579,14 @@
int x, y;
rand_map_pos(&x, &y);
- hmap(x, y) += myrand(5000);
+ if(near_singularity(x, y) /* avoid land in singularities */
+ || map_temperature(x, y) <= ICE_BASE_LEVEL/2
+ /* avoid to much land at poles */
+ ) {
+ hmap(x, y) -= myrand(5000);
+ } else {
+ hmap(x, y) += myrand(5000);
+ }
if ((i % 100) == 0) {
smooth_map();
}
@@ -1487,21 +1676,20 @@
**************************************************************************/
static void make_huts(int number)
{
- int x,y,l;
+ int x,y;
int count=0;
- while (number * map_num_tiles() >= 2000 && count++ < map_num_tiles() * 2) {
- rand_map_pos(&x, &y);
- l=myrand(6);
- if (!is_ocean(map_get_terrain(x, y)) &&
- ( map_get_terrain(x, y)!=T_ARCTIC || l<3 )
- ) {
- if (!is_hut_close(x,y)) {
+ while (number * map_num_tiles() >= 2000 && count++ < map_num_tiles() * 2) {
+ if( rand_map_pos_temperature(&x, &y,
+ 2 * ICE_BASE_LEVEL, MAX_TEMP,
+ T_ANY)
+ /* no hut in poles */
+ && !is_ocean(map_get_terrain(x, y) )/* not hut in oceans ! */
+ && !is_hut_close(x,y)) {
number--;
map_set_special(x, y, S_HUT);
/* Don't add to islands[].goodies because islands[] not
setup at this point, except for generator>1, but they
have pre-set starters anyway. */
- }
}
}
}
@@ -1526,32 +1714,35 @@
Add specials to the map with given probability.
****************************************************************************/
static void add_specials(int prob)
-{
- int xn, yn;
- int ymin = has_poles ? 1 : 0, ymax = has_poles ? map.ysize - 1 : map.ysize;
+{
enum tile_terrain_type ttype;
- for (yn = ymin; yn < ymax; yn++) {
- for (xn = 0; xn < map.xsize; xn++) {
- do_in_map_pos(x, y, xn, yn) {
- ttype = map_get_terrain(x, y);
- if ((is_ocean(ttype) && near_safe_tiles(x,y))
- || !is_ocean(ttype)) {
- if (myrand(1000) < prob) {
- if (!is_special_close(x, y)) {
- if (tile_types[ttype].special_1_name[0] != '\0'
- && (tile_types[ttype].special_2_name[0] == '\0'
- || (myrand(100) < 50))) {
- map_set_special(x, y, S_SPECIAL_1);
- } else if (tile_types[ttype].special_2_name[0] != '\0') {
- map_set_special(x, y, S_SPECIAL_2);
- }
- }
- }
- }
- } do_in_map_pos_end;
+ whole_map_iterate(x, y) {
+ ttype = map_get_terrain(x, y);
+ if ( !is_ocean(ttype) && !is_special_close(x, y)
+ && myrand(1000) < prob ) {
+ if (tile_types[ttype].special_1_name[0] != '\0'
+ && (tile_types[ttype].special_2_name[0] == '\0'
+ || (myrand(100) < 50))) {
+ map_set_special(x, y, S_SPECIAL_1);
+ } else if (tile_types[ttype].special_2_name[0] != '\0') {
+ map_set_special(x, y, S_SPECIAL_2);
+ }
+ }
+ else if(is_ocean(ttype) && near_safe_tiles(x,y)
+ && myrand(1000) < prob && !is_special_close(x, y))
+ { /* in sea there are more wales near poles */
+ if (tile_types[ttype].special_1_name[0] != '\0'
+ && (tile_types[ttype].special_2_name[0] == '\0'
+ || (myrand(15 * MAX_TEMP / 10)
+ < 6 * MAX_TEMP / 10 + map_temperature(x, y)))) {
+ map_set_special(x, y, S_SPECIAL_1);
+ } else if (tile_types[ttype].special_2_name[0] != '\0') {
+ map_set_special(x, y, S_SPECIAL_2);
+ }
}
- }
+ } whole_map_iterate_end;
+
map.have_specials = TRUE;
}
@@ -1564,15 +1755,8 @@
};
static bool map_pos_is_cold(int x, int y)
-{
- int xn, yn;
-
- if (!has_poles) {
- return FALSE;
- }
-
- map_to_native_pos(&xn, &yn, x, y);
- return (yn * 5 < map.ysize || (map.ysize - 1 - yn) * 5 > map.ysize);
+{
+ return map_temperature(x, y) <= 2 * MAX_TEMP/ 10;
}
/**************************************************************************
@@ -1832,7 +2016,8 @@
while (i > 0 && tries-->0) {
get_random_map_position_from_state(&x, &y, pstate);
map_to_native_pos(&xn, &yn, x, y);
- if (hmap(x, y) == 0 && count_card_adjc_elevated_tiles(x, y) > 0) {
+ if ((!near_singularity(x, y) || myrand(50) < 25 )
+ && hmap(x, y) == 0 && count_card_adjc_elevated_tiles(x, y) > 0) {
hmap(x, y) = 1;
i--;
if (yn >= pstate->s - 1 && pstate->s < map.ysize - 2) {
@@ -2002,8 +2187,6 @@
**************************************************************************/
static void initworld(struct gen234_state *pstate)
{
- int xn;
-
height_map = fc_malloc(sizeof(int) * map.ysize * map.xsize);
islands = fc_malloc((MAP_NCONT+1)*sizeof(struct isledata));
@@ -2013,32 +2196,9 @@
map_clear_all_specials(x, y);
map_set_owner(x, y, NULL);
} whole_map_iterate_end;
- if (has_poles) {
- for (xn = 0; xn < map.xsize; xn++) {
- do_in_map_pos(x, y, xn, 0) {
- map_set_terrain(x, y, myrand(9) > 0 ? T_ARCTIC : T_TUNDRA);
- map_set_continent(x, y, 1);
- } do_in_map_pos_end;
- if (myrand(9) == 0) {
- do_in_map_pos(x, y, xn, 1) {
- map_set_terrain(x, y, myrand(9) > 0 ? T_TUNDRA : T_ARCTIC);
- map_set_continent(x, y, 1);
- } do_in_map_pos_end;
- }
- do_in_map_pos(x, y, xn, map.ysize - 1) {
- map_set_terrain(x, y, myrand(9) > 0 ? T_ARCTIC : T_TUNDRA);
- map_set_continent(x, y, 2);
- } do_in_map_pos_end;
- if (myrand(9) == 0) {
- do_in_map_pos(x, y, xn, map.ysize - 2) {
- map_set_terrain(x, y, myrand(9) > 0 ? T_TUNDRA : T_ARCTIC);
- map_set_continent(x, y, 2);
- } do_in_map_pos_end;
- }
- }
- map.num_continents = 2;
- } else {
- map.num_continents = 0;
+ if (!map.alltemperate) {
+ make_polar();
+ assign_continent_numbers(); /* set poles numbers */
}
make_island(0, 0, pstate, 0);
islands[2].starters = 0;
@@ -2318,29 +2478,29 @@
/* set midpoints of sides to avg of side's vertices plus a random factor */
/* unset points are zero, don't reset if set */
- if (hnat((x0 + x1) / 2, y0) == 0) {
- hnat((x0 + x1) / 2, y0)
- = (val[0][0] + val[1][0]) / 2 + myrand(step) - step / 2;
- }
- if (hnat((x0 + x1) /2, y1wrap) == 0) {
- hnat((x0 + x1) / 2, y1wrap)
- = (val[0][1] + val[1][1]) / 2 + myrand(step) - step / 2;
- }
- if (hnat(x0, (y0 + y1) / 2) == 0) {
- hnat(x0, (y0 + y1) / 2)
- = (val[0][0] + val[0][1]) / 2 + myrand(step) - step / 2;
- }
- if (hnat(x1wrap, (y0 + y1) / 2) == 0) {
- hnat(x1wrap, (y0 + y1) / 2)
- = (val[1][0] + val[1][1]) / 2 + myrand(step) - step / 2;
- }
+#define set_midpoints(X,Y,V) \
+ do_in_map_pos(map_x, map_y, (X), (Y)) { \
+ if( !near_singularity( map_x, map_y) \
+ && map_temperature(map_x, map_y) > ICE_BASE_LEVEL/2 \
+ && hnat((X), (Y)) == 0 ) { \
+ hnat((X), (Y)) = V; } \
+ } do_in_map_pos_end;
+
+ set_midpoints((x0 + x1)/2, y0,
+ (val[0][0] + val[1][0])/2 + myrand(step) - step/2);
+ set_midpoints((x0 + x1)/2, y1wrap,
+ (val[0][1] + val[1][1])/2 + myrand(step) - step/2);
+ set_midpoints(x0, (y0 + y1)/2,
+ (val[0][0] + val[0][1])/2 + myrand(step) - step/2);
+ set_midpoints(x1wrap, (y0 + y1)/2,
+ (val[1][0] + val[1][1])/2 + myrand(step) - step/2);
+
/* set middle to average of midpoints plus a random factor, if not set */
- if (hnat((x0 + x1) / 2, (y0 + y1) / 2) == 0) {
- hnat((x0 + x1) / 2, (y0 + y1) / 2)
- = (val[0][0] + val[0][1] + val[1][0] + val[1][1]) / 4
- + myrand(step) - step / 2;
- }
+ set_midpoints((x0 + x1)/2, (y0 + y1)/2,
+ (val[0][0] + val[0][1] + val[1][0]
+ + val[1][1])/4 + myrand(step) - step/2);
+
/* now call recursively on the four subrectangles */
gen5rec(2 * step / 3, x0, y0, (x1 + x0) / 2, (y1 + y0) / 2);
@@ -2393,36 +2553,18 @@
/* set initial points */
for (xn = 0; xn < xdiv2; xn++) {
for (yn = 0; yn < ydiv2; yn++) {
- hnat(xn * xmax / xdiv, yn * ymax / ydiv)
- = myrand(2 * step) - (2 * step) / 2;
- }
- }
-
- /* if we aren't wrapping stay away from edges to some extent, try
- even harder to avoid the edges naturally if separatepoles is true */
- if (xnowrap) {
- for (yn = 0; yn < ydiv2; yn++) {
- hnat(0, yn * ymax / ydiv) -= avoidedge;
- hnat(xmax, yn * ymax / ydiv) -= avoidedge;
- if (map.separatepoles && has_poles) {
- hnat(2, yn * ymax / ydiv)
- = hnat(0, yn * ymax / ydiv) - myrand(3*avoidedge);
- hnat(xmax - 2, yn * ymax / ydiv)
- = hnat(xmax, yn * ymax / ydiv) - myrand(3 * avoidedge);
- }
- }
- }
-
- if (ynowrap) {
- for (xn = 0; xn < xdiv2; xn++) {
- hnat(xn * xmax / xdiv, 0) -= avoidedge;
- hnat(xn * xmax / xdiv, ymax) -= avoidedge;
- if (map.separatepoles && has_poles) {
- hnat(xn * xmax / xdiv, 2)
- = hnat(xn * xmax / xdiv, 0) - myrand(3 * avoidedge);
- hnat(xn * xmax / xdiv, ymax - 2)
- = hnat(xn * xmax / xdiv, ymax) - myrand(3 * avoidedge);
- }
+ do_in_map_pos(x, y, (xn * xmax / xdiv), (yn * ymax / ydiv)) {
+ /* set initial points */
+ hmap(x, y) =
+ myrand(2 * step) - (2 * step) / 2;
+ /* avoid edges (simplest topologicals singularities) */
+ if (near_singularity(x, y) )
+ hmap(x, y ) -= avoidedge;
+ /* separate poles || avoid too mach land at poles */
+ if ( map_temperature(x, y) <= ICE_BASE_LEVEL / 2 ) {
+ hmap(x, y) -= myrand(avoidedge);
+ }
+ } do_in_map_pos_end;
}
}
diff -ruN -Xfreeciv/diff_ignore freeciv/server/stdinhand.c
freeciv_/server/stdinhand.c
--- freeciv/server/stdinhand.c 2004-05-27 05:17:35.000000000 +0200
+++ freeciv_/server/stdinhand.c 2004-06-06 13:57:08.740401024 +0200
@@ -312,6 +312,12 @@
"be separate"), NULL,
MAP_DEFAULT_SEPARATE_POLES)
+ GEN_BOOL("alltemperate", map.alltemperate, SSET_MAP_GEN, SSET_GEOLOGY,
+ SSET_TO_CLIENT,
+ N_("All the map is temperate, no poles"),
+ N_("0 = normal Earth like planet; 1 = all temperate planet "), NULL,
+ MAP_DEFAULT_ALLTEMPERATE)
+
GEN_INT("landmass", map.landpercent, SSET_MAP_GEN, SSET_GEOLOGY,
SSET_TO_CLIENT,
N_("Amount of land vs ocean"), "", NULL,
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Marko Lindqvist, 2004/06/01
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Jason Short, 2004/06/01
- [Freeciv-Dev] (PR#8624) New clima function to best handle terrain place, used to place poles., Marcelo Burda, 2004/06/02
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Marcelo Burda, 2004/06/02
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Jason Short, 2004/06/02
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Marcelo Burda, 2004/06/02
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Marcelo Burda, 2004/06/05
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Marcelo Burda, 2004/06/06
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles.,
Marcelo Burda <=
- [Freeciv-Dev] (PR#8624) New clima function to best handle terrain place, used to place poles., Jason Short, 2004/06/06
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Marcelo Burda, 2004/06/06
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Jason Short, 2004/06/06
- [Freeciv-Dev] Re: (PR#8624) New clima function to best handle terrain place, used to place poles., Marcelo Burda, 2004/06/06
|
|