/* vi: set ts=2:
 *
 *  Eressea PB(E)M host Copyright (C) 1998-2003
 *      Christian Schlittchen (corwin@amber.kn-bremen.de)
 *      Katja Zedel (katze@felidae.kn-bremen.de)
 *      Henning Peters (faroul@beyond.kn-bremen.de)
 *      Enno Rehling (enno@eressea-pbem.de)
 *      Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
 *
 *  based on:
 *
 * Atlantis v1.0  13 September 1993 Copyright 1993 by Russell Wallace
 * Atlantis v1.7                    Copyright 1996 by Alex Schröder
 *
 * This program may not be used, modified or distributed without
 * prior permission by the authors of Eressea.
 * This program may not be sold or used commercially without prior written
 * permission from the authors.
 */

#include <config.h>
#include "eressea.h"
#include "build.h"

/* kernel includes */
#include "alchemy.h"
#include "border.h"
#include "building.h"
#include "curse.h"
#include "faction.h"
#include "group.h"
#include "item.h"
#include "magic.h"
#include "message.h"
#include "movement.h"
#include "order.h"
#include "pool.h"
#include "race.h"
#include "region.h"
#include "ship.h"
#include "skill.h"
#include "unit.h"

/* from libutil */
#include <attrib.h>
#include <base36.h>
#include <event.h>
#include <goodies.h>
#include <resolve.h>
#include <xml.h>

/* from libc */
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* attributes inclues */
#include <attributes/matmod.h>

#define STONERECYCLE 50
/* Name, MaxGroesse, MinBauTalent, Kapazitaet, {Eisen, Holz, Stein, BauSilber,
 * Laen, Mallorn}, UnterSilber, UnterSpezialTyp, UnterSpezial */


static boolean
CheckOverload(void)
{
  static int value = -1;
  if (value<0) {
    const char * str = get_param(global.parameters, "rules.check_overload");
    value = str?atoi(str):0;
  }
  return value;
}

static int
slipthru(const region * r, const unit * u, const building * b)
{
  unit *u2;
  int n, o;

  /* b ist die burg, in die man hinein oder aus der man heraus will. */

  if (!b) {
    return 1;
  }
  if (b->besieged < b->size * SIEGEFACTOR) {
    return 1;
  }
  /* u wird am hinein- oder herausschluepfen gehindert, wenn STEALTH <=
   * OBSERVATION +2 der belagerer u2 ist */

  n = eff_skill(u, SK_STEALTH, r);

  for (u2 = r->units; u2; u2 = u2->next)
    if (usiege(u2) == b) {

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

      o = eff_skill(u2, SK_OBSERVATION, r);

      if (o + 2 >= n)
        return 0;   /* entdeckt! */
    }
  return 1;
}

boolean
can_contact(const region * r, const unit * u, const unit * u2)
{

  /* hier geht es nur um die belagerung von burgen */

  if (u->building == u2->building)
    return true;

  /* unit u is trying to contact u2 - unasked for contact. wenn u oder u2
   * nicht in einer burg ist, oder die burg nicht belagert ist, ist
   * slipthru () == 1. ansonsten ist es nur 1, wenn man die belagerer */

  if (slipthru(u->region, u, u->building)
      && slipthru(u->region, u2, u2->building))
    return true;

  if (alliedunit(u, u2->faction, HELP_GIVE))
    return true;

  return false;
}


static void
contact_cmd(unit * u, order * ord, boolean tries)
{
  /* unit u kontaktiert unit u2. Dies setzt den contact einfach auf 1 -
   * ein richtiger toggle ist (noch?) nicht noetig. die region als
   * parameter ist nur deswegen wichtig, weil er an getunit ()
   * weitergegeben wird. dies wird fuer das auffinden von tempunits in
   * getnewunit () verwendet! */
  unit *u2;
  region * r = u->region;

  init_tokens(ord);
  skip_token();
  u2 = getunitg(r, u->faction);

  if (u2!=NULL) {
    if (!can_contact(r, u, u2)) {
      if (tries) cmistake(u, u->thisorder, 23, MSG_EVENT);
      return;
    }
    usetcontact(u, u2);
  }
}
/* ------------------------------------------------------------- */

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


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

struct building *
getbuilding(const struct region * r)
{
  building * b = findbuilding(getid());
  if (b==NULL || r!=b->region) return NULL;
  return b;
}

ship *
getship(const struct region * r)
{
  ship *sh, *sx = findship(getshipid());
  for (sh = r->ships; sh; sh = sh->next) {
    if (sh == sx) return sh;
  }
  return NULL;
}

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

