#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <kernel/config.h>
#include "laws.h"

#include <modules/gmcmd.h>

#include "alchemy.h"
#include "automate.h"
#include "battle.h"
#include "contact.h"
#include "economy.h"
#include "give.h"
#include "market.h"
#include "morale.h"
#include "monsters.h"
#include "move.h"
#include "randenc.h"
#include "recruit.h"
#include "renumber.h"
#include "spy.h"
#include "study.h"
#include "wormhole.h"
#include "prefix.h"
#include "reports.h"
#include "teleport.h"
#include "guard.h"
#include "volcano.h"

/* kernel includes */
#include "kernel/alliance.h"
#include "kernel/ally.h"
#include "kernel/calendar.h"
#include "kernel/callbacks.h"
#include "kernel/connection.h"
#include "kernel/curse.h"
#include "kernel/building.h"
#include "kernel/faction.h"
#include "kernel/group.h"
#include "kernel/item.h"
#include "kernel/messages.h"
#include "kernel/order.h"
#include "kernel/plane.h"
#include "kernel/pool.h"
#include "kernel/race.h"
#include "kernel/region.h"
#include "kernel/resources.h"
#include "kernel/ship.h"
#include "kernel/spell.h"
#include "kernel/spellbook.h"
#include "kernel/terrain.h"
#include "kernel/terrainid.h"
#include "kernel/unit.h"

/* util includes */
#include <kernel/attrib.h>
#include <util/base36.h>
#include <kernel/event.h>
#include <util/goodies.h>
#include "util/keyword.h"
#include <util/language.h>
#include <util/lists.h>
#include <util/log.h>
#include <util/macros.h>
#include <util/message.h>
#include <util/param.h>
#include <util/parser.h>
#include <util/password.h>
#include <util/path.h>
#include <util/rand.h>
#include <util/rng.h>
#include <util/strings.h>
#include <util/umlaut.h>
#include <util/unicode.h>

/* attributes includes */
#include <attributes/otherfaction.h>
#include <attributes/racename.h>
#include <attributes/raceprefix.h>
#include <attributes/seenspell.h>
#include <attributes/stealth.h>

#include <spells/buildingcurse.h>
#include <spells/regioncurse.h>
#include <spells/unitcurse.h>

#include <selist.h>
#include <iniparser.h>

/* libc includes */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

/* chance that a peasant dies of starvation: */
#define PEASANT_STARVATION_CHANCE 0.9
/* Pferdevermehrung */
#define HORSEGROWTH 4
/* Wanderungschance pro Pferd */
#define HORSEMOVE   3
/* Vermehrungschance pro Baum */
#define FORESTGROWTH 10000      /* In Millionstel */

/** Ausbreitung und Vermehrung */
#define MAXDEMAND      25
#define DMRISE         0.1F     /* weekly chance that demand goes up */
#define DMRISEHAFEN    0.2F     /* weekly chance that demand goes up with harbor */

param_t findparam_ex(const char *s, const struct locale * lang)
{
    param_t result = findparam(s, lang);

    if (result == NOPARAM) {
        const building_type *btype = findbuildingtype(s, lang);
        if (btype != NULL)
            return P_GEBAEUDE;
    }
    return (result == P_BUILDING) ? P_GEBAEUDE : result;
}

int NewbieImmunity(void)
{
    int result = config_get_int("NewbieImmunity", 0);
    return result;
}

bool IsImmune(const faction * f)
{
    return !fval(f, FFL_NPC) && f->age < NewbieImmunity();
}

int NMRTimeout(void)
{
    static int config, nmr_timeout, ini_timeout;
    if (config_changed(&config)) {
        nmr_timeout = config_get_int("nmr.timeout", 0);
        ini_timeout = config_get_int("game.maxnmr", 0);
    }
    if (nmr_timeout > 0) {
        if (ini_timeout > nmr_timeout) {
            return nmr_timeout;
        }
        return (ini_timeout > 0) ? ini_timeout : nmr_timeout;
    }
    return ini_timeout;
}

bool LongHunger(const struct unit *u)
{
    if (u != NULL) {
        if (!fval(u, UFL_HUNGER))
            return false;
        if (u_race(u) == get_race(RC_DAEMON))
            return false;
    }
    return config_get_int("hunger.long", 0) != 0;
}

static bool RemoveNMRNewbie(void)
{
    int value = config_get_int("nmr.removenewbie", 0);
    return value != 0;
}

static void dumbeffect(unit *u) {
    int effect = get_effect(u, oldpotiontype[P_FOOL]);
    if (effect > 0) {           /* Trank "Dumpfbackenbrot" */
        skill *sv = u->skills, *sb = NULL;
        while (sv != u->skills + u->skill_size) {
            if (sb == NULL || skill_compare(sv, sb) > 0) {
                sb = sv;
            }
            ++sv;
        }
        /* bestes Talent raussuchen */
        if (sb != NULL) {
            int weeks = u->number;
            if (weeks > effect) weeks = effect;
            reduce_skill(u, sb, weeks);
            ADDMSG(&u->faction->msgs, msg_message("dumbeffect",
                "unit weeks skill", u, weeks, (skill_t)sb->id));
        }                         /* sonst Glueck gehabt: wer nix weiss, kann nix vergessen... */
        change_effect(u, oldpotiontype[P_FOOL], -effect);
    }
}

static void astral_crumble(unit *u) {
    item **itemp = &u->items;
    while (*itemp) {
        item *itm = *itemp;
        if ((itm->type->flags & ITF_NOTLOST) == 0) {
            if (itm->type->flags & (ITF_BIG | ITF_ANIMAL | ITF_CURSED)) {
                ADDMSG(&u->faction->msgs, msg_message("itemcrumble",
                    "unit region item amount",
                    u, u->region, itm->type->rtype, itm->number));
                i_free(i_remove(itemp, itm));
                continue;
            }
        }
        itemp = &itm->next;
    }
}

static void age_unit(region * r, unit * u)
{
    const race *rc = u_race(u);

    ++u->age;
    if (u->number > 0 && rc->age_unit) {
        rc->age_unit(u);
    }
    if (u->attribs) {
        dumbeffect(u);
    }
    if (u->items && u->region && is_astral(u->region)) {
        astral_crumble(u);
    }
}

static void live(region * r)
{
    unit **up = &r->units;

    get_food(r);

    while (*up) {
        unit *u = *up;
        /* IUW: age_unit() kann u loeschen, u->next ist dann
         * undefiniert, also muessen wir hier schon das naechste
         * Element bestimmen */
        age_unit(r, u);
        if (*up == u)
            up = &u->next;
    }
}

/*
 * This procedure calculates the number of emigrating peasants for the given
 * region r. There are two incentives for peasants to emigrate:
 * 1) They prefer the less crowded areas.
 *    Example: mountains, 700 peasants (max  1000), 70% inhabited
 *             plain, 5000 peasants (max 10000), 50% inhabited
 *             700*(PEASANTSWANDER_WEIGHT/100)*((70-50)/100) peasants wander
 *             from mountains to plain.
 *    Effect : peasents will leave densely populated regions.
 * 2) Peasants prefer richer neighbour regions.
 *    Example: region A,  700 peasants, wealth $10500, $15 per head
 *             region B, 2500 peasants, wealth $25000, $10 per head
 *             Some peasants will emigrate from B to A because $15 > $10
 *             exactly: 2500*(PEASANTSGREED_WEIGHT1/100)*((15-10)/100)
 * Not taken in consideration:
 * - movement because of monsters.
 * - movement because of wars
 * - movement because of low loyalty relating to present parties.
 */

#define MAX_EMIGRATION(p) ((p)/MAXDIRECTIONS)
#define MAX_IMMIGRATION(p) ((p)*2/3)

void peasant_migration(region * r)
{
    int i;
    int maxp = region_maxworkers(r);
    int rp = rpeasants(r);
    int max_immigrants = MAX_IMMIGRATION(maxp - rp);

    if (volcano_module()) {
        static int terrain_cache;
        static const terrain_type *t_volcano;
        static const terrain_type *t_smoking;

        if (terrain_changed(&terrain_cache)) {
            t_volcano = newterrain(T_VOLCANO);
            t_smoking = newterrain(T_VOLCANO_SMOKING);
        }
        if (r->terrain == t_volcano || r->terrain == t_smoking) {
            max_immigrants = max_immigrants / 10;
        }
    }

    for (i = 0; max_immigrants > 0 && i != MAXDIRECTIONS; i++) {
        int dir = (turn + 1 + i) % MAXDIRECTIONS;
        region *rc = rconnect(r, (direction_t)dir);

        if (rc != NULL && fval(rc->terrain, LAND_REGION)) {
            int rp2 = rpeasants(rc);
            int maxp2 = region_maxworkers(rc);
            int max_emigration = MAX_EMIGRATION(rp2 - maxp2);

            if (max_emigration > 0) {
                if (max_emigration > max_immigrants) max_emigration = max_immigrants;
                if (max_emigration + r->land->newpeasants > USHRT_MAX) {
                    max_emigration = USHRT_MAX - r->land->newpeasants;
                }
                if (max_emigration + rc->land->newpeasants > USHRT_MAX) {
                    max_emigration = USHRT_MAX - rc->land->newpeasants;
                }
                r->land->newpeasants += (short)max_emigration;
                rc->land->newpeasants -= (short)max_emigration;
                max_immigrants -= max_emigration;
            }
        }
    }
}

/* Vermehrungsrate Bauern in 1/10000.
* TODO: Evt. Berechnungsfehler, reale Vermehrungsraten scheinen hoeher. */
#define PEASANTGROWTH 10
#define PEASANTLUCK 10
#define PEASANTFORCE 0.75       /* Chance einer Vermehrung trotz 90% Auslastung */

static double peasant_growth_factor(void)
{
    return config_get_flt("rules.peasants.growth.factor", 0.0001 * (double)PEASANTGROWTH);
}

static double peasant_luck_factor(void)
{
    return config_get_flt("rules.peasants.peasantluck.factor", PEASANTLUCK);
}

int peasant_luck_effect(int peasants, int luck, int maxp, double variance)
{
    int births = 0;
    double mean;
    if (luck == 0) return 0;
    mean = fmin(luck, peasants);
    mean *= peasant_luck_factor() * peasant_growth_factor();
    mean *= ((peasants / (double)maxp < .9) ? 1 : PEASANTFORCE);

    births = RAND_ROUND(normalvariate(mean, variance * mean));
    if (births <= 0)
        births = 1;
    if (births > peasants / 2)
        births = peasants / 2 + 1;
    return births;
}

static void peasants(region * r, int rule)
{
    int rp = rpeasants(r);
    int money = rmoney(r);
    int maxp = max_production(r);
    int n, satiated;
    int dead = 0, peasants = rp;

    if (peasants > 0 && rule > 0) {
        int luck = 0;
        double fraction = peasants * peasant_growth_factor();
        int births = RAND_ROUND(fraction);
        attrib *a = a_find(r->attribs, &at_peasantluck);

        if (a != NULL) {
            luck = a->data.i * 1000;
        }

        luck = peasant_luck_effect(peasants, luck, maxp, .5);
        if (luck > 0)
            ADDMSG(&r->msgs, msg_message("peasantluck_success", "births", luck));
        peasants += births + luck;
    }

    /* Alle werden satt, oder halt soviele fuer die es auch Geld gibt */

    satiated = money / maintenance_cost(NULL);
    if (satiated > peasants) satiated = peasants;
    rsetmoney(r, money - satiated * maintenance_cost(NULL));

    /* Von denjenigen, die nicht satt geworden sind, verhungert der
     * Grossteil. dead kann nie groesser als rpeasants(r) - satiated werden,
     * so dass rpeasants(r) >= 0 bleiben muss. */

     /* Es verhungert maximal die unterernaehrten Bevoelkerung. */

    n = peasants - satiated;
    if (n > rp) n = rp;
    dead += (int)(0.5 + n * PEASANT_STARVATION_CHANCE);

    if (dead > 0) {
        ADDMSG(&r->msgs, msg_message("phunger", "dead", dead));
        peasants -= dead;
    }

    rsetpeasants(r, peasants);
}

/* ------------------------------------------------------------- */

typedef struct migration {
    struct migration *next;
    region *r;
    int horses;
    int trees;
} migration;

#define MSIZE 1023
migration *migrants[MSIZE];
migration *free_migrants;

static migration *get_migrants(region * r)
{
    int key = reg_hashkey(r);
    int index = key % MSIZE;
    migration *m = migrants[index];
    while (m && m->r != r)
        m = m->next;
    if (m == NULL) {
        /* Es gibt noch keine Migration. Also eine erzeugen
         */
        m = free_migrants;
        if (!m) {
            m = calloc(1, sizeof(migration));
            if (!m) abort();
        }
        else {
            free_migrants = free_migrants->next;
            m->horses = 0;
            m->trees = 0;
        }
        m->r = r;
        m->next = migrants[index];
        migrants[index] = m;
    }
    return m;
}

static void migrate(region * r)
{
    int key = reg_hashkey(r);
    int index = key % MSIZE;
    migration **hp = &migrants[index];
    fset(r, RF_MIGRATION);
    while (*hp && (*hp)->r != r)
        hp = &(*hp)->next;
    if (*hp) {
        migration *m = *hp;
        rsethorses(r, rhorses(r) + m->horses);
        /* Was macht das denn hier?
         * Baumwanderung wird in trees() gemacht.
         * wer fragt das? Die Baumwanderung war abhaengig von der
         * Auswertungsreihenfolge der regionen,
         * das hatte ich geaendert. jemand hat es wieder geloescht, toll.
         * ich habe es wieder aktiviert, muss getestet werden.
         */
        *hp = m->next;
        m->next = free_migrants;
        free_migrants = m;
    }
}

static void horses(region * r)
{
    int horses, maxhorses;
    direction_t n;

    /* Logistisches Wachstum, Optimum bei halbem Maximalbesatz. */
    maxhorses = region_maxworkers(r) / 10;
    horses = rhorses(r);
    if (horses > 0) {
        if (maxhorses > 0) {
            double growth =
                (RESOURCE_QUANTITY * (HORSEGROWTH * 200.0 * ((double)maxhorses -
                    horses))) / (double)maxhorses;

            if (growth > 0) {
                int i;
                if (a_find(r->attribs, &at_horseluck)) {
                    growth *= 2;
                }
                /* printf("Horses: <%d> %d -> ", growth, horses); */
                i = (int)(0.5 + (horses * 0.0001) * growth);
                /* printf("%d\n", horses); */
                rsethorses(r, horses + i);
            }
        }
    }

    /* Pferde wandern in Nachbarregionen.
     * Falls die Nachbarregion noch berechnet
     * werden muss, wird eine migration-Struktur gebildet,
     * die dann erst in die Berechnung der Nachbarstruktur einfliesst.
     */

    for (n = 0; n != MAXDIRECTIONS; n++) {
        region *r2 = rconnect(r, n);
        if (r2 && fval(r2->terrain, WALK_INTO)) {
            int pt = (rhorses(r) * HORSEMOVE) / 100;
            pt = (int)normalvariate(pt, pt / 4.0);
            if (pt < 0) pt = 0;
            if (fval(r2, RF_MIGRATION))
                rsethorses(r2, rhorses(r2) + pt);
            else {
                migration *nb;
                /* haben wir die Migration schonmal benutzt?
                 * wenn nicht, muessen wir sie suchen.
                 * Wandernde Pferde vermehren sich nicht.
                 */
                nb = get_migrants(r2);
                nb->horses += pt;
            }
            /* Wandernde Pferde sollten auch abgezogen werden */
            rsethorses(r, rhorses(r) - pt);
        }
    }
    assert(rhorses(r) >= 0);
}

