static int scareaway(region * r, int anzahl) { int n, p, diff = 0, emigrants[MAXDIRECTIONS]; direction_t d; anzahl = _min(_max(1, anzahl), rpeasants(r)); /* Wandern am Ende der Woche (normal) oder wegen Monster. Die * Wanderung wird erst am Ende von demographics () ausgefuehrt. * emigrants[] ist local, weil r->newpeasants durch die Monster * vielleicht schon hochgezaehlt worden ist. */ for (d = 0; d != MAXDIRECTIONS; d++) emigrants[d] = 0; p = rpeasants(r); assert(p >= 0 && anzahl >= 0); for (n = _min(p, anzahl); n; n--) { direction_t dir = (direction_t) (rng_int() % MAXDIRECTIONS); region *rc = rconnect(r, dir); if (rc && fval(rc->terrain, LAND_REGION)) { ++diff; rc->land->newpeasants++; emigrants[dir]++; } } rsetpeasants(r, p - diff); assert(p >= diff); return diff; }
static void test_give_men_magicians(CuTest * tc) { struct give env = { 0 }; int p; message * msg; test_setup_ex(tc); mt_create_error(158); env.f2 = env.f1 = test_create_faction(NULL); setup_give(&env); set_level(env.src, SK_MAGIC, 1); CuAssertPtrNotNull(tc, msg = give_men(1, env.src, env.dst, NULL)); CuAssertStrEquals(tc, "error158", test_get_messagetype(msg)); CuAssertIntEquals(tc, 1, env.dst->number); CuAssertIntEquals(tc, 1, env.src->number); msg_release(msg); p = rpeasants(env.r); CuAssertPtrNotNull(tc, msg = disband_men(1, env.dst, NULL)); CuAssertStrEquals(tc, "give_person_peasants", test_get_messagetype(msg)); CuAssertIntEquals(tc, 0, env.dst->number); CuAssertIntEquals(tc, p+1, rpeasants(env.r)); msg_release(msg); test_teardown(); }
static void scared_by_monster(unit * u) { int n; switch (old_race(u_race(u))) { case RC_FIREDRAGON: n = rng_int() % 160 * u->number; break; case RC_DRAGON: n = rng_int() % 400 * u->number; break; case RC_WYRM: n = rng_int() % 1000 * u->number; break; default: n = rng_int() % (u->number / 4 + 1); } if (n > 0) { n = lovar(n); n = _min(rpeasants(u->region), n); if (n > 0) { n = scareaway(u->region, n); if (n > 0) { ADDMSG(&u->region->msgs, msg_message("fleescared", "amount unit", n, u)); } } } }
unit *addplayer(region * r, faction * f) { unit *u; const char * name; assert(r->land); if (rpeasants(r) < PEASANT_MIN) { rsetpeasants(r, PEASANT_MIN + rng_int() % (PEASANT_MAX - PEASANT_MIN)); } assert(f->units == NULL); faction_setorigin(f, 0, r->x, r->y); u = create_unit(r, f, 1, f->race, 0, NULL, NULL); u->thisorder = default_order(f->locale); unit_addorder(u, copy_order(u->thisorder)); name = config_get("rules.equip_first"); if (!equip_unit(u, name ? name : "first_unit")) { /* give every unit enough money to survive the first turn */ i_change(&u->items, get_resourcetype(R_SILVER)->itype, maintenance_cost(u)); } u->hp = unit_max_hp(u) * u->number; fset(u, UFL_ISNEW); if (f->race == get_race(RC_DAEMON)) { race_t urc; const race *rc; do { urc = (race_t)(rng_int() % MAXRACES); rc = get_race(urc); } while (rc == NULL || urc == RC_DAEMON || !playerrace(rc)); u->irace = rc; } f->lastorders = 0; return u; }
static void eaten_by_monster(unit * u) { /* adjustment for smaller worlds */ static double multi = 0.0; int n = 0; int horse = 0; if (multi == 0.0) { multi = RESOURCE_QUANTITY * newterrain(T_PLAIN)->size / 10000.0; } switch (old_race(u_race(u))) { case RC_FIREDRAGON: n = rng_int() % 80 * u->number; horse = get_item(u, I_HORSE); break; case RC_DRAGON: n = rng_int() % 200 * u->number; horse = get_item(u, I_HORSE); break; case RC_WYRM: n = rng_int() % 500 * u->number; horse = get_item(u, I_HORSE); break; default: n = rng_int() % (u->number / 20 + 1); } n = (int)(n * multi); if (n > 0) { n = lovar(n); n = MIN(rpeasants(u->region), n); if (n > 0) { deathcounts(u->region, n); rsetpeasants(u->region, rpeasants(u->region) - n); ADDMSG(&u->region->msgs, msg_message("eatpeasants", "unit amount", u, n)); } } if (horse > 0) { set_item(u, I_HORSE, 0); ADDMSG(&u->region->msgs, msg_message("eathorse", "unit amount", u, horse)); } }
static void absorbed_by_monster(unit * u) { int n; switch (old_race(u_race(u))) { default: n = rng_int() % (u->number / 20 + 1); } if (n > 0) { n = lovar(n); n = _min(rpeasants(u->region), n); if (n > 0) { rsetpeasants(u->region, rpeasants(u->region) - n); scale_number(u, u->number + n); ADDMSG(&u->region->msgs, msg_message("absorbpeasants", "unit race amount", u, u_race(u), n)); } } }
static void test_give_unit_to_peasants(CuTest * tc) { struct give env = { 0 }; test_setup_ex(tc); env.f1 = test_create_faction(NULL); env.f2 = 0; setup_give(&env); rsetpeasants(env.r, 0); give_unit(env.src, NULL, NULL); CuAssertIntEquals(tc, 0, env.src->number); CuAssertIntEquals(tc, 1, rpeasants(env.r)); test_teardown(); }
static direction_t richest_neighbour(region * r, faction * f, int absolut) { /* m - maximum an Geld, d - Richtung, i - index, t = Geld hier */ double m; double t; direction_t d = NODIRECTION, i; if (absolut == 1 || rpeasants(r) == 0) { m = (double)all_money(r, f); } else { m = (double)all_money(r, f) / (double)rpeasants(r); } /* finde die region mit dem meisten geld */ for (i = 0; i != MAXDIRECTIONS; i++) { region *rn = rconnect(r, i); if (rn != NULL && fval(rn->terrain, LAND_REGION)) { if (absolut == 1 || rpeasants(rn) == 0) { t = (double)all_money(rn, f); } else { t = (double)all_money(rn, f) / (double)rpeasants(rn); } if (t > m) { m = t; d = i; } } } return d; }
int region_getresource(const region * r, const resource_type * rtype) { const rawmaterial *rm; for (rm = r->resources; rm; rm = rm->next) { if (rm->type->rtype == rtype) { return rm->amount; } } if (rtype == get_resourcetype(R_SILVER)) return rmoney(r); if (rtype == get_resourcetype(R_HORSE)) return rhorses(r); if (rtype == get_resourcetype(R_PEASANT)) return rpeasants(r); return 0; }
static void test_give_peasants(CuTest * tc) { struct give env = { 0 }; message * msg; test_setup_ex(tc); env.f1 = test_create_faction(NULL); env.f2 = 0; setup_give(&env); rsetpeasants(env.r, 0); msg = disband_men(1, env.src, NULL); CuAssertStrEquals(tc, "give_person_peasants", test_get_messagetype(msg)); CuAssertIntEquals(tc, 0, env.src->number); CuAssertIntEquals(tc, 1, rpeasants(env.r)); msg_release(msg); test_teardown(); }
void plagues(region * r) { int peasants; int i; int dead = 0; peasants = rpeasants(r); dead = (int)(0.5 + PLAGUE_VICTIMS * peasants); for (i = dead; i != 0; i--) { if (rng_double() < PLAGUE_HEALCHANCE && rmoney(r) >= PLAGUE_HEALCOST) { rsetmoney(r, rmoney(r) - PLAGUE_HEALCOST); --dead; } } if (dead > 0) { message *msg = add_message(&r->msgs, msg_message("pest", "dead", dead)); msg_release(msg); deathcounts(r, dead); rsetpeasants(r, peasants - dead); } }
void terraform_region(region * r, const terrain_type * terrain) { /* Resourcen, die nicht mehr vorkommen können, löschen */ const terrain_type *oldterrain = r->terrain; rawmaterial **lrm = &r->resources; assert(terrain); while (*lrm) { rawmaterial *rm = *lrm; const resource_type *rtype = NULL; if (terrain->production != NULL) { int i; for (i = 0; terrain->production[i].type; ++i) { if (rm->type->rtype == terrain->production[i].type) { rtype = rm->type->rtype; break; } } } if (rtype == NULL) { *lrm = rm->next; free(rm); } else { lrm = &rm->next; } } r->terrain = terrain; terraform_resources(r); if (!fval(terrain, LAND_REGION)) { region_setinfo(r, NULL); if (r->land != NULL) { i_freeall(&r->land->items); freeland(r->land); r->land = NULL; } rsettrees(r, 0, 0); rsettrees(r, 1, 0); rsettrees(r, 2, 0); rsethorses(r, 0); rsetpeasants(r, 0); rsetmoney(r, 0); freset(r, RF_ENCOUNTER); freset(r, RF_MALLORN); /* Beschreibung und Namen löschen */ return; } if (r->land) { int d; for (d = 0; d != MAXDIRECTIONS; ++d) { rsetroad(r, d, 0); } i_freeall(&r->land->items); } else { static struct surround { struct surround *next; const luxury_type *type; int value; } *trash = NULL, *nb = NULL; const luxury_type *ltype = NULL; direction_t d; int mnr = 0; r->land = calloc(1, sizeof(land_region)); r->land->ownership = NULL; region_set_morale(r, MORALE_DEFAULT, -1); region_setname(r, makename()); for (d = 0; d != MAXDIRECTIONS; ++d) { region *nr = rconnect(r, d); if (nr && nr->land) { struct demand *sale = r->land->demands; while (sale && sale->value != 0) sale = sale->next; if (sale) { struct surround *sr = nb; while (sr && sr->type != sale->type) sr = sr->next; if (!sr) { if (trash) { sr = trash; trash = trash->next; } else { sr = calloc(1, sizeof(struct surround)); } sr->next = nb; sr->type = sale->type; sr->value = 1; nb = sr; } else sr->value++; ++mnr; } } } if (!nb) { // TODO: this is really lame int i = get_maxluxuries(); if (i > 0) { i = rng_int() % i; ltype = luxurytypes; while (i--) ltype = ltype->next; } } else { int i = rng_int() % mnr; struct surround *srd = nb; while (i > srd->value) { i -= srd->value; srd = srd->next; } if (srd->type) setluxuries(r, srd->type); while (srd->next != NULL) srd = srd->next; srd->next = trash; trash = nb; nb = NULL; } } if (fval(terrain, LAND_REGION)) { const item_type *itype = NULL; char equip_hash[64]; /* TODO: put the equipment in struct terrain, faster */ sprintf(equip_hash, "terrain_%s", terrain->_name); equip_items(&r->land->items, get_equipment(equip_hash)); if (r->terrain->herbs) { int len = 0; while (r->terrain->herbs[len]) ++len; if (len) itype = r->terrain->herbs[rng_int() % len]; } if (itype != NULL) { rsetherbtype(r, itype); rsetherbs(r, (short)(50 + rng_int() % 31)); } else { rsetherbtype(r, NULL); } if (oldterrain == NULL || !fval(oldterrain, LAND_REGION)) { if (rng_int() % 100 < 3) fset(r, RF_MALLORN); else freset(r, RF_MALLORN); if (rng_int() % 100 < ENCCHANCE) { fset(r, RF_ENCOUNTER); } } } if (oldterrain == NULL || terrain->size != oldterrain->size) { if (terrain == newterrain(T_PLAIN)) { rsethorses(r, rng_int() % (terrain->size / 50)); if (rng_int() % 100 < 40) { rsettrees(r, 2, terrain->size * (30 + rng_int() % 40) / 1000); } } else if (chance(0.2)) { rsettrees(r, 2, terrain->size * (30 + rng_int() % 40) / 1000); } else { rsettrees(r, 2, 0); } rsettrees(r, 1, rtrees(r, 2) / 4); rsettrees(r, 0, rtrees(r, 2) / 8); if (!fval(r, RF_CHAOTIC)) { int peasants; peasants = (maxworkingpeasants(r) * (20 + dice_rand("6d10"))) / 100; rsetpeasants(r, _max(100, peasants)); rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL, INT_MAX) + 1) + rng_int() % 5)); } } }
void do_markets(void) { quicklist *traders = 0; unit *markets[MAX_MARKETS]; region *r; for (r = regions; r; r = r->next) { if (r->land) { faction *f = region_get_owner(r); const struct race *rc = f ? f->race : NULL; int p = rpeasants(r); int numlux = rc_luxury_trade(rc), numherbs = rc_herb_trade(rc); numlux = (p + numlux - MIN_PEASANTS) / numlux; numherbs = (p + numherbs - MIN_PEASANTS) / numherbs; if (numlux > 0 || numherbs > 0) { int d, nmarkets = 0; const item_type *lux = r_luxury(r); const item_type *herb = r->land->herbtype; nmarkets += get_markets(r, markets + nmarkets, MAX_MARKETS - nmarkets); for (d = 0; d != MAXDIRECTIONS; ++d) { region *r2 = rconnect(r, d); if (r2 && r2->buildings) { nmarkets += get_markets(r2, markets + nmarkets, MAX_MARKETS - nmarkets); } } if (nmarkets) { while (lux && numlux--) { int n = rng_int() % nmarkets; unit *u = markets[n]; item *items; attrib *a = a_find(u->attribs, &at_market); if (a == NULL) { a = a_add(&u->attribs, a_new(&at_market)); ql_push(&traders, u); } items = (item *)a->data.v; i_change(&items, lux, 1); a->data.v = items; /* give 1 luxury */ } while (herb && numherbs--) { int n = rng_int() % nmarkets; unit *u = markets[n]; item *items; attrib *a = a_find(u->attribs, &at_market); if (a == NULL) { a = a_add(&u->attribs, a_new(&at_market)); ql_push(&traders, u); } items = (item *)a->data.v; i_change(&items, herb, 1); a->data.v = items; /* give 1 herb */ } } } } } if (traders) { quicklist *qliter = traders; int qli = 0; for (qli = 0; qliter; ql_advance(&qliter, &qli, 1)) { unit *u = (unit *)ql_get(qliter, qli); attrib *a = a_find(u->attribs, &at_market); item *items = (item *)a->data.v; a->data.v = NULL; while (items) { item *itm = items; items = itm->next; if (itm->number) { ADDMSG(&u->faction->msgs, msg_message("buyamount", "unit amount resource", u, itm->number, itm->type->rtype)); itm->next = NULL; i_add(&u->items, itm); } else { i_free(itm); } } a_remove(&u->attribs, a); } ql_free(traders); } }
summary *make_summary(void) { faction *f; region *r; unit *u; summary *s = calloc(1, sizeof(summary)); const struct resource_type *rhorse = get_resourcetype(R_HORSE); for (f = factions; f; f = f->next) { const struct locale *lang = f->locale; struct language *plang = s->languages; while (plang && plang->locale != lang) plang = plang->next; if (!plang) { plang = calloc(sizeof(struct language), 1); plang->next = s->languages; s->languages = plang; plang->locale = lang; } ++plang->number; f->nregions = 0; f->num_total = 0; f->money = 0; if (f->alive && f->units) { s->factions++; /* Problem mit Monsterpartei ... */ if (!is_monsters(f)) { s->factionrace[old_race(f->race)]++; } } } /* count everything */ for (r = regions; r; r = r->next) { s->pferde += rhorses(r); s->schiffe += listlen(r->ships); s->gebaeude += listlen(r->buildings); if (!fval(r->terrain, SEA_REGION)) { s->landregionen++; if (r->units) { s->landregionen_mit_spielern++; } if (fval(r, RF_ORCIFIED)) { s->orkifizierte_regionen++; } if (r->terrain == newterrain(T_VOLCANO)) { s->inactive_volcanos++; } else if (r->terrain == newterrain(T_VOLCANO_SMOKING)) { s->active_volcanos++; } } if (r->units) { s->regionen_mit_spielern++; } if (rpeasants(r) || r->units) { s->inhabitedregions++; s->peasants += rpeasants(r); s->peasantmoney += rmoney(r); /* Einheiten Info. nregions darf nur einmal pro Partei * incrementiert werden. */ for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next) { f = u->faction; if (!is_monsters(u->faction)) { skill *sv; item *itm; s->nunits++; s->playerpop += u->number; if (u->flags & UFL_HERO) { s->heroes += u->number; } s->spielerpferde += i_get(u->items, rhorse->itype); s->playermoney += get_money(u); s->armed_men += armedmen(u, true); for (itm = u->items; itm; itm = itm->next) { if (itm->type->rtype->wtype) { s->waffen += itm->number; } if (itm->type->rtype->atype) { s->ruestungen += itm->number; } } s->spielerpferde += i_get(u->items, rhorse->itype); for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) { skill_t sk = sv->id; int aktskill = eff_skill(u, sk, r); if (aktskill > s->maxskill) s->maxskill = aktskill; } if (!fval(f, FFL_SELECT)) { f->nregions++; fset(f, FFL_SELECT); } } f->num_total += u->number; f->money += get_money(u); s->poprace[old_race(u_race(u))] += u->number; } } } return s; }
/* In a->data.ca[1] steht der Prozentsatz mit dem sich die Einheit * auflöst, in a->data.ca[0] kann angegeben werden, wohin die Personen * verschwinden. Passiert bereits in der ersten Runde! */ static void dissolve_units(void) { region *r; unit *u; int n; int i; for (r = regions; r; r = r->next) { for (u = r->units; u; u = u->next) { attrib *a = a_find(u->attribs, &at_unitdissolve); if (a) { message *msg; if (u->age == 0 && a->data.ca[1] < 100) continue; /* TODO: Durch einzelne Berechnung ersetzen */ if (a->data.ca[1] == 100) { n = u->number; } else { n = 0; for (i = 0; i < u->number; i++) { if (rng_int() % 100 < a->data.ca[1]) n++; } } /* wenn keiner verschwindet, auch keine Meldung */ if (n == 0) { continue; } scale_number(u, u->number - n); switch (a->data.ca[0]) { case 1: rsetpeasants(r, rpeasants(r) + n); msg = msg_message("dissolve_units_1", "unit region number race", u, r, n, u_race(u)); break; case 2: if (r->land && !fval(r, RF_MALLORN)) { rsettrees(r, 2, rtrees(r, 2) + n); msg = msg_message("dissolve_units_2", "unit region number race", u, r, n, u_race(u)); } else { msg = msg_message("dissolve_units_3", "unit region number race", u, r, n, u_race(u)); } break; default: if (u_race(u) == get_race(RC_STONEGOLEM) || u_race(u) == get_race(RC_IRONGOLEM)) { msg = msg_message("dissolve_units_4", "unit region number race", u, r, n, u_race(u)); } else { msg = msg_message("dissolve_units_5", "unit region number race", u, r, n, u_race(u)); } break; } add_message(&u->faction->msgs, msg); msg_release(msg); } } } remove_empty_units(); }
void randomevents(void) { region *r; faction *monsters = get_monsters(); icebergs(); godcurse(); orc_growth(); demon_skillchanges(); /* Orkifizierte Regionen mutieren und mutieren zurück */ for (r = regions; r; r = r->next) { if (fval(r, RF_ORCIFIED)) { direction_t dir; double probability = 0.0; for (dir = 0; dir < MAXDIRECTIONS; dir++) { region *rc = rconnect(r, dir); if (rc && rpeasants(rc) > 0 && !fval(rc, RF_ORCIFIED)) probability += 0.02; } if (chance(probability)) { ADDMSG(&r->msgs, msg_message("deorcified", "region", r)); freset(r, RF_ORCIFIED); } } else { attrib *a = a_find(r->attribs, &at_orcification); if (a != NULL) { double probability = 0.0; if (rpeasants(r) <= 0) continue; probability = a->data.i / (double)rpeasants(r); if (chance(probability)) { fset(r, RF_ORCIFIED); a_remove(&r->attribs, a); ADDMSG(&r->msgs, msg_message("orcified", "region", r)); } else { a->data.i -= _max(10, a->data.i / 10); if (a->data.i <= 0) a_remove(&r->attribs, a); } } } } /* Vulkane qualmen, brechen aus ... */ for (r = regions; r; r = r->next) { if (r->terrain == newterrain(T_VOLCANO_SMOKING)) { if (a_find(r->attribs, &at_reduceproduction)) { ADDMSG(&r->msgs, msg_message("volcanostopsmoke", "region", r)); rsetterrain(r, T_VOLCANO); } else { if (rng_int() % 100 < 12) { ADDMSG(&r->msgs, msg_message("volcanostopsmoke", "region", r)); rsetterrain(r, T_VOLCANO); } else if (r->age > 20 && rng_int() % 100 < 8) { volcano_outbreak(r); } } } else if (r->terrain == newterrain(T_VOLCANO)) { if (rng_int() % 100 < 4) { ADDMSG(&r->msgs, msg_message("volcanostartsmoke", "region", r)); rsetterrain(r, T_VOLCANO_SMOKING); } } } /* Monumente zerfallen, Schiffe verfaulen */ for (r = regions; r; r = r->next) { building **blist = &r->buildings; while (*blist) { building *b = *blist; if (fval(b->type, BTF_DECAY) && !building_owner(b)) { b->size -= _max(1, (b->size * 20) / 100); if (b->size == 0) { remove_building(blist, r->buildings); } } if (*blist == b) blist = &b->next; } } /* monster-einheiten desertieren */ if (monsters) { for (r = regions; r; r = r->next) { unit *u; for (u = r->units; u; u = u->next) { if (u->faction && !is_monsters(u->faction) && (u_race(u)->flags & RCF_DESERT)) { if (fval(u, UFL_ISNEW)) continue; if (rng_int() % 100 < 5) { ADDMSG(&u->faction->msgs, msg_message("desertion", "unit region", u, r)); u_setfaction(u, monsters); } } } } } /* Chaos */ for (r = regions; r; r = r->next) { int i; if (fval(r, RF_CHAOTIC)) { chaos(r); } i = chaoscount(r); if (i) { chaoscounts(r, -(int)(i * ((double)(rng_int() % 10)) / 100.0)); } } #ifdef HERBS_ROT rotting_herbs(); #endif dissolve_units(); }
void destroyfaction(faction ** fp) { faction * f = *fp; unit *u = f->units; *fp = f->next; f->next = dead_factions; dead_factions = f; fset(f, FFL_QUIT); f->_alive = false; if (f->spellbook) { spellbook_clear(f->spellbook); free(f->spellbook); f->spellbook = 0; } if (f->seen_factions) { selist_free(f->seen_factions); f->seen_factions = 0; } while (u) { /* give away your stuff, to ghosts if you cannot (quest items) */ if (u->items) { region *r = u->region; int result = gift_items(u, GIFT_FRIENDS | GIFT_PEASANTS); if (result != 0) { save_special_items(u); } if (r->land && !!playerrace(u_race(u))) { const race *rc = u_race(u); int m = rmoney(r); /* Personen gehen nur an die Bauern, wenn sie auch von dort * stammen */ if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) { int p = rpeasants(u->region); int h = rhorses(u->region); item *itm; p += (int)(u->number * rc->recruit_multi); for (itm = u->items; itm; itm = itm->next) { if (itm->type->flags & ITF_ANIMAL) { h += itm->number; } } rsetpeasants(r, p); rsethorses(r, h); } m += get_money(u); rsetmoney(r, m); } } set_number(u, 0); u = u->nextF; } handle_event(f->attribs, "destroy", f); if (f->alliance) { setalliance(f, NULL); } funhash(f); /* units of other factions that were disguised as this faction * have their disguise replaced by ordinary faction hiding. */ if (rule_stealth_other()) { region *rc; for (rc = regions; rc; rc = rc->next) { for (u = rc->units; u; u = u->next) { if (u->attribs && get_otherfaction(u) == f) { a_removeall(&u->attribs, &at_otherfaction); if (rule_stealth_anon()) { fset(u, UFL_ANON_FACTION); } } } } } }
void transfermen(unit * u, unit * u2, int n) { const attrib *a; int hp = u->hp; region *r = u->region; if (n == 0) return; assert(n > 0); /* "hat attackiert"-status wird übergeben */ if (u2) { skill *sv, *sn; skill_t sk; ship *sh; assert(u2->number + n > 0); for (sk = 0; sk != MAXSKILLS; ++sk) { int weeks, level = 0; sv = get_skill(u, sk); sn = get_skill(u2, sk); if (sv == NULL && sn == NULL) continue; if (sn == NULL && u2->number == 0) { /* new unit, easy to solve */ level = sv->level; weeks = sv->weeks; } else { double dlevel = 0.0; if (sv && sv->level) { dlevel += (sv->level + 1 - sv->weeks / (sv->level + 1.0)) * n; level += sv->level * n; } if (sn && sn->level) { dlevel += (sn->level + 1 - sn->weeks / (sn->level + 1.0)) * u2->number; level += sn->level * u2->number; } dlevel = dlevel / (n + u2->number); level = level / (n + u2->number); if (level <= dlevel) { /* apply the remaining fraction to the number of weeks to go. * subtract the according number of weeks, getting closer to the * next level */ level = (int)dlevel; weeks = (level + 1) - (int)((dlevel - level) * (level + 1)); } else { /* make it harder to reach the next level. * weeks+level is the max difficulty, 1 - the fraction between * level and dlevel applied to the number of weeks between this * and the previous level is the added difficutly */ level = (int)dlevel + 1; weeks = 1 + 2 * level - (int)((1 + dlevel - level) * level); } } if (level) { if (sn == NULL) sn = add_skill(u2, sk); sn->level = (unsigned char)level; sn->weeks = (unsigned char)weeks; assert(sn->weeks > 0 && sn->weeks <= sn->level * 2 + 1); assert(u2->number != 0 || (sn->level == sv->level && sn->weeks == sv->weeks)); } else if (sn) { remove_skill(u2, sk); sn = NULL; } } a = a_find(u->attribs, &at_effect); while (a && a->type == &at_effect) { effect_data *olde = (effect_data *) a->data.v; if (olde->value) change_effect(u2, olde->type, olde->value); a = a->next; } sh = leftship(u); if (sh != NULL) set_leftship(u2, sh); u2->flags |= u->flags & (UFL_LONGACTION | UFL_NOTMOVING | UFL_HUNGER | UFL_MOVED | UFL_ENTER); if (u->attribs) { transfer_curse(u, u2, n); } } scale_number(u, u->number - n); if (u2) { set_number(u2, u2->number + n); hp -= u->hp; u2->hp += hp; /* TODO: Das ist schnarchlahm! und gehört nicht hierhin */ a = a_find(u2->attribs, &at_effect); while (a && a->type == &at_effect) { attrib *an = a->next; effect_data *olde = (effect_data *) a->data.v; int e = get_effect(u, olde->type); if (e != 0) change_effect(u2, olde->type, -e); a = an; } } else if (r->land) { if ((u_race(u)->ec_flags & ECF_REC_ETHEREAL) == 0) { const race *rc = u_race(u); if (rc->ec_flags & ECF_REC_HORSES) { /* Zentauren an die Pferde */ int h = rhorses(r) + n; rsethorses(r, h); } else { int p = rpeasants(r); p += (int)(n * rc->recruit_multi); rsetpeasants(r, p); } } } }
void get_food(region * r) { plane *pl = rplane(r); unit *u; int peasantfood = rpeasants(r) * 10; static int food_rules = -1; static int gamecookie = -1; if (food_rules < 0 || gamecookie != global.cookie) { gamecookie = global.cookie; food_rules = get_param_int(global.parameters, "rules.economy.food", 0); } if (food_rules & FOOD_IS_FREE) { return; } /* 1. Versorgung von eigenen Einheiten. Das vorhandene Silber * wird zunächst so auf die Einheiten aufgeteilt, dass idealerweise * jede Einheit genug Silber für ihren Unterhalt hat. */ for (u = r->units; u; u = u->next) { int need = lifestyle(u); /* Erstmal zurücksetzen */ freset(u, UFL_HUNGER); if (u->ship && (u->ship->flags & SF_FISHING)) { unit *v; int c = 2; for (v = u; c > 0 && v; v = v->next) { if (v->ship == u->ship) { int get = 0; if (v->number <= c) { get = lifestyle(v); } else { get = lifestyle(v) * c / v->number; } if (get) { change_money(v, get); } } c -= v->number; } u->ship->flags -= SF_FISHING; } if (food_rules & FOOD_FROM_PEASANTS) { struct faction *owner = region_get_owner(r); /* if the region is owned, and the owner is nice, then we'll get * food from the peasants - should not be used with WORK */ if (owner != NULL && (get_alliance(owner, u->faction) & HELP_MONEY)) { int rm = rmoney(r); int use = _min(rm, need); rsetmoney(r, rm - use); need -= use; } } need -= get_money(u); if (need > 0) { unit *v; for (v = r->units; need && v; v = v->next) { if (v->faction == u->faction && help_money(v)) { int give = get_money(v) - lifestyle(v); give = _min(need, give); if (give > 0) { change_money(v, -give); change_money(u, give); need -= give; } } } } } /* 2. Versorgung durch Fremde. Das Silber alliierter Einheiten wird * entsprechend verteilt. */ for (u = r->units; u; u = u->next) { int need = lifestyle(u); faction *f = u->faction; need -= _max(0, get_money(u)); if (need > 0) { unit *v; if (food_rules & FOOD_FROM_OWNER) { /* the owner of the region is the first faction to help out when you're hungry */ faction *owner = region_get_owner(r); if (owner && owner != u->faction) { for (v = r->units; v; v = v->next) { if (v->faction == owner && alliedunit(v, f, HELP_MONEY) && help_money(v)) { help_feed(v, u, &need); break; } } } } for (v = r->units; need && v; v = v->next) { if (v->faction != f && alliedunit(v, f, HELP_MONEY) && help_money(v)) { help_feed(v, u, &need); } } /* Die Einheit hat nicht genug Geld zusammengekratzt und * nimmt Schaden: */ if (need > 0) { int lspp = lifestyle(u) / u->number; if (lspp > 0) { int number = (need + lspp - 1) / lspp; if (hunger(number, u)) fset(u, UFL_HUNGER); } } } } /* 3. bestimmen, wie viele Bauern gefressen werden. * bei fehlenden Bauern den Dämon hungern lassen */ for (u = r->units; u; u = u->next) { if (u_race(u) == get_race(RC_DAEMON)) { int hungry = u->number; /* use peasantblood before eating the peasants themselves */ const struct potion_type *pt_blood = 0; const resource_type *rt_blood = rt_find("peasantblood"); if (rt_blood) { pt_blood = rt_blood->ptype; } if (pt_blood) { /* always start with the unit itself, then the first known unit that may have some blood */ unit *donor = u; while (donor != NULL && hungry > 0) { int blut = get_effect(donor, pt_blood); blut = _min(blut, hungry); if (blut) { change_effect(donor, pt_blood, -blut); hungry -= blut; } if (donor == u) donor = r->units; while (donor != NULL) { if (u_race(donor) == get_race(RC_DAEMON) && donor != u) { if (get_effect(donor, pt_blood)) { /* if he's in our faction, drain him: */ if (donor->faction == u->faction) break; } } donor = donor->next; } } } /* remaining demons feed on peasants */ if (pl == NULL || !fval(pl, PFL_NOFEED)) { if (peasantfood >= hungry) { peasantfood -= hungry; hungry = 0; } else { hungry -= peasantfood; peasantfood = 0; } if (hungry > 0) { static int demon_hunger = -1; if (demon_hunger < 0) { demon_hunger = get_param_int(global.parameters, "hunger.demons", 0); } if (demon_hunger == 0) { /* demons who don't feed are hungry */ if (hunger(hungry, u)) fset(u, UFL_HUNGER); } else { /* no damage, but set the hungry-flag */ fset(u, UFL_HUNGER); } } } } } rsetpeasants(r, peasantfood / 10); /* 3. Von den überlebenden das Geld abziehen: */ for (u = r->units; u; u = u->next) { int need = _min(get_money(u), lifestyle(u)); change_money(u, -need); } }