/* vi: set ts=2: +-------------------+ Christian Schlittchen | | Enno Rehling | Eressea PBEM host | Katja Zedel | (c) 1998 - 2003 | Henning Peters | | Ingo Wilken +-------------------+ Stefan Reich This program may not be used, modified or distributed without prior permission by the authors of Eressea. */ #include #include #include "autoseed.h" /* kernel includes */ #include #include #include #include #include #include #include #include #include #include #include /* util includes */ #include #include #include #include #include #include #include #include #include #include /* libc includes */ #include #include #include #include #include const terrain_type *random_terrain(const terrain_type * terrains[], int distribution[], int size) { int ndistribution = size; const terrain_type *terrain; int n; if (distribution) { ndistribution = 0; for (n = 0; n != size; ++n) { ndistribution += distribution[n]; } } 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; } int seed_adamantium(region * r, int base) { const resource_type *rtype = rt_find("adamantium"); rawmaterial *rm; for (rm = r->resources; rm; rm = rm->next) { if (rm->type->rtype == rtype) break; } if (!rm) { add_resource(r, 1, base, 150, rtype); } return 0; } static int count_demand(const region * r) { struct demand *dmd; int c = 0; if (r->land) { for (dmd = r->land->demands; dmd; dmd = dmd->next) c++; } return c; } static int recurse_regions(region * r, region_list ** rlist, boolean(*fun) (const region * r)) { if (!fun(r)) return 0; else { int len = 0; direction_t d; region_list *rl = calloc(sizeof(region_list), 1); rl->next = *rlist; rl->data = r; (*rlist) = rl; fset(r, RF_MARK); for (d = 0; d != MAXDIRECTIONS; ++d) { region *nr = rconnect(r, d); if (nr && !fval(nr, RF_MARK)) len += recurse_regions(nr, rlist, fun); } return len + 1; } } static boolean f_nolux(const region * r) { if (r->land && count_demand(r) != get_maxluxuries()) return true; return false; } int fix_demand(region * rd) { region_list *rl, *rlist = NULL; static const struct luxury_type **mlux = 0, **ltypes; const luxury_type *sale = NULL; int maxlux = 0; int maxluxuries = get_maxluxuries(); if (maxluxuries == 0) return 0; recurse_regions(rd, &rlist, f_nolux); if (mlux == 0) { int i = 0; mlux = (const luxury_type **)gc_add(calloc(maxluxuries, sizeof(const luxury_type *))); ltypes = (const luxury_type **)gc_add(calloc(maxluxuries, sizeof(const luxury_type *))); for (sale = luxurytypes; sale; sale = sale->next) { ltypes[i++] = sale; } } else { int i; for (i = 0; i != maxluxuries; ++i) mlux[i] = 0; } for (rl = rlist; rl; rl = rl->next) { region *r = rl->data; direction_t d; for (d = 0; d != MAXDIRECTIONS; ++d) { region *nr = rconnect(r, d); if (nr && nr->land && nr->land->demands) { struct demand *dmd; for (dmd = nr->land->demands; dmd; dmd = dmd->next) { if (dmd->value == 0) { int i; for (i = 0; i != maxluxuries; ++i) { if (mlux[i] == NULL) { maxlux = i; mlux[i] = dmd->type; break; } else if (mlux[i] == dmd->type) { break; } } break; } } } } freset(r, RF_MARK); /* undo recursive marker */ } if (maxlux < 2) { int i; for (i = maxlux; i != 2; ++i) { int j; do { int k = rng_int() % maxluxuries; mlux[i] = ltypes[k]; for (j = 0; j != i; ++j) { if (mlux[j] == mlux[i]) break; } } while (j != i); } maxlux = 2; } for (rl = rlist; rl; rl = rl->next) { region *r = rl->data; if (!fval(r, RF_CHAOTIC)) { log_debug("fixing demand in %s\n", regionname(r, NULL)); } sale = mlux[rng_int() % maxlux]; if (sale) setluxuries(r, sale); } while (rlist) { rl = rlist->next; free(rlist); rlist = rl; } return 0; } /* nach 150 Runden ist Neustart erlaubt */ #define MINAGE_MULTI 150 newfaction *read_newfactions(const char *filename) { newfaction *newfactions = NULL; FILE *F = fopen(filename, "r"); char buf[1024]; if (F == NULL) return NULL; for (;;) { faction *f; char race[20], email[64], lang[8], password[16]; newfaction *nf, **nfi; int bonus = 0, subscription = 0; int alliance = 0; if (fgets(buf, sizeof(buf), F) == NULL) break; email[0] = '\0'; password[0] = '\0'; if (sscanf(buf, "%54s %20s %8s %d %d %16s %d", email, race, lang, &bonus, &subscription, password, &alliance) < 6) break; if (email[0] == '\0') break; if (password[0] == '\0') { strcpy(password, itoa36(rng_int())); strcat(password, itoa36(rng_int())); } for (f = factions; f; f = f->next) { if (strcmp(f->email, email) == 0 && f->subscription && f->age < MINAGE_MULTI) break; } if (f && f->units) continue; /* skip the ones we've already got */ for (nf = newfactions; nf; nf = nf->next) { if (strcmp(nf->email, email) == 0) break; } if (nf) continue; nf = calloc(sizeof(newfaction), 1); if (set_email(&nf->email, email) != 0) { log_error("Invalid email address for subscription %s: %s\n", itoa36(subscription), email); continue; } nf->password = strdup(password); nf->race = rc_find(race); nf->subscription = subscription; if (alliances != NULL) { struct alliance *al = findalliance(alliance); if (al == NULL) { char zText[64]; sprintf(zText, "Allianz %d", alliance); al = makealliance(alliance, zText); } nf->allies = al; } else { nf->allies = NULL; } if (nf->race == NULL) { /* if the script didn't supply the race as a token, then it gives us a * race in the default locale (which means that itis a UTF8 string) */ nf->race = findrace(race, default_locale); if (nf->race == NULL) { char buffer[32]; size_t outbytes = sizeof(buffer) - 1; size_t inbytes = strlen(race); unicode_latin1_to_utf8(buffer, &outbytes, race, &inbytes); buffer[outbytes] = 0; nf->race = findrace(buffer, default_locale); if (nf->race == NULL) { log_error("new faction has unknown race '%s'.\n", race); free(nf); continue; } } } nf->lang = find_locale(lang); nf->bonus = bonus; assert(nf->race && nf->email && nf->lang); nfi = &newfactions; while (*nfi) { if ((*nfi)->race == nf->race) break; nfi = &(*nfi)->next; } nf->next = *nfi; *nfi = nf; } fclose(F); return newfactions; } extern int numnewbies; static const terrain_type *preferred_terrain(const struct race *rc) { terrain_t t = T_PLAIN; if (rc == rc_find("dwarf")) t = T_MOUNTAIN; if (rc == rc_find("insect")) t = T_DESERT; if (rc == rc_find("halfling")) t = T_SWAMP; if (rc == rc_find("troll")) t = T_MOUNTAIN; return newterrain(t); } #define REGIONS_PER_FACTION 2 #define PLAYERS_PER_ISLAND 20 #define MAXISLANDSIZE 50 #define MINFACTIONS 1 #define VOLCANO_CHANCE 100 static boolean virgin_region(const region * r) { direction_t d; if (r == NULL) return true; if (fval(r->terrain, FORBIDDEN_REGION)) return false; if (r->units) return false; for (d = 0; d != MAXDIRECTIONS; ++d) { const region *rn = rconnect(r, d); if (rn) { if (rn->age > r->age + 1) return false; if (rn->units) return false; if (fval(rn->terrain, FORBIDDEN_REGION)) { /* because it kinda sucks to have islands that are adjacent to a firewall */ return false; } } } return true; } static quicklist * get_island(region * root) { quicklist * ql, * result = 0; int qi = 0; fset(root, RF_MARK); ql_push(&result, root); for (ql=result,qi=0; ql; ql_advance(&ql, &qi, 1)) { int dir; region *r = (region *)ql_get(ql, qi); region * next[MAXDIRECTIONS]; get_neighbours(r, next); for (dir = 0; dir != MAXDIRECTIONS; ++dir) { region *rn = next[dir]; if (rn != NULL && rn->land && !fval(rn, RF_MARK)) { fset(rn, RF_MARK); ql_push(&result, rn); } } } for (ql=result,qi=0; ql; ql_advance(&ql, &qi, 1)) { region *r = (region *)ql_get(ql, qi); freset(r, RF_MARK); } return result; } static void get_island_info(region * root, int *size_p, int *inhabited_p, int *maxage_p) { int qi, size = 0, maxage = 0, inhabited = 0; quicklist *ql, *island = NULL; ql_push(&island, root); fset(root, RF_MARK); for (ql=island,qi=0; ql; ql_advance(&ql, &qi, 1)) { int d; region *r = (region *)ql_get(ql, qi); if (r->units) { unit *u; for (u = r->units; u; u = u->next) { if (!is_monsters(u->faction) && u->faction->age > maxage) { maxage = u->faction->age; } } ++inhabited; } ++size; for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = rconnect(r, d); if (rn && !fval(rn, RF_MARK) && rn->land) { ql_push(&island, rn); fset(rn, RF_MARK); } } } for (ql=island,qi=0; ql; ql_advance(&ql, &qi, 1)) { region *r = (region *)ql_get(ql, qi); freset(r, RF_MARK); } ql_free(island); if (size_p) *size_p = size; if (inhabited_p) *inhabited_p = inhabited; if (maxage_p) *maxage_p = maxage; } void free_newfaction(newfaction * nf) { free(nf->email); free(nf->password); free(nf); } static void frame_regions(int age, const terrain_type * terrain) { plane *hplane = get_homeplane(); region *r = regions; for (r = regions; r; r = r->next) { plane *pl = rplane(r); direction_t d; if (r->age < age) continue; if (pl != hplane) continue; /* only do this on the main world */ if (r->terrain == terrain) continue; for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = rconnect(r, d); if (rn == NULL) { int x = r->x + delta_x[d]; int y = r->y + delta_y[d]; pnormalize(&x, &y, pl); rn = new_region(x, y, pl, 0); terraform_region(rn, terrain); rn->age = r->age; } } } } static void prepare_starting_region(region * r) { int n, t; double p; assert(r->land); /* population between 30% and 60% of max */ p = rng_double(); n = (int)(r->terrain->size * (0.3 + p * 0.3)); rsetpeasants(r, n); /* trees: don't squash the peasants, and at least 5% should be forrest */ t = (rtrees(r, 2) + rtrees(r, 1) / 2) * TREESIZE; if (t < r->terrain->size / 20 || t + n > r->terrain->size) { double p2 = 0.05 + rng_double() * (1.0 - p - 0.05); int maxtrees = (int)(r->terrain->size / 1.25 / TREESIZE); /* 1.25 = each young tree will take 1/2 the space of old trees */ int trees = (int)(p2 * maxtrees); rsettrees(r, 2, trees); rsettrees(r, 1, trees / 2); rsettrees(r, 0, trees / 4); } /* horses: between 1% and 2% */ p = rng_double(); rsethorses(r, (int)(r->terrain->size * (0.01 + p * 0.01))); if (!markets_module()) { 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) { region *r = NULL; region_list *rlist = NULL; int rsize = 0, tsize = 0; 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; if (max_agediff > 0) { region *rmin = NULL; plane *hplane = get_homeplane(); /* find a spot that's adjacent to the previous island, but virgin. * like the last land virgin ocean region adjacent to land. */ for (r = regions; r; r = r->next) { struct plane *pl = rplane(r); if (r->age <= max_agediff && r->terrain == newterrain(T_OCEAN) && pl == hplane && virgin_region(r)) { direction_t d; for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = rconnect(r, d); if (rn && rn->land && rn->age <= max_agediff && virgin_region(rn)) { /* only expand islands that aren't single-islands and not too big already */ int size, inhabitants, maxage; get_island_info(rn, &size, &inhabitants, &maxage); if (maxage <= max_agediff && size >= 2 && size < MAXISLANDSIZE) { rmin = rn; break; } } } } } if (rmin != NULL) { faction *f; quicklist *ql, *rlist = get_island(rmin); int qi; for (ql=rlist,qi=0;ql;ql_advance(&ql, &qi, 1)) { region *r = (region *)ql_get(ql, qi); unit *u; for (u = r->units; u; u = u->next) { f = u->faction; if (!fval(f, FFL_MARK)) { ++psize; fset(f, FFL_MARK); } } } ql_free(rlist); if (psize > 0) { for (f = factions; f; f = f->next) { freset(f, FFL_MARK); } } if (psize < PLAYERS_PER_ISLAND) { r = rmin; } } } if (r == NULL) { region *rmin = NULL; direction_t dmin = MAXDIRECTIONS; plane *hplane = get_homeplane(); /* find an empty spot. * rmin = the youngest ocean region that has a missing neighbour * dmin = direction in which it's empty */ for (r = regions; r; r = r->next) { struct plane *pl = rplane(r); if (r->terrain == newterrain(T_OCEAN) && pl == hplane && (rmin == NULL || r->age <= max_agediff)) { direction_t d; for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = rconnect(r, d); if (rn == NULL) break; } if (d != MAXDIRECTIONS) { rmin = r; dmin = d; } } } /* create a new region where we found the empty spot, and make it the first * in our island. island regions are kept in rlist, so only new regions can * get populated, and old regions are not overwritten */ if (rmin != NULL) { plane *pl = rplane(rmin); int x = rmin->x + delta_x[dmin]; int y = rmin->y + delta_y[dmin]; pnormalize(&x, &y, pl); assert(virgin_region(rconnect(rmin, dmin))); r = new_region(x, y, pl, 0); terraform_region(r, newterrain(T_OCEAN)); } } if (r != NULL) { add_regionlist(&rlist, r); fset(r, RF_MARK); rsize = 1; } while (rsize && (nsize || isize >= REGIONS_PER_FACTION)) { int i = rng_int() % rsize; region_list **rnext = &rlist; region_list *rfind; direction_t d; while (i--) rnext = &(*rnext)->next; rfind = *rnext; r = rfind->data; freset(r, RF_MARK); *rnext = rfind->next; free(rfind); --rsize; for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = rconnect(r, d); if (rn && fval(rn, RF_MARK)) continue; if (rn == NULL) { plane *pl = rplane(r); int x = r->x + delta_x[d]; int y = r->y + delta_y[d]; pnormalize(&x, &y, pl); rn = new_region(x, y, pl, 0); terraform_region(rn, newterrain(T_OCEAN)); } if (virgin_region(rn)) { add_regionlist(&rlist, rn); fset(rn, RF_MARK); ++rsize; } } if (volcano_terrain != NULL && (rng_int() % VOLCANO_CHANCE == 0)) { terraform_region(r, volcano_terrain); } else if (nsize && (rng_int() % isize == 0 || rsize == 0)) { newfaction **nfp, *nextf = *players; faction *f; unit *u; isize += REGIONS_PER_FACTION; terraform_region(r, preferred_terrain(nextf->race)); prepare_starting_region(r); ++tsize; assert(r->land && r->units == 0); u = addplayer(r, addfaction(nextf->email, nextf->password, nextf->race, nextf->lang, nextf->subscription)); f = u->faction; fset(f, FFL_ISNEW); f->alliance = nextf->allies; log_printf(stdout, "New faction (%s), %s at %s\n", itoa36(f->no), f->email, regionname(r, NULL)); if (f->subscription) { sql_print( ("UPDATE subscriptions SET status='ACTIVE', faction='%s', firstturn=%d, lastturn=%d, password='%s' WHERE id=%u;\n", factionid(f), f->lastorders, f->lastorders, f->override, f->subscription)); } /* remove duplicate email addresses */ nfp = &nextf->next; while (*nfp) { newfaction *nf = *nfp; if (strcmp(nextf->email, nf->email) == 0) { *nfp = nf->next; free_newfaction(nf); } else nfp = &nf->next; } *players = nextf->next; free_newfaction(nextf); ++psize; --nsize; --isize; if (psize >= PLAYERS_PER_ISLAND) break; } else { terraform_region(r, random_terrain(terrainarr, distribution, nterrains)); --isize; } } if (nsize != 0) { log_error( ("Could not place all factions on the same island as requested\n")); } if (rlist) { #define MINOCEANDIST 3 #define MAXOCEANDIST 6 #define MAXFILLDIST 10 #define SPECIALCHANCE 80 region_list **rbegin = &rlist; int special = 1; int oceandist = MINOCEANDIST + (rng_int() % (MAXOCEANDIST - MINOCEANDIST)); while (oceandist--) { region_list **rend = rbegin; while (*rend) rend = &(*rend)->next; while (rbegin != rend) { direction_t d; region *r = (*rbegin)->data; rbegin = &(*rbegin)->next; for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = rconnect(r, d); if (rn == NULL) { const struct terrain_type *terrain = newterrain(T_OCEAN); plane *pl = rplane(r); int x = r->x + delta_x[d]; int y = r->y + delta_y[d]; pnormalize(&x, &y, pl); rn = new_region(x, y, pl, 0); if (rng_int() % SPECIALCHANCE < special) { terrain = random_terrain(terrainarr, distribution, nterrains); special = SPECIALCHANCE / 3; /* 33% chance auf noch eines */ } else { special = 1; } terraform_region(rn, terrain); /* the new region has an extra 20% chance to have mallorn */ if (rng_int() % 100 < 20) fset(r, RF_MALLORN); add_regionlist(rend, rn); } } } } while (*rbegin) { region *r = (*rbegin)->data; plane *pl = rplane(r); direction_t d; rbegin = &(*rbegin)->next; for (d = 0; d != MAXDIRECTIONS; ++d) if (rconnect(r, d) == NULL) { int i; for (i = 1; i != MAXFILLDIST; ++i) { int x = r->x + delta_x[d] * i; int y = r->y + delta_y[d] * i; pnormalize(&x, &y, pl); if (findregion(x, y)) { break; } } if (i != MAXFILLDIST) { while (--i) { region *rn; int x = r->x + delta_x[d] * i; int y = r->y + delta_y[d] * i; pnormalize(&x, &y, pl); rn = new_region(x, y, pl, 0); terraform_region(rn, newterrain(T_OCEAN)); } } } } while (rlist) { region_list *self = rlist; rlist = rlist->next; freset(self->data, RF_MARK); free(self); } } return tsize; } 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}, { 4, 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); plane *pl = rplane(r); int x = r->x + delta_x[dir]; int y = r->y + delta_y[dir]; pnormalize(&x, &y, pl); rn = new_region(x, y, pl, 0); terraform_region(rn, terrain); regionqueue_push(rlist, rn); if (rn->land) { ++nsize; } } } return nsize; } const terrain_type *get_ocean(direction_t dir) { return newterrain(T_OCEAN); } int region_quality(const region * r, region * rn[]) { int n, result = 0; for (n = 0; n != MAXDIRECTIONS; ++n) { if (rn[n] && rn[n]->land) { if (rn[n]->terrain == newterrain(T_VOLCANO)) { /* nobody likes volcanoes */ result -= 2000; } result += rn[n]->land->peasants; } } return result; } static void oceans_around(region * r, region * rn[]) { int n; for (n = 0; n != MAXDIRECTIONS; ++n) { region *rx = rn[n]; if (rx == NULL) { plane *pl = rplane(r); int x = r->x + delta_x[n]; int y = r->y + delta_y[n]; pnormalize(&x, &y, pl); rx = new_region(x, y, pl, 0); terraform_region(rx, newterrain(T_OCEAN)); rn[n] = rx; } } } static void smooth_island(region_list * island) { region *rn[MAXDIRECTIONS]; region_list *rlist = NULL; for (rlist = island; rlist; rlist = rlist->next) { region *r = rlist->data; int n, nland = 0; if (r->land) { get_neighbours(r, rn); for (n = 0; n != MAXDIRECTIONS && nland <= 1; ++n) { if (rn[n]->land) { ++nland; r = rn[n]; } } if (nland == 1) { get_neighbours(r, rn); oceans_around(r, rn); for (n = 0; n != MAXDIRECTIONS; ++n) { int n1 = (n + 1) % MAXDIRECTIONS; int n2 = (n + 1 + MAXDIRECTIONS) % MAXDIRECTIONS; if (!rn[n]->land && rn[n1] != r && rn[n2] != r) { r = rlist->data; runhash(r); runhash(rn[n]); SWAP_VARS(int, r->x, rn[n]->x); SWAP_VARS(int, r->y, rn[n]->y); rhash(r); rhash(rn[n]); rlist->data = r; oceans_around(r, rn); break; } } } } } } static void starting_region(region * r, region * rn[]) { int n; oceans_around(r, rn); freset(r, RF_MARK); for (n = 0; n != MAXDIRECTIONS; ++n) { freset(rn[n], RF_MARK); } terraform_region(r, newterrain(T_PLAIN)); prepare_starting_region(r); addplayer(r, addfaction("enno@eressea.de", itoa36(rng_int()), races, default_locale, 0)); } /* E3A island generation */ int build_island_e3(int x, int y, int numfactions, int minsize) { #define MIN_QUALITY 1000 int nfactions = 0; region_list *rlist = NULL; region_list *island = NULL; plane *pl = findplane(x, y); region *r = findregion(x, y); int nsize = 1; int q, maxq = INT_MIN, minq = INT_MAX; if (!r) r = new_region(x, y, pl, 0); assert(!r->units); do { terraform_region(r, random_terrain_e3(NODIRECTION)); } while (!r->land); while (r) { fset(r, RF_MARK); if (r->land) { if (nsize < minsize) { nsize += random_neighbours(r, &rlist, &random_terrain_e3); } else { nsize += random_neighbours(r, &rlist, &get_ocean); } } regionqueue_push(&island, r); r = regionqueue_pop(&rlist); } smooth_island(island); if (nsize > minsize / 2) { for (rlist = island; rlist; rlist = rlist->next) { r = rlist->data; if (r->land && fval(r, RF_MARK)) { region *rn[MAXDIRECTIONS]; get_neighbours(r, rn); q = region_quality(r, rn); if (q >= MIN_QUALITY && nfactions < numfactions) { starting_region(r, rn); minq = MIN(minq, q); maxq = MAX(maxq, q); ++nfactions; } } } for (rlist = island; rlist && nfactions < numfactions; rlist = rlist->next) { r = rlist->data; if (!r->land && fval(r, RF_MARK)) { region *rn[MAXDIRECTIONS]; get_neighbours(r, rn); q = region_quality(r, rn); if (q >= MIN_QUALITY * 4 / 3 && nfactions < numfactions) { starting_region(r, rn); minq = MIN(minq, q); maxq = MAX(maxq, q); ++nfactions; } } } } for (rlist = island; rlist; rlist = rlist->next) { r = rlist->data; if (r->units) { region *rn[MAXDIRECTIONS]; get_neighbours(r, rn); q = region_quality(r, rn); if (q - minq > (maxq - minq) * 2 / 3) { terraform_region(r, newterrain(T_HIGHLAND)); prepare_starting_region(r); } r->land->money = 50000; /* 2% = 1000 silver */ } else if (r->land) { r->land->money *= 4; } } return nfactions; }