This is all of the various patches I use for my own FreeCiv games unified in to a single huge mega-patch. Vallimar has some improvments to this patch which makes it so that the separate islands and giving the humans the best islands are user-selectable parameters; I hope to have time to make a patch which incorporates more of Vallimar's improvments available in the near future. Until then, this patch does the following: * Allow the maximum research cost to be so high as to make tech advancment a non-factor in the game. Note that the client acts buggy with this; the TCP packets assume that tech never costs more than 256 bulbs. * Allows really tiny (20x16) and really huge (255x255) maps. Note that the client is buggy with maps which are narrower than the screen width * Makes the default map be a 40x32 map using a fixed RNG seed. I like playing this map because I am very familiar with it. * Adds three new map generators: Josh's penisula map, Mark Isaac's tectonic code adapted to FreeCiv, and a variant of the tectonic code which makes more islands. Note that Josh's generator makes fairly static maps, and that Mark's code is a good deal slower than any other generator out there (but also makes far more Earth-like maps) * Makes it so the humans always gets better islands than the AI players * Makes it so only one player is on a given island; this may mean that some players will end up on tiny islands if there are no suitable islands for the players. Make sure to choose a map which gives each player a decent island. * Adds two new parameters: slowai_tech, which makes the AI get less tech, and slowai_grow, which discourages the AI from building new cities. These are added to give "training wheels" to new players who are frustrated by how hard the AI is to beat. diff -ur freeciv-1.14.0/common/game.c freeciv-1.14.0-shr2/common/game.c --- freeciv-1.14.0/common/game.c Fri Oct 11 16:35:13 2002 +++ freeciv-1.14.0-shr2/common/game.c Tue Apr 8 05:41:43 2003 @@ -714,6 +714,9 @@ game.watchtower_extra_vision=GAME_DEFAULT_WATCHTOWER_EXTRA_VISION, game.allowed_city_names = GAME_DEFAULT_ALLOWED_CITY_NAMES; + game.slowai_tech = GAME_DEFAULT_SLOWAI_TECH; + game.slowai_grow = GAME_DEFAULT_SLOWAI_GROW; + sz_strlcpy(game.rulesetdir, GAME_DEFAULT_RULESETDIR); game.firepower_factor = 1; diff -ur freeciv-1.14.0/common/game.h freeciv-1.14.0-shr2/common/game.h --- freeciv-1.14.0/common/game.h Fri Oct 11 16:35:13 2002 +++ freeciv-1.14.0-shr2/common/game.h Tue Apr 8 05:41:43 2003 @@ -217,6 +217,9 @@ bool load_private_map; /* Only makes sense if the players are loaded. */ bool load_settings; } load_options; + + int slowai_tech; + int slowai_grow; }; /* Unused? */ @@ -306,7 +309,7 @@ #define GAME_DEFAULT_RESEARCHCOST 10 #define GAME_MIN_RESEARCHCOST 4 -#define GAME_MAX_RESEARCHCOST 100 +#define GAME_MAX_RESEARCHCOST 10000 #define GAME_DEFAULT_DIPLCOST 0 #define GAME_MIN_DIPLCOST 0 @@ -455,4 +458,11 @@ #define GAME_START_YEAR -4000 +#define GAME_DEFAULT_SLOWAI_TECH 100 +#define GAME_MIN_SLOWAI_TECH 50 +#define GAME_MAX_SLOWAI_TECH 400 +#define GAME_DEFAULT_SLOWAI_GROW 100 +#define GAME_MIN_SLOWAI_GROW 25 +#define GAME_MAX_SLOWAI_GROW 100 + #endif /* FC__GAME_H */ diff -ur freeciv-1.14.0/common/map.h freeciv-1.14.0-shr2/common/map.h --- freeciv-1.14.0/common/map.h Fri Oct 11 16:35:13 2002 +++ freeciv-1.14.0-shr2/common/map.h Tue Apr 8 05:41:39 2003 @@ -573,15 +573,15 @@ #define MAP_MIN_HUTS 0 #define MAP_MAX_HUTS 500 -#define MAP_DEFAULT_WIDTH 80 -#define MAP_MIN_WIDTH 40 -#define MAP_MAX_WIDTH 200 - -#define MAP_DEFAULT_HEIGHT 50 -#define MAP_MIN_HEIGHT 25 -#define MAP_MAX_HEIGHT 100 +#define MAP_DEFAULT_WIDTH 40 +#define MAP_MIN_WIDTH 12 +#define MAP_MAX_WIDTH 255 + +#define MAP_DEFAULT_HEIGHT 32 +#define MAP_MIN_HEIGHT 12 +#define MAP_MAX_HEIGHT 255 -#define MAP_DEFAULT_SEED 0 +#define MAP_DEFAULT_SEED 8675309 #define MAP_MIN_SEED 0 #define MAP_MAX_SEED (MAX_UINT32 >> 1) @@ -619,7 +619,7 @@ #define MAP_DEFAULT_GENERATOR 1 #define MAP_MIN_GENERATOR 1 -#define MAP_MAX_GENERATOR 5 +#define MAP_MAX_GENERATOR 8 #define MAP_DEFAULT_TINYISLES FALSE #define MAP_MIN_TINYISLES FALSE diff -ur freeciv-1.14.0/common/tech.c freeciv-1.14.0-shr2/common/tech.c --- freeciv-1.14.0/common/tech.c Fri Oct 11 16:35:13 2002 +++ freeciv-1.14.0-shr2/common/tech.c Tue Apr 8 05:41:43 2003 @@ -457,6 +457,17 @@ exit(EXIT_FAILURE); } + /* For casual players, the AI gets tech far too fast. This + slows down how fast the AI gets tech by the value in + game.slowai_tech */ + if (!game.is_new_game && pplayer->ai.control && game.slowai_tech != 100) { + float fcost, factor; + fcost = cost; + factor = game.slowai_tech; + fcost *= (factor / 100); + cost = fcost; + } + /* If we have many players, tech cost may drop to 0. */ if (cost == 0) { cost = 1; diff -ur freeciv-1.14.0/server/gamehand.c freeciv-1.14.0-shr2/server/gamehand.c --- freeciv-1.14.0/server/gamehand.c Fri Oct 11 16:35:50 2002 +++ freeciv-1.14.0-shr2/server/gamehand.c Tue Apr 8 05:41:39 2003 @@ -38,15 +38,59 @@ { int i, j, x, y; int dx, dy; + int last_human_player = 0, first_ai_player; Unit_Type_id utype; int start_pos[MAX_NUM_PLAYERS]; /* indices into map.start_positions[] */ if (!map.fixed_start_positions) { - /* except in a scenario which provides them, - shuffle the start positions around... */ assert(game.nplayers==map.num_start_positions); - for (i=0; i=0; j--) { + if(!game.players[j].ai.control) { + last_human_player = j; + break; + } + } + if(last_human_player + 1< game.nplayers) { + for(i=0; i=0 ; j--) { + if(!game.players[j].ai.control) { + last_human_player = j; + break; + } + } + } + } + } + + /* except in a scenario which provides them, + shuffle the human's start positions around... */ + for (i=0; i<= last_human_player;i++) { /* no advantage to the romans!! */ + j=myrand(last_human_player + 1); x=map.start_positions[j].x; y=map.start_positions[j].y; map.start_positions[j].x=map.start_positions[i].x; @@ -54,6 +98,20 @@ map.start_positions[i].x=x; map.start_positions[i].y=y; } + + /* Also shuffle all of the ai's start positions */ + first_ai_player = last_human_player + 1; + for(i = first_ai_player;i < game.nplayers; i++) { + j = myrand(game.nplayers - first_ai_player); + j += first_ai_player; + x=map.start_positions[j].x; + y=map.start_positions[j].y; + map.start_positions[j].x=map.start_positions[i].x; + map.start_positions[j].y=map.start_positions[i].y; + map.start_positions[i].x=x; + map.start_positions[i].y=y; + } + for(i=0; i #include #include +#include /* For the plate tectonic simulator */ #include "fcintl.h" #include "game.h" @@ -41,6 +42,8 @@ static void mapgenerator3(void); static void mapgenerator4(void); static void mapgenerator5(void); +static void mapgenerator6(void); +static void mapgenerator_tectonic(void); static void smooth_map(void); static void adjust_map(int minval); @@ -61,6 +64,7 @@ int x,y; /* upper left corner of the islands */ int goodies; int starters; + int continent; }; static struct isledata *islands; @@ -258,13 +262,23 @@ while (i > 0 && j < 500) { j++; - y=myrand(map.ysize*10/180)+map.ysize*110/180; + if(map.ysize >= 18) { + y=myrand(map.ysize*10/180)+map.ysize*110/180; + } + else { + y = map.ysize*110/180; + } x=myrand(map.xsize); if (map_get_terrain(x, y)==T_GRASSLAND) { make_desert(x,y, hmap(x, y), 50); i--; } - y=myrand(map.ysize*10/180)+map.ysize*60/180; + if(map.ysize >= 18) { + y=myrand(map.ysize*10/180)+map.ysize*60/180; + } + else { + y = map.ysize*60/180; + } x=myrand(map.xsize); if (map_get_terrain(x, y)==T_GRASSLAND) { make_desert(x,y, hmap(x, y), 50); @@ -823,7 +837,7 @@ 1) with map.landpercent it generates a ocean/grassland map 2) it then calls the above functions to generate the different terrains **************************************************************************/ -static void make_land(void) +static void make_land(int window_size) { int tres=(maxval*map.landpercent)/100; int count=0; @@ -846,7 +860,7 @@ else tres*=9; tres/=10; - } while (abs(total-count)> maxval/40); + } while (abs(total-count)> window_size/40); if (map.separatepoles) { make_passable(); } @@ -962,6 +976,7 @@ for(i=0; i<=map.num_continents; i++) { islands[i].x = islands[i].y = -1; /* flag */ islands[i].goodies = islands[i].starters = 0; + islands[i].continent = 0; } /* get x and y positions: (top left) */ @@ -970,6 +985,7 @@ if (islands[cont].x == -1) { islands[cont].x = x; islands[cont].y = y; + islands[cont].continent = cont; } } whole_map_iterate_end; @@ -1062,8 +1078,8 @@ /* starters are loosers */ for (x=firstcont;x 3*(riches+oldisles-1)/oldisles ) - &&!(islands[x].goodies > (riches+oldisles-1)/oldisles)) { + if (1 == 1) { /* All islands get a starter since we give the + humans the best islands */ freelog(LOG_VERBOSE, "islands[x].goodies=%i",islands[x].goodies); islands[x].starters= (islands[x].goodies+guard1)/mingood; if(islands[x].starters == 0) { @@ -1077,7 +1093,7 @@ /* starters are winners */ for (x=firstcont;x (riches+oldisles-1)/oldisles) { - assert(islands[x].starters == 0); + /*assert(islands[x].starters == 0);*/ freelog(LOG_VERBOSE, "islands[x].goodies=%i", islands[x].goodies); islands[x].starters = (islands[x].goodies+guard1)/mingood; if(islands[x].starters == 0) { @@ -1107,6 +1123,61 @@ } /************************************************************************** + Comparison function that compares the continent number on two isles + *************************************************************************/ + +static int compar_isle_cont(const void *a,const void *b) +{ + + struct isledata *t; + int ia, ib; + t = (struct isledata *)a; + ia = t->continent; + t = (struct isledata *)b; + ib = t->continent; + return ia - ib; + +} + +/************************************************************************** + Comparison function that compares the goodies on two isles + *************************************************************************/ + +static int compar_isle_goodies(const void *a,const void *b) +{ + + struct isledata *t; + int ia, ib; + t = (struct isledata *)a; + ia = t->goodies; + t = (struct isledata *)b; + ib = t->goodies; + return ib - ia; + +} + +/************************************************************************** + Comparison function that compares the goodness of two start points (how + good the island the two start points are on + *************************************************************************/ + +static int compar_isle_goodness(const void *a,const void *b) +{ + int ia, ib; + int x,y; + struct map_position *t; + t = (struct map_position *)a; + x=t->x; + y=t->y; + ia = islands[(int)map_get_continent(x, y)].goodies; + t = (struct map_position *)b; + x=t->x; + y=t->y; + ib = islands[(int)map_get_continent(x, y)].goodies; + return ib - ia; +} + +/************************************************************************** where do the different races start on the map? well this function tries to spread them out on the different islands. @@ -1130,21 +1201,72 @@ if(dist>= map.ysize/2) dist= map.ysize/2; + /* If we make distance really big here (and do the check to make sure each + player gets their own island), we can 100% ensure that each player + gets their own island; dist is the minimum distance that two players + can be from one another on the same island */ + + dist = 10000000; + + sum=0; for (k=0; k<=map.num_continents; k++) { - sum += islands[k].starters; - if (islands[k].starters!=0) { - freelog(LOG_VERBOSE, "starters on isle %i", k); - } + sum++; + if((k>2 || !map.separatepoles) && k>0) { + islands[k].starters = 1; + } + else { + islands[k].starters = 0; + } } + + if(game.nplayers > map.num_continents - 2) { + printf("Sorry, you have too many players. Discarding some.\n"); + game.nplayers = map.num_continents - 2; + printf("You now only have %d players\n",game.nplayers); + } + + /* Sort things so that the best islands come before the bad islands */ + qsort(&islands[1],map.num_continents, + sizeof(struct isledata),compar_isle_goodies); + + /* And now, set starters appropriately so that, when there are + more islands than starters, the players always start on the + best islands, one island per player (and, with the other patch, + the humans get first nibs at the good islands) */ + sum = 0; + for (k=0; k<=map.num_continents; k++) { + if(islands[k].starters == 1) { + sum++; + islands[k].starters = sum; + } + } + + /* Since code assumes that islands[i] is the same as what map_get_continent + returns, re-sort to make this assumption true again */ + qsort(&islands[1],map.num_continents, + sizeof(struct isledata),compar_isle_cont); + assert(game.nplayers<=nr+sum); while (nr 0) { j++; - if (!is_starter_close(x, y, nr, dist)) { - islands[(int)map_get_continent(x, y)].starters--; + if(counter > 100000 && !map_has_special(x, y, S_HUT) && + (map_get_continent(x, y) > 2 || !map.separatepoles) && + land_terrain(x,y)) { + islands[isle_num].starters = 0; + map.start_positions[nr].x=x; + map.start_positions[nr].y=y; + nr++; + } + else if (!is_starter_close(x, y, nr, 1000000) && land_terrain(x, y) + && (map_get_continent(x, y) > 2 || !map.separatepoles)) { + islands[isle_num].starters = 0; map.start_positions[nr].x=x; map.start_positions[nr].y=y; nr++; @@ -1167,6 +1289,16 @@ } map.num_start_positions = game.nplayers; + /* Sort the array so that the lowered numbered starting positions are + always on the best isles; this is used in conjunction with code in + gamehand.c to make sure that the humans get the best islands; the + code works best when each player gets their own island (otherwise + all of the humans will share the best islands and all of the AIs will + share the worse islands) */ + + qsort(map.start_positions,map.num_start_positions, + sizeof(struct map_position),compar_isle_goodness); + free(islands); islands = NULL; } @@ -1196,6 +1328,10 @@ map_allocate(); /* if one mapgenerator fails, it will choose another mapgenerator */ /* with a lower number to try again */ + if (map.generator == 6 ) + mapgenerator6(); + if (map.generator == 7 || map.generator == 8) + mapgenerator_tectonic(); if (map.generator == 5 ) mapgenerator5(); if (map.generator == 4 ) @@ -1245,7 +1381,7 @@ /*!PS: I don't have the time to have several test runs */ /* to find a mapping from percents to generator 1 settings. */ - if(map.generator==1){ + if(map.generator==1 || map.generator==7){ /*map.riverlength*= 10; */ /*I leave this out, people will get too upset if they have to change all their scripts */ @@ -1289,7 +1425,7 @@ /************************************************************************** since the generated map will always have a positive number as minimum height - i reduce the height so the lowest height is zero, this makes calculations + reduce the height so the lowest height is zero, this makes calculations easier **************************************************************************/ static void adjust_map(int minval) @@ -1338,7 +1474,829 @@ maxval-=minval; adjust_map(minval); - make_land(); + make_land(maxval); + free(height_map); + height_map = NULL; +} + +/************************************************************************** + The following code was written by Mark Isaak in 1988 and converted in to + a freeciv-compatible form 2003 by Sam Trenholme + *************************************************************************/ + +/* + * A program to simulate tectonics. + * + * Copyright 1988 by Mark Isaak. + * Copyright 2003 Sam Trenholme; with Mark Isaak's permission, I have + * relicensed this under the GPL. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Wish list (from 1988): + * Do it on a sphere, not a square torus (not wanted for FreeCiv) + * Give plates angular momentum + * Make some constants variable + * Horsts and grabens + * Let 2 plates fuse together and/or 1 plate split apart sometimes + * Astroblemes + * Take density of rock into account + */ + +/* + * ---------- Structures ----------------------------------------------------- + */ + +/* + * ---------- Defined parameters --------------------------------------------- + * + * Some notes on Earth for comparison: + * Mean elevation of land = 840 m. + * Kilamanjaro = 5895 m. + * Everest = 8848 m. + * Dead Sea = -400 m. + * Gread Rift Valley = -150 m. + * Andes = 6950 m. + * Marianas Trench = -11000 m. + * Mean depth of sea = -3810 m. + */ + +struct tectonic_data { + + int xsize; /* Size of map along x axis */ + int ysize; /* Size of map along y axis */ + int nplates; /* # of plates in world at genesis */ + int maxplates; /* maximum # of plates we can have */ + int ncontinents; /* # of plates which are continental */ + int nhotspots; /* # of hot spots */ + + int landelev; /* starting elevation of land */ + int oceanelev; /* starting elevation of oceans */ + + int epochmove; /* max movement during one epoch */ + + float foldfactor; /* how much to increase ht. of folds */ + int riftelev; /* elevation for rifts */ + int subsumeheight; /* elevation change for coastal ranges */ + int trenchheight; /* elev. change for trench by coastal range */ + int hotspotheight; /* average ht. growth of hot spots */ + + int riftminerals; /* minerals in rifts */ + int subsumeminerals; /* minerals in costal ranges */ + int archipminerals; /* minerals in archipelagos */ + int hotspotminerals; /* minerals in underwater hot spots */ + + float erodefactor; /* how much of a height to erode */ + float submergefactor; /* how being underwater slows erosion */ + int skyelev; /* no erosion above this */ + int waveaction; /* erosion due entirely to waves */ + int subsidefactor; /* how fast ocean floor sinks */ + int basinelev; /* how far ocean floor can sink */ + +}; + +static struct tectonic_data tectonic; + +void init_tec_data() { + tectonic.xsize = map.xsize; /* Width of map */ + tectonic.ysize = map.ysize; /* Height of map */ + + tectonic.nplates = 20; /* # of plates in world at genesis */ + tectonic.maxplates = 27; /* maximum # of plates we can have */ + tectonic.ncontinents = 5; /* # of plates which are continental */ + tectonic.nhotspots = 10; /* # of hot spots */ + + tectonic.landelev = 800; /* starting elevation of land */ + tectonic.oceanelev = -1000; /* starting elevation of oceans */ + + tectonic.epochmove = 5; /* max movement during one epoch */ + + tectonic.foldfactor = 1.2; /* how much to increase ht. of folds */ + tectonic.riftelev = -400; /* elevation for rifts */ + tectonic.subsumeheight = 1200; /* elevation change for coastal ranges */ + tectonic.trenchheight = -500; /* elev. change for trench by coastal range */ + tectonic.hotspotheight = 600; /* average ht. growth of hot spots */ + + tectonic.riftminerals = 7; /* minerals in rifts */ + tectonic.subsumeminerals = 3; /* minerals in costal ranges */ + tectonic.archipminerals = 5; /* minerals in archipelagos */ + tectonic.hotspotminerals = 2; /* minerals in underwater hot spots */ + + tectonic.erodefactor = 0.023; /* how much of a height to erode */ + tectonic.submergefactor = 0.2; /* how being underwater slows erosion */ + tectonic.skyelev = 10000; /* no erosion above this */ + tectonic.waveaction = 400; /* erosion due entirely to waves */ + tectonic.subsidefactor = 0.05; /* how fast ocean floor sinks */ + tectonic.basinelev = -9000; /* how far ocean floor can sink */ + } + +/* + * Tectonic plate + */ +typedef struct { + float xvec, yvec; /* direction of plate movement */ + short dx, dy; /* how far do move plate per age */ + bool continent; /* whether initially continent or ocean */ +} Plate_t; + +/* + * Hot spot + */ +typedef struct { + short x, y; /* location of hot spot */ + short howhot; /* how much it spews forth */ +} Hotspot_t; + +/* + * A piece of land which moves around + */ +typedef struct rock_s { + short elev; /* elevation */ + short elevchange; /* change in elevation due to erosion */ + Plate_t *plate; /* which plate it belongs to */ + bool moved; /* whether we've moved this iteration */ + long minerals; /* how many minerals */ + struct rock_s *next; /* next in linked list */ +} Rock_t; + +/* + * ---------- Global variables ----------------------------------------------- + */ +Rock_t *surface[1024][1024]; /* surface of planet */ +Plate_t plates[1024]; /* descriptions of plates */ +Hotspot_t hotspots[1024]; /* descriptions of hot spots */ + +int nplates; /* # of tectonic plates */ +Rock_t *freeRockList; /* ptr to list of unused rocks */ +short maxelev, minelev; /* elevation extremes */ + +/* + * ---------- Utilities ------------------------------------------------------ + */ +/* + * Return a random number from 0 to n-1. + */ + int +Rnd(n) + int n; +{ + return myrand(n); /* Freeciv's RNG */ +} + +/* + * Wraparound from one side of the world to the other. + */ + int +Wrap_x(val) + register int val; /* x coordinate */ +{ + if (val < 0) + val += tectonic.xsize; + else if (val >= tectonic.xsize) + val -= tectonic.xsize; + return (val); +} + + int +Wrap_y(val) + register int val; /* y coordinate */ +{ + if (val < 0) + val += tectonic.ysize; + else if (val >= tectonic.ysize) + val -= tectonic.ysize; + return (val); +} + +/* + * Allocate a rock. + */ + Rock_t * +AllocRock(pl) + Plate_t *pl; /* which plate it goes on */ +{ + Rock_t *rp; /* ptr to rock */ + + if (freeRockList) { + rp = freeRockList; + freeRockList = rp->next; + } else + rp = (Rock_t *)malloc(sizeof(Rock_t)); + rp->plate = pl; + rp->elev = (pl->continent ? tectonic.landelev : tectonic.oceanelev); + rp->elevchange = 0; + rp->moved = FALSE; + rp->minerals = 0; + rp->next = NULL; + return (rp); +} + +/* + * ---------- Initialization ------------------------------------------------- + */ + +/* + * Put plates at random spots on the planet. + * Make tectonic.ncontinents of them land, the rest ocean. + */ +int SeedPlates() +{ + register int i; + register Plate_t *pl; /* ptr to plates */ + + pl = &plates[0]; + for (i = 0; i < tectonic.nplates; ++i, ++pl) { + pl->dx = Rnd(tectonic.xsize); + pl->dy = Rnd(tectonic.ysize); + /* + * Check if this spot is already taken. + */ + if (surface[pl->dx][pl->dy]) { + --pl; + --i; + continue; /* try again */ + } + surface[pl->dx][pl->dy] = (Rock_t *)pl; + pl->xvec = 0.0; + pl->yvec = 0.0; + pl->continent = (i < tectonic.ncontinents); + } + return 0; +} + +/* + * Initialize the surface. Allocate a rock for each spot on the surface + * and assign it to the nearest plate. + */ +int InitSurface() +{ + register short dx, dy; /* x, y distance */ + register long dist; /* distance */ + register short x, y; /* location on surface */ + register Plate_t *pl; /* plate pointer */ + register Plate_t *bestpl; /* closest plate */ + register long bestdist; /* distance of closest plate */ + register int i; /* plate counter */ + + bestpl = NULL; + + for (x = 0; x < tectonic.xsize; ++x) { + for (y = 0; y < tectonic.ysize; ++y) { + bestdist = 2 * tectonic.xsize * tectonic.ysize; + for (pl = &plates[0], i = 0; i < tectonic.nplates; ++i, ++pl) { + dx = pl->dx - x; + if (dx < 0) + dx = -dx; + if (dx > tectonic.xsize/2) + dx = tectonic.xsize - dx; + dy = pl->dy - y; + if (dy < 0) + dy = -dy; + if (dy > tectonic.ysize/2) + dy = tectonic.ysize - dy; + dist = dx * dx + dy * dy; + if (dist < bestdist) { + bestdist = dist; + bestpl = pl; + } + } + surface[x][y] = AllocRock(bestpl); + } + } + return 0; +} + +/* + * Place some hot spots randomly. + */ +int SeedHotSpots() +{ + register int i; /* hot spot counter */ + register Hotspot_t *hsp; /* hot spot pointer */ + + for (hsp = &hotspots[0], i = 0; i < tectonic.nhotspots; ++i, ++hsp) { + hsp->x = Rnd(tectonic.xsize); + hsp->y = Rnd(tectonic.ysize); + hsp->howhot = (Rnd(tectonic.hotspotheight) + Rnd(tectonic.hotspotheight) + + Rnd(tectonic.hotspotheight) + Rnd(tectonic.hotspotheight) + 2) >> 1; + } + return 0; +} + +/* + * All initialization. + */ +int Tec_Init() +{ + nplates = tectonic.nplates; + SeedPlates(); + InitSurface(); + SeedHotSpots(); + return 0; +} + +/* + * ---------- Land movement -------------------------------------------------- + */ +/* + * Assign each of the plates a direction and distance (velocity) to move. + * Velocity is added to previous value, but isn't allowed to exceed + * tectonic.epochmove in any direction. + */ +int AssignDirections() +{ + register int i; /* plate counter */ + register Plate_t *pl; /* ptr to plates */ + float dist; /* magnitude of vector */ + register int dir; /* direction of vector */ +#define DEGtoRAD (3.1415926 / 180.0) + + for (pl = &plates[0], i = 0; i < nplates; ++i, ++pl) { + dir = Rnd(360); + dist = (float)(Rnd(101) + Rnd(101) + Rnd(101) - 150) * + (float)tectonic.epochmove/150.0; + pl->xvec += dist * cos(dir * DEGtoRAD); + pl->yvec += dist * sin(dir * DEGtoRAD); + if ((dist = hypot(pl->xvec, pl->yvec)) > (float)tectonic.epochmove) { + dist = (float)tectonic.epochmove / dist; + pl->xvec *= dist; + pl->yvec *= dist; + } + } + return 0; +} + +/* + * Set plates' dx,dy to the vector given by (i+1)*v/tectonic.epochmove - i*v/tectonic.epochmove, + * where v = plate's (xvec,yvec) vector. This arrangement makes the sum of + * (dx,dy) over an epoch equal to (xvec,yvec). + */ +int SetMovementVectors(iter) + int iter; /* which iteration */ +{ + register int i; /* plate counter */ + register Plate_t *pl; /* ptr to plates */ + + for (pl = &plates[0], i = 0; i < nplates; ++i, ++pl) { + pl->dx = (short)((iter + 1) * pl->xvec / tectonic.epochmove) - + (short)(iter * pl->xvec / tectonic.epochmove); + pl->dy = (short)((iter + 1) * pl->yvec / tectonic.epochmove) - + (short)(iter * pl->yvec / tectonic.epochmove); + } + return 0; +} + +/* + * Move each rock according to its plate's movement vector. + * Use the rock's 'next' field to keep track of rocks that pile up. + */ +int MovePlates() +{ + register int x, y; /* where in the world */ + register int newx, newy; /* rock's new home */ + register Rock_t *rp; /* rock pointer */ + register Rock_t *prevrp; /* rock before rp */ + register Rock_t *nextrp; /* rock after rp */ + + for (x = 0; x < tectonic.xsize; ++x) { + for (y = 0; y < tectonic.ysize; ++y) { + prevrp = NULL; + for (rp = surface[x][y]; rp; prevrp = rp, rp = nextrp) { + nextrp = rp->next; + if (rp->moved) + continue; + /* + * Figure out where to move rock. + */ + rp->moved = TRUE; + if ((rp->plate->dx == 0) && (rp->plate->dy == 0)) + continue; + newx = Wrap_x(x + rp->plate->dx); + newy = Wrap_y(y + rp->plate->dy); + /* + * Move rock. + */ + if (prevrp == NULL) + surface[x][y] = rp->next; + else + prevrp->next = rp->next; + rp->next = surface[newx][newy]; + surface[newx][newy] = rp; + } + } + } + return 0; +} + +/* + * ---------- Mountain building and the like --------------------------------- + */ + +/* + * Create land where plates have spread apart. + */ +int Rift(x, y) + int x, y; +{ + register Plate_t *pl; /* which plate new land goes with */ + register int px, py; /* offset at which to look for plate */ + register long tries; /* # of failures to find plate */ + register Rock_t *rp; /* new rock */ + + pl = NULL; + + /* + * Find a plate to go with. + */ + for (tries = 0; tries < 30; ++tries) { + px = Wrap_x(x + Rnd(3) - 1); + py = Wrap_y(y + Rnd(3) - 1); + if ((rp = surface[px][py]) != NULL) { + pl = rp->plate; + break; + } + } + /* + * Can't find a plate; start a new one. + */ + if (pl == NULL) { + if (nplates < tectonic.maxplates) + ++nplates; + pl = &plates[nplates - 1]; + pl->xvec = pl->yvec = 0.0; + pl->dx = pl->dy = 0; + pl->continent = FALSE; + } + /* + * Make rock. + */ + rp = AllocRock(pl); + rp->elev = tectonic.riftelev; + rp->minerals = tectonic.riftminerals; /* should distinguish between ocean and continental rifts!!! */ + surface[x][y] = rp; + return 0; +} + +/* + * Raise (or lower) land near x,y by height. + * Determine direction from x,y by rp's plate's vector. + */ +int ElevNearby(x, y, rp, height) + int x, y; + Rock_t *rp; + short height; +{ + float h; /* hypotenuse of rp->plate->vec */ + int x2, y2; /* rock to raise */ + + h = hypot(rp->plate->xvec, rp->plate->yvec); + if (h < 0.01) { + x2 = x; + y2 = y; + } else { + x2 = Wrap_x(x + (int)(rp->plate->xvec / h + 1.5) - 1); + y2 = Wrap_y(y + (int)(rp->plate->yvec / h + 1.5) - 1); + } + if (surface[x2][y2]) + surface[x2][y2]->elev += height; + return 0; +} + +/* + * Continent hits continent; mountains are pushed up. + * New height = taller + 0.5 * shorter. The leftover height is + * distributed to other rocks in the area. + */ +int Fold(x, y) + int x, y; +{ + register Rock_t *rp0; /* first rock on list */ + register Rock_t *rp1; /* short rock */ + register Rock_t *rp2; /* tall rock */ + register short halfshort; /* half the short elevation */ + + rp0 = surface[x][y]; + if (rp0->next->elev > rp0->elev) { + rp1 = rp0; + rp2 = rp1->next; + } else { + rp2 = rp0; + rp1 = rp2->next; + } + halfshort = (rp1->elev >> 1) * tectonic.foldfactor; + ElevNearby(x, y, rp1, (halfshort + 1) >> 1); + ElevNearby(x, y, rp2, halfshort >> 1); + /* + * Combine rocks and free a rock structure. + */ + rp0->elev = rp2->elev + halfshort; + rp0->minerals = (rp1->minerals + rp2->minerals + 1) >> 1; + rp0->plate = Rnd(2) ? rp1->plate : rp2->plate; + + rp1 = rp0->next; + rp0->next = rp1->next; + rp1->next = freeRockList; + freeRockList = rp1; + return 0; +} + +/* + * Create a costal range where oceanic plate is subsumed by continental + * plate. Simply add a constant height and minerals to the continent. + */ +int Subsume(x, y) + int x, y; +{ + register Rock_t *rp0; /* first rock on list */ + register Rock_t *rp1; /* underwater rock */ + register Rock_t *rp2; /* higher rock */ + + rp0 = surface[x][y]; + if (rp0->next->elev > rp0->elev) { + rp1 = rp0; + rp2 = rp1->next; + } else { + rp2 = rp0; + rp1 = rp2->next; + } + ElevNearby(x, y, rp2, tectonic.trenchheight); + if (rp2->elev < 0) + rp0->minerals = rp2->minerals + tectonic.archipminerals; + else + rp0->minerals = rp2->minerals + tectonic.subsumeminerals; + rp0->elev = rp2->elev + tectonic.subsumeheight; + rp0->plate = rp2->plate; + + rp1 = rp0->next; + rp0->next = rp1->next; + rp1->next = freeRockList; + freeRockList = rp1; + return 0; +} + +/* + * For each spot on the surface, combine rocks which were piled on top of + * each other in MovePlates() into mountains, + * and create rifts where plates have spread apart leaving nothing. + */ +int MountainBuild() +{ + register int x, y; /* where in the world */ + register Rock_t *rp; /* rock pointer */ + register Rock_t *rp2; /* another rock pointer */ + + for (x = 0; x < tectonic.xsize; ++x) { + for (y = 0; y < tectonic.ysize; ++y) { + rp = surface[x][y]; + if (rp == NULL) + Rift(x, y); + else while ((rp2 = rp->next) != NULL) { + if ((rp->elev > 0) && (rp2->elev > 0)) + Fold(x, y); /* land + land */ + else + Subsume(x, y); /* land or ocean + ocean */ + } + } + } + return 0; +} + +/* + * Increase elevation of hot spots. If underwater, increase minerals. + */ +int EruptHotSpots() +{ + register int i; /* hot spot counter */ + register Hotspot_t *hsp; /* hot spot pointer */ + register Rock_t *rp; /* rock pointer */ + + for (hsp = &hotspots[0], i = 0; i < tectonic.nhotspots; ++i, ++hsp) { + rp = surface[hsp->x][hsp->y]; + if (rp->elev <= 0) + rp->minerals += tectonic.hotspotminerals; + rp->elev += hsp->howhot; + } + return 0; +} + +/* + * Wear down mountains. Amount of wear is proportional to the average + * difference in height between a rock and the surrounding rocks, + * except very tall mountains don't erode much (no weather), + * there's more erosion around seashores (waves), and underwater + * mountains don't erode much. + * Also lower sea floors. + * Mark everything unmoved. + */ +int Erode() +{ + register int x, y; /* where in the world */ + register Rock_t *rp0; /* rock pointer */ + register Rock_t *rp1; /* pointer to nearby rock */ + register short elev0, elev1; /* elevation of rp0, rp1 */ + register int i, j; /* indices to nearby rocks */ + register short diff; /* difference in height, roughly */ + register long depth; /* average height of area */ + int diffplates; /* nearby rocks belonging to other plates */ + int deepplates; /* # of nearby rocks under water */ + + rp1 = NULL; + + for (x = 0; x < tectonic.xsize; ++x) { + for (y = 0; y < tectonic.ysize; ++y) { + rp0 = surface[x][y]; + elev0 = rp0->elev; + depth = elev0; + if (elev0 < 0) + elev0 = elev0 * tectonic.submergefactor; + else if (elev0 > tectonic.skyelev) + elev0 = tectonic.skyelev; + diffplates = 0; + deepplates = 0; + for (i = -1; i <= 1; ++i) { + for (j = -1; j <= 1; ++j) { /* for each surrounding rock */ + if ((i == 0) && (j == 0)) + continue; + rp1 = surface[Wrap_x(x + i)][Wrap_y(y + j)]; + elev1 = rp1->elev; + if (rp1->plate != rp0->plate) + ++diffplates; + depth += elev1; + if (elev1 < 0) { + ++deepplates; + elev1 = elev1 * tectonic.submergefactor; + } else if (elev1 > tectonic.skyelev) + elev1 = tectonic.skyelev; + /* + * Erode according to diff between elev0, elev1. + */ + diff = elev0 - elev1; + if ((elev0 <= 0 && elev1 >= 0) || + (elev0 >= 0 && elev1 <= 0)) { + diff += tectonic.waveaction; + } + diff = tectonic.erodefactor * diff + 0.5; + rp0->elevchange -= diff; + rp1->elevchange += diff; + } + } + /* + * If 7/8 of the rocks surrounding us belong to different + * plates, join the crowd. + */ + if (diffplates >= 7) { + do { + i = Rnd(3) - 2; + j = Rnd(3) - 2; + if (i == 0 && j == 0) + continue; + rp1 = surface[Wrap_x(x + i)][Wrap_y(y + j)]; + } while (rp1->plate == rp0->plate); + rp0->plate = rp1->plate; + } + /* + * Compute subsidence of ocean basins. + */ + if (deepplates >= (rp0->elev >= 0 ? 8 : 6)) { + rp0->elevchange -= tectonic.subsidefactor * (depth/9 - tectonic.basinelev); + } + } + } + /* + * Incorporate elevchange; mark things moved. + */ + for (x = 0; x < tectonic.xsize; ++x) { + for (y = 0; y < tectonic.ysize; ++y) { + rp0 = surface[x][y]; + rp0->moved = FALSE; + rp0->elev += rp0->elevchange; + rp0->elevchange = 0; + } + } + return 0; +} + +/* + * Give all the plates directions, move them, and see what happens. + * They may move a maximum of tectonic.epochmove cells per epoch. + */ +int Epoch() +{ + register int i; + + AssignDirections(); + for (i = 0; i < tectonic.epochmove; ++i) { + SetMovementVectors(i); + MovePlates(); + MountainBuild(); + EruptHotSpots(); + Erode(); + } + return 0; +} + +/************************************************************************* + This ends Mark's plate tectonic code + *************************************************************************/ + +/************************************************************************** + mapgenerator_tectonic: Use Mark Isaak's plate tectonic simulation code + to generate a realistic world. +**************************************************************************/ +static void mapgenerator_tectonic(void) +{ + int i; + int minval=5000000; + height_map=fc_malloc (sizeof(int)*map.xsize*map.ysize); + + adjust_terrain_param(); + + /* Now, call Mark's teconic simulator to create a height map */ + + init_tec_data(); + Tec_Init(); + for(i=0;i<45;i++) + Epoch(); + + /* Convert the tectonically-synthsized map in to a freeciv height field */ + + whole_map_iterate(x, y) { + hmap(x, y) = surface[x][y]->elev; + } whole_map_iterate_end; + + /* Mark's code generates negative values; adjust + To do: Add code which uses Mark's idea of what should be land + and water to determine where the oceans go; modify Mark's + code to use map.landpercent */ + minval = 600000; + + whole_map_iterate(x, y) { + + /* Hackish code to discard some more extreme values */ + if (hmap(x, y) < -400) { + hmap(x, y) = -400; + } + if (hmap(x, y) > 10000) { + hmap(x, y) = 10000; + } + + if (hmap(x, y) < minval) { + minval = hmap(x, y); + } + } whole_map_iterate_end; + + if(minval < 0) { + whole_map_iterate(x, y) { + hmap(x, y) -= minval; + } whole_map_iterate_end; + } + + /* If we are using map generator eight, make the pure plate tectonic + land more lumpy. We do this because the plate tectonic code simulates + the Earth's processes too well to make a traditional FreeCiv game: It + makes large stretches of land and water instead of the FreeCiv + model of having many islands. + + The alleviates this by adding some lumpyness to the generated land; + this makes the continents smaller and the islands more numerous */ + + if(map.generator == 8) { + for (i=0;i<(map.xsize*map.ysize)/2;i++) { + int x,y; + rand_map_pos(&x, &y); + hmap(x, y) += myrand(4000); + } + + /* Smooth things out just a tad */ + smooth_map(); + smooth_map(); + } + + /* Find the minval and maxval now that we have made a plate tectonic + simulated map */ + + whole_map_iterate(x, y) { + if (hmap(x, y) > maxval) + maxval = hmap(x, y); + if (hmap(x, y) < minval) + minval = hmap(x, y); + } whole_map_iterate_end; + + maxval-=minval; + adjust_map(minval); + + /* based on map.landpercent, separate land from water */ + make_land(maxval / 5); + free(height_map); height_map = NULL; } @@ -1394,7 +2352,7 @@ rand_map_pos(&x, &y); l=myrand(6); if (map_get_terrain(x, y)!=T_OCEAN && - ( map_get_terrain(x, y)!=T_ARCTIC || l<3 ) + ( map_get_terrain(x, y)!=T_ARCTIC ) ) { if (!is_hut_close(x,y)) { number--; @@ -2208,7 +3166,271 @@ maxval -= minval; adjust_map(minval); - make_land(); + make_land(maxval); free(height_map); height_map = NULL; } + +static int is_ocean(int l) +{ + return (l == T_OCEAN); +} + +static int land_terrain(int x, int y) +{ + return !is_ocean(map_get_terrain(x,y)); +} + +static int border_score(int x, int y) +{ + int adj_score = 2; + int diag_score = 1; + int score = 0; + score += land_terrain(x-1,y) ? adj_score : 0; + score += land_terrain(x+1,y) ? adj_score : 0; + score += land_terrain(x,y-1) ? adj_score : 0; + score += land_terrain(x,y+1) ? adj_score : 0; + score += land_terrain(x-1,y-1) ? diag_score : 0; + score += land_terrain(x+1,y-1) ? diag_score : 0; + score += land_terrain(x-1,y+1) ? diag_score : 0; + score += land_terrain(x+1,y+1) ? diag_score : 0; + return score; +} + +static int random_new_land(int x, int y) +{ + int score; + int random; + if(land_terrain(x, y)) { + return T_PLAINS; + } + score = border_score(x, y); + random = myrand(4*2+4*1); + return random + 1 > score ? T_OCEAN : T_GRASSLAND; + +} + +/* Creates a peninsula and put the player's starting position + on it; this is used by map generator 6 */ +static void create_peninsula(int x, int y,int player_number, + int width, int height,int direction, + int remaining_count) +{ + int cx, cy; + + cx = x+width/2; + for(cy = y; cy != y + (height + 1)*direction; + cy += direction) { + map_set_terrain(cx, cy, T_GRASSLAND); + map_set_continent(cx, cy, 1); + } + remaining_count -= height; /* account for the already added strip */ + cy = y; + for(cx = x + width/2 - 2; cx <= x + width/2 + 2; cx ++) { + map_set_terrain(cx, cy, T_GRASSLAND); + map_set_continent(cx, cy, 1); + } + remaining_count -= 4; /* account for extra 4 (not 5 because that would + double count the one already done by height strip.*/ + + while (remaining_count > 0) { + for(cx = x; cx < x+width; cx++) { + for(cy = y; cy != y + (height + 1)*direction; cy += direction) { + int new_terrain = random_new_land(cx, cy); + if(new_terrain == T_GRASSLAND){ + remaining_count--; + map_set_terrain(cx, cy, T_GRASSLAND); + map_set_continent(cx, cy, 1); + hmap(cx, cy) = 20 * (cx - x) * (width - (cx - x)) + myrand(80) - 40; + } + if(remaining_count <= 0) { + goto done_with_peninsula; + } + } + } + } + + done_with_peninsula: + + map.start_positions[player_number].x = x+width/2; + map.start_positions[player_number].y = y; +} + +/* This generator creates a map with one penisula for each + player and an isthmus between. It creates a central + ocean and puts the peninsulas around the edges. It is + intented for quicker games. Should look something like this: + ***************************** + ** ***** ***** ***** ** + * *** *** *** * + * * + * * + * *** *** * + ** ***** ***** ** + ***************************** + */ +static void mapgenerator6(void) +{ + int peninsulas = game.nplayers; + int peninsulas_on_one_side = (peninsulas + 1)/2; + int isthmus_width = 10; + int peninsula_separation = 5; + int peninsula_width = + (map.xsize - isthmus_width - peninsula_separation) + /(peninsulas_on_one_side) - peninsula_separation; + /* if landpercent <= 50, then make shorter peninsulas */ + int peninsula_height = (map.landpercent <= 50) + ? map.ysize/3 + : (map.ysize*2)/5; + int continent_percent = (map.landpercent <= 50) + ? (map.landpercent * 3)/2 + : (map.landpercent * 5)/4; + int i, x, y; + int polar_height = 3; + int remaining_count = + MIN(isthmus_width * (map.ysize - polar_height*2), + ((isthmus_width+peninsula_separation) * (map.ysize - polar_height*2) + * continent_percent)/100); + + height_map = fc_malloc(sizeof(int) * map.xsize * map.ysize); + + /* initialize everything to ocean */ + for (y = 0 ; y < map.ysize ; y++) + for (x = 0 ; x < map.xsize ; x++) { + map_set_terrain(x, y, T_OCEAN); + map_set_continent(x, y, 0); + hmap(x, y) = 0; + } + + /* create polar regions */ + for (x = 0 ; x < map.xsize; x++) { + for (y = 0; y < polar_height; y++) { + int rand_num = myrand(9); + map_set_terrain(x, y, rand_num > 7 ? T_ARCTIC : + (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA)); + map_set_continent(x, y, 1); + rand_num = myrand(9); + map_set_terrain(x, map.ysize-1-y, rand_num > 7 ? T_ARCTIC : + (rand_num < 2 ? T_MOUNTAINS : T_TUNDRA)); + map_set_continent(x, map.ysize-1-y, 1); + } + } + + /* build polar regions road */ + for (x = 0 ; x < map.xsize; x++) { + y = polar_height-1; + if(map_build_road_time(x,y-1) < map_build_road_time(x,y)) + { + map_set_special(x,y-1,S_ROAD); + } else { + map_set_special(x,y,S_ROAD); + } + y = map.ysize-polar_height; + if(map_build_road_time(x,y+1) < map_build_road_time(x,y)) + { + map_set_special(x,y+1,S_ROAD); + } else { + map_set_special(x,y,S_ROAD); + } + } + + map.num_continents = 1; + + /* create isthmus centeral strip */ + x = isthmus_width/2; + for (y = polar_height; y < map.ysize - polar_height; y++) { + map_set_terrain(x, y, T_GRASSLAND); + map_set_continent(x, y, 1); + hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200); + } + + remaining_count -= (map.ysize - 2*polar_height) * 1; + + /* add additional isthmus area randomly */ + while(remaining_count > 0) { + for (y = polar_height; y < map.ysize/2; y++) { + for (x = 0; x < isthmus_width; x++) { + int new_terrain = random_new_land(x, y); + if(new_terrain == T_GRASSLAND){ + remaining_count--; + map_set_terrain(x, y, T_GRASSLAND); + map_set_continent(x, y, 1); + hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200); + } + if(remaining_count <= 0) { + goto done_with_isthmus; + } + } + } + + for (y = map.ysize - polar_height - 1; y >= map.ysize/2; y--) { + for (x = 0; x < isthmus_width; x++) { + int new_terrain = random_new_land(x, y); + if(new_terrain == T_GRASSLAND){ + remaining_count--; + map_set_terrain(x, y, T_GRASSLAND); + map_set_continent(x, y, 1); + hmap(x, y) = 100 * x * (isthmus_width - x) + (myrand(400) - 200); + } + if(remaining_count <= 0) { + goto done_with_isthmus; + } + } + } + } + done_with_isthmus: + + /* setup peninsulas */ + for(i = 0; i < game.nplayers; i++) { + /* direction is the direction to increment from the x and y location */ + int direction = (i < peninsulas_on_one_side) ? -1 : 1; + int index = (direction == -1) ? i : i - peninsulas_on_one_side; + int width = (continent_percent < 95) + ? peninsula_width + : peninsula_width + peninsula_separation - 1;/*Give more room */ + int height = peninsula_height-polar_height; + int peninsula_remaining_count = + MIN(width*height, + ((width+peninsula_separation)*height*continent_percent)/100); + x = index * (peninsula_width + peninsula_separation) + + isthmus_width + peninsula_separation; + y = (direction == -1) + ? peninsula_height + : map.ysize - 1 - peninsula_height; + create_peninsula(x,y,i,width,height,direction,peninsula_remaining_count); + } + + map.num_start_positions = game.nplayers; + + /* setup terrain */ + make_mountains(1600); + make_swamps(); + make_forests(); + make_deserts(); + make_plains(); + make_fair(); + make_rivers(); + + /* create isthmus road */ + { + int last_x, middle_x = isthmus_width/2; + last_x = middle_x; + for (y = polar_height - 1; y < map.ysize - polar_height + 1; y++) { + int best_x = middle_x; + int min_build = 100; + for(x = MAX(last_x - 1, middle_x - 1); + x != MIN(last_x + 1,middle_x + 1) + 1; x++) { + if(land_terrain(x,y) && map_build_road_time(x,y) < min_build) { + best_x = x; + min_build = map_build_road_time(x,y); + } + } + map_set_special(best_x,y,S_ROAD); + last_x = best_x; + } + } + + + free(height_map); + +} diff -ur freeciv-1.14.0/server/savegame.c freeciv-1.14.0-shr2/server/savegame.c --- freeciv-1.14.0/server/savegame.c Sun Dec 8 15:32:30 2002 +++ freeciv-1.14.0-shr2/server/savegame.c Tue Apr 8 05:41:43 2003 @@ -1862,6 +1862,12 @@ secfile_lookup_int_default(file, game.allowed_city_names, "game.allowed_city_names"); + game.slowai_tech = secfile_lookup_int_default(file, + GAME_DEFAULT_SLOWAI_TECH, "game.slowai_tech"); + + game.slowai_grow = secfile_lookup_int_default(file, + GAME_DEFAULT_SLOWAI_GROW, "game.slowai_grow"); + if(game.civstyle == 1) { string = "civ1"; } else { @@ -2184,6 +2190,8 @@ secfile_insert_int(file, game.watchtower_vision, "game.watchtower_vision"); secfile_insert_int(file, game.watchtower_extra_vision, "game.watchtower_extra_vision"); secfile_insert_int(file, game.allowed_city_names, "game.allowed_city_names"); + secfile_insert_int(file, game.slowai_tech, "game.slowai_tech"); + secfile_insert_int(file, game.slowai_grow, "game.slowai_grow"); if (TRUE) { /* Now always save these, so the server options reflect the diff -ur freeciv-1.14.0/server/settlers.c freeciv-1.14.0-shr2/server/settlers.c --- freeciv-1.14.0/server/settlers.c Fri Nov 15 19:14:22 2002 +++ freeciv-1.14.0-shr2/server/settlers.c Tue Apr 8 05:41:43 2003 @@ -34,6 +34,7 @@ #include "aiunit.h" #include "aidata.h" +#include "rand.h" #include "settlers.h" /* negative: in_city_radius, 0: unassigned, positive: city_des */ @@ -1262,8 +1263,8 @@ /* Decide whether to build a new city: * if so, modify: gx, gy, best_newv, best_act */ - if (unit_flag(punit, F_CITIES) && - pplayer->ai.control) { + if (unit_flag(punit, F_CITIES) && pplayer->ai.control && + (myrand(101) <= game.slowai_grow)) { int nx, ny; int want = evaluate_city_building(punit, &nx, &ny, &ferryboat); diff -ur freeciv-1.14.0/server/stdinhand.c freeciv-1.14.0-shr2/server/stdinhand.c --- freeciv-1.14.0/server/stdinhand.c Fri Nov 15 19:14:22 2002 +++ freeciv-1.14.0-shr2/server/stdinhand.c Tue Apr 8 05:41:43 2003 @@ -239,7 +239,9 @@ "additional\n" " smaller islands.\n" "5 = one or more large earthlike continents with some scatter.\n" - "Note: values 2,3 and 4 generate \"fairer\" (but more boring) " + "6 = equally sized peninsulas with one player each surrounding \n" + " an inland sea.\n" + "Note: values 2,3,4 and 6 generate \"fairer\" (but more boring) " "maps.\n" "(Zero indicates a scenario map.)"), NULL, MAP_MIN_GENERATOR, MAP_MAX_GENERATOR, MAP_DEFAULT_GENERATOR) @@ -820,6 +822,24 @@ "40=debuging logging."), NULL, 0, 40, 20) + GEN_INT("slowaitech", game.slowai_tech, SSET_RULES_FLEXIBLE, + SSET_SERVER_ONLY, N_("Adjust AI tech advancement by %"), + N_("This value determines how much slower the AI will learn " + "new technologies compared to human players. Setting this to " + "a value greater than 100 will slow their advancement. For " + "example, giving this a value of 200 slows down the AI's tech " + "advancment by half."), NULL, GAME_MIN_SLOWAI_TECH, + GAME_MAX_SLOWAI_TECH, GAME_DEFAULT_SLOWAI_TECH) + + GEN_INT("slowaigrowth", game.slowai_grow, SSET_RULES_FLEXIBLE, + SSET_SERVER_ONLY, N_("Adjust city growth by %"), + N_("This value determines how much slower the AI will expand in " + "relation to human players. Setting a percent value under " + "100 will cause them to expand at about the specified rate " + "as compared to normal AI expansion."), NULL, + GAME_MIN_SLOWAI_GROW, GAME_MAX_SLOWAI_GROW, GAME_DEFAULT_SLOWAI_GROW) + + GEN_END }; Only in freeciv-1.14.0-shr2/server: stdinhand.c.orig