static void
siege_cmd(unit * u, order * ord)
{
  region * r = u->region;
  unit *u2;
  building *b;
  int d;
  int bewaffnete, katapultiere = 0;
  static boolean init = false;
  static const curse_type * magicwalls_ct;
  static item_type * it_catapultammo = NULL;
  if (!init) {
    init = true; 
    magicwalls_ct = ct_find("magicwalls");
    it_catapultammo = it_find("catapultammo");
  }
  /* gibt es ueberhaupt Burgen? */

  init_tokens(ord);
  skip_token();
  b = getbuilding(r);

  if (!b) {
    cmistake(u, ord, 31, MSG_BATTLE);
    return;
  }

  if (!playerrace(u->race)) {
    /* keine Drachen, Illusionen, Untote etc */
    cmistake(u, ord, 166, MSG_BATTLE);
    return;
  }
  /* schaden durch katapulte */

  d = min(u->number,
    min(new_get_pooled(u, it_catapultammo->rtype, GET_SLACK|GET_RESERVE|GET_POOLED_SLACK), get_item(u, I_CATAPULT)));
  if (eff_skill(u, SK_CATAPULT, r) >= 1) {
    katapultiere = d;
    d *= eff_skill(u, SK_CATAPULT, r);
  } else {
    d = 0;
  }

  if ((bewaffnete = armedmen(u)) == 0 && d == 0) {
    /* abbruch, falls unbewaffnet oder unfaehig, katapulte zu benutzen */
    cmistake(u, ord, 80, MSG_EVENT);
    return;
  }

  if (!(getguard(u) & GUARD_TRAVELTHRU)) {
    /* abbruch, wenn die einheit nicht vorher die region bewacht - als
    * warnung fuer alle anderen! */
    cmistake(u, ord, 81, MSG_EVENT);
    return;
  }
  /* einheit und burg markieren - spart zeit beim behandeln der einheiten
  * in der burg, falls die burg auch markiert ist und nicht alle
  * einheiten wieder abgesucht werden muessen! */

  usetsiege(u, b);
  b->besieged += max(bewaffnete, katapultiere);

  /* definitiver schaden eingeschraenkt */

  d = min(d, b->size - 1);

  /* meldung, schaden anrichten */
  if (d && !curse_active(get_curse(b->attribs, magicwalls_ct))) {
    b->size -= d;
    new_use_pooled(u, it_catapultammo->rtype, GET_SLACK|GET_RESERVE|GET_POOLED_SLACK, d);
    d = 100 * d / b->size;
  } else d = 0;

  /* meldung fuer belagerer */
  ADDMSG(&u->faction->msgs, msg_message("siege", 
    "unit building destruction", u, b, d));

  for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH);
  fset(u->faction, FL_DH);

  /* Meldung fuer Burginsassen */
  for (u2 = r->units; u2; u2 = u2->next) {
    if (u2->building == b && !fval(u2->faction, FL_DH)) {
      fset(u2->faction, FL_DH);
      ADDMSG(&u2->faction->msgs, msg_message("siege", 
        "unit building destruction", u, b, d));
    }
  }
}

void
do_siege(void)
{
  region *r;

  for (r = regions; r; r = r->next) {
    if (rterrain(r) != T_OCEAN) {
      unit *u;

      for (u = r->units; u; u = u->next) {
        if (get_keyword(u->thisorder) == K_BESIEGE) {
          siege_cmd(u, u->thisorder);
        }
      }
    }
  }
}
/* ------------------------------------------------------------- */

static void
destroy_road(unit *u, int nmax, struct order * ord)
{
  direction_t d = getdirection(u->faction->locale);
  unit *u2;
  region *r = u->region;
  short n = (short)nmax;

  if (nmax>SHRT_MAX) n = SHRT_MAX;
  else if (nmax<0) n = 0;

  for (u2=r->units;u2;u2=u2->next) {
    if (u2->faction!=u->faction && getguard(u2)&GUARD_TAX
      && cansee(u2->faction, u->region, u, 0)
      && !alliedunit(u, u2->faction, HELP_GUARD)) {
      cmistake(u, ord, 70, MSG_EVENT);
      return;
    }
  }

  if (d==NODIRECTION) {
    cmistake(u, ord, 71, MSG_PRODUCE);
  } else {
    short road = rroad(r, d);
    n = min(n, road);
    if (n!=0) {
      region * r2 = rconnect(r,d);
      int willdo = eff_skill(u, SK_ROAD_BUILDING, r)*u->number;
      willdo = min(willdo, n);
      if (willdo==0) {
        /* TODO: error message */
      }
      if (willdo>SHRT_MAX) road = 0;
      else road = road - (short)willdo;
      rsetroad(r, d, road);
      ADDMSG(&u->faction->msgs, msg_message("destroy_road", 
        "unit from to", u, r, r2));
    }
  }
}

