/* 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 "unit.h"

#include "building.h"
#include "faction.h"
#include "group.h"
#include "karma.h"
#include "border.h"
#include "item.h"
#include "movement.h"
#include "order.h"
#include "plane.h"
#include "race.h"
#include "region.h"
#include "ship.h"
#include "skill.h"

#include <attributes/moved.h>

/* util includes */
#include <util/base36.h>
#include <event.h>
#include <goodies.h>
#include <resolve.h>
#include <variant.h>

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

#define FIND_FOREIGN_TEMP

int demonfix = 0;

const unit *
u_peasants(void)
{
	static unit peasants = { 0 };
	if (peasants.name==NULL) {
		peasants.name = strdup("die Bauern");
    peasants.no = 2;
	}
	return &peasants;
}

const unit *
u_unknown(void)
{
	static unit unknown = { 0 };
	if (unknown.name==NULL) {
		unknown.name =strdup("eine unbekannte Einheit");
    unknown.no = 1;
	}
	return &unknown;
}

#define DMAXHASH 7919
typedef struct dead {
	struct dead * nexthash;
	faction * f;
	int no;
} dead;

static dead* deadhash[DMAXHASH];

static void
dhash(int no, faction * f)
{
	dead * hash = (dead*)calloc(1, sizeof(dead));
	dead * old = deadhash[no % DMAXHASH];
	hash->no = no;
	hash->f = f;
	deadhash[no % DMAXHASH] = hash;
	hash->nexthash = old;
}

faction *
dfindhash(int no)
{
	dead * old;

	if(no < 0) return 0;

	for (old = deadhash[no % DMAXHASH]; old; old = old->nexthash)
		if (old->no == no)
			return old->f;
	return 0;
}

unit * udestroy = NULL;

/** distributes a unit's posessions to friendly units
* this happens when units die and no own units are in the region
*/
void
distribute_items(unit * u)
{
  faction * f = u->faction;
  region * r = u->region;
  unit * au;
  int number = 0;
  struct friend {
    struct friend * next;
    int number;
    faction * faction;
    unit * unit;
  } * friends = NULL;

  if (u->items==NULL) return;

  for (au=r->units;au;au=au->next) if (au->faction!=f) {
    if (alliedunit(u, au->faction, HELP_MONEY) && alliedunit(au, f, HELP_GIVE)) {
      struct friend * nf, ** fr = &friends;

      while (*fr && (*fr)->faction->no<au->faction->no) fr = &(*fr)->next;
      nf = *fr;
      if (nf==NULL || nf->faction!=au->faction) {
        nf = malloc(sizeof(struct friend));
        nf->next = *fr;
        nf->faction = au->faction;
        nf->unit = au;
        nf->number = 0;
        *fr = nf;
      }
      nf->number += au->number;
      number += au->number;
    }
  }

  if (friends && number) {
    struct friend * nf = friends;
    while (nf) {
      unit * u2 = nf->unit;
      item * itm = u->items;
      while (itm!=NULL) {
        const item_type * itype = itm->type;
        item * itn = itm->next;
        int n = itm->number;
        n = n * nf->number / number;
        if (n>0) {
          i_change(&u->items, itype, -n);
          i_change(&u2->items, itype, n);
        }
        itm = itn;
      }
      number -= nf->number;
      nf = nf->next;
      free(friends);
      friends = nf;
    }
    friends = NULL;
  }
}