static int count_race(const region * r, const race * rc)
{
    unit *u;
    int c = 0;

    for (u = r->units; u; u = u->next)
        if (u_race(u) == rc)
            c += u->number;

    return c;
}

attrib_type at_germs = {
    "germs",
    DEFAULT_INIT,
    DEFAULT_FINALIZE,
    DEFAULT_AGE,
    a_writeshorts,
    a_readshorts,
    NULL,
    ATF_UNIQUE
};


static void
growing_trees_e3(region * r, const int current_season,
    const int last_weeks_season)
{
    static const int transform[4][3] = {
        { -1, -1, 0 },
        { TREE_SEED, TREE_SAPLING, 2 },
        { TREE_SAPLING, TREE_TREE, 2 },
        { TREE_TREE, TREE_SEED, 2 }
    };

    if (r->land && current_season != last_weeks_season
        && transform[current_season][2]) {
        int src_type = transform[current_season][0];
        int dst_type = transform[current_season][1];
        int src = rtrees(r, src_type);
        int dst = rtrees(r, dst_type);
        int grow = src / transform[current_season][2];
        if (grow > 0) {
            if (src_type != TREE_TREE) {
                rsettrees(r, src_type, src - grow);
            }
            rsettrees(r, dst_type, dst + grow);

            if (dst_type == TREE_SEED && r->terrain->size) {
                region *rn[MAXDIRECTIONS];
                int d;
                double fgrow = grow / (double)MAXDIRECTIONS;

                get_neighbours(r, rn);
                for (d = 0; d != MAXDIRECTIONS; ++d) {
                    region *rx = rn[d];
                    if (rx && rx->land) {
                        double scale = 1.0;
                        int g;
                        double fg, ch;
                        int seeds = rtrees(rx, dst_type);

                        if (r->terrain->size > rx->terrain->size) {
                            scale = (scale * rx->terrain->size) / r->terrain->size;
                        }
                        fg = scale * fgrow;
                        g = (int)fg;
                        ch = fg - g;
                        if (chance(ch))
                            ++g;
                        if (g > 0) {
                            rsettrees(rx, dst_type, seeds + g);
                        }
                    }
                }
            }
        }
    }
}

static int cap_int(int i, int imin, int imax) {
    if (i > imin) {
        return ((i < imax) ? i : imax);
    }
    return imin;
}

static bool
increased_growth(const region* r, const struct race *rc_elf) {
    const unit* u;
    for (u = r->units; u; u = u->next) {
        if (u_race(u) != rc_elf) {
            return false;
        }
    }
    return true;
}

static void
growing_trees(region * r, const season_t current_season, const season_t last_weeks_season, int rules)
{
    int grownup_trees, i, seeds, sprout;
    attrib *a;
    double seedchance = config_get_flt("rules.treeseeds.chance", 0.01F) * RESOURCE_QUANTITY;

    if (current_season == SEASON_SUMMER || current_season == SEASON_AUTUMN) {
        const struct race* rc_elf = get_race(RC_ELF);
        int mp, elves = count_race(r, rc_elf);
        direction_t d;

        a = a_find(r->attribs, &at_germs);
        if (a && last_weeks_season == SEASON_SPRING) {
            /* ungekeimte Samen bleiben erhalten, Sproesslinge wachsen */
            sprout = rtrees(r, 1);
            if (sprout > a->data.sa[1]) sprout = a->data.sa[1];
            /* aus dem gesamt Sproesslingepool abziehen */
            rsettrees(r, 1, rtrees(r, 1) - sprout);
            /* zu den Baeumen hinzufuegen */
            rsettrees(r, 2, rtrees(r, 2) + sprout);

            a_removeall(&r->attribs, &at_germs);
        }

        mp = max_production(r);
        if (mp <= 0)
            return;

        /* Grundchance 1.0% */
        /* Jeder Elf in der Region erhoeht die Chance marginal */
        mp = mp / 8;
        if (elves > mp) elves = mp;
        if (elves) {
            seedchance += 1.0 - pow(0.99999, elves * RESOURCE_QUANTITY);
        }
        grownup_trees = rtrees(r, 2);
        seeds = 0;

        if (grownup_trees > 0) {
            double remainder = seedchance * grownup_trees;
            seeds = (int)(remainder);
            remainder -= seeds;
            if (chance(remainder)) {
                ++seeds;
            }
            if (seeds > 0) {
                if (rules > 2) {
                    if (increased_growth(r, rc_elf)) {
                        /* in empty regions, plant twice the seeds */
                        seeds += seeds;
                    }
                }
                seeds += rtrees(r, 0);
                rsettrees(r, 0, seeds);
            }
        }

        /* Baeume breiten sich in Nachbarregionen aus. */

        /* Gesamtzahl der Samen:
         * bis zu 6% (FORESTGROWTH*3) der Baeume samen in die Nachbarregionen */
        if (seedchance > 0) {
            seeds = (rtrees(r, 2) * FORESTGROWTH * 3) / 1000000;
            for (d = 0; d != MAXDIRECTIONS; ++d) {
                region *r2 = rconnect(r, d);
                if (r2 && fval(r2->terrain, LAND_REGION) && r2->terrain->size) {
                    /* Eine Landregion, wir versuchen Samen zu verteilen:
                     * Die Chance, das Samen ein Stueck Boden finden, in dem sie
                     * keimen koennen, haengt von der Bewuchsdichte und der
                     * verfuegbaren Flaeche ab. In Gletschern gibt es weniger
                     * Moeglichkeiten als in Ebenen. */
                    sprout = 0;
                    seedchance = (1000.0 * region_maxworkers(r2)) / r2->terrain->size;
                    for (i = 0; i < seeds / MAXDIRECTIONS; i++) {
                        if (rng_int() % 10000 < seedchance)
                            sprout++;
                    }
                    rsettrees(r2, 0, rtrees(r2, 0) + sprout);
                }
            }
        }
    }
    else if (current_season == SEASON_SPRING) {
        /* in at_germs merken uns die Zahl der Samen und Sproesslinge, die
         * dieses Jahr aelter werden duerfen, damit nicht ein Same im selben
         * Zyklus zum Baum werden kann */
        a = a_find(r->attribs, &at_germs);
        if (!a) {
            a = a_add(&r->attribs, a_new(&at_germs));
            a->data.sa[0] = (short)cap_int((8 + rtrees(r, 0)) / 9, 0, SHRT_MAX);
            a->data.sa[1] = (short)cap_int((8 + rtrees(r, 1)) / 9, 0, SHRT_MAX);
        }
        else if (a->data.sa[0] < 0 || a->data.sa[1] < 0) {
            a->data.sa[0] = (short)cap_int(a->data.sa[0], 0, SHRT_MAX);
            a->data.sa[1] = (short)cap_int(a->data.sa[1], 0, SHRT_MAX);
        }

        /* Baumwachstum */
        sprout = rtrees(r, 1);
        if (sprout > a->data.sa[1]) sprout = a->data.sa[1];
        grownup_trees = sprout;
        /* aus dem gesamt Sproesslingepool abziehen */
        rsettrees(r, 1, rtrees(r, 1) - grownup_trees);
        /* zu den Baeumen hinzufuegen */
        rsettrees(r, 2, rtrees(r, 2) + grownup_trees);

        /* Samenwachstum */
        seeds = rtrees(r, 0);
        if (seeds > a->data.sa[0]) seeds = a->data.sa[0];
        /* aus dem gesamt Samenpool abziehen */
        rsettrees(r, 0, rtrees(r, 0) - seeds);
        /* zu den Sproesslinge hinzufuegen */
        rsettrees(r, 1, rtrees(r, 1) + seeds);
    }
}

static void
growing_herbs(region * r, const int current_season, const season_t last_weeks_season)
{
    /* Jetzt die Kraeutervermehrung. Vermehrt wird logistisch:
     *
     * Jedes Kraut hat eine Wahrscheinlichkeit von (100-(vorhandene
     * Kraeuter))% sich zu vermehren. */
    UNUSED_ARG(last_weeks_season);
    if (current_season != SEASON_WINTER) {
        int i, herbs = rherbs(r);
        for (i = herbs; i > 0; --i) {
            if (rng_int() % 100 < (100 - herbs)) {
                ++herbs;
            }
        }
        rsetherbs(r, herbs);
    }
}

void immigration(void)
{
    region *r;
    int repopulate = config_get_int("rules.economy.repopulate_maximum", 90);
    log_info(" - Einwanderung...");
    for (r = regions; r; r = r->next) {
        if (r->land && r->land->newpeasants) {
            int rp = rpeasants(r) + r->land->newpeasants;
            if (rp < 0) rp = 0;
            rsetpeasants(r, rp);
            r->land->newpeasants = 0;
        }
        /* Genereate some (0-6 depending on the income) peasants out of nothing */
        /* if less than 50 are in the region and there is space and no monster or demon units in the region */
        if (repopulate) {
            int peasants = rpeasants(r);
            bool mourn = is_mourning(r, turn);
            int income = peasant_wage(r, mourn) - maintenance_cost(NULL) + 1;
            if (income >= 0 && r->land && (peasants < repopulate) && region_maxworkers(r) >(peasants + 30) * 2) {
                int badunit = 0;
                unit *u;
                for (u = r->units; u; u = u->next) {
                    if (!playerrace(u_race(u)) || u_race(u) == get_race(RC_DAEMON)) {
                        badunit = 1;
                        break;
                    }
                }
                if (badunit == 0) {
                    peasants += (int)(rng_double() * income);
                    rsetpeasants(r, peasants);
                }
            }
        }
    }
}

void nmr_warnings(void)
{
    faction *f, *fa;
#define HELP_NMR (HELP_GUARD|HELP_MONEY)
    for (f = factions; f; f = f->next) {
        if (!fval(f, FFL_NOIDLEOUT) && turn > f->lastorders) {
            ADDMSG(&f->msgs, msg_message("nmr_warning", ""));
            if (turn - f->lastorders == NMRTimeout() - 1) {
                ADDMSG(&f->msgs, msg_message("nmr_warning_final", ""));
            }
            if ((turn - f->lastorders) >= 2) {
                message *msg = NULL;
                for (fa = factions; fa; fa = fa->next) {
                    int warn = 0;
                    if (config_get_int("rules.alliances", 0) != 0) {
                        if (f->alliance && f->alliance == fa->alliance) {
                            warn = 1;
                        }
                    }
                    else if (alliedfaction(f, fa, HELP_NMR) && alliedfaction(fa, f, HELP_NMR)) {
                        warn = 1;
                    }
                    if (warn) {
                        if (msg == NULL) {
                            msg = msg_message("warn_dropout", "faction turns", f,
                                    turn - f->lastorders);
                        }
                        add_message(&fa->msgs, msg);
                    }
                }
                if (msg != NULL)
                    msg_release(msg);
            }
        }
    }
}

void demographics(void)
{
    region *r;
    int plant_rules = config_get_int("rules.grow.formula", 3);
    int horse_rules = config_get_int("rules.horses.growth", 1);
    int peasant_rules = config_get_int("rules.peasants.growth", 1);
    const struct building_type *bt_harbour = bt_find("harbour");
    season_t current_season = calendar_season(turn);
    season_t last_weeks_season = calendar_season(turn - 1);

    for (r = regions; r; r = r->next) {
        if (r->age>0 || r->units || r->attribs) {
            ++r->age; /* also oceans. no idea why we didn't always do that */
        }
        live(r);

        if (!fval(r->terrain, SEA_REGION)) {
            /* die Nachfrage nach Produkten steigt. */
            struct demand *dmd;
            if (r->land) {
                for (dmd = r->land->demands; dmd; dmd = dmd->next) {
                    if (dmd->value > 0 && dmd->value < MAXDEMAND) {
                        float rise = DMRISE;
                        if (buildingtype_exists(r, bt_harbour, true))
                            rise = DMRISEHAFEN;
                        if (rng_double() < rise)
                            ++dmd->value;
                    }
                }
                /* Seuchen erst nachdem die Bauern sich vermehrt haben
                 * und gewandert sind */

                peasant_migration(r);
                peasants(r, peasant_rules);

                if (r->age > 20) {
                    double mwp = fmax(region_maxworkers(r), 1);
                    bool mourn = is_mourning(r, turn);
                    int p_wage = peasant_wage(r, mourn);
                    double prob =
                        pow(rpeasants(r) / (mwp * p_wage * 0.13), 4.0)
                        * PLAGUE_CHANCE;

                    if (rng_double() < prob) {
                        plagues(r);
                    }
                }
                if (horse_rules > 0) {
                    horses(r);
                }
                if (plant_rules==1) { /* E3 */
                    growing_trees_e3(r, current_season, last_weeks_season);
                }
                else if (plant_rules) { /* E2 */
                    growing_trees(r, current_season, last_weeks_season, plant_rules);
                    growing_herbs(r, current_season, last_weeks_season);
                }
            }

            update_resources(r);
            if (r->land)
                migrate(r);
        }
    }
    while (free_migrants) {
        migration *m = free_migrants->next;
        free(free_migrants);
        free_migrants = m;
    };

    remove_empty_units();
    immigration();
}

int leave_cmd(unit * u, struct order *ord)
{
    region *r = u->region;

    if (fval(u, UFL_ENTER)) {
        /* if we just entered this round, then we don't leave again */
        return 0;
    }

    if (fval(r->terrain, SEA_REGION) && u->ship) {
        if (!fval(u_race(u), RCF_SWIM|RCF_FLY)) {
            cmistake(u, ord, 11, MSG_MOVE);
            return 0;
        }
        if (has_horses(u)) {
            cmistake(u, ord, 231, MSG_MOVE);
            return 0;
        }
    }
    leave(u, true);
    return 0;
}

