world generation algorithm for E3

This commit is contained in:
Enno Rehling 2009-06-23 16:17:38 +00:00
parent 27d8628084
commit fa2072b6e0
4 changed files with 376 additions and 185 deletions

View file

@ -1,6 +1,6 @@
/* vi: set ts=2: /* vi: set ts=2:
* *
* Eressea PB(E)M host Copyright (C) 1998-2003 * Eressea PB(E)M host Copyright (C) 1998-2003
* Christian Schlittchen (corwin@amber.kn-bremen.de) * Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de) * Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de) * Henning Peters (faroul@beyond.kn-bremen.de)
@ -72,9 +72,9 @@ struct unit_list;
struct weapon_type; struct weapon_type;
typedef struct ursprung { typedef struct ursprung {
struct ursprung *next; struct ursprung *next;
int id; int id;
short x, y; short x, y;
} ursprung; } ursprung;
/* ----------------- Befehle ----------------------------------- */ /* ----------------- Befehle ----------------------------------- */
@ -169,80 +169,80 @@ enum {
typedef unsigned char param_t; typedef unsigned char param_t;
enum { enum {
P_LOCALE, P_LOCALE,
P_ANY, P_ANY,
P_EACH, P_EACH,
P_PEASANT, P_PEASANT,
P_BUILDING, P_BUILDING,
P_UNIT, P_UNIT,
P_PRIVAT, P_PRIVAT,
P_BEHIND, P_BEHIND,
P_CONTROL, P_CONTROL,
P_HERBS, P_HERBS,
P_NOT, P_NOT,
P_NEXT, P_NEXT,
P_FACTION, P_FACTION,
P_GAMENAME, P_GAMENAME,
P_PERSON, P_PERSON,
P_REGION, P_REGION,
P_SHIP, P_SHIP,
P_MONEY, P_MONEY,
P_ROAD, P_ROAD,
P_TEMP, P_TEMP,
P_FLEE, P_FLEE,
P_GEBAEUDE, P_GEBAEUDE,
P_GIVE, P_GIVE,
P_FIGHT, P_FIGHT,
P_TRAVEL, P_TRAVEL,
P_GUARD, P_GUARD,
P_ZAUBER, P_ZAUBER,
P_PAUSE, P_PAUSE,
P_VORNE, P_VORNE,
P_AGGRO, P_AGGRO,
P_CHICKEN, P_CHICKEN,
P_LEVEL, P_LEVEL,
P_HELP, P_HELP,
P_FOREIGN, P_FOREIGN,
P_AURA, P_AURA,
P_FOR, P_FOR,
P_AID, P_AID,
P_MERCY, P_MERCY,
P_AFTER, P_AFTER,
P_BEFORE, P_BEFORE,
P_NUMBER, P_NUMBER,
P_ITEMS, P_ITEMS,
P_POTIONS, P_POTIONS,
P_GROUP, P_GROUP,
P_FACTIONSTEALTH, P_FACTIONSTEALTH,
P_TREES, P_TREES,
P_XEPOTION, P_XEPOTION,
P_XEBALLOON, P_XEBALLOON,
P_XELAEN, P_XELAEN,
MAXPARAMS, MAXPARAMS,
NOPARAM = (param_t) - 1 NOPARAM = (param_t) - 1
}; };
typedef enum { /* Fehler und Meldungen im Report */ typedef enum { /* Fehler und Meldungen im Report */
MSG_BATTLE, MSG_BATTLE,
MSG_EVENT, MSG_EVENT,
MSG_MOVE, MSG_MOVE,
MSG_INCOME, MSG_INCOME,
MSG_COMMERCE, MSG_COMMERCE,
MSG_PRODUCE, MSG_PRODUCE,
MSG_ORCVERMEHRUNG, MSG_ORCVERMEHRUNG,
MSG_MESSAGE, MSG_MESSAGE,
MSG_COMMENT, MSG_COMMENT,
MSG_MAGIC, MSG_MAGIC,
MAX_MSG MAX_MSG
} msg_t; } msg_t;
enum { /* Message-Level */ enum { /* Message-Level */
ML_IMPORTANT, /* Sachen, die IMO erscheinen _muessen_ */ ML_IMPORTANT, /* Sachen, die IMO erscheinen _muessen_ */
ML_DEBUG, ML_DEBUG,
ML_MISTAKE, ML_MISTAKE,
ML_WARN, ML_WARN,
ML_INFO, ML_INFO,
ML_MAX ML_MAX
}; };
extern const char *parameters[MAXPARAMS]; extern const char *parameters[MAXPARAMS];
@ -250,147 +250,147 @@ extern const char *parameters[MAXPARAMS];
/* --------------- Reports Typen ------------------------------- */ /* --------------- Reports Typen ------------------------------- */
enum { enum {
O_REPORT, /* 1 */ O_REPORT, /* 1 */
O_COMPUTER, /* 2 */ O_COMPUTER, /* 2 */
O_ZUGVORLAGE, /* 4 */ O_ZUGVORLAGE, /* 4 */
O_UNUSED_3, O_UNUSED_3,
O_STATISTICS, /* 16 */ O_STATISTICS, /* 16 */
O_DEBUG, /* 32 */ O_DEBUG, /* 32 */
O_COMPRESS, /* 64 */ O_COMPRESS, /* 64 */
O_NEWS, /* 128 */ O_NEWS, /* 128 */
O_UNUSED_8, O_UNUSED_8,
O_ADRESSEN, /* 512 */ O_ADRESSEN, /* 512 */
O_BZIP2, /* 1024 - compress as bzip2 */ O_BZIP2, /* 1024 - compress as bzip2 */
O_SCORE, /* 2048 - punkte anzeigen? */ O_SCORE, /* 2048 - punkte anzeigen? */
O_SHOWSKCHANGE, /* 4096 - Skillveränderungen anzeigen? */ O_SHOWSKCHANGE, /* 4096 - Skillveränderungen anzeigen? */
O_XML, /* 8192 - XML report versenden */ O_XML, /* 8192 - XML report versenden */
MAXOPTIONS MAXOPTIONS
}; };
/* ------------------ Talente ---------------------------------- */ /* ------------------ Talente ---------------------------------- */
enum { enum {
SK_ALCHEMY, SK_ALCHEMY,
SK_CROSSBOW, SK_CROSSBOW,
SK_MINING, SK_MINING,
SK_LONGBOW, SK_LONGBOW,
SK_BUILDING, SK_BUILDING,
SK_TRADE, SK_TRADE,
SK_LUMBERJACK, SK_LUMBERJACK,
SK_CATAPULT, SK_CATAPULT,
SK_HERBALISM, SK_HERBALISM,
SK_MAGIC, SK_MAGIC,
SK_HORSE_TRAINING, /* 10 */ SK_HORSE_TRAINING, /* 10 */
SK_RIDING, SK_RIDING,
SK_ARMORER, SK_ARMORER,
SK_SHIPBUILDING, SK_SHIPBUILDING,
SK_MELEE, SK_MELEE,
SK_SAILING, SK_SAILING,
SK_SPEAR, SK_SPEAR,
SK_SPY, SK_SPY,
SK_QUARRYING, SK_QUARRYING,
SK_ROAD_BUILDING, SK_ROAD_BUILDING,
SK_TACTICS, /* 20 */ SK_TACTICS, /* 20 */
SK_STEALTH, SK_STEALTH,
SK_ENTERTAINMENT, SK_ENTERTAINMENT,
SK_WEAPONSMITH, SK_WEAPONSMITH,
SK_CARTMAKER, SK_CARTMAKER,
SK_PERCEPTION, SK_PERCEPTION,
SK_TAXING, SK_TAXING,
SK_STAMINA, SK_STAMINA,
SK_WEAPONLESS, SK_WEAPONLESS,
MAXSKILLS, MAXSKILLS,
NOSKILL = (skill_t) -1 NOSKILL = (skill_t) -1
}; };
/* ------------- Typ von Einheiten ----------------------------- */ /* ------------- Typ von Einheiten ----------------------------- */
enum { enum {
RC_DWARF, /* 0 - Zwerg */ RC_DWARF, /* 0 - Zwerg */
RC_ELF, RC_ELF,
RC_ORC, RC_ORC,
RC_GOBLIN, RC_GOBLIN,
RC_HUMAN, RC_HUMAN,
RC_TROLL, RC_TROLL,
RC_DAEMON, RC_DAEMON,
RC_INSECT, RC_INSECT,
RC_HALFLING, RC_HALFLING,
RC_CAT, RC_CAT,
RC_AQUARIAN, RC_AQUARIAN,
RC_URUK, RC_URUK,
RC_SNOTLING, RC_SNOTLING,
RC_UNDEAD, RC_UNDEAD,
RC_ILLUSION, RC_ILLUSION,
RC_FIREDRAGON, RC_FIREDRAGON,
RC_DRAGON, RC_DRAGON,
RC_WYRM, RC_WYRM,
RC_TREEMAN, RC_TREEMAN,
RC_BIRTHDAYDRAGON, RC_BIRTHDAYDRAGON,
RC_DRACOID, RC_DRACOID,
RC_SPECIAL, RC_SPECIAL,
RC_SPELL, RC_SPELL,
RC_IRONGOLEM, RC_IRONGOLEM,
RC_STONEGOLEM, RC_STONEGOLEM,
RC_SHADOW, RC_SHADOW,
RC_SHADOWLORD, RC_SHADOWLORD,
RC_IRONKEEPER, RC_IRONKEEPER,
RC_ALP, RC_ALP,
RC_TOAD, RC_TOAD,
RC_HIRNTOETER, RC_HIRNTOETER,
RC_PEASANT, RC_PEASANT,
RC_WOLF = 32, RC_WOLF = 32,
RC_SONGDRAGON = 37, RC_SONGDRAGON = 37,
RC_SEASERPENT = 51, RC_SEASERPENT = 51,
RC_SHADOWKNIGHT, RC_SHADOWKNIGHT,
RC_CENTAUR, RC_CENTAUR,
RC_SKELETON, RC_SKELETON,
RC_SKELETON_LORD, RC_SKELETON_LORD,
RC_ZOMBIE, RC_ZOMBIE,
RC_ZOMBIE_LORD, RC_ZOMBIE_LORD,
RC_GHOUL, RC_GHOUL,
RC_GHOUL_LORD, RC_GHOUL_LORD,
RC_MUS_SPIRIT, RC_MUS_SPIRIT,
RC_GNOME, RC_GNOME,
RC_TEMPLATE, RC_TEMPLATE,
RC_CLONE, RC_CLONE,
MAXRACES, MAXRACES,
NORACE = (race_t) - 1 NORACE = (race_t) - 1
}; };
/* Richtungen */ /* Richtungen */
enum { enum {
D_NORTHWEST, D_NORTHWEST,
D_NORTHEAST, D_NORTHEAST,
D_EAST, D_EAST,
D_SOUTHEAST, D_SOUTHEAST,
D_SOUTHWEST, D_SOUTHWEST,
D_WEST, D_WEST,
MAXDIRECTIONS, MAXDIRECTIONS,
D_PAUSE, D_PAUSE,
D_SPECIAL, D_SPECIAL,
NODIRECTION = (direction_t) - 1 NODIRECTION = (direction_t) - 1
}; };
#define DONT_HELP 0 #define DONT_HELP 0
#define HELP_MONEY 1 /* Mitversorgen von Einheiten */ #define HELP_MONEY 1 /* Mitversorgen von Einheiten */
#define HELP_FIGHT 2 /* Bei Verteidigung mithelfen */ #define HELP_FIGHT 2 /* Bei Verteidigung mithelfen */
#define HELP_OBSERVE 4 /* Bei Wahrnehmung mithelfen */ #define HELP_OBSERVE 4 /* Bei Wahrnehmung mithelfen */
#define HELP_GIVE 8 /* Dinge annehmen ohne KONTAKTIERE */ #define HELP_GIVE 8 /* Dinge annehmen ohne KONTAKTIERE */
#define HELP_GUARD 16 /* Laesst Steuern eintreiben etc. */ #define HELP_GUARD 16 /* Laesst Steuern eintreiben etc. */
#define HELP_FSTEALTH 32 /* Parteitarnung anzeigen. */ #define HELP_FSTEALTH 32 /* Parteitarnung anzeigen. */
#define HELP_TRAVEL 64 /* Laesst Regionen betreten. */ #define HELP_TRAVEL 64 /* Laesst Regionen betreten. */
#define HELP_ALL (127-HELP_TRAVEL-HELP_OBSERVE) /* Alle "positiven" HELPs zusammen */ #define HELP_ALL (127-HELP_TRAVEL-HELP_OBSERVE) /* Alle "positiven" HELPs zusammen */
/* HELP_OBSERVE deaktiviert */ /* HELP_OBSERVE deaktiviert */
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/* Prototypen */ /* Prototypen */

View file

@ -49,23 +49,30 @@
#include <assert.h> #include <assert.h>
const terrain_type * const terrain_type *
random_terrain(boolean distribution) random_terrain(const terrain_type * terrains[], int distribution[], int size)
{ {
static int nterrains; int ndistribution = size;
static int ndistribution;
const terrain_type * terrain; const terrain_type * terrain;
int n; int n;
if (nterrains==0) {
for (terrain=terrains();terrain;terrain=terrain->next) { if (distribution) {
ndistribution += terrain->distribution; ndistribution = 0;
++nterrains; for (n=0;n!=size;++n) {
ndistribution += distribution[n];
} }
} }
n = rng_int() % (distribution?ndistribution:nterrains); n = rng_int() % ndistribution;
for (terrain=terrains();terrain;terrain=terrain->next) { if (distribution) {
n -= distribution?terrain->distribution:1; int i;
if (n<0) break; for (i=0;i!=size;++i) {
n -= distribution[i];
if (n<0) break;
}
assert(i<size);
terrain = terrains[i];
} else {
terrain = terrains[n];
} }
return terrain; return terrain;
} }
@ -412,9 +419,7 @@ free_newfaction(newfaction * nf)
free(nf->password); free(nf->password);
free(nf); free(nf);
} }
/** create new island with up to nsize players
* returns the number of players placed on the new island.
*/
static void static void
frame_regions(int age, const terrain_type * terrain) frame_regions(int age, const terrain_type * terrain)
{ {
@ -435,6 +440,7 @@ frame_regions(int age, const terrain_type * terrain)
} }
} }
} }
static void static void
prepare_starting_region(region * r) prepare_starting_region(region * r)
{ {
@ -467,6 +473,9 @@ prepare_starting_region(region * r)
fix_demand(r); fix_demand(r);
} }
/** create new island with up to nsize players
* returns the number of players placed on the new island.
*/
int int
autoseed(newfaction ** players, int nsize, int max_agediff) autoseed(newfaction ** players, int nsize, int max_agediff)
{ {
@ -477,7 +486,27 @@ autoseed(newfaction ** players, int nsize, int max_agediff)
int isize = REGIONS_PER_FACTION; /* target size for the island */ int isize = REGIONS_PER_FACTION; /* target size for the island */
int psize = 0; /* players on this island */ int psize = 0; /* players on this island */
const terrain_type * volcano_terrain = get_terrain("volcano"); const terrain_type * volcano_terrain = get_terrain("volcano");
static int nterrains = -1;
static const terrain_type ** terrainarr = 0;
static int * distribution;
if (nterrains<0) {
int n = 0;
const terrain_type * terrain = terrains();
for (nterrains=0;terrain;terrain=terrain->next) {
if (terrain->distribution) {
++nterrains;
}
}
terrainarr = malloc(sizeof(terrain_type *) * nterrains);
distribution = malloc(sizeof(int) * nterrains);
for (terrain = terrains();terrain;terrain = terrain->next) {
if (terrain->distribution) {
terrainarr[n] = terrain;
distribution[n++] = terrain->distribution;
}
}
}
frame_regions(16, newterrain(T_FIREWALL)); frame_regions(16, newterrain(T_FIREWALL));
if (listlen(*players)<MINFACTIONS) return 0; if (listlen(*players)<MINFACTIONS) return 0;
@ -633,7 +662,7 @@ autoseed(newfaction ** players, int nsize, int max_agediff)
--isize; --isize;
if (psize>=PLAYERS_PER_ISLAND) break; if (psize>=PLAYERS_PER_ISLAND) break;
} else { } else {
terraform_region(r, random_terrain(true)); terraform_region(r, random_terrain(terrainarr, distribution, nterrains));
--isize; --isize;
} }
} }
@ -664,7 +693,7 @@ autoseed(newfaction ** players, int nsize, int max_agediff)
const struct terrain_type * terrain = newterrain(T_OCEAN); const struct terrain_type * terrain = newterrain(T_OCEAN);
rn = new_region(r->x + delta_x[d], r->y + delta_y[d], 0); rn = new_region(r->x + delta_x[d], r->y + delta_y[d], 0);
if (rng_int() % SPECIALCHANCE < special) { if (rng_int() % SPECIALCHANCE < special) {
terrain = random_terrain(true); terrain = random_terrain(terrainarr, distribution, nterrains);
special = SPECIALCHANCE / 3; /* 33% chance auf noch eines */ special = SPECIALCHANCE / 3; /* 33% chance auf noch eines */
} else { } else {
special = 1; special = 1;
@ -706,3 +735,157 @@ autoseed(newfaction ** players, int nsize, int max_agediff)
} }
return tsize; return tsize;
} }
static void get_neighbours(const region * r, region ** list)
{
direction_t dir;
for (dir=0;dir!=MAXDIRECTIONS;++dir) {
list[dir] = rconnect(r, dir);
}
}
region_list * regionqueue_push(region_list ** rlist, region * r)
{
region_list * rnew = malloc(sizeof(region_list));
rnew->data = r;
rnew->next = 0;
while (*rlist) { rlist = &(*rlist)->next; }
*rlist = rnew;
return rnew;
}
region * regionqueue_pop(region_list ** rlist)
{
if (*rlist) {
region * r = (*rlist)->data;
region_list * rpop = *rlist;
*rlist = rpop->next;
free(rpop);
return r;
}
return 0;
}
#define GEOMAX 8
static struct geo {
int distribution;
terrain_t type;
} geography_e3[GEOMAX] = {
{ 8, T_OCEAN },
{ 3, T_SWAMP },
{ 1, T_VOLCANO },
{ 3, T_DESERT },
{ 3, T_HIGHLAND },
{ 3, T_MOUNTAIN },
{ 2, T_GLACIER },
{ 1, T_PLAIN }
};
const terrain_type * random_terrain_e3(direction_t dir)
{
static const terrain_type ** terrainarr = 0;
static int * distribution = 0;
if (!distribution) {
int n = 0;
terrainarr = malloc(GEOMAX * sizeof(const terrain_type *));
distribution = malloc(GEOMAX * sizeof(int));
for (n=0;n!=GEOMAX;++n) {
terrainarr[n] = newterrain(geography_e3[n].type);
distribution[n] = geography_e3[n].distribution;
}
}
return random_terrain(terrainarr, distribution, GEOMAX);
}
int
random_neighbours(region * r, region_list ** rlist, const terrain_type *(*terraformer)(direction_t))
{
int nsize = 0;
direction_t dir;
for (dir=0;dir!=MAXDIRECTIONS;++dir) {
region * rn = rconnect(r, dir);
if (rn==NULL) {
const terrain_type * terrain = terraformer(dir);
rn = new_region(r->x+delta_x[dir], r->y+delta_y[dir], 0);
terraform_region(rn, terrain);
if (rn->land) {
regionqueue_push(rlist, rn);
++nsize;
}
}
}
return nsize;
}
const terrain_type * get_ocean(direction_t dir)
{
return newterrain(T_OCEAN);
}
int region_quality(const region * r)
{
region * rn[MAXDIRECTIONS];
int n, result = 0;
get_neighbours(r, rn);
for (n=0;n!=MAXDIRECTIONS;++n) {
if (rn[n] && rn[n]->land) {
result += rn[n]->land->peasants;
}
}
// result += r->land->peasants * 2;
return result;
}
/* E3A island generation */
int
build_island_e3(short x, short y, int numfactions, int minsize)
{
int nfactions = 0;
region_list * rlist = NULL;
region_list * island = NULL;
region * r = new_region(x, y, 0);
int nsize = 1;
int q, maxq = INT_MIN, minq = INT_MAX;
terraform_region(r, random_terrain_e3(NODIRECTION));
while (r) {
if (r->land) {
fset(r, RF_MARK);
regionqueue_push(&island, r);
}
if (nsize<minsize) {
nsize += random_neighbours(r, &rlist, &random_terrain_e3);
} else {
nsize += random_neighbours(r, &rlist, &get_ocean);
}
r = regionqueue_pop(&rlist);
}
for (rlist=island;rlist;rlist=rlist->next) {
r = rlist->data;
if (fval(r, RF_MARK)) {
region *rn[MAXDIRECTIONS];
int n;
unit * u;
freset(r, RF_MARK);
get_neighbours(r, rn);
for (n=0;n!=MAXDIRECTIONS;++n) {
freset(rn[n], RF_MARK);
}
q = region_quality(r);
if (q>1500 && nfactions<numfactions) {
terraform_region(r, newterrain(T_PLAIN));
minq = MIN(minq, q);
maxq = MAX(maxq, q);
prepare_starting_region(r);
u = addplayer(r, addfaction("enno@eressea.de", itoa36(rng_int()), races,
default_locale, 0));
++nfactions;
}
}
}
return nfactions;
}