int
destroy_cmd(unit * u, struct order * ord)
{
  ship *sh;
  unit *u2;
  region * r = u->region;
#if 0
  const construction * con = NULL;
  int size = 0;
#endif
  const char *s;
  int n = INT_MAX;

  if (u->number < 1)
    return 0;

  init_tokens(ord);
  skip_token();
  s = getstrtoken();

  if (findparam(s, u->faction->locale)==P_ROAD) {
    destroy_road(u, INT_MAX, ord);
    return 0;
  }

  if (s && *s) {
    n = atoi(s);
    if(n <= 0) {
      cmistake(u, ord, 288, MSG_PRODUCE);
      return 0;
    }
  }

  if (getparam(u->faction->locale) == P_ROAD) {
    destroy_road(u, n, ord);
    return 0;
  }

  if (!fval(u, UFL_OWNER)) {
    cmistake(u, ord, 138, MSG_PRODUCE);
    return 0;
  }

  if (u->building) {
    building *b = u->building;
    if (a_find(b->attribs, &at_nodestroy)) {
      cmistake(u, ord, 14, MSG_EVENT);
      return 0;
    }

    if(n >= b->size) {
      /* destroy completly */
      /* all units leave the building */
      for (u2 = r->units; u2; u2 = u2->next)
        if (u2->building == b) {
          u2->building = 0;
          freset(u2, UFL_OWNER);
        }
        ADDMSG(&u->faction->msgs, msg_message("destroy", 
          "building unit", b, u));
        destroy_building(b);
    } else {
      /* partial destroy */
      b->size -= n;
      ADDMSG(&u->faction->msgs, msg_message("destroy_partial", 
        "building unit", b, u));
    }
  } else if (u->ship) {
    sh = u->ship;
    if (a_find(sh->attribs, &at_nodestroy)) {
      cmistake(u, ord, 14, MSG_EVENT);
      return 0;
    }

    if (rterrain(r) == T_OCEAN) {
      cmistake(u, ord, 14, MSG_EVENT);
      return 0;
    }

    if(n >= (sh->size*100)/sh->type->construction->maxsize) {
      /* destroy completly */
      /* all units leave the ship */
      for (u2 = r->units; u2; u2 = u2->next)
        if (u2->ship == sh) {
          u2->ship = 0;
          freset(u2, UFL_OWNER);
        }
        ADDMSG(&u->faction->msgs, msg_message("shipdestroy", 
          "unit region ship", u, r, sh));
        destroy_ship(sh);
    } else {
      /* partial destroy */
      sh->size -= (sh->type->construction->maxsize * n)/100;
      ADDMSG(&u->faction->msgs, msg_message("shipdestroy_partial", 
        "unit region ship", u, r, sh));
    }
  } else {
    log_error(("Die Einheit %s von %s war owner eines objects, war aber weder in einer Burg noch in einem Schiff.\n",
      unitname(u), u->faction->name, u->faction->email));
  }

#if 0
  /* Achtung: Nicht an ZERSTÖRE mit Punktangabe angepaßt! */
  if (con) {
    /* TODO: ZERSTÖRE - Man sollte alle Materialien zurückkriegen können: */
    int c;
    for (c=0;con->materials[c].number;++c) {
      const requirement * rq = con->materials+c;
      int recycle = (int)(rq->recycle * rq->number * size/con->reqsize);
      if (recycle)
        change_resource(u, rq->type, recycle);
    }
  }
#endif
  return 0;
}
/* ------------------------------------------------------------- */