void transfer_faction(faction *fsrc, faction *fdst) {
    unit *u;
    skill_t sk;
    int hmax, hnow;
    int skill_count[MAXSKILLS];
    int skill_limit[MAXSKILLS];

    assert(fsrc != fdst);

    for (sk = 0; sk != MAXSKILLS; ++sk) {
        skill_limit[sk] = faction_skill_limit(fdst, sk);
    }
    memset(skill_count, 0, sizeof(skill_count));

    for (u = fdst->units; u != NULL; u = u->nextF) {
        if (u->skills) {
            int i;
            for (i = 0; i != u->skill_size; ++i) {
                const skill *sv = u->skills + i;
                skill_t sk = (skill_t)sv->id;
                skill_count[sk] += u->number;
            }
        }
    }

    hnow = countheroes(fdst);
    hmax = maxheroes(fdst);
    u = fsrc->units;
    while (u) {
        unit *unext = u->nextF;

        if (u_race(u) == fdst->race) {
            if (u->flags & UFL_HERO) {
                if (u->number + hnow > hmax) {
                    u->flags &= ~UFL_HERO;
                }
                else {
                    hnow += u->number;
                }
            }
            if (give_unit_allowed(u) == 0) {
                if (fdst->magiegebiet != M_NONE) {
                    struct sc_mage *m = get_mage(u);
                    if (m && mage_get_type(m) != fdst->magiegebiet) {
                        u = unext;
                        continue;
                    }
                }

                if (u->skills) {
                    int i;
                    for (i = 0; i != u->skill_size; ++i) {
                        const skill *sv = u->skills + i;
                        skill_t sk = (skill_t)sv->id;

                        if (skill_count[sk] + u->number > skill_limit[sk]) {
                            break;
                        }
                    }
                    if (i != u->skill_size) {
                        u = unext;
                        continue;
                    }
                }
                ADDMSG(&fdst->msgs, msg_message("transfer_unit", "unit", u));
                u_setfaction(u, fdst);
            }
        }
        u = unext;
    }
}

int quit_cmd(unit * u, struct order *ord)
{
    char token[128];
    faction *f = u->faction;
    const char *passwd;
    keyword_t kwd;

    kwd = init_order(ord, NULL);
    assert(kwd == K_QUIT);
    passwd = gettoken(token, sizeof(token));
    if (checkpasswd(f, (const char *)passwd)) {
        int flags = FFL_QUIT;
        if (rule_transfermen()) {
            param_t p;
            p = getparam(f->locale);
            if (p == P_FACTION) {
#ifdef QUIT_WITH_TRANSFER
                faction *f2 = getfaction();
                if (f2 == NULL || f2 == u->faction) {
                    cmistake(u, ord, 66, MSG_EVENT);
                    flags = 0;
                }
                else if (f->race != f2->race) {
                    cmistake(u, ord, 281, MSG_EVENT);
                    flags = 0;
                }
                else {
                    unit *u2;
                    for (u2 = u->region->units; u2; u2 = u2->next) {
                        if (u2->faction == f2) {
                            if (ucontact(u2, u)) {
                                transfer_faction(u->faction, u2->faction);
                                break;
                            }
                        }
                    }
                    if (u2 == NULL) {
                        /* no target unit found */
                        cmistake(u, ord, 40, MSG_EVENT);
                        flags = 0;
                    }
                }
#else
                log_error("faction %s: QUIT FACTION is disabled.", factionname(f));
                flags = 0;
#endif
            }
        }
        f->flags |= flags;
    }
    else {
        char buffer[64];
        write_order(ord, f->locale, buffer, sizeof(buffer));
        cmistake(u, ord, 86, MSG_EVENT);
        log_warning("QUIT with illegal password for faction %s: %s\n", itoa36(f->no), buffer);
    }
    return 0;
}

static bool mayenter(region * r, unit * u, building * b)
{
    unit *u2;
    UNUSED_ARG(r);
    if (fval(b, BLD_UNGUARDED))
        return true;
    u2 = building_owner(b);

    if (u2 == NULL || ucontact(u2, u)
        || alliedunit(u2, u->faction, HELP_GUARD))
        return true;

    return false;
}

static int mayboard(const unit * u, ship * sh)
{
    unit *u2 = ship_owner(sh);

    return (!u2 || ucontact(u2, u) || alliedunit(u2, u->faction, HELP_GUARD));
}

static bool CheckOverload(void)
{
    return config_get_int("rules.check_overload", 0) != 0;
}

int enter_ship(unit * u, struct order *ord, int id, bool report)
{
    region *r = u->region;
    ship *sh;
    const race * rc = u_race(u);

    /* Muss abgefangen werden, sonst koennten Schwimmer an
     * Bord von Schiffen an Land gelangen. */
    if (!(rc->flags & (RCF_CANSAIL | RCF_WALK | RCF_FLY))) {
        if (report) {
            cmistake(u, ord, 233, MSG_MOVE);
        }
        return 0;
    }

    sh = findship(id);
    if (sh == NULL || sh->number < 1 || sh->region != r) {
        if (report) {
            cmistake(u, ord, 20, MSG_MOVE);
        }
        return 0;
    }
    if (sh == u->ship) {
        return 1;
    }
    if (!mayboard(u, sh)) {
        if (report) {
            cmistake(u, ord, 34, MSG_MOVE);
        }
        return 0;
    }
    if (CheckOverload()) {
        int sweight, scabins;
        int mweight = ship_capacity(sh);
        int mcabins = ship_cabins(sh);

        if (mweight > 0) {
            getshipweight(sh, &sweight, &scabins);
            sweight += weight(u);
            if (mcabins) {
                int pweight = u->number * u_race(u)->weight;
                /* weight goes into number of cabins, not cargo */
                scabins += pweight;
                sweight -= pweight;
            }

            if (sweight > mweight || (mcabins && (scabins > mcabins))) {
                if (report)
                    cmistake(u, ord, 34, MSG_MOVE);
                return 0;
            }
        }
    }

    if (leave(u, false)) {
        u_set_ship(u, sh);
        fset(u, UFL_ENTER);
        return 1;
    }
    else if (report) {
        cmistake(u, ord, 150, MSG_MOVE);
    }
    return 0;
}

int enter_building(unit * u, order * ord, int id, bool report)
{
    region *r = u->region;
    building *b;

    /* Schwimmer koennen keine Gebaeude betreten, ausser diese sind
     * auf dem Ozean */
    if (!fval(u_race(u), RCF_WALK) && !fval(u_race(u), RCF_FLY)) {
        if (!fval(r->terrain, SEA_REGION)) {
            if (report) {
                cmistake(u, ord, 232, MSG_MOVE);
            }
            return 0;
        }
    }

    b = findbuilding(id);
    if (b == NULL || b->region != r) {
        if (report) {
            cmistake(u, ord, 6, MSG_MOVE);
        }
        return 0;
    }
    if (!mayenter(r, u, b)) {
        if (report) {
            ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "entrance_denied",
                "building", b));
        }
        return 0;
    }

    if (leave(u, 0)) {
        fset(u, UFL_ENTER);
        u_set_building(u, b);
        return 1;
    }
    else if (report) {
        cmistake(u, ord, 150, MSG_MOVE);
    }
    return 0;
}

static void do_contact(region * r)
{
    unit * u;
    for (u = r->units; u; u = u->next) {
        order *ord;
        for (ord = u->orders; ord; ord = ord->next) {
            keyword_t kwd = getkeyword(ord);
            if (kwd == K_CONTACT) {
                contact_cmd(u, ord);
            }
        }
    }
}

void do_enter(struct region *r, bool is_final_attempt)
{
    unit **uptr;

    for (uptr = &r->units; *uptr;) {
        unit *u = *uptr;
        order **ordp = &u->orders;

        while (*ordp) {
            order *ord = *ordp;
            if (getkeyword(ord) == K_ENTER) {
                char token[128];
                param_t p;
                int id;
                unit *ulast = NULL;
                const char * s;

                init_order(ord, NULL);
                s = gettoken(token, sizeof(token));
                p = findparam_ex(s, u->faction->locale);
                id = getid();

                switch (p) {
                case P_BUILDING:
                case P_GEBAEUDE:
                    if (u->building && u->building->no == id)
                        break;
                    if (enter_building(u, ord, id, is_final_attempt)) {
                        unit *ub;
                        for (ub = u; ub; ub = ub->next) {
                            if (ub->building == u->building) {
                                ulast = ub;
                            }
                        }
                    }
                    break;

                case P_SHIP:
                    if (u->ship && u->ship->no == id)
                        break;
                    if (enter_ship(u, ord, id, is_final_attempt)) {
                        unit *ub;
                        ulast = u;
                        for (ub = u; ub; ub = ub->next) {
                            if (ub->ship == u->ship) {
                                ulast = ub;
                            }
                        }
                    }
                    break;

                default:
                    if (is_final_attempt) {
                        cmistake(u, ord, 79, MSG_MOVE);
                    }
                }
                if (ulast != NULL) {
                    if (ulast != u) {
                        /* put u behind ulast so it's the last unit in the building */
                        *uptr = u->next;
                        u->next = ulast->next;
                        ulast->next = u;
                    }
                    break;
                }
            }
            if (*ordp == ord)
                ordp = &ord->next;
        }
        if (*uptr == u)
            uptr = &u->next;
    }
}

int newbies[MAXNEWPLAYERS];
int dropouts[2];

bool nmr_death(const faction * f, int turn, int timeout)
{
    if (f->age >= timeout && turn - f->lastorders >= timeout) {
        static bool rule_destroy;
        static int config;
        
        if (config_changed(&config)) {
            rule_destroy = config_get_int("rules.nmr.destroy", 0) != 0;
        }
        if (rule_destroy) {
            unit *u;
            for (u = f->units; u; u = u->nextF) {
                if (u->building && building_owner(u->building) == u) {
                    remove_building(&u->region->buildings, u->building);
                }
            }
        }
        return true;
    }
    return false;
}

static void remove_idle_players(void)
{
    faction **fp;
    int i, timeout = NMRTimeout();

    log_info(" - beseitige Spieler, die sich zu lange nicht mehr gemeldet haben...");

    for (fp = &factions; *fp;) {
        faction *f = *fp;

        if (timeout > 0 && nmr_death(f, turn, timeout)) {
            destroyfaction(fp);
        } else {
            if (fval(f, FFL_NOIDLEOUT)) {
                f->lastorders = turn;
            }
            else if (turn != f->lastorders) {
                char info[256];
                sprintf(info, "%d Einheiten, %d Personen",
                    f->num_units, f->num_people);
            }
            fp = &f->next;
        }
    }
    log_info(" - beseitige Spieler, die sich nach der Anmeldung nicht gemeldet haben...");

    i = turn + 1;
    if (i < 4) i = 4;
    for (fp = &factions; *fp;) {
        faction *f = *fp;
        if (!is_monsters(f)) {
            if (RemoveNMRNewbie() && !fval(f, FFL_NOIDLEOUT)) {
                if (f->age >= 0 && f->age < MAXNEWPLAYERS) {
                    ++newbies[f->age];
                }
                if (f->age == 2 || f->age == 3) {
                    if (f->lastorders == turn - 2) {
                        ++dropouts[f->age - 2];
                        destroyfaction(fp);
                        continue;
                    }
                }
            }
        }
        fp = &f->next;
    }
}

void quit(void)
{
    faction **fptr = &factions;
    while (*fptr) {
        faction *f = *fptr;
        if (f->flags & FFL_QUIT) {
            destroyfaction(fptr);
        }
        else {
            ++f->age;
            fptr = &f->next;
        }
    }
    remove_idle_players();
    remove_empty_units();
}

/* ------------------------------------------------------------- */

/* HELFE partei [<ALLES | SILBER | GIB | KAEMPFE | WAHRNEHMUNG>] [NICHT] */

int ally_cmd(unit * u, struct order *ord)
{
    char token[128];
    struct allies **sfp;
    faction *f;
    int keyword, not_kw;
    const char *s;
    int sf_status;
   
    init_order(ord, NULL);
    f = getfaction();

    if (f == NULL || is_monsters(f)) {
        cmistake(u, ord, 66, MSG_EVENT);
        return 0;
    }
    if (f == u->faction)
        return 0;

    s = gettoken(token, sizeof(token));

    if (!s || !s[0]) {
        keyword = P_ANY;
    }
    else {
        keyword = findparam(s, u->faction->locale);
    }

    sfp = &u->faction->allies;
    if (fval(u, UFL_GROUP)) {
        group *g = get_group(u);
        if (g) {
            sfp = &g->allies;
        }
    }

    not_kw = getparam(u->faction->locale);        /* HELFE partei [modus] NICHT */

    sf_status = ally_get(*sfp, f);
    switch (keyword) {
    case P_NOT:
        sf_status = 0;
        break;

    case NOPARAM:
        cmistake(u, ord, 137, MSG_EVENT);
        return 0;

    case P_ANY:
        sf_status = (not_kw == P_NOT) ? 0 : HELP_ALL;
        break;

    case P_TRAVEL:
        if (not_kw == P_NOT) {
            sf_status = sf_status & (HELP_ALL - HELP_TRAVEL);
        }
        else {
            sf_status |= HELP_TRAVEL;
        }
        break;

    case P_GIVE:
        if (not_kw == P_NOT)
            sf_status &= (HELP_ALL - HELP_GIVE);
        else
            sf_status |= HELP_GIVE;
        break;

    case P_MONEY:
        if (not_kw == P_NOT)
            sf_status &= (HELP_ALL - HELP_MONEY);
        else
            sf_status |= HELP_MONEY;
        break;

    case P_FIGHT:
        if (not_kw == P_NOT)
            sf_status &= (HELP_ALL - HELP_FIGHT);
        else
            sf_status |= HELP_FIGHT;
        break;

    case P_FACTIONSTEALTH:
        if (not_kw == P_NOT)
            sf_status &= (HELP_ALL - HELP_FSTEALTH);
        else
            sf_status |= HELP_FSTEALTH;
        break;

    case P_GUARD:
        if (not_kw == P_NOT)
            sf_status &= (HELP_ALL - HELP_GUARD);
        else
            sf_status |= HELP_GUARD;
        break;
    }

    sf_status &= HelpMask();
    ally_set(sfp, f, sf_status);

    return 0;
}

static struct local_names *pnames;

static void init_prefixnames(void)
{
    int i;
    for (i = 0; localenames[i]; ++i) {
        const struct locale *lang = get_locale(localenames[i]);
        bool exist = false;
        struct local_names *in = pnames;

        while (in != NULL) {
            if (in->lang == lang) {
                exist = true;
                break;
            }
            in = in->next;
        }
        if (in == NULL) {
            in = calloc(1, sizeof(local_names));
            if (!in) abort();
        }
        in->next = pnames;
        in->lang = lang;

        if (!exist && race_prefixes) {
            int key;
            for (key = 0; race_prefixes[key]; ++key) {
                variant var;
                const char *pname =
                    LOC(lang, mkname("prefix", race_prefixes[key]));
                if (findtoken(in->names, pname, &var) == E_TOK_NOMATCH || var.i != key) {
                    var.i = key;
                    addtoken((struct tnode **)&in->names, pname, var);
                    addtoken((struct tnode **)&in->names, LOC(lang, mkname("prefix",
                        race_prefixes[key])), var);
                }
            }
        }
        pnames = in;
    }
}

