/* 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.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 #include #include "curse.h" /* kernel includes */ #include "building.h" #include "faction.h" #include "magic.h" #include "message.h" #include "objtypes.h" #include "race.h" #include "region.h" #include "ship.h" #include "skill.h" #include "unit.h" #include "version.h" /* util includes */ #include #include #include #include #include #include #include #include #include #include /* libc includes */ #include #include #include #include #include #include /* ------------------------------------------------------------- */ #define MAXENTITYHASH 7919 curse *cursehash[MAXENTITYHASH]; /* -------------------------------------------------------------------------- */ void c_setflag(curse *c, unsigned int flags) { assert(c); c->flags = (c->flags & ~flags) | (flags & (c->type->flags ^ flags)); } /* -------------------------------------------------------------------------- */ void c_clearflag(curse *c, unsigned int flags) { assert(c); c->flags = (c->flags & ~flags) | (c->type->flags & flags); } void chash(curse *c) { curse *old = cursehash[c->no %MAXENTITYHASH]; cursehash[c->no %MAXENTITYHASH] = c; c->nexthash = old; } static void cunhash(curse *c) { curse **show; for (show = &cursehash[c->no % MAXENTITYHASH]; *show; show = &(*show)->nexthash) { if ((*show)->no == c->no) break; } if (*show) { assert(*show == c); *show = (*show)->nexthash; c->nexthash = 0; } } curse * cfindhash(int i) { curse *old; for (old = cursehash[i % MAXENTITYHASH]; old; old = old->nexthash) if (old->no == i) return old; return NULL; } /* ------------------------------------------------------------- */ /* at_curse */ void curse_init(attrib * a) { a->data.v = calloc(1, sizeof(curse)); } int curse_age(attrib * a) { curse * c = (curse*)a->data.v; int result = 0; if (c->type->age) { result = c->type->age(c); } if (result!=0) { c->duration = 0; } else if (c_flags(c) & CURSE_NOAGE) { c->duration = 1; } else if (c->duration!=INT_MAX) { c->duration = max(0, c->duration-1); } return c->duration; } void destroy_curse(curse * c) { cunhash(c); if (c->data.v && c->type && c->type->typ == CURSETYP_UNIT) { free(c->data.v); } free(c); } void curse_done(attrib * a) { destroy_curse((curse *)a->data.v); } /** reads curses that have been removed from the code */ static int read_ccompat(const char * cursename, struct storage * store) { struct compat { const char * name; const char * tokens; } * seek, old_curses[] = { {"disorientationzone", ""}, {"shipdisorientation", ""}, { NULL, NULL } } ; for (seek=old_curses;seek->name;++seek) { if (strcmp(seek->tokens, cursename)==0) { const char * p; for (p=seek->name;p;++p) { switch (*p) { case 'd': store->r_int(store); break; case 's': store->r_str(store); break; case 't': store->r_tok(store); break; case 'i': store->r_id(store); break; case 'f': store->r_flt(store); break; } } return 0; } } return -1; } int curse_read(attrib * a, struct storage * store) { curse * c = (curse*)a->data.v; int ur; char cursename[64]; unsigned int flags; c->no = store->r_int(store); chash(c); store->r_tok_buf(store, cursename, sizeof(cursename)); flags = store->r_int(store); c->duration = store->r_int(store); if (store->version >= CURSEVIGOURISFLOAT_VERSION) { c->vigour = store->r_flt(store); } else { int vigour = store->r_int(store); c->vigour = vigour; } if (store->versionmagician, store, read_int, resolve_unit); } else { ur = read_reference(&c->magician, store, read_unit_reference, resolve_unit); } c->effect.i = store->r_int(store); c->type = ct_find(cursename); if (c->type==NULL) { int result = read_ccompat(cursename, store); if (result!=0) { log_error(("missing curse %s, no compatibility code either.\n", cursename)); } assert(result!=0); return AT_READ_FAIL; } if (store->version < CURSEFLAGS_VERSION) { c_setflag(c, flags); } else { c->flags = flags; } c_clearflag(c, CURSE_ISNEW); if (c->type->read) c->type->read(store, c); else if (c->type->typ==CURSETYP_UNIT) { curse_unit * cc = calloc(1, sizeof(curse_unit)); c->data.v = cc; cc->cursedmen = store->r_int(store); } if (c->type->typ == CURSETYP_REGION) { int rr = read_reference(&c->data.v, store, read_region_reference, RESOLVE_REGION(store->version)); if (ur==0 && rr==0 && !c->data.v) { return AT_READ_FAIL; } } return AT_READ_OK; } void curse_write(const attrib * a, struct storage * store) { unsigned int flags; curse * c = (curse*)a->data.v; const curse_type * ct = c->type; unit * mage = (c->magician && c->magician->number)?c->magician:NULL; /* copied from c_clearflag */ flags = (c->flags & ~CURSE_ISNEW) | (c->type->flags & CURSE_ISNEW); store->w_int(store, c->no); store->w_tok(store, ct->cname); store->w_int(store, flags); store->w_int(store, c->duration); store->w_flt(store, (float)c->vigour); write_unit_reference(mage, store); store->w_int(store, c->effect.i); if (c->type->write) c->type->write(store, c); else if (c->type->typ == CURSETYP_UNIT) { curse_unit * cc = (curse_unit*)c->data.v; store->w_int(store, cc->cursedmen); } if (c->type->typ == CURSETYP_REGION) { write_region_reference((region*)c->data.v, store); } } attrib_type at_curse = { "curse", curse_init, curse_done, curse_age, curse_write, curse_read, ATF_CURSE }; /* ------------------------------------------------------------- */ /* Spruch identifizieren */ #include typedef struct cursetype_list { struct cursetype_list * next; const curse_type * type; } cursetype_list; cursetype_list * cursetypes[256]; void ct_register(const curse_type * ct) { unsigned int hash = tolower(ct->cname[0]); cursetype_list ** ctlp = &cursetypes[hash]; while (*ctlp) { cursetype_list * ctl = *ctlp; if (ctl->type==ct) return; ctlp=&ctl->next; } *ctlp = calloc(1, sizeof(cursetype_list)); (*ctlp)->type = ct; } const curse_type * ct_find(const char *c) { unsigned int hash = tolower(c[0]); cursetype_list * ctl = cursetypes[hash]; while (ctl) { if (strcmp(c, ctl->type->cname)==0) { return ctl->type; } else { size_t k = min(strlen(c), strlen(ctl->type->cname)); if (!strncasecmp(c, ctl->type->cname, k)) { return ctl->type; } } ctl = ctl->next; } return NULL; } /* ------------------------------------------------------------- */ /* get_curse identifiziert eine Verzauberung über die ID und gibt * einen pointer auf die struct zurück. */ boolean cmp_curse(const attrib * a, const void * data) { const curse * c = (const curse*)data; if (a->type->flags & ATF_CURSE) { if (!data || c == (curse*)a->data.v) return true; } return false; } boolean cmp_cursetype(const attrib * a, const void * data) { const curse_type * ct = (const curse_type *)data; if (a->type->flags & ATF_CURSE) { if (!data || ct == ((curse*)a->data.v)->type) return true; } return false; } curse * get_cursex(attrib *ap, const curse_type * ctype, variant data, boolean(*compare)(const curse *, variant)) { attrib * a = a_select(ap, ctype, cmp_cursetype); while (a) { curse * c = (curse*)a->data.v; if (compare(c, data)) return c; a = a_select(a->next, ctype, cmp_cursetype); } return NULL; } curse * get_curse(attrib *ap, const curse_type * ctype) { attrib * a = ap; while (a) { if (a->type->flags & ATF_CURSE) { const attrib_type * at = a->type; while (a && a->type==at) { curse* c = (curse *)a->data.v; if (c->type==ctype) return c; a = a->next; } } else { a = a->nexttype; } } return NULL; } /* ------------------------------------------------------------- */ /* findet einen curse global anhand seiner 'curse-Einheitnummer' */ curse * findcurse(int cid) { return cfindhash(cid); } /* ------------------------------------------------------------- */ void remove_curse(attrib **ap, const curse *c) { attrib *a = a_select(*ap, c, cmp_curse); if (a) a_remove(ap, a); } /* gibt die allgemeine Stärke der Verzauberung zurück. id2 wird wie * oben benutzt. Dies ist nicht die Wirkung, sondern die Kraft und * damit der gegen Antimagie wirkende Widerstand einer Verzauberung */ static double get_cursevigour(const curse *c) { if (c) return c->vigour; return 0; } /* setzt die Stärke der Verzauberung auf i */ static void set_cursevigour(curse *c, double vigour) { assert(c && vigour > 0); c->vigour = vigour; } /* verändert die Stärke der Verzauberung um +i und gibt die neue * Stärke zurück. Sollte die Zauberstärke unter Null sinken, löst er * sich auf. */ double curse_changevigour(attrib **ap, curse *c, double vigour) { vigour += get_cursevigour(c); if (vigour <= 0) { remove_curse(ap, c); vigour = 0; } else { set_cursevigour(c, vigour); } return vigour; } /* ------------------------------------------------------------- */ int curse_geteffect(const curse *c) { if (c==NULL) return 0; if (c_flags(c) & CURSE_ISNEW) return 0; return c->effect.i; } /* ------------------------------------------------------------- */ static void set_curseingmagician(struct unit *magician, struct attrib *ap_target, const curse_type *ct) { curse * c = get_curse(ap_target, ct); if (c) { c->magician = magician; } } /* ------------------------------------------------------------- */ /* gibt bei Personenbeschränkten Verzauberungen die Anzahl der * betroffenen Personen zurück. Ansonsten wird 0 zurückgegeben. */ int get_cursedmen(unit *u, curse *c) { int cursedmen = u->number; if (!c) return 0; /* je nach curse_type andere data struct */ if (c->type->typ == CURSETYP_UNIT) { curse_unit * cc = (curse_unit*)c->data.v; cursedmen = cc->cursedmen; } return min(u->number, cursedmen); } /* setzt die Anzahl der betroffenen Personen auf cursedmen */ static void set_cursedmen(curse *c, int cursedmen) { if (!c) return; /* je nach curse_type andere data struct */ if (c->type->typ == CURSETYP_UNIT) { curse_unit * cc = (curse_unit*)c->data.v; cc->cursedmen = cursedmen; } } /* ------------------------------------------------------------- */ /* Legt eine neue Verzauberung an. Sollte es schon einen Zauber * dieses Typs geben, gibt es den bestehenden zurück. */ static curse * make_curse(unit *mage, attrib **ap, const curse_type *ct, double vigour, int duration, variant effect, int men) { curse *c; attrib * a; a = a_new(&at_curse); a_add(ap, a); c = (curse*)a->data.v; c->type = ct; c->flags = 0; c->vigour = vigour; c->duration = duration; c->effect = effect; c->magician = mage; c->no = newunitid(); chash(c); switch (c->type->typ) { case CURSETYP_NORM: break; case CURSETYP_UNIT: { curse_unit *cc = calloc(1, sizeof(curse_unit)); cc->cursedmen += men; c->data.v = cc; break; } } return c; } /* Mapperfunktion für das Anlegen neuer curse. Automatisch wird zum * passenden Typ verzweigt und die relevanten Variablen weitergegeben. */ curse * create_curse(unit *magician, attrib **ap, const curse_type *ct, double vigour, int duration, variant effect, int men) { curse *c; /* die Kraft eines Spruchs darf nicht 0 sein*/ assert(vigour > 0); c = get_curse(*ap, ct); if (c && (c_flags(c) & CURSE_ONLYONE)){ return NULL; } assert(c==NULL || ct==c->type); /* es gibt schon eins diese Typs */ if (c && ct->mergeflags != NO_MERGE) { if(ct->mergeflags & M_DURATION){ c->duration = max(c->duration, duration); } if(ct->mergeflags & M_SUMDURATION){ c->duration += duration; } if(ct->mergeflags & M_SUMEFFECT){ c->effect.i += effect.i; } if(ct->mergeflags & M_MAXEFFECT){ c->effect.i = max(c->effect.i, effect.i); } if(ct->mergeflags & M_VIGOUR){ c->vigour = max(vigour, c->vigour); } if(ct->mergeflags & M_VIGOUR_ADD){ c->vigour = vigour + c->vigour; } if(ct->mergeflags & M_MEN){ switch (ct->typ) { case CURSETYP_UNIT: { curse_unit * cc = (curse_unit*)c->data.v; cc->cursedmen += men; } } } set_curseingmagician(magician, *ap, ct); } else { c = make_curse(magician, ap, ct, vigour, duration, effect, men); } return c; } /* ------------------------------------------------------------- */ /* hier müssen alle c-typen, die auf Einheiten gezaubert werden können, * berücksichtigt werden */ static void do_transfer_curse(curse *c, unit * u, unit * u2, int n) { int cursedmen = 0; int men = 0; boolean dogive = false; const curse_type *ct = c->type; switch (ct->typ) { case CURSETYP_UNIT: { curse_unit * cc = (curse_unit*)c->data.v; men = cc->cursedmen; break; } default: cursedmen = u->number; } switch ((ct->flags | c->flags) & CURSE_SPREADMASK) { case CURSE_SPREADALWAYS: dogive = true; men = u2->number + n; break; case CURSE_SPREADMODULO: { int i; int u_number = u->number; for (i=0;i0;i++){ if (rng_int()%u_number < cursedmen){ ++men; --cursedmen; dogive = true; } --u_number; } break; } case CURSE_SPREADCHANCE: if (chance(u2->number/(double)(u2->number + n))) { men = u2->number + n; dogive = true; } break; case CURSE_SPREADNEVER: break; } if (dogive == true) { curse * cnew = make_curse(c->magician, &u2->attribs, c->type, c->vigour, c->duration, c->effect, men); cnew->flags = c->flags; if (ct->typ == CURSETYP_UNIT) set_cursedmen(cnew, men); } } void transfer_curse(unit * u, unit * u2, int n) { attrib * a; a = a_find(u->attribs, &at_curse); while (a && a->type==&at_curse) { curse *c = (curse*)a->data.v; do_transfer_curse(c, u, u2, n); a = a->next; } } /* ------------------------------------------------------------- */ boolean curse_active(const curse *c) { if (!c) return false; if (c_flags(c) & CURSE_ISNEW) return false; if (c->vigour <= 0) return false; return true; } boolean is_cursed_internal(attrib *ap, const curse_type *ct) { curse *c = get_curse(ap, ct); if (!c) return false; return true; } boolean is_cursed_with(const attrib *ap, const curse *c) { const attrib *a = ap; while (a) { if ((a->type->flags & ATF_CURSE) && (c == (const curse *)a->data.v)) { return true; } a = a->next; } return false; } /* ------------------------------------------------------------- */ /* cursedata */ /* ------------------------------------------------------------- */ /* * typedef struct curse_type { * const char *cname; (Name der Zauberwirkung, Identifizierung des curse) * int typ; * spread_t spread; * unsigned int mergeflags; * int (*curseinfo)(const struct locale*, const void*, int, curse*, int); * void (*change_vigour)(curse*, double); * int (*read)(struct storage * store, curse * c); * int (*write)(struct storage * store, const curse * c); * } curse_type; */ int resolve_curse(variant id, void * address) { curse * c = NULL; if (id.i!=0) { c = cfindhash(id.i); if (c==NULL) { return -1; } } *(curse**)address = c; return 0; } static const char * oldnames[MAXCURSE] = { /* OBS: when removing curses, remember to update read_ccompat() */ "fogtrap", "antimagiczone", "farvision", "gbdream", "auraboost", "maelstrom", "blessedharvest", "drought", "badlearn", "stormwind", "flyingship", "nodrift", "depression", "magicwalls", "strongwall", "astralblock", "generous", "peacezone", "magicstreet", "magicrunes", "badmagicresistancezone", "goodmagicresistancezone", "slavery", "calmmonster", "oldrace", "fumble", "riotzone", "nocostbuilding", "godcursezone", "speed", "orcish", "magicboost", "insectfur", "strength", "worse", "magicresistance", "itemcloak", "sparkle", "skillmod" }; const char * oldcursename(int id) { return oldnames[id]; } /* ------------------------------------------------------------- */ message * cinfo_simple(const void * obj, typ_t typ, const struct curse *c, int self) { struct message * msg; unused(typ); unused(self); unused(obj); msg = msg_message(mkname("curseinfo", c->type->cname), "id", c->no); if (msg==NULL) { log_error(("There is no curseinfo for %s.\n", c->type->cname)); } return msg; }