void
build_road(region * r, unit * u, int size, direction_t d)
{
  int n, left;

  if (!eff_skill(u, SK_ROAD_BUILDING, r)) {
    cmistake(u, u->thisorder, 103, MSG_PRODUCE);
    return;
  }
  if (besieged(u)) {
    cmistake(u, u->thisorder, 60, MSG_PRODUCE);
    return;
  }

  if (terrain[rterrain(r)].roadreq < 0) {
    cmistake(u, u->thisorder, 94, MSG_PRODUCE);
    return;
  }

  if (rterrain(r) == T_SWAMP) {
    /* wenn kein Damm existiert */
    static const struct building_type * bt_dam;
    if (!bt_dam) bt_dam = bt_find("dam");
    assert(bt_dam);
    if (!buildingtype_exists(r, bt_dam)) {
      cmistake(u, u->thisorder, 132, MSG_PRODUCE);
      return;
    }
  }
  if (rterrain(r) == T_DESERT) {
    static const struct building_type * bt_caravan;
    if (!bt_caravan) bt_caravan = bt_find("caravan");
    assert(bt_caravan);
    /* wenn keine Karawanserei existiert */
    if (!buildingtype_exists(r, bt_caravan)) {
      cmistake(u, u->thisorder, 133, MSG_PRODUCE);
      return;
    }
  }
  if (rterrain(r) == T_GLACIER) {
    static const struct building_type * bt_tunnel;
    if (!bt_tunnel) bt_tunnel = bt_find("tunnel");
    assert(bt_tunnel);
    /* wenn kein Tunnel existiert */
    if (!buildingtype_exists(r, bt_tunnel)) {
      cmistake(u, u->thisorder, 131, MSG_PRODUCE);
      return;
    }
  }
  if (!get_pooled(u, r, R_STONE) && u->race != new_race[RC_STONEGOLEM]) {
    cmistake(u, u->thisorder, 151, MSG_PRODUCE);
    return;
  }

  /* left kann man noch bauen */
  left = terrain[rterrain(r)].roadreq - rroad(r, d);
  /* hoffentlich ist r->road <= terrain[rterrain(r)].roadreq, n also >= 0 */
  if (left <= 0) {
    sprintf(buf, "In %s gibt es keine Brücken und Straßen "
      "mehr zu bauen", regionname(r, u->faction));
    mistake(u, u->thisorder, buf, MSG_PRODUCE);
    return;
  }

  /* baumaximum anhand der rohstoffe */
  if (u->race == new_race[RC_STONEGOLEM]){
    n = u->number * GOLEM_STONE;
  } else {
    n = get_pooled(u, r, R_STONE);
  }
  left = min(n, left);
  if (size>0) left = min(size, left);

  /* n = maximum by skill. try to maximize it */
  n = u->number * eff_skill(u, SK_ROAD_BUILDING, r);
  if (n < left) {
    item * itm = *i_find(&u->items, olditemtype[I_RING_OF_NIMBLEFINGER]);
    if (itm!=NULL && itm->number>0) {
      int rings = min(u->number, itm->number);
      n = n * (9*rings+u->number) / u->number;
    }
  }
  if (n < left) {
    int dm = get_effect(u, oldpotiontype[P_DOMORE]);
    if (dm != 0) {
      int sk = eff_skill(u, SK_ROAD_BUILDING, r);
      int todo = (left - n + sk - 1) / sk;
      todo = min(todo, u->number);
      dm = min(dm, todo);
      change_effect(u, oldpotiontype[P_DOMORE], -dm);
      n += dm * sk;
    }             /* Auswirkung Schaffenstrunk */
  }

  /* make minimum of possible and available: */
  n = min(left, n);

  /* n is now modified by several special effects, so we have to
   * minimize it again to make sure the road will not grow beyond
   * maximum. */
  rsetroad(r, d, rroad(r, d) + (short)n);

  if (u->race == new_race[RC_STONEGOLEM]) {
    int golemsused = n / GOLEM_STONE;
    if (n%GOLEM_STONE != 0){
      ++golemsused;
    }
    scale_number(u, u->number - golemsused);
  } else {
    use_pooled(u, r, R_STONE, n);
    /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
    produceexp(u, SK_ROAD_BUILDING, min(n, u->number));
  }
  ADDMSG(&u->faction->msgs, msg_message("buildroad", 
    "region unit size", r, u, n));
}
/* ------------------------------------------------------------- */

/* ** ** ** ** ** ** *
 *  new build rules  *
 * ** ** ** ** ** ** */

static int
required(int size, int msize, int maxneed)
  /* um size von msize Punkten zu bauen,
   * braucht man required von maxneed resourcen */
{
  int used;

  used = size * maxneed / msize;
  if (size * maxneed % msize)
    ++used;
  return used;
}

static int
matmod(const attrib * a, const unit * u, const resource_type * material, int value)
{
  for (a=a_find((attrib*)a, &at_matmod);a;a=a->nexttype) {
    mm_fun fun = (mm_fun)a->data.f;
    value = fun(u, material, value);
    if (value<0) return value; /* pass errors to caller */
  }
  return value;
}