int prefix_cmd(unit * u, struct order *ord)
{
    char token[128];
    attrib **ap;
    const char *s;
    local_names *in = pnames;
    variant var;
    const struct locale *lang = u->faction->locale;

    while (in != NULL) {
        if (in->lang == lang)
            break;
        in = in->next;
    }
    if (in == NULL) {
        init_prefixnames();
        for (in = pnames; in && in->lang != lang; in = in->next);
        if (!in) return 0;
    }

    init_order(ord, NULL);
    s = gettoken(token, sizeof(token));

    if (!s || !*s) {
        group *g = get_group(u);
        if (g) {
            a_removeall(&g->attribs, &at_raceprefix);
        }
        else {
            a_removeall(&u->faction->attribs, &at_raceprefix);
        }
        return 0;
    }
    else if (findtoken(in->names, s, &var) == E_TOK_NOMATCH) {
        return 0;
    }
    else if (race_prefixes[var.i] == NULL) {
        cmistake(u, ord, 299, MSG_EVENT);
    }
    else {
        group *g = get_group(u);
        if (g) {
            ap = &g->attribs;
        } 
        else {
            ap = &u->faction->attribs;
        }
        set_prefix(ap, race_prefixes[var.i]);
    }
    return 0;
}

int display_cmd(unit * u, struct order *ord)
{
    char token[128];
    char **s = NULL;
    char *str;
    region *r = u->region;

    init_order(ord, NULL);

    str = gettoken(token, sizeof(token));
    switch (findparam_ex(str, u->faction->locale)) {
    case P_BUILDING:
    case P_GEBAEUDE:
        if (!u->building) {
            cmistake(u, ord, 145, MSG_PRODUCE);
            break;
        }
        if (building_owner(u->building) != u) {
            cmistake(u, ord, 5, MSG_PRODUCE);
            break;
        }
        if (!fval(u->building->type, BTF_NAMECHANGE) && u->building->display && u->building->display[0]) {
            cmistake(u, ord, 278, MSG_EVENT);
            break;
        }
        s = &u->building->display;
        break;

    case P_SHIP:
        if (!u->ship) {
            cmistake(u, ord, 144, MSG_PRODUCE);
            break;
        }
        if (ship_owner(u->ship) != u) {
            cmistake(u, ord, 12, MSG_PRODUCE);
            break;
        }
        s = &u->ship->display;
        break;

    case P_UNIT:
        str = getstrtoken();
        if (str) {
            unicode_utf8_trim(str);
        }
        unit_setinfo(u, str);
        break;

    case P_PRIVAT:
        str = getstrtoken();
        if (str) {
            unicode_utf8_trim(str);
        }
        usetprivate(u, str);
        break;

    case P_REGION:
        if (!r->land || u->faction != region_get_owner(r)) {
            cmistake(u, ord, 147, MSG_EVENT);
            break;
        }
        s = &r->land->display;
        break;

    default:
        cmistake(u, ord, 110, MSG_EVENT);
        break;
    }

    if (s != NULL) {
        const char *s2 = getstrtoken();

        free(*s);
        if (s2) {
            char * sdup = str_strdup(s2);
            if (unicode_utf8_trim(sdup) != 0) {
                log_info("trimming info: %s", s2);
            }
            if (strlen(sdup) >= DISPLAYSIZE) {
                sdup[DISPLAYSIZE-1] = 0;
            }
            *s = sdup;
        }
        else {
            *s = NULL;
        }
    }

    return 0;
}

bool renamed_building(const building * b)
{
    const struct locale *lang = locales;
    size_t len = strlen(b->name);
    for (; lang; lang = nextlocale(lang)) {
        const char *bdname = LOC(lang, b->type->_name);
        if (bdname) {
            size_t bdlen = strlen(bdname);
            if (len >= bdlen && strncmp(b->name, bdname, bdlen) == 0) {
                return false;
            }
        }
    }
    return true;
}

static int rename_cmd(unit * u, order * ord, char **s, const char *s2)
{
	char name[NAMESIZE];
    assert(s2);
    if (!s2[0]) {
        cmistake(u, ord, 84, MSG_EVENT);
        return 0;
    }

    /* TODO: Validate to make sure people don't have illegal characters in
     * names, phishing-style? () come to mind. */
    str_strlcpy(name, s2, sizeof(name));
    if (unicode_utf8_trim(name) != 0) {
        log_info("trimming name: %s", s2);
    }

    free(*s);
    *s = str_strdup(name);
    return 0;
}

static bool try_rename(unit *u, building *b, order *ord) {
    unit *owner = b ? building_owner(b) : NULL;
    bool foreign = !(owner && owner->faction == u->faction);

    if (!b) {
        cmistake(u, ord, u->building ? 6 : 145, MSG_EVENT);
        return false;
    }

    if (!fval(b->type, BTF_NAMECHANGE) && renamed_building(b)) {
        cmistake(u, ord, 278, MSG_EVENT);
        return false;
    }

    if (foreign) {
        if (renamed_building(b)) {
            cmistake(u, ord, 246, MSG_EVENT);
            return false;
        }

        if (owner) {
            if (cansee(owner->faction, u->region, u, 0)) {
                ADDMSG(&owner->faction->msgs,
                    msg_message("renamed_building_seen",
                        "building renamer region", b, u, u->region));
            }
            else {
                ADDMSG(&owner->faction->msgs,
                    msg_message("renamed_building_notseen",
                        "building region", b, u->region));
            }
        }
    }
    if (owner && owner->faction != u->faction) {
        cmistake(u, ord, 148, MSG_PRODUCE);
        return false;
    }
    return true;
}

int
rename_building(unit * u, order * ord, building * b, const char *name)
{
    assert(name);
    if (!try_rename(u, b, ord)) {
        return -1;
    }
    return rename_cmd(u, ord, &b->name, name);
}

int name_cmd(struct unit *u, struct order *ord)
{
    char token[128];
    building *b = u->building;
    region *r = u->region;
    char **s = NULL;
    param_t p;
    bool foreign = false;
    const char *str;

    init_order(ord, u->faction->locale);
    str = gettoken(token, sizeof(token));
    p = findparam_ex(str, u->faction->locale);

    if (p == P_FOREIGN) {
        str = gettoken(token, sizeof(token));
        foreign = true;
        p = findparam_ex(str, u->faction->locale);
    }

    switch (p) {
    case P_ALLIANCE:
        if (!foreign && f_get_alliance(u->faction)) {
            alliance *al = u->faction->alliance;
            faction *lead = alliance_get_leader(al);
            if (lead == u->faction) {
                s = &al->name;
            }
        }
        break;
    case P_BUILDING:
    case P_GEBAEUDE:
        if (foreign) {
            b = getbuilding(u->region);
        }
        if (try_rename(u, b, ord)) {
            s = &b->name;
        }
        break;
    case P_FACTION:
        if (foreign) {
            faction *f;

            f = getfaction();
            if (!f) {
                cmistake(u, ord, 66, MSG_EVENT);
                break;
            }
            if (f->age < 10) {
                cmistake(u, ord, 248, MSG_EVENT);
                break;
            }
            else {
                const struct locale *lang = locales;
                size_t f_len = strlen(f->name);
                for (; lang; lang = nextlocale(lang)) {
                    const char *fdname = LOC(lang, "factiondefault");
                    size_t fdlen = strlen(fdname);
                    if (f_len >= fdlen && strncmp(f->name, fdname, fdlen) == 0) {
                        break;
                    }
                }
                if (lang == NULL) {
                    cmistake(u, ord, 247, MSG_EVENT);
                    break;
                }
            }
            if (cansee(f, r, u, 0)) {
                ADDMSG(&f->msgs,
                    msg_message("renamed_faction_seen", "unit region", u, r));
            }
            else {
                ADDMSG(&f->msgs, msg_message("renamed_faction_notseen", "", r));
            }
            s = &f->name;
        }
        else {
            s = &u->faction->name;
        }
        break;

    case P_SHIP:
        if (foreign) {
            ship *sh = getship(r);
            unit *uo;

            if (!sh) {
                cmistake(u, ord, 20, MSG_EVENT);
                break;
            }
            else {
                const struct locale *lang = locales;
                size_t sh_len = strlen(sh->name);
                for (; lang; lang = nextlocale(lang)) {
                    const char *sdname = LOC(lang, sh->type->_name);
                    size_t sdlen = strlen(sdname);
                    if (sh_len >= sdlen && strncmp(sh->name, sdname, sdlen) == 0) {
                        break;
                    }

                    sdname = LOC(lang, parameters[P_SHIP]);
                    sdlen = strlen(sdname);
                    if (sh_len >= sdlen && strncmp(sh->name, sdname, sdlen) == 0) {
                        break;
                    }

                }
                if (lang == NULL) {
                    cmistake(u, ord, 245, MSG_EVENT);
                    break;
                }
            }
            uo = ship_owner(sh);
            if (uo) {
                if (cansee(uo->faction, r, u, 0)) {
                    ADDMSG(&uo->faction->msgs,
                        msg_message("renamed_ship_seen", "ship renamer region", sh, u, r));
                }
                else {
                    ADDMSG(&uo->faction->msgs,
                        msg_message("renamed_ship_notseen", "ship region", sh, r));
                }
            }
            s = &sh->name;
        }
        else {
            unit *uo;
            if (!u->ship) {
                cmistake(u, ord, 144, MSG_PRODUCE);
                break;
            }
            uo = ship_owner(u->ship);
            if (uo->faction != u->faction) {
                cmistake(u, ord, 12, MSG_PRODUCE);
                break;
            }
            s = &u->ship->name;
        }
        break;

    case P_UNIT:
        if (foreign) {
            unit *u2 = 0;

            getunit(r, u->faction, &u2);
            if (!u2 || !cansee(u->faction, r, u2, 0)) {
                ADDMSG(&u->faction->msgs, msg_feedback(u, ord,
                    "feedback_unit_not_found", ""));
                break;
            }
            else {
                char udefault[32];
                default_name(u2, udefault, sizeof(udefault));
                if (strcmp(unit_getname(u2), udefault) != 0) {
                    cmistake(u, ord, 244, MSG_EVENT);
                    break;
                }
            }
            if (cansee(u2->faction, r, u, 0)) {
                ADDMSG(&u2->faction->msgs, msg_message("renamed_seen",
                    "renamer renamed region", u, u2, r));
            }
            else {
                ADDMSG(&u2->faction->msgs, msg_message("renamed_notseen",
                    "renamed region", u2, r));
            }
            s = &u2->_name;
        }
        else {
            s = &u->_name;
        }
        break;

    case P_REGION:
        if (u->faction != region_get_owner(r)) {
            cmistake(u, ord, 147, MSG_EVENT);
            break;
        }
        s = &r->land->name;
        break;

    case P_GROUP:
    {
        group *g = get_group(u);
        if (g) {
            s = &g->name;
            break;
        }
        else {
            cmistake(u, ord, 109, MSG_EVENT);
            break;
        }
    }
    break;
    default:
        cmistake(u, ord, 109, MSG_EVENT);
        break;
    }

    if (s != NULL) {
        const char *name = getstrtoken();
        if (name) {
            rename_cmd(u, ord, s, name);
        }
        else {
            cmistake(u, ord, 84, MSG_EVENT);
        }
    }

    return 0;
}

/* ------------------------------------------------------------- */

void
deliverMail(faction * f, region * r, unit * u, const char *s, unit * receiver)
{
    if (!cansee(f, r, u, 0)) {
        u = NULL;
    }
    if (!receiver) {              /* BOTSCHAFT an PARTEI */
        ADDMSG(&f->msgs,
            msg_message("regionmessage", "region sender string", r, u, s));
    }
    else {                      /* BOTSCHAFT an EINHEIT */
        ADDMSG(&f->msgs,
            msg_message("unitmessage", "region unit sender string", r,
                receiver, u, s));
    }
}

static void
mailunit(region * r, unit * u, int n, struct order *ord, const char *s)
{
    unit *u2 = findunitr(r, n);

    if (u2 && cansee(u->faction, r, u2, 0)) {
        deliverMail(u2->faction, r, u, s, u2);
        /* now done in prepare_mail_cmd */
    }
    else {
        /* Immer eine Meldung - sonst koennte man so getarnte EHs enttarnen:
         * keine Meldung -> EH hier. */
        ADDMSG(&u->faction->msgs,
            msg_feedback(u, ord, "feedback_unit_not_found", ""));
    }
}

static void mailfaction(unit * u, int n, struct order *ord, const char *s)
{
    faction *f;

    f = findfaction(n);
    if (f && n > 0)
        deliverMail(f, u->region, u, s, NULL);
    else
        cmistake(u, ord, 66, MSG_MESSAGE);
}

int mail_cmd(unit * u, struct order *ord)
{
    char token[128];
    region *r = u->region;
    unit *u2;
    const char *s;
    int n, cont;

    init_order(ord, NULL);
    s = gettoken(token, sizeof(token));

    /* Falls kein Parameter, ist das eine Einheitsnummer;
     * das Fuellwort "AN" muss wegfallen, da gueltige Nummer! */

    do {
        cont = 0;
        switch (findparam_ex(s, u->faction->locale)) {
        case P_REGION:
            /* koennen alle Einheiten in der Region sehen */
            s = getstrtoken();
            if (!s || !s[0]) {
                cmistake(u, ord, 30, MSG_MESSAGE);
                break;
            }
            else {
                ADDMSG(&r->msgs, msg_message("mail_result", "unit message", u, s));
                return 0;
            }

        case P_FACTION:
            n = getid();

            for (u2 = r->units; u2; u2 = u2->next) {
                if (u2->faction->no == n && seefaction(u->faction, r, u2, 0)) {
                    break;
                }
            }

            if (!u2) {
                cmistake(u, ord, 66, MSG_MESSAGE);
                break;
            }

            s = getstrtoken();
            if (!s || !s[0]) {
                cmistake(u, ord, 30, MSG_MESSAGE);
                break;
            }
            mailfaction(u, n, ord, s);
            return 0;

        case P_UNIT:
            n = getid();

            for (u2 = r->units; u2; u2 = u2->next) {
                if (u2->no == n && cansee(u->faction, r, u2, 0)) {
                    break;
                }
            }

            if (!u2) {
                ADDMSG(&u->faction->msgs, msg_feedback(u, ord,
                    "feedback_unit_not_found", ""));
                return 0;
            }

            s = getstrtoken();
            if (!s || !s[0]) {
                cmistake(u, ord, 30, MSG_MESSAGE);
                break;
            }
            else {
                mailunit(r, u, n, ord, s);
            }
            return 0;

        case P_BUILDING:
        case P_GEBAEUDE:
        {
            building *b = getbuilding(r);

            if (!b) {
                cmistake(u, ord, 6, MSG_MESSAGE);
                break;
            }

            s = getstrtoken();

            if (!s || !s[0]) {
                cmistake(u, ord, 30, MSG_MESSAGE);
                break;
            }

            for (u2 = r->units; u2; u2 = u2->next)
                freset(u2->faction, FFL_SELECT);

            for (u2 = r->units; u2; u2 = u2->next) {
                if (u2->building == b && !fval(u2->faction, FFL_SELECT)
                    && cansee(u->faction, r, u2, 0)) {
                    mailunit(r, u, u2->no, ord, s);
                    fset(u2->faction, FFL_SELECT);
                }
            }
            return 0;
        }

        case P_SHIP:
        {
            ship *sh = getship(r);

            if (!sh) {
                cmistake(u, ord, 20, MSG_MESSAGE);
                break;
            }

            s = getstrtoken();

            if (!s || !s[0]) {
                cmistake(u, ord, 30, MSG_MESSAGE);
                break;
            }

            for (u2 = r->units; u2; u2 = u2->next)
                freset(u2->faction, FFL_SELECT);

            for (u2 = r->units; u2; u2 = u2->next) {
                if (u2->ship == sh && !fval(u2->faction, FFL_SELECT)
                    && cansee(u->faction, r, u2, 0)) {
                    mailunit(r, u, u2->no, ord, s);
                    fset(u2->faction, FFL_SELECT);
                }
            }
            return 0;
        }

        default:
            /* possibly filler token? */
            s = getstrtoken();
            if (s && *s)
                cont = 1;
            break;
        }
    } while (cont);
    cmistake(u, ord, 149, MSG_MESSAGE);
    return 0;
}