void
destroy_unit(unit * u)
{
	region *r = u->region;
	boolean zombie = false;
	unit *clone;

	if (!ufindhash(u->no)) return;

	if (!fval(u->race, RCF_ILLUSIONARY)) {
		item ** p_item = &u->items;
		unit * u3;

		/* u->faction->no_units--; */ /* happens in u_setfaction now */

		if (r) for (u3 = r->units; u3; u3 = u3->next) {
			if (u3 != u && u3->faction == u->faction && playerrace(u3->race)) {
				i_merge(&u3->items, &u->items);
				u->items = NULL;
				break;
			}
		}
		u3 = NULL;
		while (*p_item) {
			item * item = *p_item;
			if (item->number && item->type->flags & ITF_NOTLOST) {
				if (u3==NULL) {
					u3 = r->units;
					while (u3 && u3!=u) u3 = u3->next;
					if (!u3) {
						zombie = true;
						break;
					}
				}
				if (u3) {
					i_add(&u3->items, i_remove(p_item, item));
				}
			}
			if (*p_item == item) p_item=&item->next;
		}
    if (u->items && strlen(u->faction->passw)>0) {
      distribute_items(u);
    }
	}

	/* Wir machen das erst nach dem Löschen der Items. Der Klon darf keine
	 * Items haben, sonst Memory-Leak. */
	
	clone = has_clone(u);
	if (clone && rand()%100 < 90) {
		attrib *a;
		int i;

		/* TODO: Messages generieren. */
		u->region = clone->region;
		u->ship = clone->ship;
		u->building = clone->building;
		u->hp = 1;
		i = u->no;
		uunhash(u);
		uunhash(clone);
		u->no = clone->no;
		clone->no = i;
		uhash(u);
		uhash(clone);
		set_number(u, 1);
		set_spellpoints(u, 0);
		a = a_find(u->attribs, &at_clone);
		a_remove(&u->attribs, a);
		a = a_find(clone->attribs, &at_clonemage);
		a_remove(&clone->attribs, a);
		fset(u, UFL_LONGACTION);
		set_number(clone, 0);
		u = clone;
		zombie = false;
	}
		
	if (zombie) {
		u_setfaction(u, findfaction(MONSTER_FACTION));
		scale_number(u, 1);
		u->race = u->irace = new_race[RC_ZOMBIE];
	} else {
		if (u->number) set_number(u, 0);
		handle_event(&u->attribs, "destroy", u);
		if (r && rterrain(r) != T_OCEAN)
			rsetmoney(r, rmoney(r) + get_money(u));
		dhash(u->no, u->faction);
		u_setfaction(u, NULL);
		if (r) leave(r, u);
		uunhash(u);
		if (r) choplist(&r->units, u);
		u->next = udestroy;
		udestroy = u;
	}
}

unit *
findnewunit (const region * r, const faction *f, int n)
{
	unit *u2;

	if (n == 0)
		return 0;

	for (u2 = r->units; u2; u2 = u2->next)
		if (u2->faction == f && ualias(u2) == n)
			return u2;
#ifdef FIND_FOREIGN_TEMP
	for (u2 = r->units; u2; u2 = u2->next)
		if (ualias(u2) == n)
			return u2;
#endif
	return 0;
}

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


/*********************/
/*   at_alias   */
/*********************/
attrib_type at_alias = {
	"alias",
	DEFAULT_INIT,
	DEFAULT_FINALIZE,
	DEFAULT_AGE,
	NO_WRITE,
	NO_READ
};

int
ualias(const unit * u) {
	attrib * a = a_find(u->attribs, &at_alias);
	if (!a) return 0;
	return a->data.i;
}

/*********************/
/*   at_private   */
/*********************/
attrib_type at_private = {
	"private",
	DEFAULT_INIT,
	a_finalizestring,
	DEFAULT_AGE,
	a_writestring,
	a_readstring
};

const char *
uprivate(const unit * u) {
	attrib * a = a_find(u->attribs, &at_private);
	if (!a) return NULL;
	return (const char*)a->data.v;
}

void
usetprivate(unit * u, const char * str) {
	attrib * a = a_find(u->attribs, &at_private);

	if(str == NULL) {
		if(a) a_remove(&u->attribs, a);
		return;
	}
	if (!a) a = a_add(&u->attribs, a_new(&at_private));
	if (a->data.v) free(a->data.v);
	a->data.v = strdup(str);
}

/*********************/
/*   at_potionuser   */
/*********************/
/* Einheit BENUTZT einen Trank */
attrib_type at_potionuser = {
	"potionuser",
	DEFAULT_INIT,
	DEFAULT_FINALIZE,
	DEFAULT_AGE,
	NO_WRITE,
	NO_READ
};

