2001-01-25 10:37:55 +01:00
|
|
|
|
/* vi: set ts=2:
|
|
|
|
|
*
|
2001-02-03 14:45:35 +01:00
|
|
|
|
* $Id: skill.c,v 1.3 2001/02/03 13:45:32 enno Exp $
|
2001-01-25 10:37:55 +01:00
|
|
|
|
* Eressea PB(E)M host Copyright (C) 1998-2000
|
|
|
|
|
* 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<EFBFBD>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 "skill.h"
|
|
|
|
|
|
|
|
|
|
#include "unit.h"
|
|
|
|
|
#include "item.h"
|
|
|
|
|
#include "magic.h"
|
|
|
|
|
#include "plane.h"
|
|
|
|
|
#include "race.h"
|
|
|
|
|
#include "curse.h"
|
|
|
|
|
#include "region.h"
|
|
|
|
|
#include "karma.h"
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
/* Umlaute hier drin, weil die in den Report kommen */
|
|
|
|
|
const char *skillnames[MAXSKILLS] =
|
|
|
|
|
{
|
|
|
|
|
"Alchemie",
|
|
|
|
|
"Armbrustschie<EFBFBD>en",
|
|
|
|
|
"Bergbau",
|
|
|
|
|
"Bogenschie<EFBFBD>en",
|
|
|
|
|
"Burgenbau",
|
|
|
|
|
"Handeln",
|
|
|
|
|
"Holzf<EFBFBD>llen",
|
|
|
|
|
"Katapultbedienung",
|
|
|
|
|
"Kr<EFBFBD>uterkunde",
|
|
|
|
|
"Magie",
|
|
|
|
|
"Pferdedressur",
|
|
|
|
|
"Reiten",
|
|
|
|
|
"R<EFBFBD>stungsbau",
|
|
|
|
|
"Schiffbau",
|
|
|
|
|
"Hiebwaffen",
|
|
|
|
|
"Segeln",
|
|
|
|
|
"Stangenwaffen",
|
|
|
|
|
"Spionage",
|
|
|
|
|
"Steinbau",
|
|
|
|
|
"Stra<EFBFBD>enbau",
|
|
|
|
|
"Taktik",
|
|
|
|
|
"Tarnung",
|
|
|
|
|
"Unterhaltung",
|
|
|
|
|
"Waffenbau",
|
|
|
|
|
"Wagenbau",
|
|
|
|
|
"Wahrnehmung",
|
|
|
|
|
"Steuereintreiben",
|
|
|
|
|
"Ausdauer",
|
|
|
|
|
"Waffenloser Kampf"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** skillmod attribut **/
|
|
|
|
|
static void
|
|
|
|
|
init_skillmod(attrib * a) {
|
|
|
|
|
a->data.v = calloc(sizeof(skillmod_data), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
finalize_skillmod(attrib * a)
|
|
|
|
|
{
|
|
|
|
|
free(a->data.v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_type at_skillmod = {
|
|
|
|
|
"skillmod",
|
|
|
|
|
init_skillmod,
|
|
|
|
|
finalize_skillmod,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL, /* can't write function pointers */
|
|
|
|
|
NULL, /* can't read function pointers */
|
|
|
|
|
ATF_PRESERVE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
skillmods * modhash[MAXRACES];
|
|
|
|
|
|
2001-02-03 14:45:35 +01:00
|
|
|
|
attrib *
|
|
|
|
|
make_skillmod(skill_t skill, unsigned int flags, int(*special)(const struct unit*, const struct region*, skill_t, int), double multiplier, int bonus)
|
|
|
|
|
{
|
|
|
|
|
attrib * a = a_new(&at_skillmod);
|
|
|
|
|
skillmod_data * smd = (skillmod_data*)a->data.v;
|
|
|
|
|
|
|
|
|
|
smd->skill=skill;
|
|
|
|
|
smd->special=special;
|
|
|
|
|
smd->bonus=bonus;
|
|
|
|
|
smd->multiplier=multiplier;
|
|
|
|
|
smd->flags=flags;
|
|
|
|
|
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-25 10:37:55 +01:00
|
|
|
|
int
|
|
|
|
|
skillmod(const attrib * a, const unit * u, const region * r, skill_t sk, int value, int flags)
|
|
|
|
|
{
|
|
|
|
|
for (a = a_find((attrib*)a, &at_skillmod); a; a=a->nexttype) {
|
|
|
|
|
skillmod_data * smd = (skillmod_data *)a->data.v;
|
2001-02-03 14:45:35 +01:00
|
|
|
|
if (smd->skill!=NOSKILL && smd->skill!=sk) continue;
|
2001-01-25 10:37:55 +01:00
|
|
|
|
if (flags!=SMF_ALWAYS && (smd->flags & flags) == 0) continue;
|
|
|
|
|
if (smd->special) {
|
|
|
|
|
value = smd->special(u, r, sk, value);
|
|
|
|
|
if (value<0) return value; /* pass errors back to caller */
|
|
|
|
|
}
|
2001-02-03 14:45:35 +01:00
|
|
|
|
if (smd->multiplier) value = (int)(value*smd->multiplier);
|
2001-01-25 10:37:55 +01:00
|
|
|
|
value += smd->bonus;
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
att_modification(const unit *u, skill_t sk)
|
|
|
|
|
{
|
|
|
|
|
curse *c;
|
|
|
|
|
attrib *a;
|
|
|
|
|
unit *mage;
|
|
|
|
|
int mod;
|
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
|
|
result += get_curseeffect(u->attribs, C_ALLSKILLS, 0);
|
|
|
|
|
result += get_curseeffect(u->attribs, C_SKILL, (int)sk);
|
|
|
|
|
|
|
|
|
|
/* TODO hier kann nicht mit get/iscursed gearbeitet werden, da nur der
|
|
|
|
|
* jeweils erste vom Typ C_GBDREAM zur<EFBFBD>ckgegen wird, wir aber alle
|
|
|
|
|
* durchsuchen und aufaddieren m<EFBFBD>ssen */
|
|
|
|
|
if (is_cursed(u->region->attribs, C_GBDREAM, 0)){
|
|
|
|
|
a = a_select(u->region->attribs, packids(C_GBDREAM, 0), cmp_oldcurse);
|
|
|
|
|
while(a) {
|
|
|
|
|
c = (curse*)a->data.v;
|
|
|
|
|
mage = c->magician;
|
|
|
|
|
mod = c->effect;
|
|
|
|
|
|
|
|
|
|
if (mod > 0
|
|
|
|
|
&& (mage == NULL
|
|
|
|
|
|| allied(mage, u->faction, HELP_GUARD)))
|
|
|
|
|
{
|
|
|
|
|
result += mod;
|
|
|
|
|
} else if (mod < 0
|
|
|
|
|
&& (mage == NULL
|
|
|
|
|
|| !allied(mage, u->faction, HELP_GUARD)))
|
|
|
|
|
{
|
|
|
|
|
result += mod;
|
|
|
|
|
}
|
|
|
|
|
a = a_select(a->next, packids(C_GBDREAM, 0), cmp_oldcurse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
if(sk == SK_STEALTH && get_item(u, I_PRESSCARD) >= u->number) {
|
|
|
|
|
*val = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
signed char
|
|
|
|
|
skill_mod(race_t typ, skill_t sk, terrain_t t)
|
|
|
|
|
{
|
|
|
|
|
signed char result = 0;
|
|
|
|
|
|
|
|
|
|
result = race[typ].bonus[sk];
|
|
|
|
|
|
|
|
|
|
switch (sk) {
|
|
|
|
|
case SK_TACTICS:
|
|
|
|
|
if (typ == RC_DWARF) {
|
|
|
|
|
if (t == T_MOUNTAIN || t == T_GLACIER) ++result;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (typ == RC_INSECT) {
|
|
|
|
|
if (t == T_MOUNTAIN || t == T_GLACIER)
|
|
|
|
|
--result;
|
|
|
|
|
else if (t == T_DESERT || t == T_SWAMP)
|
|
|
|
|
++result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int modcount;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
skill_done(void)
|
|
|
|
|
{
|
|
|
|
|
race_t typ;
|
|
|
|
|
for (typ=0;typ!=MAXRACES;++typ) {
|
|
|
|
|
skillmods * mods = modhash[typ];
|
|
|
|
|
if (mods) free(mods);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
skill_init(void)
|
|
|
|
|
{
|
|
|
|
|
terrain_t t;
|
|
|
|
|
skill_t sk;
|
|
|
|
|
race_t typ;
|
|
|
|
|
for (typ=0;typ!=MAXRACES;++typ) {
|
|
|
|
|
skillmods * mods = modhash[typ];
|
|
|
|
|
|
|
|
|
|
if (!mods) {
|
|
|
|
|
++modcount;
|
|
|
|
|
mods = (skillmods*)calloc(1, sizeof(skillmods));
|
|
|
|
|
|
|
|
|
|
for (t=0;t!=MAXTERRAINS;++t) {
|
|
|
|
|
for (sk=0;sk!=MAXSKILLS;++sk) {
|
|
|
|
|
mods->mod[t].value[sk] = skill_mod(typ, sk, t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
modhash[typ] = mods;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
level_days(int level)
|
|
|
|
|
{
|
|
|
|
|
return 30 * ((level+1) * level / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
level(int days)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
#ifdef SLOW_SKILLS
|
|
|
|
|
/* that's just too many function calls, baby: */
|
|
|
|
|
i = 0;
|
|
|
|
|
while (level_days(i+1) <= days) ++i;
|
|
|
|
|
#else
|
|
|
|
|
static int ldays[32];
|
|
|
|
|
static boolean init = false;
|
|
|
|
|
if (!init) {
|
|
|
|
|
init = true;
|
|
|
|
|
for (i=0;i!=32;++i) ldays[i] = level_days(i+1);
|
|
|
|
|
}
|
|
|
|
|
for (i=0;i!=32;++i) if (ldays[i]>days) return i;
|
|
|
|
|
#endif
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef OLD_FAMILIAR_MOD
|
|
|
|
|
extern int skillmod_familiar(const struct region *r, const struct unit *fam, skill_t sk);
|
|
|
|
|
#endif
|
|
|
|
|
char
|
|
|
|
|
eff_skill(const unit * u, skill_t sk, const region * r)
|
|
|
|
|
{
|
|
|
|
|
int i, result = 0;
|
|
|
|
|
|
|
|
|
|
if (u->number==0) return 0;
|
|
|
|
|
if (r->planep && sk == SK_STEALTH && fval(r->planep, PFL_NOSTEALTH)) return 0;
|
|
|
|
|
|
|
|
|
|
result = level(get_skill(u, sk) / u->number);
|
|
|
|
|
if (result == 0) return 0;
|
|
|
|
|
|
|
|
|
|
assert(r);
|
|
|
|
|
result = (char)(result + modhash[u->race]->mod[rterrain(r)].value[sk]);
|
|
|
|
|
|
|
|
|
|
if (u->race==RC_ELF && r_isforest(r)) switch (sk) {
|
|
|
|
|
case SK_OBSERVATION:
|
|
|
|
|
++result;
|
|
|
|
|
break;
|
|
|
|
|
case SK_STEALTH:
|
|
|
|
|
if (r_isforest(r)) ++result;
|
|
|
|
|
break;
|
|
|
|
|
case SK_TACTICS:
|
|
|
|
|
if (r_isforest(r)) result += 2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result += att_modification(u, sk);
|
|
|
|
|
item_modification(u, sk, &result);
|
|
|
|
|
|
|
|
|
|
i = skillmod(u->attribs, u, r, sk, result, SMF_ALWAYS);
|
|
|
|
|
if (i!=result) {
|
|
|
|
|
result = i;
|
|
|
|
|
} else {
|
|
|
|
|
#ifdef OLD_TRIGGER
|
|
|
|
|
unit * familiar = get_familiar(u);
|
|
|
|
|
if (familiar!=NULL)
|
|
|
|
|
result += skillmod_familiar(r, familiar, sk);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
result -= 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HUNGER_REDUCES_SKILL
|
|
|
|
|
if (fval(u, FL_HUNGER)) result = result/2;
|
|
|
|
|
#endif
|
|
|
|
|
return (char)max(result, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
pure_skill(unit * u, skill_t sk, region * r)
|
|
|
|
|
{
|
|
|
|
|
int result = 0;
|
|
|
|
|
unused(r);
|
|
|
|
|
|
|
|
|
|
if (u->number==0) return 0;
|
|
|
|
|
|
|
|
|
|
result = level(get_skill(u, sk) / u->number);
|
|
|
|
|
|
|
|
|
|
return max(result, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
remove_zero_skills(void)
|
|
|
|
|
{
|
|
|
|
|
region *r;
|
|
|
|
|
unit *u;
|
|
|
|
|
skill_t sk;
|
|
|
|
|
|
|
|
|
|
for(r=regions; r; r=r->next) {
|
|
|
|
|
for(u=r->units; u; u=u->next) {
|
|
|
|
|
for (sk = 0; sk != MAXSKILLS; sk++) {
|
|
|
|
|
if(get_skill(u, sk) < u->number) {
|
|
|
|
|
set_skill(u, sk, 0);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|