/* ------------------------------------------------------------- */

int banner_cmd(unit * u, struct order *ord)
{
    const char * s;

    init_order(ord, NULL);
    s = getstrtoken();
    if (!s || !s[0]) {
        cmistake(u, ord, 125, MSG_EVENT);
    }
    else {
        faction_setbanner(u->faction, s);
        ADDMSG(&u->faction->msgs, msg_message("changebanner", "value", s));
    }

    return 0;
}

int email_cmd(unit * u, struct order *ord)
{
    const char *s;

    init_order(ord, NULL);
    s = getstrtoken();

    if (!s || !s[0]) {
        cmistake(u, ord, 85, MSG_EVENT);
    }
    else {
        faction *f = u->faction;
        if (check_email(s) == 0) {
          faction_setemail(f, s);
          ADDMSG(&f->msgs, msg_message("changemail", "value", faction_getemail(f)));
        } else {
            log_error("Invalid email address for faction %s: %s\n", itoa36(f->no), s);
            ADDMSG(&f->msgs, msg_message("changemail_invalid", "value", s));
        }
    }
    return 0;
}

bool password_wellformed(const char *password)
{
    unsigned char *c = (unsigned char *)password;
    int i;
    if (!password || password[0]=='\0') {
        return false;
    }
    for (i = 0; c[i] && i != PASSWORD_MAXSIZE; ++i) {
        if (!isalnum(c[i])) {
            return false;
        }
    }
    return true;
}

int password_cmd(unit * u, struct order *ord)
{
    char pwbuf[PASSWORD_MAXSIZE + 1];
    const char *s;

    init_order(ord, NULL);
    pwbuf[PASSWORD_MAXSIZE] = '\n';
    s = gettoken(pwbuf, sizeof(pwbuf));
    if (pwbuf[PASSWORD_MAXSIZE] == '\0') {
        cmistake(u, ord, 321, MSG_EVENT);
        pwbuf[PASSWORD_MAXSIZE - 1] = '\0';
    }

    if (!s || !password_wellformed(s)) {
        if (s) {
            cmistake(u, ord, 283, MSG_EVENT);
        }
        password_generate(pwbuf, PASSWORD_MAXSIZE);
    }
    faction_setpassword(u->faction, password_hash(pwbuf, PASSWORD_DEFAULT));
    ADDMSG(&u->faction->msgs, msg_message("changepasswd", "value", pwbuf));
    u->faction->flags |= FFL_PWMSG;
    return 0;
}

int send_cmd(unit * u, struct order *ord)
{
    char token[128];
    const char *s;
    int option;

    init_order(ord, NULL);
    s = gettoken(token, sizeof(token));

    option = findoption(s, u->faction->locale);

    if (option == -1) {
        cmistake(u, ord, 135, MSG_EVENT);
    }
    else {
        if (getparam(u->faction->locale) == P_NOT) {
            if (option == O_COMPRESS || option == O_BZIP2) {
                cmistake(u, ord, 305, MSG_EVENT);
            }
            else {
                u->faction->options = u->faction->options & ~(1 << option);
            }
        }
        else {
            u->faction->options = u->faction->options | (1 << option);
            if (option == O_COMPRESS)
                u->faction->options &= ~(1 << O_BZIP2);
            if (option == O_BZIP2)
                u->faction->options &= ~(1 << O_COMPRESS);
        }
    }
    return 0;
}

static void display_potion(unit * u, const item_type * itype) {
    show_item(u, itype);
}

static void display_item(unit * u, const item_type * itype)
{
    faction * f = u->faction;
    const char *name;
    const char *key;
    const char *info;

    name = resourcename(itype->rtype, 0);
    key = mkname("iteminfo", name);
    info = locale_getstring(f->locale, key);

    if (info == NULL) {
        info = LOC(f->locale, mkname("iteminfo", "no_info"));
    }
    ADDMSG(&f->msgs, msg_message("displayitem", "weight item description",
        itype->weight, itype->rtype, info));
}

static void display_race(faction * f, const race * rc)
{
    char buf[2048];
    sbstring sbs;

    sbs_init(&sbs, buf, sizeof(buf));
    report_raceinfo(rc, f->locale, &sbs);
    addmessage(0, f, buf, MSG_EVENT, ML_IMPORTANT);
}

static void reshow_other(unit * u, struct order *ord, const char *s) {
    int err = 21;
    bool found = false;

    if (s) {
        const spell *sp = 0;
        const item_type *itype;
        const race *rc;
        /* check if it's an item */
        itype = finditemtype(s, u->faction->locale);
        sp = unit_getspell(u, s, u->faction->locale);
        rc = findrace(s, u->faction->locale);

        if (itype) {
            /* if this is a potion, we need the right alchemy skill */
            err = 36; /* we do not have this item? */
            if (itype->flags & ITF_POTION) {
                /* we don't have the item, but it is a potion. do we know it? */
                int level = potion_level(itype);
                if (level > 0 && 2 * level <= effskill(u, SK_ALCHEMY, NULL)) {
                    display_potion(u, itype);
                    found = true;
                }
            }
            else {
                int i = i_get(u->items, itype);
                if (i > 0) {
                    found = true;
                    display_item(u, itype);
                }
            }
        }

        if (sp) {
            reset_seen_spells(u->faction, sp);
            found = true;
        }

        if (rc && u_race(u) == rc) {
            display_race(u->faction, rc);
            found = true;
        }
    }
    if (!found) {
        cmistake(u, ord, err, MSG_EVENT);
    }
}

static void reshow(unit * u, struct order *ord, const char *s, param_t p)
{
    switch (p) {
    case P_ZAUBER:
        reset_seen_spells(u->faction, NULL);
        break;
    case P_POTIONS:
        if (!display_potions(u)) {
            cmistake(u, ord, 285, MSG_EVENT);
        }
        break;
    case NOPARAM:
        reshow_other(u, ord, s);
        break;
    default:
        cmistake(u, ord, 222, MSG_EVENT);
        break;
    }
}

int promotion_cmd(unit * u, struct order *ord)
{
    const struct resource_type *rsilver = get_resourcetype(R_SILVER);
    int money, people;

    if (fval(u, UFL_HERO)) {
        /* TODO: message "is already a hero" */
        return 0;
    }

    if (maxheroes(u->faction) < countheroes(u->faction) + u->number) {
        ADDMSG(&u->faction->msgs,
            msg_feedback(u, ord, "heroes_maxed", "max count",
                maxheroes(u->faction), countheroes(u->faction)));
        return 0;
    }
    if (!valid_race(u->faction, u_race(u))) {
        ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "heroes_race", "race",
            u_race(u)));
        return 0;
    }
    people = u->faction->num_people * u->number;
    money = get_pooled(u, rsilver, GET_ALL, people);

    if (people > money) {
        ADDMSG(&u->faction->msgs,
            msg_feedback(u, ord, "heroes_cost", "cost have", people, money));
        return 0;
    }
    use_pooled(u, rsilver, GET_ALL, people);
    fset(u, UFL_HERO);
    ADDMSG(&u->faction->msgs, msg_message("hero_promotion", "unit cost",
        u, people));
    return 0;
}

int group_cmd(unit * u, struct order *ord)
{
    keyword_t kwd;

    kwd = init_order(ord, NULL);
    assert(kwd == K_GROUP);
    join_group(u, getstrtoken());
    return 0;
}

int origin_cmd(unit * u, struct order *ord)
{
    int px, py;

    init_order(ord, NULL);

    px = getint();
    py = getint();

    faction_setorigin(u->faction, getplaneid(u->region), px, py);
    return 0;
}

int guard_off_cmd(unit * u, struct order *ord)
{
    assert(getkeyword(ord) == K_GUARD);
    init_order(ord, NULL);

    if (getparam(u->faction->locale) == P_NOT) {
        setguard(u, false);
    }
    return 0;
}

int reshow_cmd(unit * u, struct order *ord)
{
    char lbuf[64];
    const char *s;
    param_t p = NOPARAM;

    init_order(ord, NULL);
    s = gettoken(lbuf, sizeof(lbuf));

    if (s && isparam(s, u->faction->locale, P_ANY)) {
        p = getparam(u->faction->locale);
        s = NULL;
    }

    reshow(u, ord, s, p);
    return 0;
}

int status_cmd(unit * u, struct order *ord)
{
    char token[128];
    const char *s;

    init_order(ord, NULL);
    s = gettoken(token, sizeof(token));
    switch (findparam(s, u->faction->locale)) {
    case P_NOT:
        unit_setstatus(u, ST_AVOID);
        break;
    case P_BEHIND:
        unit_setstatus(u, ST_BEHIND);
        break;
    case P_FLEE:
        unit_setstatus(u, ST_FLEE);
        break;
    case P_CHICKEN:
        unit_setstatus(u, ST_CHICKEN);
        break;
    case P_AGGRO:
        unit_setstatus(u, ST_AGGRO);
        break;
    case P_VORNE:
        unit_setstatus(u, ST_FIGHT);
        break;
    case P_HELP:
        if (getparam(u->faction->locale) == P_NOT) {
            fset(u, UFL_NOAID);
        }
        else {
            freset(u, UFL_NOAID);
        }
        break;
    default:
        if (s && s[0]) {
            add_message(&u->faction->msgs,
                msg_feedback(u, ord, "unknown_status", ""));
        }
        else {
            unit_setstatus(u, ST_FIGHT);
        }
    }
    return 0;
}

int combatspell_cmd(unit * u, struct order *ord)
{
    char token[128];
    const char *s;
    int level = 0;
    spell *sp = 0;

    init_order(ord, NULL);
    s = gettoken(token, sizeof(token));

    /* KAMPFZAUBER [NICHT] loescht alle gesetzten Kampfzauber */
    if (!s || *s == 0 || findparam(s, u->faction->locale) == P_NOT) {
        unset_combatspell(u, 0);
        return 0;
    }

    /* Optional: STUFE n */
    if (findparam(s, u->faction->locale) == P_LEVEL) {
        /* Merken, setzen kommt erst spaeter */
        level = getuint();
        s = gettoken(token, sizeof(token));
    }

    sp = unit_getspell(u, s, u->faction->locale);
    if (!sp) {
        cmistake(u, ord, 173, MSG_MAGIC);
        return 0;
    }

    s = gettoken(token, sizeof(token));

    if (findparam(s, u->faction->locale) == P_NOT) {
        /* KAMPFZAUBER "<Spruchname>" NICHT  loescht diesen speziellen
         * Kampfzauber */
        unset_combatspell(u, sp);
        return 0;
    }
    else {
        /* KAMPFZAUBER "<Spruchname>"  setzt diesen Kampfzauber */
        /* knowsspell prueft auf ist_magier, ist_spruch, kennt_spruch */
        if (!knowsspell(u->region, u, sp)) {
            /* Fehler 'Spell not found' */
            cmistake(u, ord, 173, MSG_MAGIC);
        }
        else if (!u_hasspell(u, sp)) {
            /* Diesen Zauber kennt die Einheit nicht */
            cmistake(u, ord, 169, MSG_MAGIC);
        }
        else if (!(sp->sptyp & ISCOMBATSPELL)) {
            /* Diesen Kampfzauber gibt es nicht */
            cmistake(u, ord, 171, MSG_MAGIC);
        }
        else {
            set_combatspell(u, sp, ord, level);
        }
    }

    return 0;
}

int guard_on_cmd(unit * u, struct order *ord)
{
    assert(getkeyword(ord) == K_GUARD);
    assert(u);
    assert(u->faction);

    init_order(ord, NULL);

    /* GUARD NOT is handled in goard_off_cmd earlier in the turn */
    if (getparam(u->faction->locale) == P_NOT) {
        return 0;
    }

    if (fval(u, UFL_MOVED)) {
        cmistake(u, ord, 187, MSG_EVENT);
    }
    else if (fval(u_race(u), RCF_ILLUSIONARY)) {
        cmistake(u, ord, 95, MSG_EVENT);
    }
    else {
        int err = can_start_guarding(u);
        if (err == E_GUARD_OK) {
            setguard(u, true);
        }
        else if (err == E_GUARD_TERRAIN) {
            cmistake(u, ord, 2, MSG_EVENT);
        }
        else if (err == E_GUARD_UNARMED) {
            ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "unit_unarmed", ""));
        }
        else if (err == E_GUARD_FLEEING) {
            cmistake(u, ord, 320, MSG_EVENT);
        }
        else if (err == E_GUARD_NEWBIE) {
            cmistake(u, ord, 304, MSG_EVENT);
        }
    }
    return 0;
}

void sinkships(struct region * r)
{
    ship **shp = &r->ships;

    while (*shp) {
        ship *sh = *shp;

        if (sh->number > 0) {
            if (!sh->type->construction || sh->size >= sh->type->construction->maxsize) {
                unit *cap = ship_owner(sh);
                if (fval(r->terrain, SEA_REGION)) {
                    if (!ship_crewed(sh, cap)) {
                        /* ship is at sea, but not enough people to control it */
                        double dmg = config_get_flt("rules.ship.damage.nocrewocean", 0.3);
                        damage_ship(sh, dmg);
                    }
                }
                else if (!cap) {
                    /* any ship lying around without an owner slowly rots */
                    double dmg = config_get_flt("rules.ship.damage.nocrew", 0.05);
                    damage_ship(sh, dmg);
                }
            }
            if (sh->damage >= sh->size * DAMAGE_SCALE) {
                sink_ship(sh);
                remove_ship(shp, sh);
            }
        }
        else {
            remove_ship(shp, sh);
        }
        if (*shp == sh)
            shp = &sh->next;
    }
}