void
usetpotionuse(unit * u, const potion_type * ptype)
{
	attrib * a = a_find(u->attribs, &at_potionuser);
	if (!a) a = a_add(&u->attribs, a_new(&at_potionuser));
	a->data.v = (void*)ptype;
}

const potion_type *
ugetpotionuse(const unit * u) {
	attrib * a = a_find(u->attribs, &at_potionuser);
	if (!a) return NULL;
	return (const potion_type *)a->data.v;
}

/*********************/
/*   at_target   */
/*********************/
attrib_type at_target = {
	"target",
	DEFAULT_INIT,
	DEFAULT_FINALIZE,
	DEFAULT_AGE,
	NO_WRITE,
	NO_READ
};

unit *
utarget(const unit * u) {
	attrib * a;
	if (!fval(u, UFL_TARGET)) return NULL;
	a = a_find(u->attribs, &at_target);
	assert (a || !"flag set, but no target found");
	return (unit*)a->data.v;
}

void
usettarget(unit * u, const unit * t)
{
	attrib * a = a_find(u->attribs, &at_target);
	if (!a && t) a = a_add(&u->attribs, a_new(&at_target));
	if (a) {
		if (!t) {
			a_remove(&u->attribs, a);
			freset(u, UFL_TARGET);
		}
		else {
			a->data.v = (void*)t;
			fset(u, UFL_TARGET);
		}
	}
}

/*********************/
/*   at_siege   */
/*********************/

void
a_writesiege(const attrib * a, FILE * f)
{
	struct building * b = (struct building*)a->data.v;
	write_building_reference(b, f);
}

int
a_readsiege(attrib * a, FILE * f)
{
	return read_building_reference((struct building**)&a->data.v, f);
}

attrib_type at_siege = {
	"siege",
	DEFAULT_INIT,
	DEFAULT_FINALIZE,
	DEFAULT_AGE,
	a_writesiege,
	a_readsiege
};

struct building *
usiege(const unit * u) {
	attrib * a;
	if (!fval(u, UFL_SIEGE)) return NULL;
	a = a_find(u->attribs, &at_siege);
	assert (a || !"flag set, but no siege found");
	return (struct building *)a->data.v;
}

void
usetsiege(unit * u, const struct building * t)
{
	attrib * a = a_find(u->attribs, &at_siege);
	if (!a && t) a = a_add(&u->attribs, a_new(&at_siege));
	if (a) {
		if (!t) {
			a_remove(&u->attribs, a);
			freset(u, UFL_SIEGE);
		}
		else {
			a->data.v = (void*)t;
			fset(u, UFL_SIEGE);
		}
	}
}

/*********************/
/*   at_contact   */
/*********************/
attrib_type at_contact = {
	"contact",
	DEFAULT_INIT,
	DEFAULT_FINALIZE,
	DEFAULT_AGE,
	NO_WRITE,
	NO_READ
};

void
usetcontact(unit * u, const unit * u2)
{
	attrib * a = a_find(u->attribs, &at_contact);
	while (a && a->data.v!=u2) a = a->nexttype;
	if (a) return;
	a_add(&u->attribs, a_new(&at_contact))->data.v = (void*)u2;
}

boolean
ucontact(const unit * u, const unit * u2)
/* Prüft, ob u den Kontaktiere-Befehl zu u2 gesetzt hat. */
{
	attrib *ru;
  if (u->faction==u2->faction) return true;

	/* Explizites KONTAKTIERE */
	for (ru = a_find(u->attribs, &at_contact); ru; ru = ru->nexttype)
		if (((unit*)ru->data.v) == u2)
			return true;

	return false;
}

/***
 ** init & cleanup module
 **/

void
free_units(void)
{
	while (udestroy) {
		unit * u = udestroy;
		udestroy = udestroy->next;
		stripunit(u);
		free(u);
	}
}

