server/src/common/kernel/unit.c
Enno Rehling 7233af54b4 Simple allow/deny style restrictions for archetypes (let only humans recruit gamedesigners, etc).
These need to be more powerful to be useful, and the current way they are implemented is not, but the general idea strikes me as good.

Also, feedback should be configurable for each failed rule.
2007-05-30 01:21:09 +00:00

1286 lines
26 KiB
C

/* 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 "save.h"
#include "ship.h"
#include "skill.h"
#include "terrain.h"
#include <attributes/moved.h>
/* util includes */
#include <util/base36.h>
#include <util/event.h>
#include <util/goodies.h>
#include <util/resolve.h>
#include <util/rng.h>
#include <util/variant.h>
/* libc includes */
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <math.h>
#define FIND_FOREIGN_TEMP
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 && au->number>0) {
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) {
while (friends) {
struct friend * nf = friends;
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;
friends = nf->next;
free(nf);
}
friends = NULL;
}
}
void
remove_unit(unit * u)
{
region * r = u->region;
assert(u->number==0);
uunhash(u);
if (r) choplist(&r->units, u);
u->next = udestroy;
udestroy = u;
}
void
destroy_unit(unit * u)
{
region *r = u->region;
boolean zombie = false;
#if 0
unit *clone;
#endif
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 && (u->faction==NULL || 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. */
#if 0
/* broken. */
clone = has_clone(u);
if (clone && rng_int()%100 < 90) {
attrib *a;
int i;
/* TODO: Messages generieren. */
if (u->region!=clone->region) {
move_unit(u, clone->region, NULL);
}
if (fval(u->race, RCF_CANSAIL)) {
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);
if (a!=NULL) a_remove(&u->attribs, a);
a = a_find(clone->attribs, &at_clonemage);
if (a!=NULL) a_remove(&clone->attribs, a);
fset(u, UFL_LONGACTION|UFL_NOTMOVING);
set_number(clone, 0);
} else
#endif
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 && !fval(r->terrain, SEA_REGION)) {
rsetmoney(r, rmoney(r) + get_money(u));
}
dhash(u->no, u->faction);
u_setfaction(u, NULL);
if (r) leave(r, 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->type==&at_contact && a->data.v!=u2) a = a->next;
if (a && a->type==&at_contact) 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->type==&at_contact; ru = ru->next)
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, a_writeint, a_readint
};
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 ((fval(r->terrain, WALK_INTO) && (u->race->flags & RCF_WALK))
|| (fval(r->terrain, SWIM_INTO) && (u->race->flags & RCF_SWIM))
|| (fval(r->terrain, FLY_INTO) && (u->race->flags & RCF_FLY)))
{
static const curse_type * ctype = NULL;
if (get_item(u, I_HORSE) && !fval(r->terrain, WALK_INTO))
return false;
if (!ctype) ctype = ct_find("holyground");
if (fval(u->race, RCF_UNDEAD) && curse_active(get_curse(r->attribs, ctype)))
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==0) return;
assert(n > 0);
/* "hat attackiert"-status wird übergeben */
if (u2) {
skill *sv, *sn;
skill_t sk;
ship * sh;
assert(u2->number+n>0);
for (sk=0; sk!=MAXSKILLS; ++sk) {
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 && a->type==&at_effect) {
effect_data * olde = (effect_data*)a->data.v;
if (olde->value) change_effect(u2, olde->type, olde->value);
a = a->next;
}
sh = leftship(u);
if (sh!=NULL) set_leftship(u2, sh);
u2->flags |= u->flags&(UFL_LONGACTION|UFL_NOTMOVING|UFL_HUNGER|UFL_MOVED|UFL_ENTER);
if (u->attribs) {
transfer_curse(u, u2, n);
}
}
scale_number(u, u->number - n);
if (u2) {
set_number(u2, u2->number + n);
hp -= u->hp;
u2->hp += hp;
/* TODO: Das ist schnarchlahm! und gehört ncht hierhin */
a = a_find(u2->attribs, &at_effect);
while (a && a->type==&at_effect) {
attrib * an = a->next;
effect_data * olde = (effect_data*)a->data.v;
int e = get_effect(u, olde->type);
if (e!=0) change_effect(u2, olde->type, -e);
a = an;
}
}
else if (r->land)
if(u->race != 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 (count_unit(u)) --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 (count_unit(u)) ++f->no_units;
}
}
/* vorsicht Sprüche können u->number == RS_FARVISION haben! */
void
set_number(unit * u, int count)
{
assert (count >= 0);
assert (count <= UNIT_MAXSIZE);
#ifndef NDEBUG
assert (u->faction != NULL || 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 && rng_int()%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;
curse * c;
if (!init) {
init = true;
skillmod_ct = ct_find("skillmod");
gbdream_ct = ct_find("gbdream");
worse_ct = ct_find("worse");
}
c = get_curse(u->attribs, worse_ct);
if (c!=NULL) result += curse_geteffect(c);
if (skillmod_ct) {
attrib * a = a_find(u->attribs, &at_curse);
while (a && a->type==&at_curse) {
curse * c = (curse *)a->data.v;
if (c->type==skillmod_ct && c->data.i==sk) {
result += curse_geteffect(c);
break;
}
a = a->next;
}
}
/* 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_find(u->region->attribs, &at_curse);
while (a && a->type==&at_curse) {
curse * c = (curse*)a->data.v;
if (c->type==gbdream_ct) {
int mod = curse_geteffect(c);
unit * mage = c->magician;
/* wir suchen jeweils den größten Bonus und den größten Malus */
if (mod>bonus) {
if (mage==NULL || alliedunit(mage, u->faction, HELP_GUARD)) {
bonus = mod;
}
} else if (mod < malus) {
if (mage == NULL || !alliedunit(mage, u->faction, HELP_GUARD)) {
malus = mod;
}
}
}
a = a->next;
}
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);
#ifdef KARMA_MODULE
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;
}
}
#endif
#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_order(u->thisorder);
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