void restack_units(void)
{
    region *r;
    for (r = regions; r; r = r->next) {
        unit **up = &r->units;
        bool sorted = false;
        while (*up) {
            unit *u = *up;
            if (!fval(u, UFL_MARK)) {
                struct order *ord;
                for (ord = u->orders; ord; ord = ord->next) {
                    if (getkeyword(ord) == K_SORT) {
                        char token[128];
                        const char *s;
                        param_t p;
                        int id;
                        unit *v;

                        init_order(ord, NULL);
                        s = gettoken(token, sizeof(token));
                        p = findparam(s, u->faction->locale);
                        id = getid();
                        v = findunit(id);

                        if (!v || v->faction != u->faction || v->region != r) {
                            cmistake(u, ord, 258, MSG_EVENT);
                        }
                        else if (v->building != u->building || v->ship != u->ship) {
                            cmistake(u, ord, 259, MSG_EVENT);
                        }
                        else if (u->building && building_owner(u->building) == u) {
                            cmistake(u, ord, 260, MSG_EVENT);
                        }
                        else if (u->ship && ship_owner(u->ship) == u) {
                            cmistake(u, ord, 260, MSG_EVENT);
                        }
                        else if (v == u) {
                            syntax_error(u, ord);
                        }
                        else {
                            switch (p) {
                            case P_AFTER:
                                *up = u->next;
                                u->next = v->next;
                                v->next = u;
                                fset(u, UFL_MARK);
                                sorted = true;
                                break;
                            case P_BEFORE:
                                if (v->ship && ship_owner(v->ship) == v) {
                                    cmistake(v, ord, 261, MSG_EVENT);
                                }
                                else if (v->building && building_owner(v->building) == v) {
                                    cmistake(v, ord, 261, MSG_EVENT);
                                }
                                else {
                                    unit **vp = &r->units;
                                    while (*vp != v)
                                        vp = &(*vp)->next;
                                    *vp = u;
                                    *up = u->next;
                                    u->next = v;
                                }
                                fset(u, UFL_MARK);
                                sorted = true;
                                break;
                            default:
                                /* TODO: syntax error message? */
                                break;
                            }
                        }
                        break;
                    }
                }
            }
            if (u == *up)
                up = &u->next;
        }
        if (sorted) {
            unit *u;
            for (u = r->units; u; u = u->next) {
                freset(u, UFL_MARK);
            }
        }
    }
}

/* blesses stone circles create an astral protection in the astral region
 * above the shield, which prevents chaos suction and other spells.
 * The shield is created when a magician enters the blessed stone circle,
 * and lasts for as long as his skill level / 2 is, at no mana cost.
 *
 * TODO: this would be nicer in a btype->age function, but we don't have it.
 */
static void age_stonecircle(building *b) {
    const struct resource_type *rtype = get_resourcetype(R_UNICORN);
    region *r = b->region;
    unit *u, *mage = NULL;

    /* step 1: give unicorns to people in the building,
     * find out if there's a magician in there. */
    for (u = r->units; u; u = u->next) {
        if (b == u->building && inside_building(u)) {
            if (!mage && is_mage(u)) {
                mage = u;
            }
            if (rtype) {
                int n, unicorns = 0;
                for (n = 0; n != u->number; ++n) {
                    if (chance(0.02)) {
                        i_change(&u->items, rtype->itype, 1);
                        ++unicorns;
                    }
                    if (unicorns) {
                        ADDMSG(&u->faction->msgs, msg_message("scunicorn",
                                                              "unit amount rtype",
                                                              u, unicorns, rtype));
                    }
                }
            }
        }
    }

    /* step 2: if there's a magician, and a connection to astral space, create the
     * curse. */
    if (get_astralplane()) {
        region *rt = r_standard_to_astral(r);
        if (mage && rt && !fval(rt->terrain, FORBIDDEN_REGION)) {
            curse *c = get_curse(rt->attribs, &ct_astralblock);
            if (!c) {
                int sk = effskill(mage, SK_MAGIC, NULL);
                if (sk > 0) {
                    int vig = sk;
                    int dur = (sk + 1) / 2;
                    float effect = 100;
                    /* the mage reactivates the circle */
                    c = create_curse(mage, &rt->attribs, &ct_astralblock,
                        vig, dur, effect, 0);
                    ADDMSG(&r->msgs,
                        msg_message("astralshield_activate", "region unit", r, mage));
                }
            }
            else {
                int sk = effskill(mage, SK_MAGIC, NULL);
                if (c->duration < sk / 2) c->duration = sk / 2;
                if (c->vigour < sk) c->vigour = sk;
            }
        }
    }
}

static building *age_building(building * b)
{
    if (is_building_type(b->type, "blessedstonecircle")) {
        age_stonecircle(b);
    }
    a_age(&b->attribs, b);
    handle_event(b->attribs, "timer", b);

    return b;
}

static void age_region(region * r)
{
    a_age(&r->attribs, r);
    handle_event(r->attribs, "timer", r);

    if (!r->land)
        return;

    morale_update(r);
}

static void ageing(void)
{
    faction *f;
    region *r;

    /* altern spezieller Attribute, die eine Sonderbehandlung brauchen?  */
    for (r = regions; r; r = r->next) {
        unit *u;

        for (u = r->units; u; u = u->next) {
            /* Goliathwasser */
            int i = get_effect(u, oldpotiontype[P_STRONG]);
            if (i > 0) {
                if (i > u->number) i = u->number;
                change_effect(u, oldpotiontype[P_STRONG], - i);
            }
            /* Berserkerblut */
            i = get_effect(u, oldpotiontype[P_BERSERK]);
            if (i > 0) {
                if (i > u->number) i = u->number;
                change_effect(u, oldpotiontype[P_BERSERK], - i);
            }

            if (u->attribs) {
                curse * c = get_curse(u->attribs, &ct_oldrace);
                if (c && curse_active(c)) {
                    if (c->duration == 1 && !(c_flags(c) & CURSE_NOAGE)) {
                        u_setrace(u, get_race(curse_geteffect_int(c)));
                        u->irace = NULL;
                    }
                }
            }
        }
    }

    /* Borders */
    age_borders();

    /* Factions */
    for (f = factions; f; f = f->next) {
        a_age(&f->attribs, f);
        handle_event(f->attribs, "timer", f);
    }

    /* Regionen */
    for (r = regions; r; r = r->next) {
        building **bp;
        unit **up;
        ship **sp;

        age_region(r);

        /* Einheiten */
        for (up = &r->units; *up;) {
            unit *u = *up;
            a_age(&u->attribs, u);
            if (u == *up) 
                handle_event(u->attribs, "timer", u);
            if (u == *up) /*-V581 */
                up = &(*up)->next;
        }

        /* Schiffe */
        for (sp = &r->ships; *sp;) {
            ship *s = *sp;
            a_age(&s->attribs, s);
            if (s == *sp)
                handle_event(s->attribs, "timer", s);
            if (s == *sp) /*-V581 */
                sp = &(*sp)->next;
        }

        /* Gebaeude */
        for (bp = &r->buildings; *bp;) {
            building *b = *bp;
            age_building(b);
            if (b == *bp)
                bp = &b->next;
        }

        if (rule_region_owners()) {
            update_owners(r);
        }
    }
}

static int maxunits(const faction * f)
{
    int flimit = rule_faction_limit();
    int alimit = rule_alliance_limit();
    UNUSED_ARG(f);
    if (alimit == 0) {
        return flimit;
    }
    if (flimit == 0) {
        return alimit;
    }
    return (alimit > flimit) ? flimit : alimit;
}

int checkunitnumber(const faction * f, int add)
{
    int alimit, flimit;
    int fno = f->num_units + add;
    flimit = rule_faction_limit();
    if (flimit && fno > flimit) {
        return 2;
    }

    alimit = rule_alliance_limit();
    if (alimit) {
        int unitsinalliance = alliance_size(f->alliance);
        if (unitsinalliance + add > alimit) {
            return 1;
        }
    }

    return 0;
}

void maketemp_cmd(unit *u, order **olist) 
{
    order *makeord;
    int err = checkunitnumber(u->faction, 1);

    makeord = *olist;
    if (err) {
        if (err == 1) {
            ADDMSG(&u->faction->msgs,
                msg_feedback(u, makeord,
                    "too_many_units_in_alliance",
                    "allowed", maxunits(u->faction)));
        }
        else {
            ADDMSG(&u->faction->msgs,
                msg_feedback(u, makeord,
                    "too_many_units_in_faction",
                    "allowed", maxunits(u->faction)));
        }
        *olist = makeord->next;
        makeord->next = NULL;
        free_order(makeord);
        while (*olist) {
            keyword_t kwd;
            order * ord = *olist;
            *olist = ord->next;
            ord->next = NULL;
            kwd = getkeyword(ord);
            free_order(ord);
            if (kwd == K_END) {
                break;
            }
        }
    }
    else {
        char token[128];
        const char *s;
        int alias;
        ship *sh;
        unit *u2;
        order **ordp, **oinsert;
#ifndef NDEBUG
        keyword_t kwd = init_order(makeord, NULL);
        assert(kwd == K_MAKETEMP);
#endif
        alias = getid();
        s = gettoken(token, sizeof(token));
        if (s && s[0] == '\0') {
            /* empty name? => generate one */
            s = NULL;
        }
        u2 = create_unit(u->region, u->faction, 0, u->faction->race, alias, s, u);
        fset(u2, UFL_ISNEW);
        usetalias(u2, alias);
        sh = leftship(u);
        if (sh) {
            set_leftship(u2, sh);
        }
        unit_setstatus(u2, u->status);

        /* copy orders until K_END from u to u2 */
        ordp = &makeord->next;
        oinsert = &u2->orders;

        while (*ordp) {
            order *ord = *ordp;
            *ordp = ord->next;
            if (getkeyword(ord) == K_END) {
                ord->next = NULL;
                free_order(ord);
                break;
            }
            *oinsert = ord;
            oinsert = &ord->next;
            *oinsert = NULL;
        }
        *olist = *ordp;
        makeord->next = NULL;
        free_order(makeord);

        if (!u2->orders) {
            order *deford = default_order(u2->faction->locale);
            if (deford) {
                set_order(&u2->thisorder, NULL);
                unit_addorder(u2, deford);
            }
        }
    }
}

void new_units(void)
{
    region *r;
    unit *u;

    /* neue einheiten werden gemacht und ihre befehle (bis zum "ende" zu
     * ihnen rueberkopiert, damit diese einheiten genauso wie die alten
     * einheiten verwendet werden koennen. */

    for (r = regions; r; r = r->next) {
        for (u = r->units; u; u = u->next) {
            order **ordp = &u->orders;

            /* this needs to happen very early in the game somewhere. since this is
             ** pretty much the first function called per turn, and I am lazy, I
             ** decree that it goes here */
            if (u->flags & UFL_GUARD) {
                fset(r, RF_GUARDED);
            }

            while (*ordp) {
                order *ord = *ordp;
                if (getkeyword(ord) == K_MAKETEMP) {
                    maketemp_cmd(u, ordp);
                }
                else {
                    ordp = &ord->next;
                }
            }
        }
    }
}

void update_long_order(unit * u)
{
    order *ord;
    bool exclusive = true;
    keyword_t thiskwd = NOKEYWORD;
    bool hunger = LongHunger(u);

    freset(u, UFL_MOVED);
    freset(u, UFL_LONGACTION);

    /* check all orders for a potential new long order this round: */
    for (ord = u->orders; ord; ord = ord->next) {
        keyword_t kwd = getkeyword(ord);
        if (kwd == NOKEYWORD) continue;

        if (u->old_orders && is_repeated(kwd)) {
            /* this new order will replace the old defaults */
            free_orders(&u->old_orders);
        }

        /* hungry units do not get long orders: */
        if (hunger) {
            if (u->old_orders) {
                /* keep looking for repeated orders that might clear the old_orders */
                continue;
            }
            break;
        }

        if (is_long(kwd)) {
            if (thiskwd == NOKEYWORD) {
                /* we have found the (first) long order
                 * some long orders can have multiple instances: */
                switch (kwd) {
                    /* Wenn gehandelt wird, darf kein langer Befehl ausgefuehrt
                     * werden. Da Handel erst nach anderen langen Befehlen kommt,
                     * muss das vorher abgefangen werden. Wir merken uns also
                     * hier, ob die Einheit handelt. */
                case K_BUY:
                case K_SELL:
                case K_CAST:
                    /* non-exclusive orders can be used with others. BUY can be paired with SELL,
                     * CAST with other CAST orders. compatibility is checked once the second
                     * long order is analyzed (below). */
                    exclusive = false;
                    break;

                default:
                    set_order(&u->thisorder, copy_order(ord));
                    break;
                }
                thiskwd = kwd;
            }
            else {
                /* we have found a second long order. this is okay for some, but not all commands.
                 * u->thisorder is already set, and should not have to be updated. */
                switch (kwd) {
                case K_CAST:
                    if (thiskwd != K_CAST) {
                        cmistake(u, ord, 52, MSG_EVENT);
                    }
                    break;
                case K_SELL:
                    if (thiskwd != K_SELL && thiskwd != K_BUY) {
                        cmistake(u, ord, 52, MSG_EVENT);
                    }
                    break;
                case K_BUY:
                    if (thiskwd != K_SELL) {
                        cmistake(u, ord, 52, MSG_EVENT);
                    }
                    else {
                        thiskwd = K_BUY;
                    }
                    break;
                default:
                    cmistake(u, ord, 52, MSG_EVENT);
                    break;
                }
            }
        }
    }
    if (hunger) {
        /* Hungernde Einheiten fuehren NUR den default-Befehl aus */
        set_order(&u->thisorder, default_order(u->faction->locale));
    }
    else if (!exclusive) {
        /* Wenn die Einheit handelt oder zaubert, muss der Default-Befehl geloescht werden. */
        set_order(&u->thisorder, NULL);
    }
}

static int use_item(unit * u, const item_type * itype, int amount, struct order *ord)
{
    int i;

    i = get_pooled(u, itype->rtype, GET_DEFAULT, amount);
    if (amount > i) {
        /* TODO: message? eg. "not enough %, using only %" */
        amount = i;
    }
    if (amount == 0) {
        return ENOITEM;
    }

    if (itype->flags & ITF_CANUSE) {
        int result = callbacks.use_item(u, itype, amount, ord);
        if (result > 0) {
            use_pooled(u, itype->rtype, GET_DEFAULT, result);
        }
        return result;
    }
    return EUNUSABLE;
}

void monthly_healing(void)
{
    region *r;

    for (r = regions; r; r = r->next) {
        unit *u;
        double healingcurse = 0;

        if (r->attribs) {
            /* bonus zuruecksetzen */
            curse *c = get_curse(r->attribs, &ct_healing);
            if (c != NULL) {
                healingcurse = curse_geteffect(c);
            }
        }
        for (u = r->units; u; u = u->next) {
            int umhp = unit_max_hp(u) * u->number;
            double p = 1.0;

            /* hp ueber Maximum bauen sich ab. Wird zb durch Elixier der Macht
             * oder veraendertes Ausdauertalent verursacht */
            if (u->hp > umhp) {
                int diff = u->hp - umhp;
                u->hp -= (int)ceil(diff / 2.0);
                if (u->hp < umhp) {
                    u->hp = umhp;
                }
                continue;
            }

            if (u_race(u)->flags & RCF_NOHEAL)
                continue;
            if (fval(u, UFL_HUNGER))
                continue;

            if (fval(r->terrain, SEA_REGION) && u->ship == NULL
                && !(canswim(u) || canfly(u))) {
                continue;
            }

            p *= u_heal_factor(u);
            if (u->hp < umhp) {
                double maxheal = fmax(u->number, umhp / 20.0);
                int addhp;
                if (active_building(u, bt_find("inn"))) {
                    p *= 1.5;
                }
                /* pro punkt 5% hoeher */
                p *= (1.0 + healingcurse * 0.05);

                maxheal = p * maxheal;
                addhp = (int)maxheal;
                maxheal -= addhp;
                if (maxheal > 0.0 && chance(maxheal))
                    ++addhp;

                /* Aufaddieren der geheilten HP. */
                if (umhp > u->hp + addhp) umhp = u->hp + addhp;
                u->hp = umhp;

                /* soll man an negativer regeneration sterben koennen? */
                assert(u->hp > 0);
            }
        }
    }
}