void
write_unit_reference(const unit * u, FILE * F)
{
	fprintf(F, "%s ", (u!=NULL && u->no!=0)?itoa36(u->no):"0");
}

void *
resolve_unit(variant id)
{
  return ufindhash(id.i);
}

int
read_unit_reference(unit ** up, FILE * F)
{
  char zId[10];
  variant var;

  assert(up!=NULL);
  fscanf(F, "%s", zId);
  var.i = atoi36(zId);
  if (var.i==0) {
    *up = NULL;
    return AT_READ_FAIL;
  }
  *up = findunit(var.i);
  if (*up==NULL) ur_add(var, (void**)up, resolve_unit);
  return AT_READ_OK;
}

attrib_type at_stealth = {
	"stealth", NULL, NULL, NULL, DEFAULT_WRITE, DEFAULT_READ
};

void
u_seteffstealth(unit * u, int value)
{
	attrib * a = NULL;
  if (fval(u, UFL_STEALTH)) {
    a = a_find(u->attribs, &at_stealth);
  }
  if (value<0) {
    if (a!=NULL) {
      freset(u, UFL_STEALTH);
      a_remove(&u->attribs, a);
    }
    return;
  }
  if (a==NULL) {
    a = a_add(&u->attribs, a_new(&at_stealth));
    fset(u, UFL_STEALTH);
  }
	a->data.i = value;
}

int
u_geteffstealth(const struct unit * u)
{
  if (fval(u, UFL_STEALTH)) {
    attrib * a = a_find(u->attribs, &at_stealth);
    if (a!=NULL) return a->data.i;
  }
  return -1;
}

int
get_level(const unit * u, skill_t id)
{
	skill * sv = u->skills;
	while (sv != u->skills + u->skill_size) {
		if (sv->id == id) {
			return sv->level;
		}
		++sv;
	}
	return 0;
}

void 
set_level(unit * u, skill_t sk, int value)
{
	skill * sv = u->skills;
	if (value==0) {
		remove_skill(u, sk);
		return;
	}
	while (sv != u->skills + u->skill_size) {
		if (sv->id == sk) {
			sk_set(sv, value);
			return;
		}
		++sv;
	}
	sk_set(add_skill(u, sk), value);
}

static int  
leftship_age(struct attrib * a)
{
  /* must be aged, so it doesn't affect report generation (cansee) */
  unused(a);
  return 0; /* remove me */
}

static attrib_type at_leftship = {
	"leftship", NULL, NULL, leftship_age
};

static attrib *
make_leftship(struct ship * leftship)
{
	attrib * a = a_new(&at_leftship);
	a->data.v = leftship;
	return a;
}

void
set_leftship(unit *u, ship *sh)
{
	a_add(&u->attribs, make_leftship(sh));
}

ship *
leftship(const unit *u)
{
	attrib * a = a_find(u->attribs, &at_leftship);

	/* Achtung: Es ist nicht garantiert, daß der Rückgabewert zu jedem
	 * Zeitpunkt noch auf ein existierendes Schiff zeigt! */

	if (a) return (ship *)(a->data.v);

	return NULL;
}

void
leave_ship(unit * u)
{
	struct ship * sh = u->ship;
	if (sh==NULL) return;
	u->ship = NULL;
	set_leftship(u, sh);

	if (fval(u, UFL_OWNER)) {
		unit *u2, *owner = NULL;
		freset(u, UFL_OWNER);

		for (u2 = u->region->units; u2; u2 = u2->next) {
			if (u2->ship == sh) {
				if (u2->faction == u->faction) {
					owner = u2;
					break;
				} 
				else if (owner==NULL) owner = u2;
			}
		}
		if (owner!=NULL) fset(owner, UFL_OWNER);
	}
}