/** Use up resources for building an object.
* Build up to 'size' points of 'type', where 'completed'
* of the first object have already been finished. return the
* actual size that could be built.
*/
int
build(unit * u, const construction * ctype, int completed, int want)
{
  const construction * type = ctype;
  int skills; /* number of skill points remainig */
  int dm = get_effect(u, oldpotiontype[P_DOMORE]);
  int made = 0;
  int basesk, effsk;

  if (want<=0) return 0;
  if (type==NULL) return 0;
  if (type->improvement==NULL && completed==type->maxsize)
    return ECOMPLETE;

  basesk = effskill(u, type->skill);
  if (basesk==0) return ENEEDSKILL;

  effsk = basesk;
  if (inside_building(u)) {
    effsk = skillmod(u->building->type->attribs, u, u->region, type->skill, effsk, SMF_PRODUCTION);
  }
  effsk = skillmod(type->attribs, u, u->region, type->skill, effsk, SMF_PRODUCTION);
  if (effsk<0) return effsk; /* pass errors to caller */
  if (effsk==0) return ENEEDSKILL;

  skills = effsk * u->number;

  /* technically, nimblefinge and domore should be in a global set of "game"-attributes,
   * (as at_skillmod) but for a while, we're leaving them in here. */

  if (dm != 0) {
    /* Auswirkung Schaffenstrunk */
    dm = min(dm, u->number);
    change_effect(u, oldpotiontype[P_DOMORE], -dm);
    skills += dm * effsk;
  }
  for (;want>0 && skills>0;) {
    int c, n;

    /* skip over everything that's already been done:
     * type->improvement==NULL means no more improvements, but no size limits
     * type->improvement==type means build another object of the same time while material lasts
     * type->improvement==x means build x when type is finished
     */
    while (type->improvement!=NULL &&
         type->improvement!=type &&
         type->maxsize>0 &&
         type->maxsize<=completed)
    {
      completed -= type->maxsize;
      type = type->improvement;
    }
    if (type==NULL) {
      if (made==0) return ECOMPLETE;
      break; /* completed */
    }

    /*  Hier ist entweder maxsize == -1, oder completed < maxsize.
     *  Andernfalls ist das Datenfile oder sonstwas kaputt...
     *  (enno): Nein, das ist für Dinge, bei denen die nächste Ausbaustufe
     *  die gleiche wie die vorherige ist. z.b. gegenstände.
     */
    if (type->maxsize>1) {
      completed = completed % type->maxsize;
    }
    else {
      completed = 0; assert(type->reqsize>=1);
    }

    if (basesk < type->minskill) {
      if (made==0) return ELOWSKILL; /* not good enough to go on */
    }

    /* n = maximum buildable size */
    if (type->minskill > 1) {
      n = skills / type->minskill;
    } else {
      n = skills;
    }
    /* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
     * Talente */
    if (max_skill(u->faction, type->skill)==INT_MAX) {
      int i = 0;
      item * itm = *i_find(&u->items, olditemtype[I_RING_OF_NIMBLEFINGER]);
      if (itm!=NULL) i = itm->number;
      if (i>0) {
        int rings = min(u->number, i);
        n = n * (9*rings+u->number) / u->number;
      }
    }

    if (want>0) {
      n = min(want, n);
    }

    if (type->maxsize>0) {
      n = min(type->maxsize-completed, n);
      if (type->improvement==NULL) {
        want = n;
      }
    }

    if (type->materials) for (c=0;n>0 && type->materials[c].number;c++) {
      resource_t rtype = type->materials[c].type;
      int need;
      int have = get_pooled(u, NULL, rtype);
      int prebuilt;
      int canuse = have;
      if (inside_building(u)) {
        canuse = matmod(u->building->type->attribs, u, oldresourcetype[rtype], canuse);
#if 0
      /* exploit-check */
      } else if (u->building) {
        int abuse = matmod(u->building->type->attribs, u, oldresourcetype[rtype], canuse);
        if (abuse>canuse) {
        log_printf("ABUSE: %s saves %u %s through exploit\n",
               itoa36(u->faction->no), abuse-canuse,
               oldresourcetype[rtype]->_name[0]);
        }
#endif
      }
      if (canuse<0) return canuse; /* pass errors to caller */
      canuse = matmod(type->attribs, u, oldresourcetype[rtype], canuse);
      if (type->reqsize>1) {
        prebuilt = required(completed, type->reqsize, type->materials[c].number);
        for (;n;) {
          need = required(completed + n, type->reqsize, type->materials[c].number);
          if (need-prebuilt<=canuse) break;
          --n; /* TODO: optimieren? */
        }
      } else {
        int maxn = canuse / type->materials[c].number;
        if (maxn < n) n = maxn;
      }
    }
    if (n<=0) {
      if (made==0) return ENOMATERIALS;
      else break;
    }
    if (type->materials) for (c=0;type->materials[c].number;c++) {
      resource_t rtype = type->materials[c].type;
      int prebuilt = required(completed, type->reqsize, type->materials[c].number);
      int need = required(completed + n, type->reqsize, type->materials[c].number);
      int multi = 1;
      int canuse = 100; /* normalization */
      if (inside_building(u)) canuse = matmod(u->building->type->attribs, u, oldresourcetype[rtype], canuse);
      if (canuse<0) return canuse; /* pass errors to caller */
      canuse = matmod(type->attribs, u, oldresourcetype[rtype], canuse);

      assert(canuse % 100 == 0 || !"only constant multipliers are implemented in build()");
      multi = canuse/100;
      if (canuse<0) return canuse; /* pass errors to caller */

      use_pooled(u, NULL, rtype, (need-prebuilt+multi-1)/multi);
    }
    made += n;
    skills -= n * type->minskill;
    want -= n;
    completed = completed + n;
  }
  /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
  produceexp(u, ctype->skill, min(made, u->number));

  return made;
}

int
maxbuild(const unit * u, const construction * cons)
  /* calculate maximum size that can be built from available material */
  /* !! ignores maximum objectsize and improvements...*/
{
  int c;
  int maximum = INT_MAX;
  for (c=0;cons->materials[c].number;c++) {
    resource_t rtype = cons->materials[c].type;
    int have = get_pooled(u, NULL, rtype);
    int need = required(1, cons->reqsize, cons->materials[c].number);
    if (have<need) {
      cmistake(u, u->thisorder, 88, MSG_PRODUCE);
      return 0;
    }
    else maximum = min(maximum, have/need);
  }
  return maximum;
}

