nexus/flag.c

587 lines
13 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "defs.h"
#include "flag.h"
#include "io.h"
#include "lf.h"
#include "objects.h"
#include "spell.h"
#include "text.h"
extern enum GAMEMODE gamemode;
extern int needredraw;
extern int statdirty;
extern lifeform_t *player;
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 *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);
}
flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text, int lifetime, int known, long obfromid) {
flag_t *f;
int i;
int doredraw = B_FALSE;
// identified things mean all new flags are autmaticlaly known.
if (hasflag(fp, F_IDENTIFIED)) {
known = B_KNOWN;
}
if ((id == F_POISONED) && isimmuneto(fp, DT_POISON)) {
return NULL;
}
// certain flags stack...
if (flagstacks(id)) {
f = hasflag(fp, id);
if (f) {
// add values!
f->val[0] += val1;
f->val[1] += val2;
f->val[2] += val3;
// TODO: how to handle text??
return f;
}
}
// override values sometimes
/*
if ((id == F_WET) && (val2 == NA)) {
val2 = WETTIME;
}
*/
if (fp->first == NULL) {
fp->first = malloc(sizeof(flag_t));
f = fp->first;
f->prev = NULL;
} else {
// go to end of list
f = fp->last;
f->next = malloc(sizeof(flag_t));
f->next->prev = f;
f = f->next;
}
fp->last = f;
f->next = NULL;
// fill in props
f->id = id;
f->lifetime = lifetime;
f->known = known;
f->obfrom = obfromid;
// first blank values
for (i = 0; i < 3; i++) {
f->val[i] = NA;
}
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;
// notify
if ((gamemode == GM_GAMESTARTED)) {
if (f->pile->owner) {
if (announceflaggain(f->pile->owner, f)) {
addflag(f->pile, F_INTERRUPTED, B_TRUE, NA, NA, NULL); // note: recursive call!
f->known = B_TRUE;
if (f->obfrom) {
object_t *ob;
ob = findobbyid(f->pile->owner->pack, f->obfrom);
if (ob) {
flag_t *f2;
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;
}
}
}
}
// player flags which cause a redraw
doredraw = flagcausesredraw(f->pile->owner, f->id);
} else if (f->pile->ob) {
if (announceobflaggain(f->pile->ob, f)) {
f->known = B_TRUE;
}
}
}
// special effects
if (f->pile->owner) {
switch (f->id) {
case F_ASLEEP:
stopallspells(f->pile->owner);
break;
case F_NONCORPOREAL:
killflagsofid(f->pile->owner->flags, F_BEINGSTONED);
break;
default:
break;
}
}
if ((gamemode == GM_GAMESTARTED) && doredraw) {
statdirty = B_TRUE;
needredraw = B_TRUE;
drawscreen();
}
return f;
}
flagpile_t *addflagpile(lifeform_t *owner, object_t *ob) {
flagpile_t *fp;
fp = malloc(sizeof(flagpile_t));
fp->first = NULL;
fp->last = NULL;
fp->owner = owner;
fp->ob = ob;
return fp;
}
void copyflag(flagpile_t *dst, flagpile_t *src, enum FLAG id) {
flag_t *f;
for (f = src->first ; f ; f = f->next) {
if (f->id == id) {
addflag_real(dst, f->id, f->val[0], f->val[1], f->val[2], f->text,
f->lifetime, f->known, -1);
}
}
}
void copyflags(flagpile_t *dst, flagpile_t *src, int lifetime) {
flag_t *f;
for (f = src->first ; f ; f = f->next) {
addflag_real(dst, f->id, f->val[0], f->val[1], f->val[2], f->text,
(lifetime == NA) ? f->lifetime : lifetime, f->known, -1);
}
}
int flagcausesredraw(lifeform_t *lf, enum FLAG fid) {
if (!lf) return B_FALSE;
if (isplayer(lf)) {
// player
switch (fid) {
case F_BLIND:
case F_DETECTLIFE:
case F_DETECTOBS:
case F_FASTMOVE:
case F_HIDING:
case F_INVISIBLE:
case F_SEEINDARK:
case F_SEEINVIS:
case F_SPRINTING:
case F_SLOWMOVE:
case F_TIRED:
return B_TRUE;
default:
break;
}
} else if (haslos(player, lf->cell)) {
switch (fid) {
case F_INVISIBLE:
return B_TRUE;
default:
break;
}
// nonplayer
}
return B_FALSE;
}
int flagstacks(enum FLAG fid) {
int res = B_FALSE;
switch (fid) {
case F_EVASION:
res = B_TRUE;
break;
default:
res = B_FALSE;
break;
}
return res;
}
flag_t *hasflag(flagpile_t *fp, int id) {
return hasflag_real(fp, id, NA, NULL);
}
flag_t *hasflagknown(flagpile_t *fp, int id) {
return hasflag_real(fp, id, B_TRUE, NULL);
}
flag_t *hasflag_real(flagpile_t *fp, int id, int wantknown, flag_t *exception) {
flag_t *f;
lifeform_t *owner;
owner = fp->owner;
for (f = fp->first ; f ; f = f->next) {
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); // 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); // must be known
}
flag_t *hasflagval_real(flagpile_t *fp, int id, int val1, int val2, int val3, char *text, int wantknown) {
flag_t *f;
lifeform_t *owner;
owner = fp->owner;
for (f = fp->first ; f ; f = f->next) {
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;
}
// returns true if we did something
int killflagsofid(flagpile_t *fp, enum FLAG fid) {
flag_t *f,*nextf;
int donesomething = B_FALSE;
for (f = fp->first ; f ; f = nextf) {
nextf = f->next;
if (f->id == fid) {
killflag(f);
donesomething = B_TRUE;
}
}
return donesomething;
}
void killflag(flag_t *f) {
flag_t *nextone, *lastone;
lifeform_t *lf;
int doredraw = B_FALSE;
lf = f->pile->owner;
// flags which cause a redraw
doredraw = flagcausesredraw(f->pile->owner, f->id);
// notify
if ((gamemode == GM_GAMESTARTED)) {
if (lf) {
// special cases
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)) {
addtempflag(lf->flags, F_NOFLEEFROM, f->val[0], NA, NA, NULL, 10);
}
}
// announce
if (announceflagloss(lf, f)) {
// don't include flags which interrupt will kill!
switch (f->id) {
case F_RESTING:
case F_RUNNING:
case F_AUTOCMD:
break;
default:
addflag(lf->flags, F_INTERRUPTED, B_TRUE, NA, NA, NULL);
break;
}
}
} else if (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;
}
}
// free mem
// remove from list
nextone = f->next;
if (nextone != NULL) {
nextone->prev = f->prev;
} else { /* last */
f->pile->last = f->prev;
}
if (f->prev == NULL) {
/* first */
nextone = f->next;
f->pile->first = nextone;
free(f);
} else {
lastone = f->prev;
free (lastone->next );
lastone->next = nextone;
}
if ((gamemode == GM_GAMESTARTED) && doredraw) {
statdirty = B_TRUE;
needredraw = B_TRUE;
drawscreen();
}
}
void killflagpile(flagpile_t *fp) {
while (fp->first) {
killflag(fp->first);
}
free(fp);
}
void timeeffectsflag(flag_t *f, int howlong) {
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;
}
}
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:
warn("Your ability to jump is starting to run out...");;
break;
default:
break;
}
break;
case F_DTIMMUNE:
warn("Your %s immunity is starting to run out...", getdamname(f->val[0]));
break;
case F_DTRESIST:
warn("Your %s resistance is starting to run out...", getdamname(f->val[0]));
break;
case F_DTVULN:
warn("You feel a little less vulnerable to %s...", getdamname(f->val[0]));
break;
case F_MAGSHIELD:
warn("Your magnetic shield is weakening...");
break;
case F_POLYMORPHED:
warn("You are starting to revert to your original form...");
break;
default: // no message
break;
}
}
} else if (f->lifetime == 3) {
if (isplayer(f->pile->owner)) {
switch (f->id) {
case F_SPRINTING:
if (f->val[0]) {
warn("You will have to stop sprinting soon...");
}
break;
default:
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:
warn("Your ability to jump is about to expire!");;
break;
default:
break;
}
break;
case F_DTIMMUNE:
warn("Your %s immunity is about to expire!", getdamname(f->val[0]));
break;
case F_DTRESIST:
warn("Your %s resistance is about to expire!", getdamname(f->val[0]));
break;
case F_DTVULN:
warn("You feel a little less vulnerable to %s...", getdamname(f->val[0]));
break;
case F_MAGSHIELD:
warn("Your magnetic shield is about to expire!");
break;
case F_POLYMORPHED:
warn("You are about to revert to your original form!");
break;
default: // no message
break;
}
}
// sprinting is special
if (f->id == F_SPRINTING) {
if (f->val[0]) {
enum SKILLLEVEL slev;
lifeform_t *who;
int tiredtime;
who = f->pile->owner;
// now you get slow
// you get tired when you finish sprinting
tiredtime = 15;
// adjust for athletics skill. -2 per level.
slev = getskill(who, SK_ATHLETICS);
if (slev != PR_INEPT) {
tiredtime -= (2*slev);
}
// adjust for constitution
tiredtime = tiredtime - (int) ((float)tiredtime * (getstatmod(who, A_CON) / 100) );
// enforce minimum
if (tiredtime < 1) tiredtime = 1;
f->val[0] = B_FALSE;
f->lifetime = tiredtime;
if (isplayer(who)) {
msg("You are exhausted.");
} else if (cansee(player, who)) {
char lfname[BUFLEN];
getlfname(who, lfname);
msg("%s looks exhausted.",lfname);
}
needredraw = B_TRUE;
statdirty = B_TRUE;
}
}
}
}
if (f->id == F_BEINGSTONED) {
f->val[0]--;
if (f->val[0] == 0) {
if (!stone(f->pile->owner)) {
// lf turned to stone!
return;
} else {
// stoning failed. stop being stoned.
killflag(f);
return;
}
}
}
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] = WETTIME;
// TODO: announce
}
}
}
}
void sumflags(flagpile_t *fp, int id, int *val0, int *val1, int *val2) {
flag_t *f;
if (val0) *val0 = 0;
if (val1) *val1 = 0;
if (val2) *val2 = 0;
for (f = fp->first ; f ; f = f->next) {
if (f->id == id) {
if (val0) *val0 = *val0 + f->val[0];
if (val1) *val1 = *val1 + f->val[1];
if (val2) *val2 = *val2 + f->val[2];
}
}
}
void timeeffectsflags(flagpile_t *fp) {
flag_t *f,*nextf;
for (f = fp->first ; f ; f = nextf) {
nextf = f->next;
timeeffectsflag(f, 1);
}
}