void
leave_building(unit * u)
{
	struct building * b = u->building;
	if (!b) return;
	u->building = NULL;

	if (fval(u, UFL_OWNER)) {
		unit *u2, *owner = NULL;
		freset(u, UFL_OWNER);

		for (u2 = u->region->units; u2; u2 = u2->next) {
			if (u2->building == b) {
				if (u2->faction == u->faction) {
					owner = u2;
					break;
				} 
				else if (owner==NULL) owner = u2;
			}
		}
		if (owner!=NULL) fset(owner, UFL_OWNER);
	}
}

void
leave(struct region * r, unit * u)
{
	if (u->building) leave_building(u);
	else if (u->ship) leave_ship(u);
	unused(r);
}

const struct race * 
urace(const struct unit * u)
{
	return u->race;
}

boolean
can_survive(const unit *u, const region *r)
{
  if (((terrain[rterrain(r)].flags & WALK_INTO)
			&& (u->race->flags & RCF_WALK)) ||
			((terrain[rterrain(r)].flags & SWIM_INTO)
			&& (u->race->flags & RCF_SWIM)) ||
			((terrain[rterrain(r)].flags & FLY_INTO)
			&& (u->race->flags & RCF_FLY))) {

		if (get_item(u, I_HORSE) && !(terrain[rterrain(r)].flags & WALK_INTO))
			return false;

		if (fval(u->race, RCF_UNDEAD) && is_cursed(r->attribs, C_HOLYGROUND, 0))
			return false;

		return true;
	}
	return false;
}

void
move_unit(unit * u, region * r, unit ** ulist)
{
	int maxhp = 0;
	assert(u && r);

	if (u->region == r) return;
	if (u->region!=NULL) maxhp = unit_max_hp(u);
	if (!ulist) ulist = (&r->units);
	if (u->region) {
#ifdef DELAYED_OFFENSE
		set_moved(&u->attribs);
#endif
		setguard(u, GUARD_NONE);
		fset(u, UFL_MOVED);
		if (u->ship || u->building) leave(u->region, u);
		translist(&u->region->units, ulist, u);
	} else
		addlist(ulist, u);

#ifdef SMART_INTERVALS
  update_interval(u->faction, r);
#endif
	u->region = r;
	/* keine automatische hp reduzierung bei bewegung */
	/* if (maxhp>0) u->hp = u->hp * unit_max_hp(u) / maxhp; */
}

/* ist mist, aber wegen nicht skalierender attirbute notwendig: */
#include "alchemy.h"

void
transfermen(unit * u, unit * u2, int n)
{
	const attrib * a;
	int hp = u->hp;
	region * r = u->region;

	if (!n) return;

	/* "hat attackiert"-status wird übergeben */

	if (u2) {
		skill *sv, *sn;
		skill_t sk;
		assert(u2->number+n>0);

		if (demonfix && u2->race==new_race[RC_DAEMON]) fset(u2, UFL_DEBUG);

		for (sk=0; sk!=MAXSKILLS; ++sk) {
			double dlevel = 0.0;
			int weeks, level = 0;

			sv = get_skill(u, sk);
			sn = get_skill(u2, sk);

			if (sv==NULL && sn==NULL) continue;
			if (sv && sv->level) {
				dlevel += (sv->level + 1 - sv->weeks/(sv->level+1.0)) * n;
				level += sv->level * n;
			}
			if (sn && sn->level) {
				dlevel += (sn->level + 1 - sn->weeks/(sn->level+1.0)) * u2->number;
				level += sn->level * u2->number;
			}

			dlevel = dlevel / (n + u2->number);
			level = level / (n + u2->number);
			if (level<=dlevel) {
				/* apply the remaining fraction to the number of weeks to go.
				 * subtract the according number of weeks, getting closer to the
				 * next level */
				level = (int)dlevel;
				weeks = (level+1) - (int)((dlevel - level) * (level+1));
			} else {
				/* make it harder to reach the next level. 
				 * weeks+level is the max difficulty, 1 - the fraction between
				 * level and dlevel applied to the number of weeks between this
				 * and the previous level is the added difficutly */
				level = (int)dlevel+1;
				weeks = 1 + 2 * level - (int)((1 + dlevel - level) * level);
			}
			if (level) {
				if (sn==NULL) sn = add_skill(u2, sk);
				sn->level = (unsigned char)level;
				sn->weeks = (unsigned char)weeks;
				assert(sn->weeks>0 && sn->weeks<=sn->level*2+1);
			} else if (sn) {
				remove_skill(u2, sk);
				sn = NULL;
			}
		}
		a = a_find(u->attribs, &at_effect);
		while (a) {
			effect_data * olde = (effect_data*)a->data.v;
			if (olde->value) change_effect(u2, olde->type, olde->value);
			a = a->nexttype;
		}
		if (fval(u, UFL_LONGACTION)) fset(u2, UFL_LONGACTION);
		if (u->attribs) {
			transfer_curse(u, u2, n);
		}
	}
	scale_number(u, u->number - n);
	if (u2) {
		set_number(u2, u2->number + n);
		hp -= u->hp;
		u2->hp += hp;
		/* TODO: Das ist schnarchlahm! und gehört ncht hierhin */
		a = a_find(u2->attribs, &at_effect);
		while (a) {
			attrib * an = a->nexttype;
			effect_data * olde = (effect_data*)a->data.v;
			int e = get_effect(u, olde->type);
			if (e!=0) change_effect(u2, olde->type, -e);
			a = an;
		}
	}
	else if (r->land)
		if(u->race != new_race[RC_DAEMON]) {
			if(u->race != new_race[RC_URUK]) {
				rsetpeasants(r, rpeasants(r) + n);
			} else {
				rsetpeasants(r, rpeasants(r) + n/2);
			}
		}
}