static void remove_exclusive(order ** ordp)
{
    while (*ordp) {
        order *ord = *ordp;
        if (is_exclusive(ord)) {
            *ordp = ord->next;
            ord->next = NULL;
            free_order(ord);
        }
        else {
            ordp = &ord->next;
        }
    }
}

void defaultorders(void)
{
    region *r;

    assert(!keyword_disabled(K_DEFAULT));
    for (r = regions; r; r = r->next) {
        unit *u;
        for (u = r->units; u; u = u->next) {
            bool neworders = false;
            order **ordp = &u->orders;
            while (*ordp != NULL) {
                order *ord = *ordp;
                if (getkeyword(ord) == K_DEFAULT) {
                    char lbuf[8192];
                    order *new_order = 0;
                    const char *s;
                    init_order(ord, NULL);
                    s = gettoken(lbuf, sizeof(lbuf));
                    if (s) {
                        new_order = parse_order(s, u->faction->locale);
                    }
                    *ordp = ord->next;
                    ord->next = NULL;
                    free_order(ord);
                    if (!neworders) {
                        /* lange Befehle aus orders und old_orders loeschen zu gunsten des neuen */
                        /* TODO: why only is_exclusive, not is_long? what about CAST, BUY, SELL? */
                        remove_exclusive(&u->orders);
                        remove_exclusive(&u->old_orders);
                        neworders = true;
                        ordp = &u->orders;  /* we could have broken ordp */
                    }
                    if (new_order)
                        addlist(&u->old_orders, new_order);
                }
                else
                    ordp = &ord->next;
            }
        }
    }
}

/* ************************************************************ */
/* GANZ WICHTIG! ALLE GEAENDERTEN SPRUECHE NEU ANZEIGEN */
/* GANZ WICHTIG! FUEGT AUCH NEUE ZAUBER IN DIE LISTE DER BEKANNTEN EIN */
/* ************************************************************ */
#define COMMONSPELLS 1          /* number of new common spells per level */
#define MAXMAGES 128            /* should be enough */

static int faction_getmages(faction * f, unit ** results, int numresults)
{
    unit *u;
    int maxlevel = 0, n = 0;

    for (u = f->units; u; u = u->nextF) {
        if (u->number > 0) {
            struct sc_mage * mage = get_mage(u);
            if (mage) {
                int level = effskill(u, SK_MAGIC, NULL);
                if (level > maxlevel) {
                    maxlevel = level;
                }
                if (n < numresults) {
                    results[n++] = u;
                }
            }
        }
    }
    if (n < numresults) {
        results[n] = 0;
    }
    return maxlevel;
}

static void copy_spells(const spellbook * src, spellbook * dst, int maxlevel)
{
    assert(dst);
    if (src && src->spells) {
        selist *ql;
        int qi;
        for (qi = 0, ql = src->spells; ql; selist_advance(&ql, &qi, 1)) {
            spellbook_entry * sbe = (spellbook_entry *)selist_get(ql, qi);
            if (sbe->level <= maxlevel) {
                spell *sp = spellref_get(&sbe->spref);
                if (!spellbook_get(dst, sp)) {
                    spellbook_add(dst, sp, sbe->level);
                }
            }
        }
    }
}

static void show_new_spells(faction * f, int level, const spellbook *book)
{
    if (book) {
        selist *ql = book->spells;
        int qi;

        for (qi = 0; ql; selist_advance(&ql, &qi, 1)) {
            spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi);
            if (sbe->level <= level) {
                show_spell(f, sbe);
            }
        }
    }
}

static void update_spells(void)
{
    faction *f;

    for (f = factions; f; f = f->next) {
        if (f->magiegebiet != M_NONE && !is_monsters(f)) {
            unit *mages[MAXMAGES];
            int i;
            int maxlevel = faction_getmages(f, mages, MAXMAGES);
            struct spellbook *fsb;

            if (maxlevel && FactionSpells()) {
                spellbook * book = get_spellbook(magic_school[f->magiegebiet]);
                if (!f->spellbook) {
                    f->spellbook = create_spellbook(NULL);
                }
                copy_spells(book, f->spellbook, maxlevel);
                if (maxlevel > f->max_spelllevel) {
                    spellbook * common_spells = get_spellbook(magic_school[M_COMMON]);
                    pick_random_spells(f, maxlevel, common_spells, COMMONSPELLS);
                }
            }
            fsb = faction_get_spellbook(f);
            show_new_spells(f, maxlevel, fsb);
            for (i = 0; i != MAXMAGES && mages[i]; ++i) {
                unit * u = mages[i];
                spellbook *sb = unit_get_spellbook(u);
                if (sb != fsb) {
                    int level = effskill(u, SK_MAGIC, NULL);
                    show_new_spells(f, level, sb);
                }
            }
        }
    }
}

int use_cmd(unit * u, struct order *ord)
{
    char token[128];
    const char *t;
    int n, err = ENOITEM;
    const item_type *itype;

    init_order(ord, NULL);

    t = gettoken(token, sizeof(token));
    if (!t) {
        cmistake(u, ord, 43, MSG_PRODUCE);
        return err;
    }
    n = atoip((const char *)t);
    if (n == 0) {
        if (isparam(t, u->faction->locale, P_ANY)) {
            /* BENUTZE ALLES Yanxspirit */
            n = INT_MAX;
            t = gettoken(token, sizeof(token));
        }
        else {
            /* BENUTZE Yanxspirit */
            n = 1;
        }
    }
    else {
        /* BENUTZE 42 Yanxspirit */
        t = gettoken(token, sizeof(token));
    }
    itype = t ? finditemtype(t, u->faction->locale) : NULL;

    if (itype != NULL) {
        err = use_item(u, itype, n, ord);
    }
    switch (err) {
    case ENOITEM:
        cmistake(u, ord, 43, MSG_PRODUCE);
        break;
    case EUNUSABLE:
        cmistake(u, ord, 76, MSG_PRODUCE);
        break;
    case ENOSKILL:
        cmistake(u, ord, 50, MSG_PRODUCE);
        break;
    default:
        /* no error */
        break;
    }
    return err;
}

int pay_cmd(unit * u, struct order *ord)
{
    if (!u->building) {
        cmistake(u, ord, 6, MSG_EVENT);
    }
    else {
        param_t p;
        int id;

        init_order(ord, NULL);
        p = getparam(u->faction->locale);
        id = getid();
        if (p == P_NOT) {
            unit *owner = building_owner(u->building);
            /* If the unit is not the owner of the building: error */
            if (owner->no != u->no) {
                /* The building is not ours error */
                cmistake(u, ord, 1222, MSG_EVENT);
            }
            else {
                /* If no building id is given or it is the id of our building, just set the do-not-pay flag */
                if (id == 0 || id == u->building->no) {
                    u->building->flags |= BLD_DONTPAY;
                }
                else {
                    /* Find the building that matches to the given id*/
                    building *b = findbuilding(id);
                    /* If there is a building and it is in the same region as the unit continue, else: error */
                    if (b && b->region == u->region)
                    {
                        /* If the unit is also the building owner (this is true if rules.region_owner_pay_building */
                        /* is set and no one is in the building) set the do-not-pay flag for this building, else: error */
                        if (building_owner(b) == u) {
                            b->flags |= BLD_DONTPAY;
                        }
                        else
                        {
                            /* The building is not ours error */
                            cmistake(u, ord, 1222, MSG_EVENT);
                        }

                    }
                    else
                    {
                        /* Building not found error */
                        cmistake(u, ord, 6, MSG_PRODUCE);
                    }
                }
            }
        }
    }
    return 0;
}

static int reserve_i(unit * u, struct order *ord, int flags)
{
    if (u->number > 0) {
        char token[128];
        int use, count, para;
        const item_type *itype;
        const char *s;

        init_order(ord, NULL);
        s = gettoken(token, sizeof(token));
        count = s ? atoip(s) : 0;
        para = findparam(s, u->faction->locale);

        if (count == 0 && para == P_EACH) {
            count = getint() * u->number;
        }
        s = gettoken(token, sizeof(token));
        itype = s ? finditemtype(s, u->faction->locale) : 0;
        if (itype == NULL)
            return 0;

        set_resvalue(u, itype, 0);      /* make sure the pool is empty */

        if (count == 0 && para == P_ANY) {
            count = get_resource(u, itype->rtype);
        }
        use = use_pooled(u, itype->rtype, flags, count);
        if (use) {
            set_resvalue(u, itype, use);
            change_resource(u, itype->rtype, use);
            return use;
        }
    }
    return 0;
}

int reserve_cmd(unit * u, struct order *ord) {
    return reserve_i(u, ord, GET_DEFAULT);
}

int reserve_self(unit * u, struct order *ord) {
    return reserve_i(u, ord, GET_RESERVE | GET_SLACK);
}

int claim_cmd(unit * u, struct order *ord)
{
    char token[128];
    const char *t;
    int n = 1;
    const item_type *itype = 0;

    init_order(ord, NULL);

    t = gettoken(token, sizeof(token));
    if (t) {
        n = atoip((const char *)t);
        if (n == 0) {
            n = 1;
        }
        else {
            t = gettoken(token, sizeof(token));
        }
        if (t) {
            itype = finditemtype(t, u->faction->locale);
        }
    }
    if (itype) {
        item **iclaim = i_find(&u->faction->items, itype);
        if (iclaim && *iclaim) {
            if (n > (*iclaim)->number) n = (*iclaim)->number;
            i_change(iclaim, itype, -n);
            i_change(&u->items, itype, n);
        }
    }
    else {
        cmistake(u, ord, 43, MSG_PRODUCE);
    }
    return 0;
}

enum {
    PROC_THISORDER = 1 << 0,
    PROC_LONGORDER = 1 << 1
};

typedef enum processor_t { PR_GLOBAL, PR_REGION_PRE, PR_UNIT, PR_ORDER, PR_REGION_POST } processor_t;

typedef struct processor {
    struct processor *next;
    int priority;
    processor_t type;
    unsigned int flags;
    union {
        struct {
            keyword_t kword;
            int(*process) (struct unit *, struct order *);
        } per_order;
        struct {
            void(*process) (struct unit *);
        } per_unit;
        struct {
            void(*process) (struct region *);
        } per_region;
        struct {
            void(*process) (void);
        } global;
    } data;
    const char *name;
} processor;

static processor *processors;

static processor *add_proc(int priority, const char *name, processor_t type)
{
    processor **pproc = &processors;
    processor *proc;

    while (*pproc) {
        proc = *pproc;
        if (proc->priority > priority)
            break;
        else if (proc->priority == priority && proc->type >= type)
            break;
        pproc = &proc->next;
    }

    proc = (processor *)malloc(sizeof(processor));
    if (!proc) abort();
    proc->priority = priority;
    proc->type = type;
    proc->name = name;
    proc->next = *pproc;
    *pproc = proc;
    return proc;
}

void
add_proc_order(int priority, keyword_t kword, int(*parser) (struct unit *,
struct order *), unsigned int flags, const char *name)
{
    if (!keyword_disabled(kword)) {
        processor *proc = add_proc(priority, name, PR_ORDER);
        if (proc) {
            proc->data.per_order.process = parser;
            proc->data.per_order.kword = kword;
            proc->flags = flags;
        }
    }
}

void add_proc_global(int priority, void(*process) (void), const char *name)
{
    processor *proc = add_proc(priority, name, PR_GLOBAL);
    if (proc) {
        proc->data.global.process = process;
    }
}

void add_proc_region(int priority, void(*process) (region *), const char *name)
{
    processor *proc = add_proc(priority, name, PR_REGION_PRE);
    if (proc) {
        proc->data.per_region.process = process;
    }
}

void
add_proc_postregion(int priority, void(*process) (region *), const char *name)
{
    processor *proc = add_proc(priority, name, PR_REGION_POST);
    if (proc) {
        proc->data.per_region.process = process;
    }
}

void add_proc_unit(int priority, void(*process) (unit *), const char *name)
{
    processor *proc = add_proc(priority, name, PR_UNIT);
    if (proc) {
        proc->data.per_unit.process = process;
    }
}

bool long_order_allowed(const unit *u)
{
    const region *r = u->region;
    if (fval(u, UFL_LONGACTION)) {
        /* this message was already given in laws.update_long_order
        cmistake(u, ord, 52, MSG_PRODUCE);
        */
        return false;
    }
    else if (fval(r->terrain, SEA_REGION)
        && u_race(u) != get_race(RC_AQUARIAN)
        && !(u_race(u)->flags & RCF_SWIM)) {
        /* error message disabled by popular demand */
        return false;
    }
    return true;
}

/* per priority, execute processors in order from PR_GLOBAL down to PR_ORDER */
void process(void)
{
    processor *proc = processors;
    faction *f;

    while (proc) {
        int prio = proc->priority;
        region *r;
        processor *pglobal = proc;

        log_debug("- Step %u", prio);
        while (proc && proc->priority == prio) {
            if (proc->name) {
                log_debug(" - %s", proc->name);
            }
            proc = proc->next;
        }

        while (pglobal && pglobal->priority == prio && pglobal->type == PR_GLOBAL) {
            pglobal->data.global.process();
            pglobal = pglobal->next;
        }
        if (pglobal == NULL || pglobal->priority != prio) {
            continue;
        }

        for (r = regions; r; r = r->next) {
            unit *u;
            processor *pregion = pglobal;

            while (pregion && pregion->priority == prio
                && pregion->type == PR_REGION_PRE) {
                pregion->data.per_region.process(r);
                pregion = pregion->next;
            }
            if (pregion == NULL || pregion->priority != prio) {
                continue;
            }

            if (r->units) {
                for (u = r->units; u; u = u->next) {
                    processor *porder, *punit = pregion;

                    while (punit && punit->priority == prio && punit->type == PR_UNIT) {
                        punit->data.per_unit.process(u);
                        punit = punit->next;
                    }
                    if (punit == NULL || punit->priority != prio) {
                        continue;
                    }

                    porder = punit;
                    while (porder && porder->priority == prio && porder->type == PR_ORDER) {
                        order **ordp = &u->orders;
                        if (porder->flags & PROC_THISORDER) {
                            ordp = &u->thisorder;
                        }
                        while (*ordp) {
                            order *ord = *ordp;
                            if (getkeyword(ord) == porder->data.per_order.kword) {
                                if (porder->flags & PROC_LONGORDER) {
                                    if (u->number == 0) {
                                        ord = NULL;
                                    }
                                    else if (u_race(u) == get_race(RC_INSECT)
                                        && r_insectstalled(r)
                                        && !is_cursed(u->attribs, &ct_insectfur)) {
                                        ord = NULL;
                                    }
                                    else if (LongHunger(u)) {
                                        cmistake(u, ord, 224, MSG_MAGIC);
                                        ord = NULL;
                                    }
                                    else if (!long_order_allowed(u)) {
                                        ord = NULL;
                                    }
                                }
                                if (ord) {
                                    porder->data.per_order.process(u, ord);
                                    if (!u->orders) {
                                        /* GIVE UNIT or QUIT delete all orders of the unit, stop */
                                        break;
                                    }
                                }
                            }
                            if (!ord || *ordp == ord) {
                                ordp = &(*ordp)->next;
                            }
                        }
                        porder = porder->next;
                    }
                }
            }

            while (pregion && pregion->priority == prio
                && pregion->type != PR_REGION_POST) {
                pregion = pregion->next;
            }

            while (pregion && pregion->priority == prio
                && pregion->type == PR_REGION_POST) {
                pregion->data.per_region.process(r);
                pregion = pregion->next;
            }
            if (pregion == NULL || pregion->priority != prio) {
                continue;
            }
        }
    }

    log_debug("\n - Leere Gruppen loeschen...\n");
    for (f = factions; f; f = f->next) {
        group **gp = &f->groups;
        while (*gp) {
            group *g = *gp;
            if (g->members == 0) {
                *gp = g->next;
                free_group(g);
            }
            else
                gp = &g->next;
        }
    }

}