/** old build routines */

void
build_building(unit * u, const building_type * btype, int want, order * ord)
{
  region * r = u->region;
  boolean newbuilding = false;
  int c, built = 0, id;
  building * b = NULL;
  /* einmalige Korrektur */
  static char buffer[8 + IDSIZE + 1 + NAMESIZE + 1];
  const char * btname;
  order * new_order = NULL;
  const struct locale * lang = u->faction->locale;

  if (eff_skill(u, SK_BUILDING, r) == 0) {
    cmistake(u, ord, 101, MSG_PRODUCE);
    return;
  }

  /* Falls eine Nummer angegeben worden ist, und ein Gebaeude mit der
   * betreffenden Nummer existiert, ist b nun gueltig. Wenn keine Burg
   * gefunden wurde, dann wird nicht einfach eine neue erbaut. Ansonsten
   * baut man an der eigenen burg weiter. */

  /* Wenn die angegebene Nummer falsch ist, KEINE Burg bauen! */
  id = atoi36(getstrtoken());
  if (id!=0){ /* eine Nummer angegeben, keine neue Burg bauen */
    b = findbuilding(id);
    if (!b || b->region != u->region){ /* eine Burg mit dieser Nummer gibt es hier nicht */
      /* vieleicht Tippfehler und die eigene Burg ist gemeint? */
      if (u->building && u->building->type==btype) {
        b = u->building;
      } else {
        /* keine neue Burg anfangen wenn eine Nummer angegeben war */
        cmistake(u, ord, 6, MSG_PRODUCE);
        return;
      }
    }
  }

  if (b) btype = b->type;

  if (b && fval(btype, BTF_UNIQUE) && buildingtype_exists(r, btype)) {
    /* only one of these per region */
    cmistake(u, ord, 93, MSG_PRODUCE);
    return;
  }
  if (besieged(u)) {
    /* units under siege can not build */
    cmistake(u, ord, 60, MSG_PRODUCE);
    return;
  }
  if (btype->flags & BTF_NOBUILD) {
    /* special building, cannot be built */
    cmistake(u, ord, 221, MSG_PRODUCE);
    return;
  }

  if (b) built = b->size;
  if (want<=0 || want == INT_MAX) {
    if(b == NULL) {
      if(btype->maxsize > 0) {
        want = btype->maxsize - built;
      } else {
        want = INT_MAX;
      }
    } else {
      if(b->type->maxsize > 0) {
        want = b->type->maxsize - built;
      } else {
        want = INT_MAX;
      }
    }
  }
  built = build(u, btype->construction, built, want);

  switch (built) {
  case ECOMPLETE:
    /* the building is already complete */
    cmistake(u, ord, 4, MSG_PRODUCE);
    return;
  case ENOMATERIALS: {
    /* something missing from the list of materials */
    const construction * cons = btype->construction;
    char * ch = buf;
    assert(cons);
    for (c=0;cons->materials[c].number; c++) {
      int n;
      if (c!=0) strcat(ch++, ",");
      n = cons->materials[c].number / cons->reqsize;
      sprintf(ch, " %d %s", n?n:1,
        LOC(lang, resname(cons->materials[c].type, cons->materials[c].number!=1)));
      ch = ch+strlen(ch);
    }
    ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "build_required",
      "required", buf));
    return;
  }
  case ELOWSKILL:
  case ENEEDSKILL:
    /* no skill, or not enough skill points to build */
    cmistake(u, ord, 50, MSG_PRODUCE);
    return;
  }


  /* at this point, the building size is increased. */
  if (b==NULL) {
    /* build a new building */
    b = new_building(btype, r, lang);
    b->type = btype;
    fset(b, BLD_MAINTAINED);

    /* Die Einheit befindet sich automatisch im Inneren der neuen Burg. */
    leave(r, u);
    u->building = b;
    fset(u, UFL_OWNER);

#if WDW_PYRAMID
    if(b->type == bt_find("wdw_pyramid") && u->faction->alliance != NULL) {
      attrib * a = a_add(&b->attribs, a_new(&at_alliance));
      a->data.i = u->faction->alliance->id;
    }
#endif

    newbuilding = true;
  }

  btname = LOC(lang, btype->_name);

  if (want-built <= 0) {
    /* gebäude fertig */
    strcpy(buffer, LOC(lang, "defaultorder"));
    new_order = parse_order(buffer, lang);
  } else if (want!=INT_MAX) {
    /* reduzierte restgröße */
    sprintf(buffer, "%s %d %s %s", LOC(lang, keywords[K_MAKE]), want-built, btname, buildingid(b));
    new_order = parse_order(buffer, lang);
  } else if (btname) {
    /* Neues Haus, Befehl mit Gebäudename */
    sprintf(buffer, "%s %s %s", LOC(lang, keywords[K_MAKE]), btname, buildingid(b));
    new_order = parse_order(buffer, u->faction->locale);
  }

  if (new_order) {
#ifdef LASTORDER
    set_order(&u->lastorder, new_order);
#else
    replace_order(&u->orders, ord, new_order);
    free_order(new_order);
#endif
  }

  b->size += built;
  update_lighthouse(b);


  ADDMSG(&u->faction->msgs, msg_message("buildbuilding", 
    "building unit size", b, u, built));
}

