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

View File

@ -49,23 +49,30 @@
#include <assert.h>
const terrain_type *
random_terrain(boolean distribution)
random_terrain(const terrain_type * terrains[], int distribution[], int size)
{
static int nterrains;
static int ndistribution;
int ndistribution = size;
const terrain_type * terrain;
int n;
if (nterrains==0) {
for (terrain=terrains();terrain;terrain=terrain->next) {
ndistribution += terrain->distribution;
++nterrains;
if (distribution) {
ndistribution = 0;
for (n=0;n!=size;++n) {
ndistribution += distribution[n];
}
}
n = rng_int() % (distribution?ndistribution:nterrains);
for (terrain=terrains();terrain;terrain=terrain->next) {
n -= distribution?terrain->distribution:1;
if (n<0) break;
n = rng_int() % ndistribution;
if (distribution) {
int i;
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;
}
@ -412,9 +419,7 @@ free_newfaction(newfaction * nf)
free(nf->password);
free(nf);
}
/** create new island with up to nsize players
* returns the number of players placed on the new island.
*/
static void
frame_regions(int age, const terrain_type * terrain)
{
@ -435,6 +440,7 @@ frame_regions(int age, const terrain_type * terrain)
}
}
}
static void
prepare_starting_region(region * r)
{
@ -467,6 +473,9 @@ prepare_starting_region(region * r)
fix_demand(r);
}
/** create new island with up to nsize players
* returns the number of players placed on the new island.
*/
int
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 psize = 0; /* players on this island */
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));
if (listlen(*players)<MINFACTIONS) return 0;
@ -633,7 +662,7 @@ autoseed(newfaction ** players, int nsize, int max_agediff)
--isize;
if (psize>=PLAYERS_PER_ISLAND) break;
} else {
terraform_region(r, random_terrain(true));
terraform_region(r, random_terrain(terrainarr, distribution, nterrains));
--isize;
}
}
@ -664,7 +693,7 @@ autoseed(newfaction ** players, int nsize, int max_agediff)
const struct terrain_type * terrain = newterrain(T_OCEAN);
rn = new_region(r->x + delta_x[d], r->y + delta_y[d], 0);
if (rng_int() % SPECIALCHANCE < special) {
terrain = random_terrain(true);
terrain = random_terrain(terrainarr, distribution, nterrains);
special = SPECIALCHANCE / 3; /* 33% chance auf noch eines */
} else {
special = 1;
@ -706,3 +735,157 @@ autoseed(newfaction ** players, int nsize, int max_agediff)
}
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 void get_island(struct region * root, struct region_list ** rlist);
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 build_island_e3(short x, short y, int numfactions, int minsize);
#ifdef __cplusplus
}

View File

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