View file

@ -39,9 +39,10 @@ extern int autoseed(newfaction ** players, int nsize, int max_agediff);
extern newfaction * read_newfactions(const char * filename); extern newfaction * read_newfactions(const char * filename);
extern void get_island(struct region * root, struct region_list ** rlist); extern void get_island(struct region * root, struct region_list ** rlist);
extern int fix_demand(struct region *r); extern int fix_demand(struct region *r);
extern const struct terrain_type * random_terrain(boolean use_distribution); extern const struct terrain_type * random_terrain(const struct terrain_type * terrains[], int distribution[], int size);
extern int seed_adamantium(struct region * r, int base); extern int seed_adamantium(struct region * r, int base);
extern int build_island_e3(short x, short y, int numfactions, int minsize);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -28,6 +28,7 @@
#include <modules/arena.h> #include <modules/arena.h>
#endif #endif
#include <modules/wormhole.h> #include <modules/wormhole.h>
#include <modules/autoseed.h>
#if DUNGEON_MODULE #if DUNGEON_MODULE
#include <modules/dungeon.h> #include <modules/dungeon.h>
#endif #endif
@ -56,6 +57,7 @@
#include <items/itemtypes.h> #include <items/itemtypes.h>
#include <util/log.h> #include <util/log.h>
#include <util/rng.h>
#include <util/base36.h> #include <util/base36.h>
#include <util/storage.h> #include <util/storage.h>
@ -727,6 +729,7 @@ handlekey(state * st, int c)
region *r; region *r;
char sbuffer[80]; char sbuffer[80];
static char kbuffer[80]; static char kbuffer[80];
int n;
switch(c) { switch(c) {
case FAST_RIGHT: case FAST_RIGHT:
@ -785,7 +788,11 @@ handlekey(state * st, int c)
} }
break; break;
case 'B': case 'B':
/*
make_block((short)st->cursor.x, (short)st->cursor.y, 6, select_terrain(st, NULL)); make_block((short)st->cursor.x, (short)st->cursor.y, 6, select_terrain(st, NULL));
*/
n = rng_int() % 8 + 8;
build_island_e3((short)st->cursor.x, (short)st->cursor.y, n, n*4);
st->modified = 1; st->modified = 1;
st->wnd_info->update |= 1; st->wnd_info->update |= 1;
st->wnd_status->update |= 1; st->wnd_status->update |= 1;