static void
build_ship(unit * u, ship * sh, int want)
{
  const construction * construction = sh->type->construction;
  int size = (sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE;
  int n;
#if 0
  int can = u->number * effskill(u, SK_SHIPBUILDING) / construction->minskill;
  if (want > 0) can = min(want, can);
  can = min(can, construction->maxsize+sh->damage); /* 100% bauen + 100% reparieren */
#endif
  int can = build(u, construction, size, want);

  if ((n=construction->maxsize - sh->size)>0 && can>0) {
    if (can>=n) {
     sh->size += n;
     can -= n;
    }
    else {
     sh->size += can;
     n=can;
     can = 0;
    }
  }

  if (sh->damage && can) {
    int repair = min(sh->damage, can * DAMAGE_SCALE);
    n += repair / DAMAGE_SCALE;
    if (repair % DAMAGE_SCALE) ++n;
    sh->damage = sh->damage - repair;
  }

  if (n) ADDMSG(&u->faction->msgs, 
    msg_message("buildship", "ship unit size", sh, u, n));
}

void
create_ship(region * r, unit * u, const struct ship_type * newtype, int want, order * ord)
{
  static char buffer[IDSIZE + 2 * KEYWORDSIZE + 3];
  ship *sh;
  int msize;
  const construction * cons = newtype->construction;
  order * new_order;

  if (!eff_skill(u, SK_SHIPBUILDING, r)) {
    cmistake(u, ord, 100, MSG_PRODUCE);
    return;
  }
  if (besieged(u)) {
    cmistake(u, ord, 60, MSG_PRODUCE);
    return;
  }

  /* check if skill and material for 1 size is available */
  if (eff_skill(u, cons->skill, r) < cons->minskill) {
    sprintf(buf, "Um %s zu bauen, braucht man ein Talent von "
      "mindestens %d.", newtype->name[1], cons->minskill);
    mistake(u, ord, buf, MSG_PRODUCE);
    return;
  }

  msize = maxbuild(u, cons);
  if (msize==0) {
    cmistake(u, ord, 88, MSG_PRODUCE);
    return;
  }
  if (want>0) want = min(want, msize);
  else want = msize;

  sh = new_ship(newtype, u->faction->locale, r);

  leave(r, u);
  u->ship = sh;
  fset(u, UFL_OWNER);
  sprintf(buffer, "%s %s %s",
    locale_string(u->faction->locale, keywords[K_MAKE]), locale_string(u->faction->locale, parameters[P_SHIP]), shipid(sh));

  new_order = parse_order(buffer, u->faction->locale);
#ifdef LASTORDER
  set_order(&u->lastorder, new_order);
#else
  replace_order(&u->orders, ord, new_order);
  free_order(new_order);
#endif

  build_ship(u, sh, want);
}

void
continue_ship(region * r, unit * u, int want)
{
  const construction * cons;
  ship *sh;
  int msize;

  if (!eff_skill(u, SK_SHIPBUILDING, r)) {
    cmistake(u, u->thisorder, 100, MSG_PRODUCE);
    return;
  }

  /* Die Schiffsnummer bzw der Schiffstyp wird eingelesen */
  sh = getship(r);

  if (!sh) sh = u->ship;

  if (!sh) {
    cmistake(u, u->thisorder, 20, MSG_PRODUCE);
    return;
  }
  cons = sh->type->construction;
  assert(cons->improvement==NULL); /* sonst ist construction::size nicht ship_type::maxsize */
  if (sh->size==cons->maxsize && !sh->damage) {
    cmistake(u, u->thisorder, 16, MSG_PRODUCE);
    return;
  }
  if (eff_skill(u, cons->skill, r) < cons->minskill) {
    sprintf(buf, "Um %s zu bauen, braucht man ein Talent von "
        "mindestens %d.", sh->type->name[1], cons->minskill);
    mistake(u, u->thisorder, buf, MSG_PRODUCE);
    return;
  }
  msize = maxbuild(u, cons);
  if (msize==0) {
    cmistake(u, u->thisorder, 88, MSG_PRODUCE);
    return;
  }
  if (want > 0) want = min(want, msize);
  else want = msize;

  build_ship(u, sh, want);
}
/* ------------------------------------------------------------- */

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

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

  return false;
}

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

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

}

