[Freeciv-Dev] [PATCH] Add generator which simulates plate tectonics
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
[Sorry about the last posting]
I can't take credit for this plate tectonics code. I was looking around
on the internet for some information on map generation algorithms.
I found out that there was once a mailing list called USML (Universe
Simulation Mailing List) which looked at all kinds of different
algorithms for simulating universes.
Included with these algorithms was code written by Mark Isaac to simulate
plate tectonics. Since Mark is still an active net.citizen, and is
currently a regular on talk.origins [1], I was able to find his current
email address to ask him permission to GPL his tectonic simulator.
He very kindly gave me this permission, so I have modified his code
to compile cleanly in FreeCiv; the code is from 1988 and still has
its K&R-style function arguments.
This patch is designed to be applied after applying Josh Cogliati's patch
(http://rt.freeciv.org/Ticket/Display.html?id=3489) which adds a sixth
map generator to freeciv.
To use this patch, simply set the generator to 7 (which will generate
pure tectonic shift maps) or 8 (which will generate hybrid maps: Maps
which are tectonically generated maps with filtered noise added so that
the oceans have more islands to explore).
Note that this code takes far longer to generate maps than the other
generators; on the other hand, the lands generated are far more realistic,
especially for larger maps.
- Sam
[1] For the record, I am a staunch anti-creationist. I am also a
devout Catholic.
--- freeciv-1.14.0/common/map.h.orig Sat Mar 22 17:25:36 2003
+++ freeciv-1.14.0/common/map.h Sat Mar 22 17:42:07 2003
@@ -619,7 +619,7 @@
#define MAP_DEFAULT_GENERATOR 1
#define MAP_MIN_GENERATOR 1
-#define MAP_MAX_GENERATOR 6
+#define MAP_MAX_GENERATOR 8
#define MAP_DEFAULT_TINYISLES FALSE
#define MAP_MIN_TINYISLES FALSE
--- freeciv-1.14.0/server/mapgen.c.orig Sat Mar 22 17:25:49 2003
+++ freeciv-1.14.0/server/mapgen.c Sat Mar 22 18:01:44 2003
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <math.h> /* For the plate tectonic simulator */
#include "fcintl.h"
#include "game.h"
@@ -42,6 +43,7 @@
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);
@@ -824,7 +826,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;
@@ -847,7 +849,7 @@
else
tres*=9;
tres/=10;
- } while (abs(total-count)> maxval/40);
+ } while (abs(total-count)> window_size/40);
if (map.separatepoles) {
make_passable();
}
@@ -1199,6 +1201,8 @@
/* 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 )
@@ -1248,7 +1252,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 */
@@ -1292,7 +1296,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)
@@ -1341,7 +1345,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;
}
@@ -2211,7 +3037,7 @@
maxval -= minval;
adjust_map(minval);
- make_land();
+ make_land(maxval);
free(height_map);
height_map = NULL;
}
@@ -2256,7 +3082,7 @@
}
/* Creates a peninsula and put the player's starting position
- on it */
+ 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)
|
|