struct building * 
inside_building(const struct unit * u)
{
	if (u->building==NULL) return NULL;

	if (!fval(u->building, BLD_WORKING)) {
		/* Unterhalt nicht bezahlt */
		return NULL;
	} else if (u->building->size < u->building->type->maxsize) {
		/* Gebäude noch nicht fertig */
		return NULL;
	} else {
		int p = 0, cap = buildingcapacity(u->building);
		const unit * u2;
		for (u2 = u->region->units; u2; u2 = u2->next) {
			if (u2->building == u->building) {
				p += u2->number;
				if (u2 == u) {
					if (p <= cap) return u->building;
					return NULL;
				}
				if (p > cap) return NULL;
			}
		}	
	}
	return NULL;
}

void
u_setfaction(unit * u, faction * f)
{
  int cnt = u->number;

  if (u->faction==f) return;
  if (u->faction) {
    unit ** iunit;
    set_number(u, 0);
    if (playerrace(u->race)) {
      --u->faction->no_units;
    }
    join_group(u, NULL);
    free_orders(&u->orders);
    set_order(&u->thisorder, NULL);
#ifdef LASTORDER
    set_order(&u->lastorder, NULL);
#endif

    iunit = &u->faction->units;
    while (*iunit && *iunit!=u) {
      iunit=&(*iunit)->nextF;
    }
    assert(*iunit);
    *iunit = u->nextF;
  }

  if (f!=NULL) {
    u->nextF = f->units;
    f->units = u;
  }
  else u->nextF = NULL;

  u->faction = f;
  if (u->region) update_interval(f, u->region);
	if (cnt && f) {
		set_number(u, cnt);
    if (playerrace(u->race)) {
      ++f->no_units;
    }
	}
}

/* vorsicht Sprüche können u->number == 0 (RS_FARVISION) haben! */
void
set_number(unit * u, int count)
{
  assert (count >= 0);
  assert (count <= USHRT_MAX);

#ifndef NDEBUG
  assert (u->faction != 0 || u->number > 0);
#endif
  
  if (playerrace(u->race)) {
    u->faction->num_people += count - u->number;
  }
  u->number = (unsigned short)count;
}

boolean
learn_skill(unit * u, skill_t sk, double chance)
{
	skill * sv = u->skills;
	if (chance < 1.0 && rand()%10000>=chance*10000) return false;
	while (sv != u->skills + u->skill_size) {
		assert (sv->weeks>0);
		if (sv->id == sk) {
			if (sv->weeks<=1) {
				sk_set(sv, sv->level+1);
			} else {
				sv->weeks--;
			}
			return true;
		}
		++sv;
	}
	sv = add_skill(u, sk);
	sk_set(sv, 1);
	return true;
}