void
remove_contacts(void)
{
  region *r;
  unit *u;
  attrib *a;

  for (r = regions; r; r = r->next) {
    for (u = r->units; u; u = u->next) {
      a = (attrib *)a_find(u->attribs, &at_contact);
      while(a != NULL) {
        attrib * ar = a;
        a = a->nexttype;
        a_remove(&u->attribs, ar);
      }
    }
  }
}

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

  if (r->terrain == T_OCEAN && u->ship) {
    if(!fval(u->race, RCF_SWIM)) {
      cmistake(u, ord, 11, MSG_MOVE);
      return 0;
    }
    if(get_item(u, I_HORSE)) {
      cmistake(u, ord, 231, MSG_MOVE);
      return 0;
    }
  }
  if (!slipthru(r, u, u->building)) {
    sprintf(buf, "%s wird belagert.", buildingname(u->building));
    mistake(u, ord, buf, MSG_MOVE);
  } else {
    leave(r, u);
  }
  return 0;
}

static boolean
entership(unit * u, ship * sh, struct order * ord, boolean lasttry)
{
  /* Muß abgefangen werden, sonst könnten Schwimmer an
   * Bord von Schiffen an Land gelangen. */
  if( !fval(u->race, RCF_WALK) &&
      !fval(u->race, RCF_FLY)) {
    cmistake(u, ord, 233, MSG_MOVE);
    return false;
  }

  if (!sh) {
    if (lasttry) cmistake(u, ord, 20, MSG_MOVE);
    return false;
  }
  if (sh==u->ship) return true;
  if (!mayboard(u, sh)) {
    if (lasttry) cmistake(u, ord, 34, MSG_MOVE);
    return false;
  }
  if (CheckOverload()) {
    int sweight, scabins;
    int mweight = shipcapacity(sh);
    int mcabins = sh->type->cabins;
    if (mweight>0 && mcabins>0) {
      getshipweight(sh, &sweight, &scabins);
      sweight += weight(u);
      scabins += u->number;
      sweight = ((sweight+99) / 100) * 100; /* Silberreste aufrunden */

      if (sweight > mweight || scabins > mcabins) {
        if (lasttry) cmistake(u, ord, 34, MSG_MOVE);
        return false;
      }
    }
  }

  leave(u->region, u);
  u->ship = sh;

  if (shipowner(sh) == 0) {
    fset(u, UFL_OWNER);
  }
  return true;
}

void
do_misc(boolean lasttry)
{
  region *r;
  ship *sh;
  building *b;

  /* lasttry: Fehler nur im zweiten Versuch melden. Sonst konfus. */

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

    for (u = r->units; u; u = u->next) {
      order * ord;
      for (ord = u->orders; ord; ord = ord->next) {
        switch (get_keyword(ord)) {
        case K_CONTACT:
          contact_cmd(u, ord, lasttry);
          break;
        }
      }
    }

    for (u = r->units; u; u = u->next) {
      order ** ordp = &u->orders;
      while (*ordp) {
        order * ord = *ordp;
        if (get_keyword(ord) == K_ENTER) {
          init_tokens(ord);
          skip_token();
          switch (getparam(u->faction->locale)) {
          case P_BUILDING:
          case P_GEBAEUDE:
            /* Schwimmer können keine Gebäude betreten, außer diese sind
             * auf dem Ozean */
            if( !fval(u->race, RCF_WALK) && !fval(u->race, RCF_FLY)) {
              if (rterrain(r) != T_OCEAN){
                if (lasttry) cmistake(u, ord, 232, MSG_MOVE);
                break;
              }
            }

            b = getbuilding(r);

            if (!b) {
              if(lasttry) cmistake(u, ord, 6, MSG_MOVE);
              break;
            }
            /* Gebäude auf dem Ozean sollte man betreten dürfen
            if(rterrain(r) == T_OCEAN) {
              if (lasttry) cmistake(u, ord, 297, MSG_MOVE);
              break;
            }
            */
            if (!mayenter(r, u, b)) {
              if(lasttry) {
                sprintf(buf, "Der Eintritt in %s wurde verwehrt",
                    buildingname(b));
                mistake(u, ord, buf, MSG_MOVE);
              }
              break;
            }
            if (!slipthru(r, u, b)) {
              if(lasttry) {
                sprintf(buf, "%s wird belagert", buildingname(b));
                mistake(u, ord, buf, MSG_MOVE);
              }
              break;
            }

            /* Wenn wir hier angekommen sind, war der Befehl
             * erfolgreich und wir löschen ihn, damit er im
             * zweiten Versuch nicht nochmal ausgeführt wird. */
            *ordp = ord->next;
            ord->next = NULL;
            free_order(ord);
            leave(r, u);
            u->building = b;
            if (buildingowner(r, b) == 0) {
              fset(u, UFL_OWNER);
            }
            break;

          case P_SHIP:
            sh = getship(r);
            entership(u, sh, ord, lasttry);
            break;

          default:
            if (lasttry) cmistake(u, ord, 79, MSG_MOVE);

          }
        }
        if (*ordp==ord) ordp = &ord->next;
      }
    }
  }
}