int armedmen(const unit * u, bool siege_weapons)
{
    item *itm;
    int n = 0;
    if (!(u_race(u)->flags & RCF_NOWEAPONS)) {
        if (effskill(u, SK_WEAPONLESS, NULL) >= 1) {
            /* kann ohne waffen bewachen: fuer drachen */
            n = u->number;
        }
        else {
            /* alle Waffen werden gezaehlt, und dann wird auf die Anzahl
            * Personen minimiert */
            for (itm = u->items; itm; itm = itm->next) {
                const weapon_type *wtype = resource2weapon(itm->type->rtype);
                if (wtype == NULL || (!siege_weapons && (wtype->flags & WTF_SIEGE)))
                    continue;
                if (effskill(u, wtype->skill, NULL) >= 1)
                    n += itm->number;
                if (n >= u->number)
                    break;
            }
            if (n > u->number) n = u->number;
        }
    }
    return n;
}

static void enter_1(region * r)
{
    do_enter(r, 0);
}

static void enter_2(region * r)
{
    do_enter(r, 1);
}

bool help_enter(unit *uo, unit *u) {
    return uo->faction == u->faction || alliedunit(uo, u->faction, HELP_GUARD);
}

static void do_force_leave(region *r) {
    force_leave(r, NULL);
}

bool rule_force_leave(int flags) {
    int rules = config_get_int("rules.owners.force_leave", 0);
    return (rules&flags) == flags;
}

void init_processor(void)
{
    int p;

    while (processors) {
        processor * next = processors->next;
        free(processors);
        processors = next;
    }

    p = 10;
    add_proc_global(p, nmr_warnings, "NMR Warnings");
    add_proc_global(p, new_units, "Neue Einheiten erschaffen");

    p += 10;
    add_proc_unit(p, update_long_order, "Langen Befehl aktualisieren");
    add_proc_order(p, K_BANNER, banner_cmd, 0, NULL);
    add_proc_order(p, K_EMAIL, email_cmd, 0, NULL);
    add_proc_order(p, K_PASSWORD, password_cmd, 0, NULL);
    add_proc_order(p, K_SEND, send_cmd, 0, NULL);
    add_proc_order(p, K_GROUP, group_cmd, 0, NULL);

    p += 10;
    add_proc_order(p, K_URSPRUNG, origin_cmd, 0, NULL);
    add_proc_order(p, K_ALLY, ally_cmd, 0, NULL);
    add_proc_order(p, K_PREFIX, prefix_cmd, 0, NULL);
    add_proc_order(p, K_SETSTEALTH, setstealth_cmd, 0, NULL);
    add_proc_order(p, K_STATUS, status_cmd, 0, NULL);
    add_proc_order(p, K_COMBATSPELL, combatspell_cmd, 0, NULL);
    add_proc_order(p, K_DISPLAY, display_cmd, 0, NULL);
    add_proc_order(p, K_NAME, name_cmd, 0, NULL);
    add_proc_order(p, K_GUARD, guard_off_cmd, 0, NULL);
    add_proc_order(p, K_RESHOW, reshow_cmd, 0, NULL);

    if (config_get_int("rules.alliances", 0) == 1) {
        p += 10;
        add_proc_global(p, alliance_cmd, NULL);
    }

    p += 10;
    add_proc_region(p, do_contact, "Kontaktieren");
    add_proc_order(p, K_MAIL, mail_cmd, 0, "Botschaften");

    p += 10;                      /* all claims must be done before we can USE */
    add_proc_region(p, enter_1, "Betreten (1. Versuch)");     /* for GIVE CONTROL */
    add_proc_order(p, K_USE, use_cmd, 0, "Benutzen");

    p += 10;                      /* in case it has any effects on alliance victories */
    add_proc_order(p, K_LEAVE, leave_cmd, 0, "Verlassen");

    p += 10;
    add_proc_region(p, enter_1, "Betreten (2. Versuch)"); /* to allow a buildingowner to enter the castle pre combat */

    p += 10;
    add_proc_global(p, do_battles, "Attackieren");

    p += 10;                      /* can't allow reserve before siege (weapons) */
    add_proc_region(p, enter_1, "Betreten (3. Versuch)");  /* to claim a castle after a victory and to be able to DESTROY it in the same turn */
    if (config_get_int("rules.reserve.twophase", 0)) {
        add_proc_order(p, K_RESERVE, reserve_self, 0, "RESERVE (self)");
        p += 10;
    }
    add_proc_order(p, K_RESERVE, reserve_cmd, 0, "RESERVE (all)");
    add_proc_order(p, K_CLAIM, claim_cmd, 0, NULL);

    p += 10;                      /* in case it has any effects on alliance victories */
    add_proc_order(p, K_GIVE, give_control_cmd, 0, "GIB KOMMANDO");

    p += 10;                      /* reset rng again before economics */
    if (rule_force_leave(FORCE_LEAVE_ALL)) {
        add_proc_region(p, do_force_leave, "kick non-allies out of buildings/ships");
    }
    add_proc_region(p, economics, "Geben, Vergessen");
    add_proc_region(p+1, recruit, "Rekrutieren");
    add_proc_region(p+2, destroy, "Zerstoeren");
    add_proc_unit(p, follow_cmds, "Folge auf Einheiten setzen");
    add_proc_order(p, K_QUIT, quit_cmd, 0, "Stirb");

    /* all recruitment must be finished before we can calculate 
     * promotion cost of ability */
    p += 10;
    add_proc_global(p, quit, "Sterben");
    add_proc_order(p, K_PROMOTION, promotion_cmd, 0, "Heldenbefoerderung");

    p += 10;
    if (!keyword_disabled(K_PAY)) {
        add_proc_order(p, K_PAY, pay_cmd, 0, "Gebaeudeunterhalt (BEZAHLE NICHT)");
    }
    add_proc_postregion(p, maintain_buildings, "Gebaeudeunterhalt");

    if (!keyword_disabled(K_CAST)) {
        p += 10;
        add_proc_global(p, magic, "Zaubern");
    }

    p += 10;
    add_proc_region(p, do_autostudy, "study automation");
    add_proc_order(p, K_TEACH, teach_cmd, PROC_THISORDER | PROC_LONGORDER,
        "Lehren");
    p += 10;
    add_proc_order(p, K_STUDY, study_cmd, PROC_THISORDER | PROC_LONGORDER,
        "Lernen");

    p += 10;
    add_proc_order(p, K_MAKE, make_cmd, PROC_THISORDER | PROC_LONGORDER,
        "Produktion");
    add_proc_postregion(p, produce, "Arbeiten, Handel, Rekruten");
    add_proc_postregion(p, split_allocations, "Produktion II");

    p += 10;
    add_proc_region(p, enter_2, "Betreten (4. Versuch)"); /* Once again after QUIT */

    p += 10;
    add_proc_region(p, sinkships, "Schiffe sinken");

    p += 10;
    add_proc_global(p, movement, "Bewegungen");

    if (config_get_int("work.auto", 0)) {
        p += 10;
        add_proc_region(p, auto_work, "Arbeiten (auto)");
    }

    p += 10;
    add_proc_order(p, K_GUARD, guard_on_cmd, 0, "Bewache (an)");

    p += 10;
    add_proc_unit(p, monster_kills_peasants,
        "Monster fressen und vertreiben Bauern");

    p += 10;
    add_proc_global(p, randomevents, "Zufallsereignisse");

    p += 10;

    add_proc_global(p, monthly_healing, "Regeneration (HP)");
    add_proc_global(p, regenerate_aura, "Regeneration (Aura)");
    if (!keyword_disabled(K_DEFAULT)) {
        add_proc_global(p, defaultorders, "Defaults setzen");
    }
    add_proc_global(p, demographics, "Nahrung, Seuchen, Wachstum, Wanderung");

    if (!keyword_disabled(K_SORT)) {
        p += 10;
        add_proc_global(p, restack_units, "Einheiten sortieren");
    }
    if (!keyword_disabled(K_NUMBER)) {
        add_proc_order(p, K_NUMBER, renumber_cmd, 0, "Neue Nummern (Einheiten)");
        p += 10;
        add_proc_global(p, renumber_factions, "Neue Nummern");
    }
    add_proc_order(p, K_LOCALE, locale_cmd, 0, "Sprache wechseln");
}

static void reset_game(void)
{
    region *r;
    faction *f;
    for (r = regions; r; r = r->next) {
        unit *u;
        building *b;
        r->flags &= RF_SAVEMASK;
        for (u = r->units; u; u = u->next) {
            u->flags &= UFL_SAVEMASK;
        }
        for (b = r->buildings; b; b = b->next) {
            b->flags &= BLD_SAVEMASK;
        }
        if (r->land && r->land->ownership && r->land->ownership->owner) {
            faction *owner = r->land->ownership->owner;
            if (owner == get_monsters()) {
                /* some compat-fix, i believe. */
                owner = update_owners(r);
            }
            if (owner) {
                fset(r, RF_GUARDED);
            }
        }
    }
    for (f = factions; f; f = f->next) {
        f->flags &= FFL_SAVEMASK;
    }
}

void turn_begin(void)
{
    int handle_start = first_turn();
    assert(turn >= 0);
    if (turn < handle_start) {
        /* this should only happen during tests */
        turn = handle_start;
    }
    ++turn;
    reset_game();
}

void turn_process(void)
{
    init_processor();
    process();

    if (markets_module()) {
        do_markets();
    }
}

void turn_end(void)
{
    log_info(" - Attribute altern");
    ageing();
    remove_empty_units();

    /* must happen AFTER age, because that would destroy them right away */
    if (config_get_int("modules.wormhole", 0)) {
        wormholes_update();
    }

    /* immer ausfuehren, wenn neue Sprueche dazugekommen sind, oder sich
     * Beschreibungen geaendert haben */
    update_spells();
}

/**
 * Determine if unit can be seen by faction.
 *
 * @param f -- the observiong faction
 * @param u -- the unit that is observed
 * @param r -- the region that u is obesrved from (see below)
 * @param m -- terrain modifier to stealth
 *
 * r kann != u->region sein, wenn es um Durchreisen geht,
 * oder Zauber (sp_generous, sp_fetchastral).
 * Es muss auch niemand aus f in der region sein, wenn sie vom Turm
 * erblickt wird.
 */
bool cansee(const faction * f, const region * r, const unit * u, int modifier)
{
    int stealth, rings;

    if (u->faction == f || omniscient(f)) {
        return true;
    }
    else if (u->number == 0) {
        attrib *a = a_find(u->attribs, &at_creator);
        if (a) {
            /* u is an empty temporary unit. In this special case we look at the creating unit. */
            u = (unit *)a->data.v;
        }
        else {
            return false;
        }
    }

    /* simple visibility, just gotta have a viewer in the region to see 'em */
    if (leftship(u) || is_guard(u) || u->building || u->ship) {
        return true;
    }

    rings = invisible(u, NULL);
    stealth = eff_stealth(u, r) - modifier;

    unit *u2;
    for (u2 = r->units; u2; u2 = u2->next) {
        if (u2->faction == f) {
            if (rings < u->number || invisible(u, u2) < u->number) {
                if (skill_enabled(SK_PERCEPTION)) {
                    int observation = effskill(u2, SK_PERCEPTION, NULL);

                    if (observation >= stealth) {
                        return true;
                    }
                }
                else {
                    return true;
                }
            }
        }
    }

    return (rings <= 0 && stealth <= 0);
}

bool cansee_unit(const unit * u, const unit * target, int modifier)
/* target->region kann != u->region sein, wenn es um durchreisen geht */
{
    if (target->number == 0)
        return false;
    else if (target->faction == u->faction)
        return true;
    else {
        int n, rings;

        if (is_guard(target) || target->building
            || target->ship) {
            return true;
        }

        n = eff_stealth(target, target->region) - modifier;
        rings = invisible(target, NULL);
        if (rings == 0 && n <= 0) {
            return true;
        }

        if (rings && invisible(target, u) >= target->number) {
            return false;
        }
        if (skill_enabled(SK_PERCEPTION)) {
            int o = effskill(u, SK_PERCEPTION, target->region);
            if (o >= n) {
                return true;
            }
        }
        else {
            return true;
        }
    }
    return false;
}

bool
cansee_durchgezogen(const faction * f, const region * r, const unit * u,
    int modifier)
    /* r kann != u->region sein, wenn es um durchreisen geht */
    /* und es muss niemand aus f in der region sein, wenn sie vom Turm
    * erblickt wird */
{
    unit *u2;

    if (u->number == 0)
        return false;
    else if (u->faction == f)
        return true;
    else {
        int rings, n;

        if (is_guard(u) || u->building || u->ship) {
            return true;
        }

        n = eff_stealth(u, r) - modifier;
        rings = invisible(u, NULL);
        if (rings == 0 && n <= 0) {
            return true;
        }

        for (u2 = r->units; u2; u2 = u2->next) {
            if (u2->faction == f) {
                int o;

                if (rings && invisible(u, u2) >= u->number)
                    continue;

                o = effskill(u2, SK_PERCEPTION, NULL);

                if (o >= n) {
                    return true;
                }
            }
        }
    }
    return false;
}

bool
seefaction(const faction * f, const region * r, const unit * u, int modifier)
{
    if (((f == u->faction) || !fval(u, UFL_ANON_FACTION))
        && cansee(f, r, u, modifier))
        return true;
    return false;
}

int locale_cmd(unit * u, order * ord)
{
    char token[128];
    const char * name;
    faction *f = u->faction;
    bool del = config_get_int("translate.delete_orders", 0) != 0;

    init_order(ord, f->locale);
    name = gettoken(token, sizeof(token));
    if (name) {
        const struct locale *lang = get_locale(name);
        if (lang && lang != f->locale) {
            change_locale(f, lang, del);
        }
    }
    return 0;
}