void
remove_skill(unit *u, skill_t sk)
{
	skill * sv = u->skills;
	for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
		if (sv->id==sk) {
			skill * sl = u->skills + u->skill_size - 1;
			if (sl!=sv) {
				*sv = *sl;
			}
			--u->skill_size;
			return;
		}
	}
}

skill * 
add_skill(unit * u, skill_t id)
{
  skill * sv = u->skills;
#ifndef NDEBUG
  for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
	assert(sv->id != id);
  }
#endif
  ++u->skill_size;
  u->skills = realloc(u->skills, u->skill_size * sizeof(skill));
  sv = (u->skills + u->skill_size - 1);
  sv->level = (unsigned char)0;
  sv->weeks = (unsigned char)1;
  sv->old   = (unsigned char)0;
  sv->id    = (unsigned char)id;
  return sv;
}

skill *
get_skill(const unit * u, skill_t sk)
{
	skill * sv = u->skills;
	while (sv!=u->skills+u->skill_size) {
		if (sv->id==sk) return sv;
		++sv;
	}
	return NULL;
}

boolean
has_skill(const unit * u, skill_t sk)
{
	skill * sv = u->skills;
	while (sv!=u->skills+u->skill_size) {
		if (sv->id==sk) {
			return (sv->level>0);
		}
		++sv;
	}
	return false;
}

static int
item_modification(const unit *u, skill_t sk, int val)
{
	/* Presseausweis: *2 Spionage, 0 Tarnung */
	if(sk == SK_SPY && get_item(u, I_PRESSCARD) >= u->number) {
		val = val * 2;
	} else if(sk == SK_STEALTH) {
#if NEWATSROI == 1
		if (get_item(u, I_RING_OF_INVISIBILITY)
				+ 100 * get_item(u, I_SPHERE_OF_INVISIBILITY) >= u->number) {
			val += ROIBONUS;
		}
#endif
		if(get_item(u, I_PRESSCARD) >= u->number) {
			val = 0;
		}
	}
#if NEWATSROI == 1
	if(sk == SK_OBSERVATION) {
		if(get_item(u, I_AMULET_OF_TRUE_SEEING) >= u->number) {
			val += ATSBONUS;
		}
	}
#endif
	return val;
}

static int
att_modification(const unit *u, skill_t sk)
{
	int bonus = 0, malus = 0;
	attrib * a;
	int result = 0;
	static boolean init = false;
	static const curse_type * skillmod_ct, * gbdream_ct, * worse_ct;
	if (!init) { 
		init = true; 
		skillmod_ct = ct_find("skillmod"); 
		gbdream_ct = ct_find("gbdream");
    worse_ct = ct_find("worse");
	}

	result += curse_geteffect(get_curse(u->attribs, worse_ct));
	if (skillmod_ct) {
    curse * c;
    variant var;
    var.i = sk;
		c = get_cursex(u->attribs, skillmod_ct, var, cmp_cursedata_int);
		result += curse_geteffect(c);
	}

	/* TODO hier kann nicht mit get/iscursed gearbeitet werden, da nur der
	 * jeweils erste vom Typ C_GBDREAM zurückgegen wird, wir aber alle
	 * durchsuchen und aufaddieren müssen */
	a = a_select(u->region->attribs, gbdream_ct, cmp_cursetype);
	while (a) {
		curse * c = (curse*)a->data.v;
		int mod = curse_geteffect(c);
		unit * mage = c->magician;
		/* wir suchen jeweils den größten Bonus und den größten Malus */
		if (mod>0 && (mage==NULL || alliedunit(mage, u->faction, HELP_GUARD))) 
		{
			if (mod > bonus ) bonus = mod;
		} else if (mod < 0 && 
			(mage == NULL || !alliedunit(mage, u->faction, HELP_GUARD)))
		{
			if (mod < malus ) malus = mod;
		}
		a = a_select(a->next, gbdream_ct, cmp_cursetype);
	}
	result = result + bonus + malus;

	return result;
}

