#include #include #include #include #include #include "defs.h" #include "flag.h" #include "god.h" #include "io.h" #include "lf.h" #include "map.h" #include "nexus.h" #include "objects.h" #include "spell.h" #include "text.h" extern enum GAMEMODE gamemode; extern int needredraw; extern int statdirty; extern lifeform_t *godlf[]; extern int ngodlfs; extern lifeform_t *player; extern int flagcheck; int flagdb = B_FALSE; altflagval_t *addaltval(flag_t *f, enum FLAG id, int val1, int val2, int val3, char *text) { f->altval = malloc(sizeof(altflagval_t)); f->altval->id = id; f->altval->val[0] = val1; f->altval->val[1] = val2; f->altval->val[2] = val3; if (text) { f->altval->text = strdup(text); } else { f->altval->text = strdup(""); } return f->altval; } void addcondition(flag_t *f, enum FLAGCONDITION fc, int chance) { f->condition = fc; f->chance = chance; } flag_t *addflag(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text) { return addflag_real(fp, id, val1, val2, val3, text, PERMENANT, B_KNOWN, -1); } flag_t *addflagifneeded(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text) { if (!hasflag(fp, id)) { return addflag_real(fp, id, val1, val2, val3, text, PERMENANT, B_KNOWN, -1); } return NULL; } flag_t *addtempflag(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text, int timeleft) { return addflag_real(fp, id, val1, val2, val3, text, timeleft, B_KNOWN, -1); } int getflaghash(enum FLAG fid) { int h; h = (int) fid % NHASHBUCKETS; assert(h >= 0); assert(h < NHASHBUCKETS); return h; } flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text, int lifetime, int known, long obfromid) { lifeform_t *lf; flag_t *f = NULL; //map_t *redolight = NULL; int redrawscreenatend = B_FALSE; int redrawstatatend = B_FALSE; int i; int rv; int hash; enum { NONE, FIRST, FIRST2, MIDDLE, LAST } fml = NONE; //checkflagpile(fp); //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging // identified things mean all new flags are autmaticlaly known. if (hasflag(fp, F_IDENTIFIED)) { known = B_KNOWN; } // debug if (id == F_DOOR) { if (fp->ob && fp->ob->type->id == OT_LEAF) { raise(SIGINT); } } if (fp->owner && (id == F_VAULTISPLAYERSTART)) { dblog("added vaultisplayerstart"); } if (id == F_INTERRUPTED) { if (fp->owner == player) { dblog("player got interrupted"); } } // impossible flags if ((id == F_POISONED) && isimmuneto(fp, DT_POISON, B_FALSE)) return NULL; if ((id == F_VEGETARIAN) && hasflag(fp, F_CARNIVORE)) return NULL; if ((id == F_PARTVEGETARIAN) && hasflag(fp, F_CARNIVORE)) return NULL; if ((id == F_CARNIVORE) && (hasflag(fp, F_VEGETARIAN) || hasflag(fp, F_PARTVEGETARIAN)) ) return NULL; // no dupes allowed if (id == F_LINKRACE) { if (hasflagval(fp, F_LINKRACE, val1, val2, val3, NULL)) { return NULL; } } if (id == F_FILLPOT) { if (hasflagval(fp, F_FILLPOT, val1, NA, NA, NULL)) { return NULL; } } // overrite NA rarity if ((id == F_RARITY) && (val3 == NA)) { val3 = RR_COMMON; } // special case which lets us confer f_canwill. (with conferred flags, we can't // supply val[2]). if (id == F_CANWILL) { if ((val3 == NA) && (val2 > 0)) { val2 = val3; } } // auto-increment order for EXTRADESC flag if ((id == F_EXTRADESC) && (val1 == NA)) { flag_t *retflag[MAXCANDIDATES]; int nretflags; int nextorder = 0,i; getflags(fp, retflag, &nretflags, F_EXTRADESC, F_NONE); for (i = 0; i < nretflags; i++) { int this; this = retflag[i]->val[0]; if (this == NA) this = 0; if (this >= nextorder) nextorder = this+1; } val1 = nextorder; } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging lf = fp->owner; /* if (gamemode == GM_GAMESTARTED) { if (id == F_PRODUCESLIGHT) { if (lf && lf->cell && (lf->cell->map == player->cell->map)) { // someone started producing light redolight = lf->cell->map; } else if (fp->ob) { cell_t *obloc; obloc = getoblocation(fp->ob); if (obloc && (obloc->map == player->cell->map)) { // an object started producing light redolight = obloc->map; } } } else if (id == F_ASLEEP) { // someone fell asleep - ie their glyph will turn upsidedown if (lf && (isplayer(lf) || cansee(player, lf))) { redolight = lf->cell->map; } } } */ //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging // determine bucket hash = getflaghash(id); //////////////////////////////// // ACTUAL FLAG ADDITION IS HERE //////////////////////////////// if (fp->first[hash] == NULL) { fp->first[hash] = (flag_t *)calloc(1, sizeof(flag_t)); f = fp->first[hash]; f->prev = NULL; // also the last fp->last[hash] = f; f->next = NULL; fml = FIRST; } else { flag_t *ff,*insertbefore = NULL,*insertafter = NULL; // we will keep flags sorted. // find correct position in list... ff = fp->first[hash]; while (ff && (ff->id < id)) { ff = ff->next; } if (ff) { insertbefore = ff; insertafter = ff->prev; } if (ff) { // start or middle of list // insert BEFORE this one flag_t *newone = NULL; flag_t *prev; prev = ff->prev; // allocate new flag newone = (flag_t *)calloc(1, sizeof(flag_t)); assert(newone != NULL); assert(newone != ff); // link to previous element if (prev) { flag_t *origpn; //f = (flag_t *)malloc(sizeof(flag_t)); assert(f); //prev->next = f; origpn = prev->next; prev->next = newone; assert(prev->next != origpn); assert(prev->next != ff); //f = prev->next; f = newone; fml = MIDDLE; } else { // first one //fp->first = (flag_t *)calloc(1, sizeof(flag_t)); fp->first[hash] = newone; assert(fp->first[hash] != NULL); //f = fp->first; f = newone; fml = FIRST2; } assert(prev != f); f->prev = prev; // link to next element assert(ff != f); f->next = ff; ff->prev = f; } else { // last one. insert at end of list. f = fp->last[hash]; f->next = (flag_t *)malloc(sizeof(flag_t)); /// <- died here! f->next->prev = f; f = f->next; fp->last[hash] = f; f->next = NULL; fml = LAST; } /* // go to end of list f = fp->last; f->next = malloc(sizeof(flag_t)); f->next->prev = f; f = f->next; */ } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging assert(f->prev != f); assert(f->next != f); // fill in props f->id = id; f->lifetime = lifetime; f->origlifetime = lifetime; f->known = known; f->obfrom = obfromid; f->fromlev = NA; f->skillfrom = NULL; f->altval = NULL; f->condition = FC_NOCONDITION; f->chance = 100; // first blank values for (i = 0; i < 3; i++) { f->val[i] = NA; } for (i = 0; i < 3; i++) { f->obmodignoreval[i] = B_FALSE; } f->val[0] = val1; f->nvals = 1; if (val2 != NA) { f->val[1] = val2; f->nvals++; } if (val3 != NA) { f->val[2] = val3; f->nvals++; } if (text) { f->text = strdup(text); } else { f->text = strdup(""); } f->pile = fp; //checkflagpile(fp); //updatefpindex(fp); //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging // notify if (f->pile->owner) { if ( ((gamemode == GM_CHARGEN) && isplayer(f->pile->owner)) || ((gamemode == GM_GAMESTARTED) && announceflaggain(f->pile->owner, f)) ) { if (gamemode == GM_GAMESTARTED) { if (flagcausesinterrupt(f, GL_GAIN)) { addflag(f->pile, F_INTERRUPTED, B_TRUE, NA, NA, NULL); } // only some flags will interrupt player actions /* switch (f->id) { case F_RUNNING: case F_TRAINING: case F_AUTOCMD: case F_PRODUCESLIGHT: break; case F_ASLEEP: if (f->val[2] == NA) { // ie. sleeping, not resting addflag(f->pile, F_INTERRUPTED, B_TRUE, NA, NA, NULL); } break; default: addflag(f->pile, F_INTERRUPTED, B_TRUE, NA, NA, NULL); break; } */ } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging f->known = B_TRUE; if (f->obfrom) { object_t *ob; ob = findobbyid(f->pile->owner->pack, f->obfrom); if (ob) { flag_t *f2 = NULL; switch (f->lifetime) { case FROMOBEQUIP: f2 = hasflagval(ob->flags, F_EQUIPCONFER, f->id, NA, NA, NULL); break; case FROMOBHOLD: f2 = hasflagval(ob->flags, F_HOLDCONFER, f->id, NA, NA, NULL); break; case FROMOBACTIVATE: f2 = hasflagval(ob->flags, F_ACTIVATECONFER, f->id, NA, NA, NULL); break; } if (f2) { f2->known = B_TRUE; } } } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } // player flags which cause a redraw rv = flagcausesredraw(f->pile->owner, f->id); if (rv < redrawscreenatend) redrawscreenatend = rv; if (flagcausesstatredraw(f->pile->owner, f->id)) redrawstatatend = B_TRUE; //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } else if (f->pile->ob) { if (gamemode == GM_GAMESTARTED) { //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (announceobflaggain(f->pile->ob, f)) { f->known = B_TRUE; } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (flagcausesloscalc(f->id)) { cell_t *where = NULL; // everyone who can see this cell recalcs their los //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging where = getflagpilelocation(f->pile); //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (where) { lifeform_t *l; for (l = where->map->lf ; l ; l = l->next) { //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (haslos(l, where)) { //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging setlosdirty(l); //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging //precalclos(l); if (isplayer(l)) { //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (!redrawscreenatend) redrawscreenatend = B_TRUE; //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } // special effects if (f->pile->owner) { float pct; switch (f->id) { case F_ASLEEP: stopallspellsexcept(f->pile->owner, OT_S_ALARM, OT_NONE); break; case F_NONCORPOREAL: killflagsofid(f->pile->owner->flags, F_BEINGSTONED); break; case F_RAGE: f->val[0] = f->pile->owner->hp; // remember old maxhp f->val[1] = f->pile->owner->maxhp; // remember old maxhp pct = (float)f->pile->owner->hp / (float)f->pile->owner->maxhp; f->pile->owner->maxhp = (float)f->pile->owner->maxhp * 1.5; f->pile->owner->hp = pct * (float)f->pile->owner->maxhp; break; default: break; } } //if ((gamemode == GM_GAMESTARTED) && (redrawscreenatend || redrawstatatend || redolight)) { if ((gamemode == GM_GAMESTARTED) && (redrawscreenatend || redrawstatatend )) { //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging /* if (redolight) { //dblog("CALCINGLIGHT from flag\n"); if (!redrawscreenatend) redrawscreenatend = B_TRUE; calclight(redolight); //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (redolight == player->cell->map) { setlosdirty(player); } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging //precalclos(player); } */ //dblog("DRAWINGSCREEN from flag\n"); if (redrawscreenatend) needredraw = B_TRUE; if (redrawstatatend) statdirty = B_TRUE; //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (redrawscreenatend == B_FORCE) { forceredraw(); } else { drawscreen(); } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } //checkflagpile(fp); //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging //checkflagpile(fp); return f; } flagpile_t *addflagpile(lifeform_t *owner, object_t *ob) { int i; flagpile_t *fp; fp = malloc(sizeof(flagpile_t)); foreach_bucket(i) { fp->first[i] = NULL; fp->last[i] = NULL; //fp->nitems = 0; } fp->owner = owner; fp->ob = ob; return fp; } int canbemadepermenant(enum FLAG id) { switch (id) { case F_BLIND: case F_BREATHWATER: case F_CAFFEINATED: case F_DEAF: case F_DETECTAURAS: case F_DETECTOBS: case F_DETECTMETAL: case F_DISEASEIMMUNE: case F_DRUNK: case F_ENHANCESEARCH: case F_ENHANCESMELL: case F_EXTRAINFO: case F_EXTRALUCK: case F_FLYING: case F_PHOTOMEM: case F_REFLECTION: case F_SEEINDARK: case F_SEEINVIS: case F_STABILITY: case F_STENCH: case F_TREMORSENSE: return B_TRUE; default: break; } return B_FALSE; } void changeflagtext(flag_t *f, char *newtext) { free(f->text); if (newtext) { f->text = strdup(newtext); } else { f->text = strdup(""); } } /* void//checkflagpile_mapobs(map_t *m) { cell_t *c; object_t *o; int nbad = 0,x,y; for (y = 0; y < m->h; y++) { for (x = 0; x < m->w; x++) { c = getcellat(m, x, y); if (c) { for (o = c->obpile->first ; o ; o = o->next) { if (fpisbad(o->flags)) { dblog("bad flagpile found in ob %s at (%d,%d)",o->type->name, c->x, c->y); nbad++; } } } } } if (nbad) { dblog("total bad flagpiles: %d", nbad); assert("found bad flagpiles in checkmapflags!" == 0); } } void//checkflagpile_maplfs(map_t *m) { lifeform_t *l; if (!flagcheck) return; for (l = m->lf ; l ; l = l->next) { //checkflagpile(l->flags); } } void//checkflagpile(flagpile_t *fp) { if (!flagcheck) return; if (fpisbad(fp)) { assert("flagpile is corrupt!" == 0); } } int fpisbad(flagpile_t *fp) { flag_t *f; int i; if (!flagcheck) return B_FALSE; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = f->next) { if (f->next) { if(f->next->prev != f) { dblog("flag after %d is bad! its prev = 0x%x, should be this flag (0x%x)",f->id, f->next->prev, f); return B_TRUE; } } if (fp->ob && fp->owner) { dblog("flag %d has both ob and owner",f->id); return B_TRUE; } } } return B_FALSE; } */ // returns # of flags copied int copyflag(flagpile_t *dst, flagpile_t *src, enum FLAG id) { flag_t *f; int ndone = 0, i; foreach_bucket(i) { for (f = src->first[i] ; f ; f = f->next) { // gone past the requrested id's number - ie. it's not there. //if (f->id > id) break; if (f->id == id) { flag_t *newf; newf = addflag_real(dst, f->id, f->val[0], f->val[1], f->val[2], f->text, f->lifetime, f->known, -1); newf->condition = f->condition; newf->chance = f->chance; newf->origlifetime = f->origlifetime; if (f->altval) { addaltval(newf, f->altval->id, f->altval->val[0], f->altval->val[1], f->altval->val[2], f->altval->text); } ndone++; } } } return ndone; } void copyflags(flagpile_t *dst, flagpile_t *src, int lifetime) { flag_t *f,*newf; int i; foreach_bucket(i) { for (f = src->first[i] ; f ; f = f->next) { // omit certain flags if (lifetime == FROMBRAND) { if ((f->id == F_ONLYFOROBTYPE) || (f->id == F_ONLYFORDAMTYPE) || (f->id == F_ONLYFORWEPSKILL)) { continue; } } newf = addflag_real(dst, f->id, f->val[0], f->val[1], f->val[2], f->text, (lifetime == NA) ? f->lifetime : lifetime, f->known, -1); // newf might not be set, if (for example) we are coyping a duplicate // f_linkrace flag. if (newf) { newf->condition = f->condition; newf->origlifetime = f->origlifetime; newf->chance = f->chance; if (f->altval) { addaltval(newf, f->altval->id, f->altval->val[0], f->altval->val[1], f->altval->val[2], f->altval->text); } } } } } int countflags(flagpile_t *fp) { flag_t *f; int count = 0, i; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = f->next) { count++; } } return count; } int countflagsofid(flagpile_t *fp, enum FLAG fid) { flag_t *f; int count = 0, i; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = f->next) { if (f->id == fid) count++; } } return count; } void dumpflags(flagpile_t *fp) { flag_t *f; dblog("START FLAGDUMP"); int i; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = f->next) { dblog("[b=%d] fid=%d, v0=%d v1=%d v2=%d text=[%s]", i, f->id, f->val[0], f->val[1], f->val[2], f->text); } } dblog("END FLAGDUMP"); } // returns TRUE if knowingly gaining/losing this flag will // interrupt player actions like resting, training or eating. int flagcausesinterrupt(flag_t *f, enum GAINORLOSS gol ) { switch (f->id) { case F_ASLEEP: if (f->val[2] == NA) { // ie. sleeping, not resting return B_TRUE; } break; case F_BEINGSTONED: case F_CONFUSED: case F_CHARMEDBY: case F_FEIGNINGDEATH: case F_FLYING: case F_LEVITATING: case F_NONCORPOREAL: case F_PAIN: case F_PARALYZED: case F_POISONED: case F_RAGE: case F_SPRINTING: case F_STUNNED: return B_TRUE; case F_BLIND: case F_CAFFEINATED: case F_DRUNK: case F_FROZEN: case F_GRABBEDBY: case F_HOTFEET: case F_INJURY: case F_INVISIBLE: if (gol == GL_GAIN) return B_TRUE; break; default: break; } return B_FALSE; } int flagcausesloscalc(enum FLAG fid) { switch (fid) { case F_ASLEEP: case F_BLIND: case F_BLOCKSVIEW: case F_FULLSHIELD: case F_NIGHTVISRANGEMOD: case F_PRONE: case F_PRODUCESLIGHT: case F_SEEINDARK: case F_SHADOWED: case F_AWARENESS: case F_VISRANGE: case F_VISRANGEMOD: return B_TRUE; default: break; } return B_FALSE; } // eitehr returns: // B_FALSE - gaining/losing the flag doesn't neccessitate a redraw // B_TRUE - need to mark the game window as dirty when you see someone gaining/losing this flag // B_FORCE - need to CLEAR the game window to force a redraw. int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { if (!lf) return B_FALSE; if (isplayer(lf)) { // player switch (fid) { case F_ASLEEP: case F_BLIND: case F_DETECTLIFE: case F_DETECTOBS: case F_ENHANCESMELL: case F_FASTMOVE: case F_FEIGNINGDEATH: case F_HIDING: case F_INVISIBLE: case F_PRODUCESLIGHT: case F_PRONE: case F_SECRET: case F_SEEINDARK: case F_SEEINVIS: case F_AWARENESS: case F_SPRINTING: case F_SLOWMOVE: return B_TRUE; case F_GRABBEDBY: case F_ATTACHEDTO: case F_RAGE: return B_FORCE; break; default: break; } } else if (haslos(player, lf->cell)) { switch (fid) { case F_ASLEEP: case F_HIDING: case F_INVISIBLE: case F_PRODUCESLIGHT: case F_PRONE: return B_TRUE; default: break; } // nonplayer } return B_FALSE; } int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid) { if (!lf || !isplayer(lf)) return B_FALSE; switch (fid) { case F_ASLEEP: case F_ARBOOST: case F_ATTRMOD: case F_BLIND: case F_CLIMBING: case F_CONFUSED: case F_DRUNK: case F_EATING: case F_FASTMOVE: case F_FLYING: case F_FULLSHIELD: case F_GRAVBOOSTED: case F_GUNTARGET: case F_HASNEWLEVEL: case F_HIDING: case F_ICESLIDE: case F_INJURY: case F_INVISIBLE: case F_LEVITATING: case F_PARALYZED: case F_POISONED: case F_PRODUCESLIGHT: case F_PRONE: case F_RAGE: case F_SILENCED: case F_SPRINTING: case F_SLOWMOVE: case F_STUNNED: case F_TRAINING: return B_TRUE; default: break; } return B_FALSE; } flag_t *hasflag(flagpile_t *fp, int id) { return hasflag_real(fp, id, NA, NULL, NA); } flag_t *hasflagknown(flagpile_t *fp, int id) { return hasflag_real(fp, id, B_TRUE, NULL, NA); } /* flag_t *flag_bsearch(flagpile_t *fp, int id, int *iterp) { flag_t *f,*searchfrom = NULL; int pos; int iter = 0; int l,r; // binary search for this flag id. l = 0; r = fp->nitems-1; while (!searchfrom) { //if (db) dblog(" iter#%d: l=%d,r=%d", iter, l, r); if (r < l) { // NOT FOUND. //if (db) dblog(" r < l: not found!"); break; } else if (fp->item[l]->id > id) { // NOT FOUND. //if (db) dblog(" leftid %d > wantid %d. not found!",fp->item[l]->id, id); break; } else if (fp->item[r]->id < id) { // NOT FOUND. //if (db) dblog(" rightid %d > wantid %d. not found!",fp->item[r]->id, id); break; } // half way inbetween l and r pos = ((l+r)/2); f = fp->item[pos]; //if (db) dblog(" iter#%d: pos=%d. item here is %d (looking for %d)", iter, pos, f->id, id); if (f->id == id) { // go back to first occurence while (f->prev && (f->prev->id == id)) { f = f->prev; iter++; } searchfrom = f; break; } else if (f->id > id) { // go left r = pos-1; } else { // go right l = pos+1; } iter++; } if (iterp) *iterp += iter; return searchfrom; } */ flag_t *flag_hsearch(flagpile_t *fp, int id, int *iterp) { flag_t *f,*searchfrom = NULL; int hash; int iter = 0; hash = getflaghash(id); for (f = fp->first[hash]; f ; f=f->next) { iter++; if (f->id == id) { searchfrom = f; break; } } if (iterp) *iterp += iter; return searchfrom; } flag_t *hasflag_real(flagpile_t *fp, int id, int wantknown, flag_t *exception, int lifetimeexception) { flag_t *f,*foundflag = NULL, *searchfrom = NULL; lifeform_t *owner; int iter = 0,subiter = 0; int db = B_FALSE; owner = fp->owner; if (flagdb) db = B_TRUE; if (db) dblog("call to hasflag %d in flagpile", id); // find first occurence of this id //searchfrom = flag_bsearch(fp, id, &iter); searchfrom = flag_hsearch(fp, id, &iter); // now find a valid one f = searchfrom; //while (f && (f->id == id)) { for binary search while (f) { int valid = B_FALSE; if (f->id == id) { valid = B_TRUE; if (f == exception) valid = B_FALSE; if ((lifetimeexception != NA) && (f->lifetime == lifetimeexception)) valid = B_FALSE; if ((wantknown != NA) && (f->known != wantknown)) valid = B_FALSE; if (owner && (f->lifetime == FROMJOB) && !getjob(owner)) valid = B_FALSE; //if (owner && (f->lifetime == FROMSKILL) && !getskill(owner, xxx)) valid = B_FALSE; } if (valid) { foundflag = f; break; } else { f = f->next; subiter++; } } if (db) dblog("finished - %s - iter=%d, subiter=%d", foundflag ? "found it" : "NOT FOUND",iter,subiter); return foundflag; /* for (f = fp->first ; f ; f = f->next) { // gone past the requrested id's number - ie. it's not there. if (f->id > id) break; if ((f->id == id) && (f != exception)) { int valid = B_TRUE; if ((wantknown != NA) && (f->known != wantknown)) valid = B_FALSE; if (owner && (f->lifetime == FROMJOB) && !getjob(owner)) { valid = B_FALSE; } if (valid) { return f; } } } */ return NULL; } flag_t *hasflagval(flagpile_t *fp, int id, int val1, int val2, int val3, char *text) { return hasflagval_real(fp, id, val1, val2, val3, text, B_FALSE, NA); // doesn't have to be known } flag_t *hasflagvalknown(flagpile_t *fp, int id, int val1, int val2, int val3, char *text) { return hasflagval_real(fp, id, val1, val2, val3, text, B_TRUE, NA); // must be known } int getcounter(flagpile_t *fp) { flag_t *f; f = hasflag(fp, F_COUNTER); if (f) { return f->val[0]; } return 0; } flag_t *hasflagval_real(flagpile_t *fp, int id, int val1, int val2, int val3, char *text, int wantknown, int lifetimeexception) { flag_t *f,*foundflag = NULL, *searchfrom = NULL; lifeform_t *owner; int iter = 0; int db = B_FALSE; owner = fp->owner; if (flagdb) db = B_TRUE; //if (db) dblog("hasflag %d in flagpile with %d entries", id, fp->nitems); searchfrom = flag_hsearch(fp, id, &iter); // now find a valid one f = searchfrom; while (f) { if (f->id == id) { if ((lifetimeexception != NA) && (f->lifetime == lifetimeexception)) { // invalid } else if (owner && (f->lifetime == FROMJOB) && !getjob(owner)) { // invalid } else if ( ((val1 == NA) || (f->val[0] == val1)) && ((val2 == NA) || (f->val[1] == val2)) && ((val3 == NA) || (f->val[2] == val3)) && ((text == NULL) || strstr(f->text, text))) { if (!wantknown || f->known) { foundflag = f; break; } } } f = f->next; } if (db) dblog("finished - %s - iter=%d", foundflag ? "found it" : "NOT FOUND", iter ); return foundflag; /* for (f = fp->first ; f ; f = f->next) { // gone past the requrested id's number - ie. it's not there. if (f->id > id) break; if (f->id == id) { if (owner && (f->lifetime == FROMJOB) && !getjob(owner)) { // invalid } else { if ( ((val1 == NA) || (f->val[0] == val1)) && ((val2 == NA) || (f->val[1] == val2)) && ((val3 == NA) || (f->val[2] == val3)) && ((text == NULL) || strstr(f->text, text))) { if (!wantknown || f->known) { return f; } } } } } return NULL; */ } flag_t *incflag(flagpile_t *fp, enum FLAG fid, int val1, int val2, int val3) { flag_t *f; f = hasflag(fp, fid); if (!f) { return addflag(fp, fid, val1, val1, val3, NULL); } // make sure there is only one! if (f->next && (f->next->id == fid)) { if (gamemode == GM_GAMESTARTED) msg("WARNING: incflag() found multiple flag instances"); dblog("WARNING: incflag() found multiple flag instances (fid=%d)", (int)fid); } if (val1 != NA) f->val[0] += val1; if (val2 != NA) f->val[1] += val2; if (val3 != NA) f->val[2] += val3; return f; } int istransitoryflag(flag_t *f) { if ((f->lifetime >= FROMEXTERNAL_LOW) && (f->lifetime <= FROMEXTERNAL_HIGH)) { return B_FALSE; } return B_TRUE; } int killflagsofid(flagpile_t *fp, enum FLAG fid) { return real_killflagsofid(fp, fid, B_TRUE); } // returns # flags killed int real_killflagsofid(flagpile_t *fp, enum FLAG fid, int wantannounce) { flag_t *f; //,*nextf; int ndone = 0; /* for (f = fp->first ; f ; f = nextf) { nextf = f->next; // gone past the requrested id's number - ie. it's not there. if (f->id > fid) break; if (f->id == fid) { killflag(f); donesomething = B_TRUE; } } */ f = hasflag(fp, fid); while (f) { real_killflag(f, wantannounce); ndone++; f = hasflag(fp, fid); } return ndone; } // returns # flags removed if we did something int killflagsofval(int count, flagpile_t *fp, enum FLAG fid, int v0, int v1, int v2, int lifetime, long obfrom) { int ndone = 0; int nretflags,i; flag_t *retflag[MAXCANDIDATES],*f; getflags(fp, retflag, &nretflags, fid, F_NONE); for (i = 0; i < nretflags; i++) { int matched = B_TRUE; f = retflag[i]; if (matched && (v0 != NA) && (f->val[0] != v0)) matched = B_FALSE; if (matched && (v1 != NA) && (f->val[1] != v1)) matched = B_FALSE; if (matched && (v2 != NA) && (f->val[2] != v2)) matched = B_FALSE; if (matched && (lifetime != NA) && (f->lifetime != lifetime)) matched = B_FALSE; if (matched && (obfrom != NA) && (f->obfrom != obfrom)) matched = B_FALSE; if (matched) { killflag(f); ndone++; if ((count != ALL) && (ndone >= count)) { break; } } } return ndone; } void killflag(flag_t *f) { real_killflag(f, B_TRUE); } void real_killflag(flag_t *f, int wantannounce) { flag_t *nextone, *lastone; flagpile_t *pile; lifeform_t *lf; enum FLAG fid; //map_t *redolight = NULL; cell_t *redolos = NULL; int redostat = B_FALSE; int redoscreen = B_FALSE; int i,hash,dopleasegod[MAXGODS]; flagpile_t *newflags = NULL; // remember the pile so that we can re-index pile = f->pile; fid = f->id; for (i = 0; i < ngodlfs; i++) { dopleasegod[i] = 0; } lf = f->pile->owner; if (gamemode == GM_GAMESTARTED) { /* if (f->id == F_PRODUCESLIGHT) { if (lf && lf->cell && (lf->cell->map == player->cell->map)) { // someone stopped producing light redolight = lf->cell->map; } else if (f->pile->ob) { cell_t *obloc; obloc = getoblocation(f->pile->ob); if (obloc && (obloc->map == player->cell->map)) { // an object stopped producing light redolight = obloc->map; } } } else if (f->id == F_ASLEEP) { // someone woke up - ie their glyph will turn upsidedown if (lf && (isplayer(lf) || cansee(player, lf))) { redolight = lf->cell->map; } } else */ if (f->id == F_FLEEFROM) { if (lf && lf->cell) { lifeform_t *fleefrom; fleefrom = findlf(lf->cell->map, f->val[0]); // ie. this lf was fleeing from the player, and // got away. if (fleefrom && isplayer(fleefrom) && !cansee(lf, fleefrom)) { for (i = 0; i < ngodlfs; i++) { if (godlf[i] && (godlf[i]->race->id == R_GODMERCY)) dopleasegod[i] += 5; } } } } } //checkflagpile(pile); if (gamemode == GM_GAMESTARTED) { // flags which cause a redraw if (flagcausesredraw(f->pile->owner, f->id)) redoscreen = B_TRUE; if (flagcausesstatredraw(f->pile->owner, f->id)) redostat = B_TRUE; if (flagcausesloscalc(f->id)) { redolos = getflagpilelocation(f->pile); } } //checkflagpile(pile); // notify if (gamemode == GM_GAMESTARTED) { if (lf) { // special effects before announcement if (f->id == F_FLEEFROM) { // once you recover from fleeing from something, // you don't find it scary for a little while. if (!lfhasflagval(lf, F_NOFLEEFROM, f->val[0], NA, NA, NULL)) { if (!newflags) newflags = addflagpile(NULL, NULL); addtempflag(newflags, F_NOFLEEFROM, f->val[0], NA, NA, NULL, 10); } } else if (f->id == F_RAGE) { if (!isdead(lf)) { // restore original maxhp float pct; pct = (float)lf->hp / (float)lf->maxhp; lf->maxhp = f->val[1]; lf->hp = pct * (float)lf->maxhp; if (lf->hp < 1) lf->hp = 1; } } // announce if (wantannounce && announceflagloss(lf, f)) { if (flagcausesinterrupt(f, GL_LOSS) && !hasflag(lf->flags, F_INTERRUPTED)) { if (!newflags) newflags = addflagpile(NULL, NULL); addflag(newflags, F_INTERRUPTED, B_TRUE, NA, NA, NULL); } /* // don't include flags which interrupt will kill! switch (f->id) { case F_RUNNING: case F_TRAINING: case F_AUTOCMD: case F_PRODUCESLIGHT: break; case F_ASLEEP: if (f->val[2] != NA) { // ie. resting addflag(lf->flags, F_INTERRUPTED, B_TRUE, NA, NA, NULL); } break; default: addflag(lf->flags, F_INTERRUPTED, B_TRUE, NA, NA, NULL); break; } */ } // special effects after announcement if (f->id == F_RAGE) { // you are now exhausted setstamina(lf, 0); } } else if (f->pile->ob && !isdeadob(f->pile->ob)) { announceobflagloss(f->pile->ob, f); } // we will revert to our original form at the end of timeeffectslf(). if (lf && (f->id == F_POLYMORPHED)) { if (lfhasflag(lf, F_ORIGRACE)) { lf->polyrevert = B_TRUE; } } } //checkflagpile(pile); // determine bucket hash = getflaghash(f->id); ///////////////////////////////////////////// // now we are actually removing the flag. ///////////////////////////////////////////// // free mem if (f->text) { free(f->text); f->text = NULL; } if (f->altval) { if (f->altval->text) { free(f->altval->text); f->altval->text = NULL; } free(f->altval); f->altval = NULL; } // remove from list nextone = f->next; if (nextone != NULL) { nextone->prev = f->prev; } else { /* last */ f->pile->last[hash] = f->prev; } if (f->prev == NULL) { /* first */ nextone = f->next; f->pile->first[hash] = nextone; free(f); } else { lastone = f->prev; free (lastone->next ); lastone->next = nextone; } ////////////////////////////////////////// // "f" is now gone. don't reference it // anymore! ////////////////////////////////////////// f = NULL; //checkflagpile(pile); // re-index the pile //updatefpindex(pile); //checkflagpile(pile); if (gamemode == GM_GAMESTARTED) { for (i = 0; i < ngodlfs; i++) { if (godlf[i] && dopleasegod[i]) { pleasegodmaybe(godlf[i]->race->id, dopleasegod[i]); } } if (redolos) { // - if this was a lf's flag, they recalc their los. // - if it was an object flag, then everyone who can see // its cell recalcs their los. if (lf) { setlosdirty(lf); //precalclos(f->pile->owner); if (isplayer(lf)) redoscreen = B_TRUE; } else { // everyone who can see this cell recalcs their los lifeform_t *l; for (l = redolos->map->lf ; l ; l = l->next) { if (haslos(l, redolos)) { setlosdirty(l); //precalclos(l); if (isplayer(l)) redoscreen = B_TRUE; } } } } /* if (redolight) { calclight(redolight); setlosdirty(player); //precalclos(player); needredraw = B_TRUE; } */ if (redoscreen) { needredraw = B_TRUE; } if (redostat) { statdirty = B_TRUE; } if (statdirty || needredraw ) { drawscreen(); } } //checkflagpile(pile); // NOW add the new flags. if (newflags) { copyflags(pile, newflags, NA); killflagpile(newflags); } } void killflagpile(flagpile_t *fp) { int i; if (fp->ob && !fp->ob->dying) { assert(0 == "calling killflagpile on non-dying object"); } //checkflagpile(fp); foreach_bucket(i) { while (fp->first[i]) { killflag(fp->first[i]); } } //checkflagpile(fp); free(fp); } int killtransitoryflags(flagpile_t *fp, enum FLAG fid) { flag_t *f,*nextf; int nkilled = 0, i; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = nextf) { nextf = f->next; // gone past the requrested id's number - ie. it's not there. //if (f->id > fid) break; if (istransitoryflag(f) && (f->id == fid)) { killflag(f); nkilled++; } } } return nkilled; } int killtransitoryflagvals(flagpile_t *fp, enum FLAG fid, int val1, int val2, int val3, char *text) { flag_t *f, *nextf; int nkilled = 0, i; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = nextf) { nextf = f->next; // gone past the requrested id's number - ie. it's not there. //if (f->id > fid) break; if (istransitoryflag(f) && (f->id == fid)) { if ( ((val1 == NA) || (f->val[0] == val1)) && ((val2 == NA) || (f->val[1] == val2)) && ((val3 == NA) || (f->val[2] == val3)) && ((text == NULL) || strstr(f->text, text))) { killflag(f); nkilled++; } } } } return nkilled; } void timeeffectsflag(flag_t *f, int howlong) { // special case: if (f->id == F_TRAIL) { return; } // always nauseated in the sewer if ((f->id == F_NAUSEATED) && f->pile->owner && f->pile->owner->cell && (f->pile->owner->cell->habitat->id == H_SEWER)) { return; } if ((f->lifetime != PERMENANT) && (f->lifetime > 0)) { // special case - fast metabolism speeds up poison too. if (f->id == F_POISONED) { int multiplier; sumflags(f->pile, F_FASTMETAB, &multiplier, NULL, NULL); if (multiplier > 0) { howlong *= multiplier; } } else if ((f->id == F_INJURY) && hasflag(f->pile, F_ASLEEP)) { howlong *= 2; } f->lifetime -= howlong; if (f->lifetime <= 0) { killflag(f); return; } else if (f->lifetime == 5) { // warn about certain flags...... if (isplayer(f->pile->owner)) { switch (f->id) { case F_CANWILL: switch (f->val[0]) { case OT_A_JUMP: msg("Your ability to jump is starting to run out...");; break; default: break; } break; case F_DTIMMUNE: msg("Your %s immunity is starting to run out...", getdamname(f->val[0])); break; case F_DTRESIST: msg("Your %s resistance is starting to run out...", getdamname(f->val[0])); break; case F_DTVULN: msg("You feel a little less vulnerable to %s...", getdamname(f->val[0])); break; case F_INVISIBLE: msg("Your body is starting to reappear..."); break; case F_MAGSHIELD: msg("Your magnetic shield is weakening..."); break; case F_POLYMORPHED: msg("You are starting to revert to your original form..."); break; case F_LEVITATING: msg("Your levitation is starting to expire..."); break; case F_FLYING: msg("Your ability to fly is starting to expire..."); break; case F_SPIDERCLIMB: msg("Your skin is becoming less adhesive..."); more(); break; default: // no message break; } } } else if (f->lifetime == 2) { if (isplayer(f->pile->owner)) { switch (f->id) { case F_NONCORPOREAL: warn("You feel your body solidifying..."); break; default: break; } } } else if (f->lifetime == 1) { // warn about certain flags...... if (isplayer(f->pile->owner)) { switch (f->id) { case F_CANWILL: switch (f->val[0]) { case OT_A_JUMP: msg("Your ability to jump is about to expire!"); more(); break; default: break; } break; case F_DTIMMUNE: msg("Your %s immunity is about to expire!", getdamname(f->val[0])); more(); break; case F_DTRESIST: msg("Your %s resistance is about to expire!", getdamname(f->val[0])); more(); break; case F_DTVULN: msg("You feel a little less vulnerable to %s...", getdamname(f->val[0])); break; case F_INVISIBLE: msg("Your invisibility is about to expire!"); more(); break; case F_MAGSHIELD: msg("Your magnetic shield is about to expire!"); more(); break; case F_LEVITATING: msg("Your levitation is about to expire!"); more(); break; case F_SPIDERCLIMB: msg("Your skin adhesion is about to expire!"); more(); break; case F_POLYMORPHED: msg("You are about to revert to your original form!"); more(); break; case F_FLYING: msg("Your ability to fly is about to expire!"); more(); break; default: // no message break; } } } } if (f->id == F_WET) { f->val[1]--; if (f->val[1] <= 0) { f->val[0]--; if (f->val[0] <= W_DRY) { killflag(f); return; } else { // reset timer f->val[1] = TM_WETTIME; // TODO: announce } } } if (f->id == F_BEINGSTONED) { f->val[0]--; if (f->val[0] == 0) { if (stone(f->pile->owner)) { // stoning failed killflag(f); } return; } else { // announce if (isplayer(f->pile->owner)) { if (f->val[0] == 1) { msg("^%cYou have almost completely turned to stone!",getlfcol(f->pile->owner, CC_VBAD)); } else { msg("^%cYou continue turning to stone...",getlfcol(f->pile->owner, CC_VBAD)); } more(); } } } } // these flags don't get replaced when you polymorph int flagretainedduringpoly(enum FLAG fid) { switch (fid) { case F_FLEEFROM: case F_HOSTILE: case F_LYCANTHROPE: case F_ALIGNMENT: case F_GENDER: return B_TRUE; default: break; } return B_FALSE; } int flagtomaxhp(flag_t *f) { int hp; int nsides = HITDIESIDES; if (f->val[2] > 0) { nsides = f->val[2]; } hp = f->val[0] * nsides; if (f->val[1] > 0) { hp += f->val[1]; } return hp; } cell_t *getflagpilelocation(flagpile_t *fp) { cell_t *where = NULL; // everyone who can see this cell recalcs their los if (fp->owner) { where = fp->owner->cell; } else if (fp->ob) { where = getoblocation(fp->ob); } return where; } // populates retflag & nretflags with matching flags int getflags(flagpile_t *fp, flag_t **retflag, int *nretflags, ... ) { va_list flags; enum FLAG wantflag[MAXCANDIDATES]; int nwantflags,i; flag_t *f; va_start(flags, nretflags); nwantflags = 0; wantflag[nwantflags] = va_arg(flags, enum FLAG); while (wantflag[nwantflags] != F_NONE) { nwantflags++; wantflag[nwantflags] = va_arg(flags, enum FLAG); } va_end(flags); assert(nwantflags < MAXCANDIDATES); // now populate retflag[] with matches flags *nretflags = 0; for (i = 0; i < nwantflags; i++) { f = hasflag(fp, wantflag[i]); while (f && (f->id == wantflag[i])) { retflag[(*nretflags)++] = f; f = f->next; } } return *nretflags; } char *getflagtext(flagpile_t *fp, enum FLAG fid) { flag_t *f; f = hasflag(fp, fid); if (f) { return f->text; } return "?unknownflagtext?"; } void getmaxflags(flagpile_t *fp, int id, int *max0, int *max1, int *max2) { int i; flag_t *f; if (max0) *max0 = 0; if (max1) *max1 = 0; if (max2) *max2 = 0; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = f->next) { // gone past the requrested id's number - ie. it's not there. //if (f->id > id) break; if (f->id == id) { if (max0 && (f->val[0] > *max0)) *max0 = f->val[0]; if (max1 && (f->val[1] > *max1)) *max1 = f->val[1]; if (max2 && (f->val[2] > *max2)) *max2 = f->val[2]; } } } } flag_t *getrandomflag(flagpile_t *fp, enum FLAG fid) { flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; getflags(fp, retflag, &nretflags, fid, F_NONE); if (nretflags) { return retflag[rnd(0,nretflags-1)]; } return NULL; } int modcounter(flagpile_t *fp, int amt) { flag_t *f; f = hasflag(fp, F_COUNTER); if (f) { f->val[0] += amt; } else { f = addflag(fp, F_COUNTER, amt, NA, NA, NULL); } return f->val[0]; } flag_t *modflag(flagpile_t *fp, enum FLAG fid, int val1, int val2, int val3, char *text) { flag_t *f; f = hasflag(fp, fid); if (!f) { return addflag(fp, fid, val1, val1, val3, text); } // make sure there is only one! if (f->next && (f->next->id == fid)) { if (gamemode == GM_GAMESTARTED) msg("WARNING: modflag() found multiple flag instances"); dblog("WARNING: modflag() found multiple flag instances (fid=%d)", (int)fid); } if (val1 != NA) f->val[0] = val1; if (val2 != NA) f->val[1] = val2; if (val3 != NA) f->val[2] = val3; if (text) { changeflagtext(f, text); } return f; } // make the objects providing this flag be known. void revealflagob(lifeform_t *lf, flag_t *f) { int willid = B_FALSE; if (!isplayer(lf)) return; switch (f->lifetime) { case FROMOBEQUIP: case FROMOBHOLD: case FROMOBACTIVATE: willid = B_TRUE; break; default: break; } if (willid) { object_t *o; o = findobbyid(lf->pack, f->obfrom); if (o) { makeknown(o->type->id); } } } void sumflags(flagpile_t *fp, enum FLAG id, int *val0, int *val1, int *val2) { flag_t *retflag[MAXCANDIDATES]; int nretflags = 0,i; if (val0) *val0 = 0; if (val1) *val1 = 0; if (val2) *val2 = 0; getflags(fp, retflag, &nretflags, id, F_NONE); for (i = 0; i < nretflags; i++) { if (val0) *val0 = *val0 + retflag[i]->val[0]; if (val1) *val1 = *val1 + retflag[i]->val[1]; if (val2) *val2 = *val2 + retflag[i]->val[2]; } } void timeeffectsflags(flagpile_t *fp) { flag_t *f,*nextf; enum FLAG thisid; int i; foreach_bucket(i) { for (f = fp->first[i] ; f ; f = nextf) { nextf = f->next; thisid = f->id; timeeffectsflag(f, 1); // if this checkflag() crashes, we know there we a bug during processing of // flag 'thisid' //checkflagpile(fp); } } //checkflagpile(fp); } // generate an index /* void updatefpindex(flagpile_t *fp) { return; flag_t *f; fp->nitems = 0; for (f = fp->first ;f ; f = f->next) { if (f->next) { assert(f->next->prev == f); } fp->item[fp->nitems] = f; fp->nitems++; assert(fp->nitems < MAXFLAGS); } } */