int
get_modifier(const unit *u, skill_t sk, int level, const region *r, boolean noitem)
{
	int bskill = level;
	int skill = bskill;

	if (r->planep && sk == SK_STEALTH && fval(r->planep, PFL_NOSTEALTH)) return 0;

	assert(r);
	skill += rc_skillmod(u->race, r, sk);
	skill += att_modification(u, sk);

	if (noitem == false) {
		skill = item_modification(u, sk, skill);
	}
	skill = skillmod(u->attribs, u, r, sk, skill, SMF_ALWAYS);

	if (fspecial(u->faction, FS_TELEPATHY)) {
		switch(sk) {
		case SK_ALCHEMY:
		case SK_HERBALISM:
		case SK_MAGIC:
		case SK_SPY:
		case SK_STEALTH:
		case SK_OBSERVATION:
			break;
		default:
			skill -= 2;
		}
	}

#ifdef HUNGER_REDUCES_SKILL
	if (fval(u, UFL_HUNGER)) {
		skill = skill/2;
	}
#endif
	return skill - bskill;
}

int
eff_skill(const unit * u, skill_t sk, const region * r)
{
	int level = get_level(u, sk);
	if (level>0) {
		int mlevel = level + get_modifier(u, sk, level, r, false);

		if (mlevel>0) {
			int skillcap = SkillCap(sk);
			if (skillcap && mlevel>skillcap) {
				return skillcap;
			}
			return mlevel;
		}
	}
	return 0;
}

int
eff_skill_study(const unit * u, skill_t sk, const region * r)
{
	int level = get_level(u, sk);
	if (level>0) {
		int mlevel = level + get_modifier(u, sk, level, r, true);

		if (mlevel>0) return mlevel;
	}
	return 0;
}

int
invisible(const unit *target, const unit * viewer)
{
#if NEWATSROI == 1
	return 0;
#else
  if (viewer && viewer->faction==target->faction) return 0;
  else {
    int hidden = get_item(target, I_RING_OF_INVISIBILITY) + 100 * get_item(target, I_SPHERE_OF_INVISIBILITY);
    if (hidden) {
      hidden = min(hidden, target->number);
      if (viewer) hidden -= get_item(viewer, I_AMULET_OF_TRUE_SEEING);
    }
    return hidden;
  }
#endif
}

void
stripunit(unit * u)
{
	free(u->name);
	free(u->display);
	free_orders(&u->orders);
	if(u->skills) free(u->skills);
	while (u->items) {
		item * it = u->items->next;
		u->items->next = NULL;
		i_free(u->items);
		u->items = it;
	}
	while (u->attribs) a_remove (&u->attribs, u->attribs);
  while (u->reservations) {
    struct reservation *res = u->reservations;
    u->reservations = res->next;
    free(res);
  }
}


void 
unitlist_clear(struct unit_list **ul)
{
  while (*ul) {
    unit_list * rl2 = (*ul)->next;
    free(*ul);
    *ul = rl2;
  }
}

void 
unitlist_insert(struct unit_list **ul, struct unit *u)
{
  unit_list *rl2 = (unit_list*)malloc(sizeof(unit_list));

  rl2->data = u;
  rl2->next = *ul;

  *ul = rl2;
}

#ifdef HEROES
int 
maxheroes(const struct faction * f)
{
  int nsize = count_all(f);
  if (nsize==0) return 0;
  else {
    int nmax = (int)(log10(nsize / 50.0) * 20);
    return (nmax<0)?0:nmax;
  }
}

int 
countheroes(const struct faction * f)
{
  const unit * u = f->units;
  int n = 0;
  while (u) {
    if (fval(u, UFL_HERO)) n+= u->number;
    u = u->nextF;
  }
  return n;
}

#endif