From 69982623edd7b01843db2e7e0184a66d547d2a7a Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Wed, 25 May 2011 02:12:00 +0000 Subject: [PATCH] Monster AI improvements: - [+] replace f_target with f_targetlf - [+] wantrange. for melee fighters (default), it's 0. for spells/archers, it's higiher. - [+] if we are further away than bestrange, move towards. - [+] if we are closer than bestrange, move away - [+] AND: move to wantrange before doing spells etc - [+] if we have a ranged attack and wantrange is default, increase it. - [+] in movetowards/away, cells in the wrong dir should NEVER be acceptable! * [+] mflag_push for monsters http://roguelikedeveloper.blogspot.com/2007/10/unangband-monst er-ai-part-three.html - [+] randomly use ranged attacks when adjacent * [+] show trail in askcoords - [+] when throwing, pass range to askcoords - [+] max monsters per room is depth + 1 * [+] why do humans take ages to appear? - [+] fireball spell is slow - [+] why can i throw objects through magic barriers? - [+] add a bonus for mastery of cartography - magic mapping every 50 turns. - [+] crash with monsters moving off edge of world map - [+] make magic barriers be ON stairs, not around them. - [+] handle DIAGONAL entry to maps - [+] OR make this impossible. - [+] druid should get auto nature knoeldge as they levle up (levs 5 10 15). - [+] CRASH flagpile corrupt - [+] happening during AI movement. - [+] make lightning storm only hit enemies - [+] store last known movement dir in TARGETLF and PETOF. - [+] limit monsters per room not working?!?! - [+] make askcoords let you show object piles with . - [+] make askcoords say "A magical barrier (+xx other objects)" - [+] combine getlastknownmovedir into getlastknowncell * [+] BUG: secret doors are showing up as . again! * [+] implement trails (footprints & scent) * [+] aimovetowardslf(lf, wantattack) * [+] make pets use wantdist code? - [+] what does expert/master tracking give you? - [+] ex: your tracks don't last as long - [+] ms: you don't leave tracks. - [+] change f_reducemovement from multiplier to addition - [+] comma still showing up scents and footprints incorrectly!!!!!!!! Initial shallow/deep water: - [+] restrict movement - [+] check for drowning in turneffectslf AND movelf - [+] warn before walking onto dangerous objects. - [+] change how walkdam works for deepwater. - [+] don't use walkdam flags. - [+] don't make splashes of water on top of deepwater. * [+] deep water will drown you if - [+] don't leave footprints in either - [+] create steam on fire damage, but don't CONVERT to it. - [+] f_waterbreathing - [+] can't drown in water - [+] extra damage from cold/elec if in deep/shallow water Initial swimming implementation * [+] sacred/cursed ground - [+] vending machine - [+] don't transfer f_rarity flag when making objects / lifeforms. Initial work on adding a goal to the game! --- ai.c | 710 ++++++++++++++++++++++++++++--------------------- ai.h | 4 +- attack.c | 5 +- defs.h | 76 +++++- doc/glyphs.txt | 4 +- flag.c | 12 +- io.c | 326 +++++++++++++++++------ io.h | 7 +- lf.c | 640 +++++++++++++++++++++++++++++++++----------- lf.h | 9 +- log.txt | 539 +++++++++++++------------------------ map.c | 128 +++++++-- map.h | 7 +- move.c | 390 ++++++++++++++++++++------- move.h | 4 +- nexus.c | 43 ++- nexus.h | 3 + objects.c | 528 +++++++++++++++++++++++++++++++----- objects.h | 9 +- spell.c | 256 ++++++++++-------- spell.h | 2 +- 21 files changed, 2466 insertions(+), 1236 deletions(-) diff --git a/ai.c b/ai.c index 42158e7..f2d9109 100644 --- a/ai.c +++ b/ai.c @@ -34,7 +34,7 @@ void aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit) { } // already targetting this lf? - f = lfhasflagval(lf, F_TARGET, victim->id, NA, NA, NULL); + f = lfhasflagval(lf, F_TARGETLF, victim->id, NA, NA, NULL); if (f) { if ((f->lifetime > 0) && (f->lifetime < timelimit)) { f->lifetime = timelimit; @@ -46,17 +46,17 @@ void aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit) { char lfname[BUFLEN],vicname[BUFLEN]; getlfname(lf, lfname); getlfname(victim, vicname); - dblog(".oO { %s settings new target: %s }", lfname, vicname); + dblog(".oO { %s setting new target: %s }", lfname, vicname); } - killflagsofid(lf->flags, F_TARGET); + killflagsofid(lf->flags, F_TARGETLF); killflagsofid(lf->flags, F_TARGETCELL); if ((timelimit == PERMENANT) || (timelimit == UNLIMITED)) { - addflag(lf->flags, F_TARGET, victim->id, victim->cell->x, victim->cell->y, NULL); + addflag(lf->flags, F_TARGETLF, victim->id, victim->cell->x, victim->cell->y, NULL); } else { - addtempflag(lf->flags, F_TARGET, victim->id , victim->cell->x , victim->cell->y, NULL,timelimit); + addtempflag(lf->flags, F_TARGETLF, victim->id , victim->cell->x , victim->cell->y, NULL,timelimit); } // tell the player if (cansee(player, lf)) { @@ -155,6 +155,109 @@ enum OBTYPE aigetfleespell(lifeform_t *lf) { return OT_NONE; } +// this function assumes that you can't just SEE the target! +cell_t *aigetlastknownpos(lifeform_t *lf, lifeform_t *target, int *lastx, int *lasty, int *lastdir) { + flag_t *f, *tflag, *bestflag; + cell_t *c = NULL; + int besttime = -1; + int i; + + // check scent/footprints first + for (i = 0; i < lf->nlos; i++) { + if (hastrailof(lf->los[i]->obpile, target, NA, &tflag, lf)) { + if (tflag->lifetime > besttime) { + besttime = tflag->lifetime; + bestflag = tflag; + c = lf->los[i]; + } + } + } + if (c) { + if (lastx) *lastx = c->x; + if (lasty) *lasty = c->y; + if (lastdir) { + // can only obtain direction from footprints if your + // tracking skill is high enough. + if (bestflag->val[2] == S_SIGHT) { + if (getskill(lf, SK_TRACKING) >= PR_SKILLED) { + *lastdir = bestflag->val[1]; + } else { + *lastdir = D_NONE; + } + } else { + *lastdir = bestflag->val[1]; + } + } + return c; + } + + + f = ispetortarget(lf, target); + if (f) { + c = getcellat(lf->cell->map, f->val[1], f->val[2]); + if (c) { + if (lastx) *lastx = c->x; + if (lasty) *lasty = c->y; + if (lastdir && strlen(f->text)) { + *lastdir = atoi(f->text); + } + } + return c; + } + + if (lastx) *lastx = NA; + if (lasty) *lasty = NA; + if (lastdir) *lastdir = D_NONE; + return NULL; +} + +object_t *aigetrangedattack(lifeform_t *lf, enum RANGEATTACK *ra) { + int db = B_FALSE; + object_t *o; + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + o = getfirearm(lf); + if (o && getammo(lf)) { + if (db) { + char gunname[BUFLEN]; + getobname(o, gunname, o->amt); + if (db) dblog(".oO { will fire my gun (%s) at target. }",gunname); + } + *ra = RA_GUN; + return o; + } + + // do we have a wand we can zap? + if (lfhasflag(lf, F_HUMANOID) || hasbp(lf, BP_HANDS)) { + if (lfhasflag(lf, F_FLEEFROM)) { + o = aigetwand(lf, F_AICASTTOFLEE); + } else { + o = aigetwand(lf, F_AICASTTOATTACK); + } + if (o) { + + if (db) dblog(".oO { will zap %s instead of moving }", o->type->name); + + *ra = RA_WAND; + return o; + } + } + + // can we attack by throwing something? + if (hasbp(lf, BP_HANDS)) { + o = getbestthrowmissile(lf); + if (o) { + if (db) dblog(".oO { will throw %s at my target }", o->type->name); + *ra = RA_THROW; + return o; + } + } + + if (db) dblog(".oO { found no ranged attack. }"); + *ra = RA_NONE; + return NULL; +} + void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose) { int specialcase = B_FALSE; flag_t *f; @@ -293,7 +396,7 @@ flag_t *aigoto(lifeform_t *lf, cell_t *c, enum MOVEREASON why, void *data, int t dblog(".oO { %s going to targecell: %d, %d }", lfname, c->x, c->y); } - killflagsofid(lf->flags, F_TARGET); + killflagsofid(lf->flags, F_TARGETLF); killflagsofid(lf->flags, F_TARGETCELL); if (why == MR_LF) { @@ -312,6 +415,296 @@ flag_t *aigoto(lifeform_t *lf, cell_t *c, enum MOVEREASON why, void *data, int t return f; } + +// returns B_FALSE if we did something. +// returns B_TRUE if we failed (ie. did nothing) +int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { + int db = B_FALSE; + int ismaster = B_FALSE; + flag_t *targetflag = NULL; + + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + targetflag = lfhasflagval(lf, F_PETOF, target->id, NA, NA, NULL); + if (targetflag) { + ismaster = B_TRUE; + } else { + targetflag = lfhasflagval(lf, F_TARGETLF, target->id, NA, NA, NULL); + } + + if (cansee(lf, target)) { + int dist,wantdistmin,wantdistmax; + int attackok; + enum OBTYPE spell; + object_t *rangedob = NULL; + enum RANGEATTACK rangedattack = RA_NONE; + int movefailed = B_FALSE; + + // pet movement + if (ismaster) { + if (isresting(target)) { + // rest as well. + rest(lf, B_TRUE); + return B_FALSE; + } else if (isadjacent(lf->cell, target->cell)) { + if (db) dblog(".oO { i can see my master adjacent - moving randomly }"); + // move randomly. TODO: just return ?? + dorandommove(lf, B_NOBADMOVES, B_TRUE); + return B_FALSE; + } + // otherwise fall through to below movement code. + } + + // how far away is my target ? + dist = getcelldist(lf->cell, target->cell); + // how far away do i _want_ to be? + getwantdistance(lf,&wantdistmin,&wantdistmax, wantattack); + + // reset F_TARGET lifetime to full. + if (targetflag) { + if (targetflag->id == F_TARGETLF) { + targetflag->lifetime = AI_FOLLOWTIME; + } + if (db) dblog(".oO { i can see my target (at %d,%d). might move towards it. }",target->cell->x,target->cell->y); + // remember their location + targetflag->val[1] = target->cell->x; + targetflag->val[2] = target->cell->y; + } + + // is an attack possible? + attackok = B_FALSE; + if (wantattack) { + if (dist == 1) { + attackok = B_TRUE; + } else if (!lfhasflag(lf, F_HIDING)) { + attackok = B_TRUE; + } + } + + if (attackok) { + objecttype_t *st; + // drink boost potions + if (!useitemwithflag(lf, F_AIBOOSTITEM)) { + return B_FALSE; + } + + // try spells first. + // can we attack with spells (ie. ones which target the victim)? + // if target is adjacent, we will normally just attack rather than try a spell. + spell = aigetattackspell(lf, target); + st = findot(spell); + if ( (spell != OT_NONE) && // found a valid spell/ability to use + ((dist != 1) || // there is distance between us and target + (st->obclass->id == OC_ABILITY) || // OR this works from adjacent + (rnd(1,3) == 1)) // OR random chance of using anyway... + ) { + int spellfailed = B_FALSE; + lifeform_t *spelllf = NULL; + cell_t *spellcell = NULL; + object_t *spellob = NULL; + if (db) { + dblog(".oO { will cast attack spell: %s }", st->name); + } + + // special cases: eg. spells like telekenesis + if (spell == OT_S_TELEKINESIS) { + float maxweight; + object_t *poss[MAXPILEOBS]; + int nposs; + int i; + // find nearest object which can be picked up + + // this is copied out of the telekenesis spell code! + maxweight = getlfweight(lf, B_NOOBS) + + (getlfweight(lf, B_NOOBS) * (getstatmod(lf, A_IQ) / 100)); + + nposs = 0; + for (i = 0; i < lf->nlos; i++) { + object_t *o; + for (o = lf->los[i]->obpile->first ; o ; o = o->next) { + if (!hasflag(o->flags, F_NOPICKUP) && + getobweight(o) <= maxweight) { + poss[nposs] = o; + nposs++; + if (nposs >= MAXPILEOBS) break; + } + } + if (nposs >= MAXPILEOBS) break; + } + if (nposs > 0) { + spellob = poss[rnd(0,nposs-1)]; + } else { + spellfailed = B_TRUE; + } + + // cast spell at the player + spelllf = target; + spellcell = target->cell; + } else { + // pick targets based on spell flags + aigetspelltarget(lf, st, target, &spelllf, &spellcell, &spellob, F_AICASTTOATTACK); + } + + if (spellfailed) { + if (db) dblog(".oO { cast spell/ability failed (1)! }"); + } else { + if (getschool(spell) == SS_ABILITY) { + spellfailed = useability(lf, spell, spelllf, spellcell); + } else { + spellfailed = castspell(lf, spell, spelllf, spellob, spellcell); + } + } + if (spellfailed) { + if (db) dblog(".oO { cast spell/ability tried but failed (2)! reason = %d }", reason); + } else { + // spell succesful + } + return B_FALSE; + } + + // see if we have a ranged attack. if so, adjust wantdist + // to maintain distance. + rangedob = aigetrangedattack(lf, &rangedattack); + if (rangedattack != RA_NONE) { + if (wantdistmin < 2) wantdistmin = 2; + if (wantdistmax < wantdistmin) wantdistmax = wantdistmin; + } + } // end if attackok + + // move towards the target lf. + // try to get to our ideal range from them. + if (db) dblog(".oO { i am at distance %d, want to be at %d-%d }", dist, wantdistmin, wantdistmax); + if (dist > wantdistmax) { + if (db) dblog(".oO { moving towards target. }"); + if (!movetowards(lf, target->cell, DT_ORTH)) { + // success + return B_FALSE; + } else { + if (db) dblog(".oO { move towards failed! - reason = %d }",reason); + movefailed = B_TRUE; + } + } else if (dist < wantdistmin) { + if (db) dblog(".oO { moving away from target. }"); + if (!moveawayfrom(lf, target->cell, DT_ORTH)) { + // success + return B_FALSE; + } else { + if (db) dblog(".oO { move towards failed! - reason = %d }",reason); + movefailed = B_TRUE; + } + } + + // if we got here, we're either at the correct distance or couldn't + // move. + if (attackok) { + enum IQBRACKET iqb; + iqb = getiqname(getattr(lf, A_IQ), NULL); + + // if not adjacent, check for guns, wands, throwing + if ( (rangedattack != RA_NONE) && + haslof(lf->cell, target->cell, LOF_NEED, NULL) && // and we have line of fire to them + (onein(2) || (getcelldist(lf->cell, target->cell) > 1) ) && // and we're not adjacent to target OR random + (iqb > IQ_ANIMAL) ) { // and we are smarter than an animal + if (rangedattack == RA_GUN) { + setguntarget(lf, target); + if (!shoot(lf)) { + // succesful + return B_FALSE; + } else { + if (db) dblog(".oO { shoot gun failed! reason = %d }", reason); + } + } else if (rangedattack == RA_THROW) { + // try to throw it! + if (!throwat(lf, rangedob, target->cell)) { + // succesful + return B_FALSE; + } else { + if (db) dblog(".oO { throw failed! }"); + } + } else if (rangedattack == RA_WAND) { + objecttype_t *st; + cell_t *zapcell = NULL; + st = getlinkspell(rangedob); + if (st) { + enum FLAG purpose; + if (lfhasflag(lf, F_FLEEFROM)) { + purpose = F_AICASTTOFLEE; + } else { + purpose = F_AICASTTOATTACK; + } + aigetspelltarget(lf, st, target, NULL, &zapcell, NULL, purpose); + } else { + // no linkspell - just zap it. + zapcell = NULL; + } + + // zap it + + if (!operate(lf, rangedob, zapcell)) { + // succesful + return B_FALSE; + } else { + if (db) dblog(".oO { zap failed! }"); + } + } + } // end if rangedattackok + } // end if attackok + + // if we could see our traget, but everything we tried failed (spells, moving and ranged attack), + // just rest. + if (movefailed) { + rest(lf, B_TRUE); + return B_FALSE; + } + } else { + // can't see target. + // move towards their last known location instead + cell_t *targcell; + int lastx,lasty; + int lastdir; + + targcell = aigetlastknownpos(lf, target, &lastx, &lasty, &lastdir); + + if (targcell) { + // are we already AT their last known location? + if (targcell == lf->cell) { + if (db) dblog(".oO { cannot see target. i am already at their last known loc %d/%d }",lastx, lasty); + + // go in their last known direction. + if (lastdir == D_NONE) { + if (db) dblog(".oO { i don't know my target's last known movement dir. }"); + } else { + // try going in last known dir + if (db) dblog(".oO { trying my master's last known move dir (%s) }",getdirname(lastdir)); + if (!trymove(lf, lastdir, B_TRUE)) { + if (db) dblog(".oO { ...successfully }"); + // we now don't know their last known dir. + if (targetflag) { + free(targetflag->text); + targetflag->text = strdup(""); + } + return B_FALSE; + } + } + } else { + // not already at their last known cell. try to go there. + if (db) dblog(".oO { i cannot see my target. moving to last known loc %d/%d }",lastx,lasty); + if (!aigoto(lf, targcell, MR_LF, target, PERMENANT)) { + if (db) dblog(".oO { aigoto target's last known loc failed! }"); + } + } + } else { + // we don't know their last known location.... + if (db) dblog(".oO { go to target's last known loc failed! }"); + } + } + + if (db) dblog(".oO { aimovetolf failed. }"); + // FAILED. + return B_TRUE; +} + + void aimovetotargetcell(lifeform_t *lf, flag_t *f) { int x,y; cell_t *c; @@ -402,7 +795,6 @@ void aiturn(lifeform_t *lf) { int icanattack = B_FALSE; object_t *curgun,*bestgun; flag_t *f; - flag_t *mf; //flag_t *nextf; // lifeform_t *fleefrom = NULL; lifeform_t *target,*newtarget; @@ -450,9 +842,9 @@ void aiturn(lifeform_t *lf) { // are we a pet? - mf = lfhasflagval(lf, F_PETOF, NA, NA, NA, NULL); - if (mf && (getallegiance(lf) == AL_FRIENDLY)) { - master = findlf(lf->cell->map, mf->val[0]); + f = lfhasflagval(lf, F_PETOF, NA, NA, NA, NULL); + if (f && (getallegiance(lf) == AL_FRIENDLY)) { + master = findlf(lf->cell->map, f->val[0]); } /////////////////////////////////////////////// @@ -597,7 +989,7 @@ void aiturn(lifeform_t *lf) { /////////////////////////////////////////////// // do we already have a target we are attacking? - f = hasflag(lf->flags, F_TARGET); + f = hasflag(lf->flags, F_TARGETLF); if (f) { int targid; int lastx,lasty; @@ -608,222 +1000,9 @@ void aiturn(lifeform_t *lf) { target = findlf(lf->cell->map, targid); if (target) { if (db) dblog(".oO { my target is lfid %d (%s). }", targid, target->race->name); - if (cansee(lf, target)) { - int goingtomove = B_TRUE; - enum OBTYPE spell; - object_t *gun; - - // reset F_TARGET lifetime to full. - f->lifetime = AI_FOLLOWTIME; - if (db) dblog(".oO { i can see my target (at %d,%d). might move towards it. }",target->cell->x,target->cell->y); - // remember last known loc - f->val[1] = target->cell->x; - f->val[2] = target->cell->y; - - goingtomove = B_TRUE; - - // drink boost potions - if (!useitemwithflag(lf, F_AIBOOSTITEM)) { - return; - } - - if (!lfhasflag(lf, F_HIDING)) { - objecttype_t *st; - // can we attack with spells (ie. ones which target the victim)? - // if target is adjacent, we will normally just attack rather than try a spell. - spell = aigetattackspell(lf, target); - st = findot(spell); - if ( (spell != OT_NONE) && // found a valid spell/ability to use - ((getcelldist(lf->cell, target->cell) != 1) || // there is distance between us and target - (st->obclass->id == OC_ABILITY) || // OR this works from adjacent - (rnd(1,3) == 1)) // OR random chance of using anyway... - ) { - int spellfailed = B_FALSE; - lifeform_t *spelllf = NULL; - cell_t *spellcell = NULL; - object_t *spellob = NULL; - if (db) { - dblog(".oO { will cast attack spell: %s }", st->name); - } - - // special cases: eg. spells like telekenesis - if (spell == OT_S_TELEKINESIS) { - float maxweight; - object_t *poss[MAXPILEOBS]; - int nposs; - int i; - // find nearest object which can be picked up - - // this is copied out of the telekenesis spell code! - maxweight = getlfweight(lf, B_NOOBS) + - (getlfweight(lf, B_NOOBS) * (getstatmod(lf, A_IQ) / 100)); - - nposs = 0; - for (i = 0; i < lf->nlos; i++) { - object_t *o; - for (o = lf->los[i]->obpile->first ; o ; o = o->next) { - if (!hasflag(o->flags, F_NOPICKUP) && - getobweight(o) <= maxweight) { - poss[nposs] = o; - nposs++; - if (nposs >= MAXPILEOBS) break; - } - } - if (nposs >= MAXPILEOBS) break; - } - if (nposs > 0) { - spellob = poss[rnd(0,nposs-1)]; - } else { - spellfailed = B_TRUE; - } - - // cast spell at the player - spelllf = target; - spellcell = target->cell; - } else { - // pick targets based on spell flags - aigetspelltarget(lf, st, target, &spelllf, &spellcell, &spellob, F_AICASTTOATTACK); - } - - if (spellfailed) { - if (db) dblog(".oO { cast spell/ability failed (1)! }"); - } else { - if (getschool(spell) == SS_ABILITY) { - spellfailed = useability(lf, spell, spelllf, spellcell); - } else { - spellfailed = castspell(lf, spell, spelllf, spellob, spellcell); - } - } - if (spellfailed) { - if (db) dblog(".oO { cast spell/ability tried but failed (2)! reason = %d }", reason); - } else { - // spell succesful - return; - } - } - - // if not adjacent, check for guns, wands, throwing - if (goingtomove && // if we are still planning on moving - (getcelldist(lf->cell, target->cell) > 1) && // and we're not adjacent to target - haslof(lf->cell, target->cell, LOF_NEED, NULL) && // and we have line of fire to them - (iqb > IQ_ANIMAL) ) { // and we are smarter than an animal - // can we attack by firing a weapon? - gun = getfirearm(lf); - if (goingtomove && gun && getammo(lf)) { - if (db) { - char gunname[BUFLEN]; - getobname(gun, gunname, gun->amt); - dblog(".oO { will fire my gun (%s) at target. }",gunname); - } - setguntarget(lf, target); - if (!shoot(lf)) { - // succesful - return; - } else { - if (db) dblog(".oO { shoot gun failed! reason = %d }", reason); - } - } else { - if (db) dblog(".oO { not firing out gun }"); - } - - // can we attack by throwing something? - if (goingtomove && hasbp(lf, BP_HANDS)) { - o = getbestthrowmissile(lf); - if (o) { - if (db) dblog(".oO { will throw %s at my target instead of moving }", o->type->name); - // try to throw it! - if (!throwat(lf, o, target->cell)) { - // succesful - return; - } else { - if (db) dblog(".oO { throw failed! }"); - } - } - } - - // do we have a wand we can zap? - if (lfhasflag(lf, F_HUMANOID) || hasbp(lf, BP_HANDS)) { - if (lfhasflag(lf, F_FLEEFROM)) { - o = aigetwand(lf, F_AICASTTOFLEE); - } else { - o = aigetwand(lf, F_AICASTTOATTACK); - } - if (o) { - objecttype_t *st; - cell_t *zapcell = NULL; - - st = getlinkspell(o); - if (st) { - enum FLAG purpose; - if (lfhasflag(lf, F_FLEEFROM)) { - purpose = F_AICASTTOFLEE; - } else { - purpose = F_AICASTTOATTACK; - } - aigetspelltarget(lf, st, target, NULL, &zapcell, NULL, purpose); - } else { - // no linkspell - just zap it. - zapcell = NULL; - } - - // zap it - if (db) dblog(".oO { will zap %s instead of moving }", o->type->name); - - if (!operate(lf, o, zapcell)) { - // succesful - return; - } else { - if (db) dblog(".oO { zap failed! }"); - } - } - } - } - } // end if !hiding - - // do we have a valid melee attack? - if (!icanattack) { - if (db) dblog(".oO { won't move towards target - i have no weapon. }"); - goingtomove = B_FALSE; - } - - // if we are hiding, only attack when adjacent to maximise damage - if (lfhasflag(lf, F_HIDING) && - (getcelldist(lf->cell, target->cell) != 1)) { - goingtomove = B_FALSE; - } - - if (goingtomove) { - if (!movetowards(lf, target->cell, DT_ORTH)) { - // success - return; - } else { - if (db) dblog(".oO { move towards failed! - reason = %d }",reason); - } - } - } else { - cell_t *targcell; - if (db) dblog(".oO { i cannot see my target. moving to last known loc %d/%d }",lastx,lasty); - - // can't see target. - // move towards their last known location instead - targcell = getcellat(lf->cell->map, lastx, lasty); - if (targcell) { - if (!aigoto(lf, targcell, MR_LF, target, PERMENANT)) { - - if (db) dblog(".oO { aigoto target's last known loc failed! }"); - } - } else { - if (db) dblog(".oO { go to target's last known loc failed! }"); - } - - /* - // just try to move in a random direction - if (db) dblog(".oO { will move randomly }"); - // dorandommove will call taketime() if it fails, - // so it's safe to just return - dorandommove(lf, B_NOBADMOVES); + if (!aimovetolf(lf, target, B_TRUE)) { + // success return; - */ } } } @@ -1025,68 +1204,10 @@ void aiturn(lifeform_t *lf) { // master is resting. the normal rest code underneath this section // will never be called. if (master) { - lifeform_t *master; - master = findlf(lf->cell->map, mf->val[0]); - if (master && cansee(lf, master)) { - // can see my master - if (isresting(master)) { - // rest as well. - rest(lf, B_TRUE); - return; - } - // - either move towards them or randomly - if (isadjacent(lf->cell, master->cell)) { - if (db) dblog(".oO { i can see my master - moving randomly }"); - dorandommove(lf, B_NOBADMOVES, B_TRUE); - } else { - // move towards master if not adjacent - if (db) dblog(".oO { i can see my master - moving towards them }"); - if (movetowards(lf, master->cell, DT_ORTH)) { - // failed - if (db) dblog(".oO { failed. moving randomly }"); - dorandommove(lf, B_NOBADMOVES, B_TRUE); - } else { - // success - if (db) dblog(".oO { success. }"); - } - } - return; - } else { - // try to move towards master's last known loc - if (mf->val[1] != NA) { - flag_t *movetoflag = NULL; - cell_t *newcell; - newcell = getcellat(lf->cell->map, mf->val[1], mf->val[2]); - - if (newcell == lf->cell) { - int lastdir; - - lastdir = getownerlastdir(lf); - if (lastdir != D_NONE) { - // try going in last known dir - if (db) dblog(".oO { cannot see my master and am at last known loc. trying last known dir (%s) }",getdirname(lastdir)); - if (!trymove(lf, lastdir, B_TRUE)) { - if (db) dblog(".oO { ...successfully }"); - killflagsofid(lf->flags, F_OWNERLASTDIR); - return; - } - } - } else { - if (db) dblog(".oO { cannot see my master - adding F_TARGETCELL for last known loc }"); - movetoflag = aigoto(lf, newcell, MR_LF, master, PERMENANT); - } - - - if (movetoflag) { - aimovetotargetcell(lf, movetoflag); - } else { - if (db) dblog(".oO { cannot see my master and aigoto last known loc/dir failed. randommove. }"); - dorandommove(lf, B_NOBADMOVES, B_TRUE); - } - } else { - if (db) dblog(".oO { cannot see my master and dont have a last known location. randommove. }"); - dorandommove(lf, B_NOBADMOVES, B_TRUE); - } + //lifeform_t *master; + //master = findlf(lf->cell->map, mf->val[0]); + if (!aimovetolf(lf, master, B_FALSE)) { + // success return; } } @@ -1273,13 +1394,13 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG ok = B_TRUE; } } else { - dblog(".oO { cant cast %s - specialcase conditions not yet coded }", ot ? ot->name : "?unkownspell?"); + if (db) dblog(".oO { cant cast %s - specialcase conditions not yet coded }", ot ? ot->name : "?unkownspell?"); return B_FALSE; } } if (!ok) { - dblog(".oO { cant cast %s - targetting conditions cannot be met }", ot ? ot->name : "?unkownspell?"); + if (db) dblog(".oO { cant cast %s - targetting conditions cannot be met }", ot ? ot->name : "?unkownspell?"); return B_FALSE; } @@ -1409,15 +1530,6 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG } -int getownerlastdir(lifeform_t *lf) { - flag_t *odflag; - odflag = lfhasflag(lf, F_OWNERLASTDIR); - if (odflag) { - return odflag->val[0]; - } - return D_NONE; -} - object_t *hasbetterarmour(lifeform_t *lf, obpile_t *op) { object_t *o; diff --git a/ai.h b/ai.h index 9b23b35..f92ad97 100644 --- a/ai.h +++ b/ai.h @@ -4,16 +4,18 @@ void addignorecell(lifeform_t *lf, cell_t *c); void aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit); enum OBTYPE aigetattackspell(lifeform_t *lf, lifeform_t *victim); enum OBTYPE aigetfleespell(lifeform_t *lf); +cell_t *aigetlastknownpos(lifeform_t *lf, lifeform_t *target, int *lastx, int *lasty, int *lastdir); +object_t *aigetrangedattack(lifeform_t *lf, enum RANGEATTACK *ra); void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose); object_t *aigetwand(lifeform_t *lf, enum FLAG purpose); flag_t *aigoto(lifeform_t *lf, cell_t *c, enum MOVEREASON why, void *data, int timelimit); +int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack); void aimovetotargetcell(lifeform_t *lf, flag_t *f); int aipickup(lifeform_t *lf, object_t *o); int aipickupok(lifeform_t *lf, object_t *o); int aiobok(lifeform_t *lf, object_t *o, lifeform_t *target); int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG purpose); void aiturn(lifeform_t *lf); -int getownerlastdir(lifeform_t *lf); object_t *hasbetterarmour(lifeform_t *lf, obpile_t *op); object_t *hasbetterweapon(lifeform_t *lf, obpile_t *op); int lookforobs(lifeform_t *lf, int covetsonly); diff --git a/attack.c b/attack.c index ab12379..1a9f5b3 100644 --- a/attack.c +++ b/attack.c @@ -475,7 +475,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) getskill(lf, SK_BACKSTAB) && // able to backstab !cansee(victim, lf) && // victim can't see us !lfhasflagval(victim, F_STABBEDBY, lf->id, NA, NA, NULL) && // haven't stabbed them before - !lfhasflagval(victim, F_TARGET, lf->id, NA, NA, NULL) // victim isnt attacking us + !lfhasflagval(victim, F_TARGETLF, lf->id, NA, NA, NULL) // victim isnt attacking us ) { addflag(victim->flags, F_STABBEDBY, lf->id, NA, NA, NULL); dam[0] *= (getskill(lf, SK_BACKSTAB)*2); @@ -1163,6 +1163,9 @@ char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam } } else if (damtype == DT_TOUCH) { return "touch"; + } else if (damtype == DT_WATER) { + // for when water-vulnerable things go into water + return "hurt"; } else if (damtype == DT_UNARMED) { if (onein(2)) { return "punch"; diff --git a/defs.h b/defs.h index c4325a0..60d4e22 100644 --- a/defs.h +++ b/defs.h @@ -50,7 +50,9 @@ enum SKILL { SK_SPELLCASTING, SK_SPOTHIDDEN, SK_STEALTH, + SK_SWIMMING, SK_TECHUSAGE, + SK_TRACKING, SK_TRAPS, SK_TWOWEAPON, // knowledge @@ -83,7 +85,7 @@ enum SKILL { SK_SS_TRANSLOCATION, SK_SS_WILD, }; -#define MAXSKILLS 44 +#define MAXSKILLS 46 // proficiency levels enum SKILLLEVEL { @@ -169,6 +171,14 @@ enum LFCONDITION { C_HEALTHY = 5, }; +enum SENSE { + S_HEARING, + S_SIGHT, + S_SMELL, + S_TASTE, + S_TOUCH, +}; + // AI defs // if target lf is out of view for this many turns, abandon chase @@ -210,7 +220,11 @@ enum LFCONDITION { #define TICK_INTERVAL (20) +#define SCENTTIME (15) +#define FOOTPRINTTIME (20) + // STRINGS +#define BUFLENTINY 10 #define BUFLENSMALL 64 #define BUFLEN 128 #define HUGEBUFLEN 1024 @@ -341,6 +355,8 @@ enum LOFTYPE { #define SP_ULTRASLOW 35 #define SP_SLOWEST 40 +#define SPEEDUNIT 5 + // speed settings (lower is faster) #define SPEED_ATTACK SP_NORMAL #define SPEED_DEAD 50 @@ -734,6 +750,8 @@ enum OBTYPE { OT_WOODENTABLE, OT_WOODENBARREL, OT_WOODENSTOOL, + OT_HOLYCIRCLE, + OT_PENTAGRAM, OT_STAIRSDOWN, OT_STAIRSUP, OT_VENDINGMACHINE, @@ -1196,6 +1214,8 @@ enum OBTYPE { OT_SPLASHWATER, OT_PUDDLEWATER, OT_PUDDLEWATERL, + OT_WATERSHALLOW, + OT_WATERDEEP, OT_ACIDSPLASH, OT_ACIDPUDDLE, OT_ACIDPOOL, @@ -1208,6 +1228,9 @@ enum OBTYPE { OT_MELTEDWAX, OT_SOGGYPAPER, OT_FLESHCHUNK, + // trail objects + OT_FOOTPRINT, + OT_SCENT, // effects OT_FIRELARGE, OT_FIREMED, @@ -1396,6 +1419,15 @@ enum BODYPART { }; #define MAXBODYPARTS (12) +// depth on a human +enum DEPTH { + DP_HEAD = 4, + DP_SHOULDERS = 3, + DP_WAIST = 2, + DP_FEET = 1, + DP_NONE = 0, +}; + // empty types #define WE_WALKABLE 1 #define WE_EMPTY 2 @@ -1437,6 +1469,13 @@ enum POISONTYPE { P_WEAKNESS, }; +enum RANGEATTACK { + RA_NONE = 0, + RA_GUN, + RA_THROW, + RA_WAND, +}; + enum FLAG { F_NONE, // dummy flag // map flags @@ -1452,6 +1491,14 @@ enum FLAG { // text is an object it contains. F_IDWHENUSED, // fully identify an object when worn/weilded/operated/etc F_STARTBLESSED, // v0 = b_blessed or b_cursed + F_REPELBLESSED, // v0 = b_blessed or b_cursed. repels other obejcts + // of this blesstype. + F_TRAIL, // this object denotes the trail left by a lf. + // v0 = raceid of lf who left it + // v1 = direction the lf moved out of this cell + // v2 = enum sense used to see this (ie. s_smell, s_sight) + // (optional) text = lfid of lf who left this. + // should only be used for SCENT, not footprints. // for items in shops F_SHOPITEM, // causes shops to show this item as identified F_VALUE, // how much an item is worth (over its base weight+material) @@ -1473,7 +1520,7 @@ enum FLAG { // v0 = con skillcheck difficulty. F_BLOCKSVIEW, // if v0 = true, cannot see past this // if v0 > 0, reduces your vision by v0. - F_BLOCKSTHROW, // cannot throw past this + F_BLOCKSLOF, // this object interrupts line of fire F_THEREISHERE, // announce "there is xx here!", not "you see xx here" // text[0] is punctuation to use. F_OBDIETEXT, // text when the object dies @@ -1484,6 +1531,9 @@ enum FLAG { F_NOQUALITY, // can't be masterwork / shoddy F_CORPSEOF, // this is a corpse of montype val0. F_DTCONVERT, // damtype val0 converts this to f->text + F_DTCREATEOB, // damtype val0 creates object f->text here + // v1 = radius to burst in + // v2 = dirtype F_NODTCONVERT, // overrides DTCONVERT . F_NOMATCONVERT, // overrides MATCONVERT . F_MATCONVERT, // touching material id val0 converts this to f->text @@ -1537,6 +1587,7 @@ enum FLAG { // v2 = nutrition left when partially eaten F_DRINKABLE, // you can drink this. val1 = nutrition. 100 = a meal // -1 means "nutrition is weight x abs(val1)" + // if v2=DONTKILL, this object does NOT die when drunk. F_OPERABLE, // can operate? F_POURABLE, // can pour? F_PUSHABLE, // can push this object @@ -1556,6 +1607,7 @@ enum FLAG { F_JAMMED, // is this door jammed? v0 is # turns it'll take to open it. F_SECRET, // this object is secret. v0 is sc_search difficulty // to find it. + // NA means 'can never find this' // stairs / teleporters / portals F_CLIMBABLE, // this is a stiarcase, v0 = up/down/in // also use this for portals @@ -1664,6 +1716,7 @@ enum FLAG { F_HASHIDDENNAME, // whether this object class has a hidden name F_IDENTIFIED, // whether this object is fully identified // bad flags + F_DEEPWATER, // v0 = depth.oooooooooooo F_WALKDAM, // val0 = damtype, text = dam per sec F_WALKDAMBP, // v0 = bodypart, v1 = damtype, text = dam per sec // if v2 == FALLTHRU, damage falls through to lf if @@ -1756,6 +1809,8 @@ enum FLAG { // v1 = whether this spell needs line of fire // MONSTER AI FLAGS F_DEMANDSBRIBE, // lf will demand gold from the player. + F_NOSWAP, // other mosnters won't swap with this one. + // cleared at start of turn. F_VARLEVEL, // lf is generated with random level between // 1 and its map dificulty/depth. // if v0 is set, this is the max level. @@ -1767,10 +1822,11 @@ enum FLAG { F_XPMULTIPLY, // multiply xp val for killing this lf by v0 F_NOJOBTEXT, // this lf's name is 'a xxx', not 'a xxx wizard' etc F_LASTDIR, // this is the last direction we moved. - F_OWNERLASTDIR, // for pets, this it the last dir our owner moved + //F_OWNERLASTDIR, // for pets, this it the last dir our owner moved // when we could see them. F_PETOF, // this lf is a pet of lfid v0 // v1/2 = last known location of my owner. + // optional text is last known movement dir. F_SUMMONEDBY, // this lf was summoned by lfid v0. if they die, we // vanish. // v1 is lifetime left. this decrements each turn. @@ -1789,7 +1845,10 @@ enum FLAG { F_FLEEONDAM, // lf will run away instead of counterattacking F_FLEEONHPPCT, // lf will run away if its hp drop to v0% or lower F_NOFLEE, // lf will not run away - F_TARGET, // lf will attack lfid v0. lastknown x/y is v1/v2 + F_ATTACKRANGE, // v0/v1 = min/max celldist to stay away + // from f_targetlf (ie. lf we are attacking) + F_TARGETLF, // lf will attack lfid v0. lastknown x/y is v1/v2 + // optional text is last known movement dir. F_IGNORECELL, // won't accept targetcells of v0=x v1=y // this is cleared at start of turn. F_TARGETCELL, // lf will go towards this place. val0=x,val1=y @@ -1837,6 +1896,8 @@ enum FLAG { // text=spell text // for monsters F_DOESNTMOVE, // this race doesn't move (but can still attack) + F_AQUATIC, // this race can attack normally in water and suffers no + // movement penalties F_HUMANOID, // this race can wear armour / use weapons F_INSECT, // this race is classed as an insect F_UNDEAD, // this race is classed as undead @@ -1870,6 +1931,7 @@ enum FLAG { // this flag does not get announced. F_BLEEDABIL, // will automatically use the ability v0 when // this lf starts bleeding. + F_BREATHWATER, // can breath normally underwater F_CANWILL, // can cast the spell/ability val0 without using MP // v1 is counter untiluse // v2 is what you need to use it @@ -1890,6 +1952,7 @@ enum FLAG { F_DETECTOBS, // autodetect nearby obs in orthog dist v0 F_DRUNK, // v1 is drunknness - 1-5. F_ENHANCESEARCH, // gives v0 bonus on search checks. + F_ENHANCESMELL, // can 'see' scents. F_EXTRADAM, // do 'text' extra damage of damtype v0 when you hit // if v1 is TRUE, also deal extra damage based on // the flagpile's F_BONUS flag. @@ -1912,6 +1975,7 @@ enum FLAG { F_HIDING, // lifeform is hiding. v0 is modifier to stealth checks. F_INVISIBLE, // lifeform is invisible F_INVULNERABLE,// immune to most damage + // this can apply to objects too! F_QUICKBITE, // deals v0 d d1 + d2 damage when you hit a bleeding victim // (bypasses armour) F_GRAVBOOSTED,// cannot walk or throw stuff @@ -2037,6 +2101,8 @@ enum HUNGER { #define B_FALSE (0) #define B_TRUE (-1) +#define B_DONTKILL (-1) + #define FALLTHRU (-8765) //#define B_TEMP (-1) @@ -2164,6 +2230,8 @@ enum ERROR { E_NOOB = 52, E_LEVITATING = 53, E_PRONE = 54, + E_PENTAGRAM = 55, + E_SWIMMING = 56, }; diff --git a/doc/glyphs.txt b/doc/glyphs.txt index 252b8f7..8068048 100644 --- a/doc/glyphs.txt +++ b/doc/glyphs.txt @@ -1,6 +1,8 @@ @ = human : = timid animal -} = gas cloud +{ = water +} = gas +^ = trap / dangerous thing ) = dancing weapon A = avian / bird a = ant diff --git a/flag.c b/flag.c index aa69ded..ecb1ba1 100644 --- a/flag.c +++ b/flag.c @@ -117,7 +117,6 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, f->pile = fp; - // notify if ((gamemode == GM_GAMESTARTED)) { if (f->pile->owner) { @@ -253,6 +252,7 @@ int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { case F_BLIND: case F_DETECTLIFE: case F_DETECTOBS: + case F_ENHANCESMELL: case F_FASTMOVE: case F_HIDING: case F_INVISIBLE: @@ -289,6 +289,7 @@ int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid) { switch (fid) { case F_ASLEEP: + case F_ATTRMOD: case F_BLIND: case F_EATING: case F_FASTMOVE: @@ -415,6 +416,10 @@ void killflag(flag_t *f) { int redostat = B_FALSE; int redoscreen = B_FALSE; + if (f->id == F_SIZE) { + dblog("xxx"); + } + lf = f->pile->owner; if (gamemode == GM_GAMESTARTED) { @@ -534,6 +539,11 @@ void killflagpile(flagpile_t *fp) { } void timeeffectsflag(flag_t *f, int howlong) { + // special case: + if (f->id == F_TRAIL) { + return; + } + if ((f->lifetime != PERMENANT) && (f->lifetime > 0)) { // special case - fast metabolism speeds up poison too. if (f->id == F_POISONED) { diff --git a/io.c b/io.c index 1d47604..456202a 100644 --- a/io.c +++ b/io.c @@ -29,6 +29,8 @@ int statdirty = B_TRUE; int hascolour = B_TRUE; +int noredraw = B_FALSE; + extern int needredraw; extern int numdraws; @@ -291,15 +293,16 @@ void animradial(cell_t *src, int radius, char ch, int colour) { gl.ch = ch; gl.colour = colour; + + // update screen + updateviewfor(src); + drawlevelfor(player); + // hide cursor curs_set(0); for (i = 0; i <= radius; i++) { int drawn = B_FALSE; - // update screen - updateviewfor(src); - drawlevelfor(player); - for (y = src->y - radius ; y <= src->y + radius ; y++) { for (x = src->x - radius ; x <= src->x + radius ; x++) { c = getcellat(src->map, x, y); @@ -331,14 +334,16 @@ void animradialorth(cell_t *src, int radius, char ch,int colour) { gl.ch = ch; gl.colour = colour; + + // update screen + updateviewfor(src); + drawlevelfor(player); + // hide cursor curs_set(0); for (i = 0; i <= radius; i++) { int drawn = B_FALSE; - // update screen - updateviewfor(src); - drawlevelfor(player); for (y = src->y - radius ; y <= src->y + radius ; y++) { for (x = src->x - radius ; x <= src->x + radius ; x++) { @@ -448,7 +453,7 @@ char askchar(char *prompt, char *validchars, char *def, int showchars) { return ch; } -cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange) { +cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange, enum LOFTYPE loftype, int wanttrail) { static int startlf = -1; int finished = B_FALSE; int moved = B_FALSE; @@ -531,6 +536,8 @@ cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange) drawlevelfor(player); if (moved) { + int inlof = B_TRUE, inrange = B_TRUE; + cell_t *trailtarg = NULL; // show what we are over in msg bar strcpy(buf, ""); if (haslos(player, c)) { @@ -739,21 +746,13 @@ cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange) // if scanned, show it. } else { // otherwise, show objects - object_t *o; // find top object name - o = gettopobject(c); - if (o) { - getobname(o, buf, o->amt); - } + gettopobname(c, buf); } } } else { - object_t *o; // find top object name - o = gettopobject(c); - if (o) { - getobname(o, buf, o->amt); - } + gettopobname(c, buf); } } else { // can't see objects or lf there @@ -772,12 +771,51 @@ cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange) capitalise(buf); if (srclf && (maxrange != UNLIMITED)) { if (getcelldist(srclf->cell, c) > maxrange) { - strcat(buf, " [outofrange]"); + inrange = B_FALSE; + strcat(buf, " [too-far]"); } } + + + trailtarg = c; + if (loftype != LOF_DONTNEED) { + cell_t *newcell; + if (!haslof(srclf->cell, c, loftype, &newcell)) { + inlof = B_FALSE; + strcat(buf, " [no-lof]"); + } + if (newcell) { + trailtarg = newcell; + } + } + + wclear(msgwin); mvwprintw(msgwin, 0, 0, "%s",buf); wrefresh(msgwin); + + // show our line of fire + if (wanttrail && inrange) { + cell_t *retcell[MAXRETCELLS]; + int nretcell,i; + glyph_t screenglyph; + // calc path + calcbresnham(srclf->cell->map, srclf->cell->x, srclf->cell->y, trailtarg->x, trailtarg->y, retcell, &nretcell); + for (i = 0; i < nretcell; i++) { + int thisx,thisy; + thisx = retcell[i]->x - viewx; + thisy = retcell[i]->y - viewy; + // get screen cell; + screenglyph.ch = mvwinch(gamewin, thisy, thisx) & A_CHARTEXT; + screenglyph.colour = PAIR_NUMBER(mvwinch(gamewin, thisy, thisx) & A_COLOR); + // draw it inverse. + setcol(gamewin, screenglyph.colour); + wattron(gamewin, A_REVERSE); + mvwprintw(gamewin, thisy, thisx, "%lc", screenglyph.ch); + wattroff(gamewin, A_REVERSE); + unsetcol(gamewin, screenglyph.colour); + } + } } @@ -791,10 +829,23 @@ cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange) clearmsg(); // remember this target - if (c->lf) { + if (c->lf && cansee(player, c->lf)) { startlf = c->lf->id; } return c; + } else if (ch == 'v') { // view + // TODO: show obpile or view lf + if (c->lf && cansee(player, c->lf)) { + showlfstats(c->lf, B_FALSE); + } else if (haslos(player, c)) { + object_t *o; + // show objects + o = doaskobject(c->obpile, "Describe which object", NULL, B_FALSE, B_FALSE, AO_NONE, F_NONE); + while (o) { + describeob(o); + o = doaskobject(c->obpile, "Describe which object", NULL, B_FALSE, B_FALSE, AO_NONE, F_NONE); + } + } } else if (ch == 27) { // ESC - cancel finished = B_TRUE; } else if ((ch == '\'') && (ntargets > 0)) { // cycle targets @@ -984,6 +1035,12 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_BREATHWATER: + if (isplayer(lf)) { + msg("%s can now breath underwater.",lfname); + donesomething = B_TRUE; + } + break; case F_CANCAST: if (isplayer(lf)) { // don't know if monsters get it objecttype_t *ot; @@ -1139,6 +1196,12 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_ENHANCESMELL: + if (isplayer(lf)) { + msg("Your sense of smell is enhanced!"); + } + donesomething = B_TRUE; + break; case F_DRUNK: msg("%s %s tipsy.", lfname, isplayer(lf) ? "feel" : "looks"); donesomething = B_TRUE; @@ -1454,6 +1517,12 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { msg("%s can see again.",lfname); donesomething = B_TRUE; break; + case F_BREATHWATER: + if (isplayer(lf)) { + msg("%s can no longer breath underwater.",lfname); + donesomething = B_TRUE; + } + break; case F_CANCAST: if (isplayer(lf)) { // don't know if monsters lose it objecttype_t *ot; @@ -1516,6 +1585,12 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_ENHANCESMELL: + if (isplayer(lf)) { + msg("Your sense of smell is no longer enhanced."); + } + donesomething = B_TRUE; + break; case F_DRUNK: msg("%s %s more steady now.", lfname, isplayer(lf) ? "feel" : "looks"); donesomething = B_TRUE; @@ -2078,10 +2153,14 @@ object_t *doaskobject(obpile_t *op, char *prompt, int *count, int forpickup, int ammo = getammo(op->owner); } - if (countobs(op) <= 0) { - // no objects in pack + if (countobs(op, B_TRUE) <= 0) { + // no objects here cls(); - mvwprintw(mainwin, 2, 0, "You have no possessions."); + if (op->owner) { + mvwprintw(mainwin, 2, 0, "You have no possessions."); + } else { + mvwprintw(mainwin, 2, 0, "There is nothing here."); + } // wait for key centre(mainwin, getmaxy(mainwin)-1, "[Press any key]"); getch(); @@ -2115,11 +2194,15 @@ object_t *doaskobject(obpile_t *op, char *prompt, int *count, int forpickup, int // can we include this object? ok = B_TRUE; - // check for wanted flags - for (n = 0; n < nwantflags; n++) { - if (!hasflag(o->flags, wantflag[n])) { - ok = B_FALSE; - break; + if (!canseeob(player, o)) ok = B_FALSE; + + if (ok) { + // check for wanted flags + for (n = 0; n < nwantflags; n++) { + if (!hasflag(o->flags, wantflag[n])) { + ok = B_FALSE; + break; + } } } @@ -2270,7 +2353,7 @@ int askobjectmulti(obpile_t *op, char *prompt, long opts) { clearretobs(); - if (countobs(op) <= 0) { + if (countobs(op, B_FALSE) <= 0) { // no objects in pack cls(); mvwprintw(mainwin, 2, 0, "You have no possessions."); @@ -2304,8 +2387,13 @@ int askobjectmulti(obpile_t *op, char *prompt, long opts) { for (o = op->first ; o ; o = o->next) { if (o->type->obclass->id == sortorder[c]) { int ok; + // can we include this object? - ok = obmatchescondition(o, opts); + if (!canseeob(player, o)) { + ok = B_FALSE; + } else { + ok = obmatchescondition(o, opts); + } if (ok) { mylist[i] = o; @@ -2550,7 +2638,12 @@ void updatestatus(void) { wrefresh(statwin); } -void updateviewfor(cell_t *cell) { + +// returns true if view changed +int updateviewfor(cell_t *cell) { + int oldvx,oldvy; + oldvx = viewx; + oldvy = viewy; // calculate viewport if required if ((viewx == -9999) || (viewy == -9999)) { // try to centre player @@ -2570,6 +2663,11 @@ void updateviewfor(cell_t *cell) { while ((cell->y - viewy) <= (SCREENH/3)) { viewy--; } + + if ((viewx != oldvx) || (viewy != oldvy)) { + return B_TRUE; + } + return B_FALSE; } @@ -2968,6 +3066,9 @@ void describeob(object_t *o) { case F_BLIND: mvwprintw(mainwin, y, 0, "%s prevents you from seeing.", buf); y++; break; + case F_BREATHWATER: + mvwprintw(mainwin, y, 0, "%s allows you to breath normally while underwater.", buf); y++; + break; case F_CONTROL: mvwprintw(mainwin, y, 0, "%s lets you control teleportation and polymorphic effects.", buf); y++; break; @@ -2986,6 +3087,9 @@ void describeob(object_t *o) { case F_ENHANCESEARCH: mvwprintw(mainwin, y, 0, "%s enhances your searching ability.", buf); y++; break; + case F_ENHANCESMELL: + mvwprintw(mainwin, y, 0, "%s enhances your sense of smell.", buf); y++; + break; case F_DRUNK: mvwprintw(mainwin, y, 0, "%s makes you tipsy.", buf); y++; break; @@ -3375,7 +3479,7 @@ void docomms(void) { char ch; flag_t *f; - where = askcoords("Talk to who?", TT_MONSTER, player, UNLIMITED); + where = askcoords("Talk to who?", TT_MONSTER, player, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf && cansee(player, where->lf)) { lf = where->lf; } @@ -3416,7 +3520,7 @@ void docomms(void) { char lfname2[BUFLEN]; case 'a': sprintf(buf, "Tell %s to attack who?",lfname); - c = askcoords(buf, TT_MONSTER, player, UNLIMITED); + c = askcoords(buf, TT_MONSTER, player, UNLIMITED, LOF_DONTNEED, B_FALSE); if (c && c->lf && cansee(player, c->lf)) { lf2 = c->lf; @@ -3890,7 +3994,8 @@ void dolook(cell_t *where, int onpurpose) { // (also count objects without this flag) numobs = 0; for (o = where->obpile->first ; o ; o = o->next) { - if (hasflag(o->flags, F_SECRET)) continue; + if (!canseeob(player, o)) continue; + //if (hasflag(o->flags, F_SECRET)) continue; if (hasflag(o->flags, F_COSMETIC)) continue; f = hasflag(o->flags, F_THEREISHERE); @@ -3922,11 +4027,11 @@ void dolook(cell_t *where, int onpurpose) { msg("You %s %s here.", seeverb, buf); } } else if ((numobs > 1) && (numobs <= 3)) { - msg("You %s a few objects here.", seeverb); + msg("You %s a few things here.", seeverb); } else if ((numobs > 3) && (numobs <= 6)) { - msg("You %s some objects here.", seeverb); + msg("You %s some things here.", seeverb); } else if (numobs > 6) { - msg("You %s many objects here.", seeverb); + msg("You %s many things here.", seeverb); } seensomething = B_TRUE; } @@ -4253,29 +4358,49 @@ int dopickup(obpile_t *op) { lifeform_t *fromlf = NULL; char lfname[BUFLEN]; char buf[BUFLEN]; + int needtoask = B_TRUE; + int failed = B_FALSE; if (op->owner) { fromlf = op->owner; getlfname(fromlf, lfname); } - - obcount = countobs(op); + obcount = countobs(op, B_TRUE); // anything here? if (obcount == 0) { + failed = B_TRUE; + } else if (!fromlf && (obcount == 1)) { + object_t *o; + // find first visible object + o = op->first; + while (o && !canseeob(player, o)) { + o = o->next; + } + if (o) { + if (o->amt == 1) { + // just get it + howmany = ALL; + retobs[0] = op->first; + retobscount[0] = op->first->amt; + nretobs = 1; + needtoask = B_FALSE; + } + } else { + failed = B_TRUE; + } + } + + if (failed) { if (fromlf) { msg("%s is not carrying anything!", lfname); } else { msg("There is nothing here to pick up!"); } return B_TRUE; - } else if (!fromlf && (obcount == 1) && (op->first->amt == 1)) { - // just get it - howmany = ALL; - retobs[0] = op->first; - retobscount[0] = op->first->amt; - nretobs = 1; - } else { + } + + if (needtoask) { if (fromlf) { sprintf(buf, "Take what from %s",lfname); } else { @@ -4329,33 +4454,9 @@ void doenter(lifeform_t *lf) { } void doexplain(char *question) { - char buf[BUFLEN]; - cell_t *where; - int done = B_FALSE; - - while (!done) { - //sprintf(buf, "Select glyph to explain (ESC to cancel):"); - where = askcoords(question, TT_NONE, player, UNLIMITED); - if (!where) { - clearmsg(); - break; - } - - // explain it - if (where->lf && cansee(player, where->lf)) { - showlfstats(where->lf, B_FALSE); - } else { - object_t *o; - // object description - o = gettopobject(where); - if (o) { - getobdesc(o, buf); - } - msg("%s", buf); - } - } - msg("Done."); + askcoords(question, TT_NONE, player, UNLIMITED, LOF_DONTNEED, B_FALSE); restoregamewindows(); + msg("Done."); } void dofinaloblist(obpile_t *op) { @@ -4715,7 +4816,7 @@ void doselguntarget(void) { getobname(gun, gunname, 1); sprintf(buf, "Aim %s where?",gunname); - where = askcoords(buf, TT_MONSTER, player, UNLIMITED); + where = askcoords(buf, TT_MONSTER, player, UNLIMITED, LOF_NEED, B_TRUE); if (where) { if (where->lf && haslof(player->cell, where, LOF_NEED, NULL)) { setguntarget(player, where->lf); @@ -4787,7 +4888,7 @@ void dothrow(obpile_t *op) { // ask where to throw it sprintf(buf2, "Throw %s where?",buf); - where = askcoords(buf2, TT_MONSTER, player, UNLIMITED); + where = askcoords(buf2, TT_MONSTER, player, maxdist, LOF_NEED, B_TRUE); if (where) { cell_t *newwhere = NULL; @@ -4942,6 +5043,10 @@ void drawlevelfor(lifeform_t *lf) { int db = B_FALSE; int w,h; + if (noredraw) { + return; + } + map = lf->cell->map; needredraw = B_FALSE; @@ -4960,8 +5065,7 @@ void drawlevelfor(lifeform_t *lf) { } for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { - glyph_t glyph,screenglyph; - int needdraw = B_FALSE; + glyph_t glyph; cell = getcellat(map, x + viewx, y + viewy); if (cell) { @@ -4972,23 +5076,16 @@ void drawlevelfor(lifeform_t *lf) { } // only draw if screen char/colour is different - screenglyph.ch = mvwinch(gamewin, y, x) & A_CHARTEXT; - if (screenglyph.ch != glyph.ch) { - needdraw = B_TRUE; - } else { - screenglyph.colour = PAIR_NUMBER(mvwinch(gamewin, y, x) & A_COLOR); - if (screenglyph.colour != glyph.colour) { - needdraw = B_TRUE; - } - } - if (needdraw) { + if (!screenglyphmatches(x, y, &glyph)) { drawglyph(&glyph, x, y); + /* if (db) { dblog(" drawing char '%lc'/%d at %d,%d (screenglyph was '%lc'/%d)\n\n", glyph.ch, glyph.ch, x,y, screenglyph.ch, screenglyph.ch); } + */ ndrawn++; } } @@ -5574,7 +5671,7 @@ void handleinput(void) { // something here? if (hasgettableobs) { stoprunning(player); - } else if (!canmove(player, dir, NULL)) { // can't move anymore? + } else if (!moveclear(player, dir, NULL)) { // can't move anymore? stoprunning(player); } else { if (trymove(player, dir, B_TRUE)) { @@ -6104,6 +6201,12 @@ void drawstatus(void) { unsetcol(statwin, C_RED); } + if (isswimming(player)) { + setcol(statwin, C_BOLDBLUE); + wprintw(statwin, " Swimming"); + unsetcol(statwin, C_BOLDBLUE); + } + // paralysed somehow? if (isresting(player)) { setcol(statwin, C_CYAN); @@ -6448,6 +6551,17 @@ void redraw(void) { wrefresh(gamewin); } +void redrawpause(void) { + noredraw = B_TRUE; +} + +void redrawresume(void) { + noredraw = B_FALSE; + if (needredraw) { + drawlevelfor(player); + } +} + void restoregamewindows(void) { needredraw = B_TRUE; statdirty = B_TRUE; @@ -6456,6 +6570,28 @@ void restoregamewindows(void) { drawscreen(); } +int screenglyphmatches(int x, int y, glyph_t *g) { + int attribs; + glyph_t screenglyph; + + screenglyph.ch = mvwinch(gamewin, y, x) & A_CHARTEXT; + if (screenglyph.ch != g->ch) { + return B_FALSE; + } + + screenglyph.colour = PAIR_NUMBER(mvwinch(gamewin, y, x) & A_COLOR); + if (screenglyph.colour != g->colour) { + return B_FALSE; + } + + // reverse video highlight from askcoords? + attribs = mvwinch(gamewin, y, x) & A_ATTRIBUTES; + if (attribs & A_REVERSE) { + return B_FALSE; + } + return B_TRUE; +} + void setcol(WINDOW *win, enum COLOUR col) { if (needsbold(col)) { wattron(win, A_BOLD); @@ -7687,6 +7823,10 @@ void showlfstats(lifeform_t *lf, int showall) { } // flags which aren't really intrinsics + if (lfhasflag(lf, F_AQUATIC)) { + mvwprintw(mainwin, y, 0, "%s %s aquatic, and move normally through water.", you(lf), is(lf)); + y++; + } if (lfhasflag(lf, F_VEGETARIAN)) { mvwprintw(mainwin, y, 0, "%s %s a vegetarian.", you(lf), is(lf)); y++; @@ -7746,6 +7886,11 @@ void showlfstats(lifeform_t *lf, int showall) { mvwprintw(mainwin, y, 0, buf); y++; } + f = lfhasknownflag(lf, F_BREATHWATER); + if (f) { + mvwprintw(mainwin, y, 0, "%s can breath normally while underwater.", you(lf)); + y++; + } f = lfhasknownflag(lf, F_CONTROL); if (f) { mvwprintw(mainwin, y, 0, "%s can control teleportation and polymorphic effects.", you(lf)); @@ -7782,6 +7927,11 @@ void showlfstats(lifeform_t *lf, int showall) { mvwprintw(mainwin, y, 0, "%s searching ability is enhanced.", you(lf), isplayer(lf) ? "Your" : "Its"); y++; } + f = lfhasknownflag(lf, F_ENHANCESMELL); + if (f) { + mvwprintw(mainwin, y, 0, "%s %s an enhanced sense of smell.", you(lf), isplayer(lf) ? "have" : "has"); + y++; + } f = lfhasknownflag(lf, F_DRUNK); if (f) { @@ -8058,7 +8208,7 @@ void showlfstats(lifeform_t *lf, int showall) { y = 2; if (lfhasflag(lf, F_NOPACK)) { mvwprintw(mainwin, y, 0, "It cannot carry anything."); - } else if (countobs(lf->pack)) { + } else if (countobs(lf->pack, B_FALSE)) { char invtitle[BUFLEN]; float packweight,maxweight,pct; packweight = getobpileweight(lf->pack); diff --git a/io.h b/io.h index 8f3f952..223764d 100644 --- a/io.h +++ b/io.h @@ -20,7 +20,7 @@ object_t *askobjectwithflag(obpile_t *op, char *title, int *count, long opts, en object_t *doaskobject(obpile_t *op, char *title, int *count, int forpickup, int showpoints, long opts, ...); int askobjectmulti(obpile_t *op, char *prompt, long opts); char askchar(char *prompt, char *validchars, char *def, int showchars); -cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange); +cell_t *askcoords(char *prompt, int targettype, lifeform_t *srclf, int maxrange, enum LOFTYPE loftype, int wanttrail); char *askstring(char *prompt, char punc, char *retbuf, int retbuflen, char *def); void centre(WINDOW *win, int y, char *format, ... ); int chartodir(char ch); @@ -95,7 +95,10 @@ int needsbold(enum COLOUR col); void nothinghappens(void); void dblog(char *format, ... ); void redraw(void); +void redrawpause(void); +void redrawresume(void); void restoregamewindows(void); +int screenglyphmatches(int x, int y, glyph_t *g); void setcol(WINDOW *win, enum COLOUR col); void unsetcol(WINDOW *win, enum COLOUR col); void setobcolour(WINDOW *win, object_t *o, int set); @@ -103,4 +106,4 @@ void showlfarmour(lifeform_t *lf); void showlfstats(lifeform_t *lf, int showall); void tombstone(lifeform_t *lf); void updatestatus(void); -void updateviewfor(cell_t *cell); +int updateviewfor(cell_t *cell); diff --git a/lf.c b/lf.c index e911441..c862f2d 100644 --- a/lf.c +++ b/lf.c @@ -45,6 +45,8 @@ extern int needredraw; extern int loading; +extern int obdb; + extern enum ERROR reason; // for xplist @@ -143,6 +145,10 @@ void bleed(lifeform_t *lf) { return; } + if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { + return; + } + f = lfhasflag(lf, F_BLOODOB); if (f) { if (f->text) { @@ -682,7 +688,7 @@ int canpickup(lifeform_t *lf, object_t *o, int amt) { } // space in pack? - if (countobs(lf->pack) >= MAXPILEOBS) { + if (countobs(lf->pack, B_FALSE) >= MAXPILEOBS) { reason = E_NOSPACE; return B_FALSE; } @@ -703,7 +709,7 @@ int canpolymorphto(enum RACE rid) { if (!appearsrandomly(rid)) return B_FALSE; - if (hasflag(r->flags, F_UNDEAD)) return B_FALSE; + if (hasflag(r->flags, F_UNDEAD) || (r->raceclass->id == RC_UNDEAD)) return B_FALSE; return B_TRUE; } @@ -1138,6 +1144,62 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar return rv; } +// returns TRUE if something happened. +int checkfordrowning(lifeform_t *lf, object_t *o) { + int depth,i; + int didsomething = B_FALSE; + flag_t *f; + + depth = getobdepth(o, lf); + // will you drown? + if (depth >= DP_HEAD) { + if (!getskill(lf, SK_SWIMMING) && !lfhasflag(lf, F_BREATHWATER)) { + // you drown. + if (isplayer(lf)) { + msg("You drown."); + didsomething = B_TRUE; + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s drowns.",lfname); + didsomething = B_TRUE; + } + addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); + lf->hp = 0; + setlastdam(lf, "drowning"); + } + } + + // apply damage to armour now. + for (i = 0; i <= depth; i++) { + object_t *armour = NULL; + if (i == DP_FEET) { + armour = getouterequippedob(lf, BP_FEET); if (armour) takedamage(armour, 4, DT_WATER); + } else if (i == DP_WAIST) { + armour = getouterequippedob(lf, BP_LEGS); if (armour) takedamage(armour, 4, DT_WATER); + armour = getouterequippedob(lf, BP_WAIST); if (armour) takedamage(armour, 4, DT_WATER); + } else if (i == DP_SHOULDERS) { + armour = getouterequippedob(lf, BP_BODY); if (armour) takedamage(armour, 4, DT_WATER); + armour = getouterequippedob(lf, BP_SHOULDERS); if (armour) takedamage(armour, 4, DT_WATER); + } else if (i == DP_HEAD) { + armour = getouterequippedob(lf, BP_HEAD); if (armour) takedamage(armour, 4, DT_WATER); + } + } + + f = isvulnto(lf->flags, DT_WATER); + if (f) { + int dam; + if (strlen(f->text)) { + dam = roll(f->text); + } else { + dam = roll("1d6"); + } + applywalkdam(lf, dam, DT_WATER, o); + } + + return didsomething; +} + /* void checkxp(enum RACE rid) { int i,xp; @@ -1498,7 +1560,7 @@ void die(lifeform_t *lf) { fragments(lf->cell, "chunk of ice", 2, UNLIMITED); } else { if (lfhasflag(lf, F_NOCORPSE)) { - if (lfhasflag(lf, F_UNDEAD) && cansee(player, lf)) { + if (isundead(lf) && cansee(player, lf)) { getlfname(lf, buf); msg("%s crumbles to dust.", buf); } @@ -2101,6 +2163,50 @@ void enhanceskills(lifeform_t *lf) { more(); } + // give job-based level rewards + f = levelabilityready(lf); + while (f) { + if (f->id == F_LEVABIL) { + addflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text); + } else if (f->id == F_LEVFLAG) { + addflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text); + } else if (f->id == F_LEVSKILL) { + giveskill(lf, f->val[1]); + } else if (f->id == F_LEVSPELL) { + addflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL); + } else if (f->id == F_LEVSPELLSCHOOL) { // select a spell from school + if (isplayer(lf)) { + makespellchoicelist(&prompt, player, "Learn which new spell:","Describe which spell:", f->val[1], B_TRUE, B_FALSE); + if (prompt.nchoices > 0) { + objecttype_t *ot; + getchoicestr(&prompt, B_TRUE, B_TRUE); + ot = prompt.result; + if (ot) { + addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); + } + } else { + msg("There are no new spells for you to learn at this time."); + } + } else { + makespellchoicelist(&prompt, player, "xx","xx:", f->val[1], B_FALSE, B_FALSE); + if (prompt.nchoices > 0) { + objecttype_t *ot; + // pick one randomly + ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices)].data; + if (ot) { + addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); + } + } + } + } + f->lifetime = LEVABILITYDONE; // mark as done. + + // get next one + f = levelabilityready(lf); + } + // now refresh them all for next level. + refreshlevelabilities(lf); + // increase str/int etc if we can f = lfhasflag(lf, F_STATGAINREADY); while (f && (f->val[2] > 0)) { @@ -2133,6 +2239,7 @@ void enhanceskills(lifeform_t *lf) { f = lfhasflag(lf, F_STATGAINREADY); } + // now ask about learning/enhancing skills if (isplayer(lf)) { char eorl = 'e'; int skillstoenhance = 0; @@ -2241,10 +2348,10 @@ void enhanceskills(lifeform_t *lf) { } // whiel skillstolearn || skillstoenhance statdirty = B_TRUE; } else { + // monsters will just enhance a random skill, they never learn new ones. enum SKILL poss[MAXSKILLS]; int nposs = 0; int sel; - // monsters will just enhance a random skill, they never learn new ones. for (f = lf->flags->first ; f ; f = f->next) { if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) { poss[nposs] = f->val[0]; @@ -2257,51 +2364,6 @@ void enhanceskills(lifeform_t *lf) { lf->skillpoints--; } // end if isplayer - // now get job-based level rewards - f = levelabilityready(lf); - while (f) { - if (f->id == F_LEVABIL) { - addflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text); - } else if (f->id == F_LEVFLAG) { - addflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text); - } else if (f->id == F_LEVSKILL) { - giveskill(lf, f->val[1]); - } else if (f->id == F_LEVSPELL) { - addflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL); - } else if (f->id == F_LEVSPELLSCHOOL) { // select a spell from school - if (isplayer(lf)) { - makespellchoicelist(&prompt, player, "Learn which new spell:","Describe which spell:", f->val[1], B_TRUE, B_FALSE); - if (prompt.nchoices > 0) { - objecttype_t *ot; - getchoicestr(&prompt, B_TRUE, B_TRUE); - ot = prompt.result; - if (ot) { - addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); - } - } else { - msg("There are no new spells for you to learn at this time."); - } - } else { - makespellchoicelist(&prompt, player, "xx","xx:", f->val[1], B_FALSE, B_FALSE); - if (prompt.nchoices > 0) { - objecttype_t *ot; - // pick one randomly - ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices)].data; - if (ot) { - addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); - } - } - } - } - f->lifetime = LEVABILITYDONE; // mark as done. - - // get next one - f = levelabilityready(lf); - } - - // now refresh them all for next level. - refreshlevelabilities(lf); - killflagsofid(lf->flags, F_HASNEWLEVEL); // ready for another level? @@ -3017,6 +3079,7 @@ int getarmourrating(lifeform_t *lf) { return ar; } +// how far away should we be before attacking? int getattackspeed(lifeform_t *lf) { object_t *w; @@ -3458,6 +3521,29 @@ object_t *getfirearm(lifeform_t *lf) { return NULL; } +int getfootprinttime(lifeform_t *lf) { + int time; + + time = FOOTPRINTTIME; + switch (getlfsize(lf)) { + case SZ_MINI: time = 1; break; + case SZ_TINY: time -= 15; break; + case SZ_SMALL: time -= 10; break; + default: break; + case SZ_LARGE: time += 10; break; + case SZ_HUGE: time += 20; break; + case SZ_ENORMOUS: time += 30; break; + } + + if (time > 0) { + if (getskill(lf, SK_TRACKING) == PR_EXPERT) { + time /= 2; + limit(&time, 1, NA); + } + } + return time; +} + lifeform_t *getguntarget(lifeform_t *lf) { flag_t *f; f = hasflag(lf->flags, F_GUNTARGET); @@ -4096,10 +4182,34 @@ int getmovespeed(lifeform_t *lf) { if (speed < 1) speed = 1; - for (o = lf->cell->obpile->first ; o ; o = o->next) { - f = hasflag(o->flags, F_REDUCEMOVEMENT); - if (f) { - speed *= f->val[0]; + if (!isairborne(lf)) { + for (o = lf->cell->obpile->first ; o ; o = o->next) { + f = hasflag(o->flags, F_REDUCEMOVEMENT); + if (f) { + if (hasflag(o->flags, F_DEEPWATER)) { + int modamt = 0; + // water + if (lfhasflag(lf, F_AQUATIC)) { + modamt = 0; + } else { + switch (getskill(lf, SK_SWIMMING)) { + default: + case PR_NOVICE: + case PR_INEPT: modamt = f->val[0]; break; // normal penalty + case PR_BEGINNER: modamt = f->val[0] - 2; break; + case PR_ADEPT: modamt = 0; break; + case PR_SKILLED: modamt = -1; break; + case PR_EXPERT: modamt = -2; break; + case PR_MASTER: modamt = -2; break; + } + } + limit(&modamt, 0, 5); + speed += (modamt * SPEEDUNIT); + } else { + // something else + speed += (f->val[0] * SPEEDUNIT); + } + } } } @@ -4912,6 +5022,24 @@ int getthrowspeed(int str) { return speed; } +void getwantdistance(lifeform_t *lf, int *min, int *max, int attacking) { + flag_t *f; + // default - run into them + *min = 0; + *max = 0; + if (attacking) { + f = lfhasflag(lf, F_ATTACKRANGE); + if (f) { + *min = f->val[0]; + *max = f->val[1]; + } + } else { + *min = 1; + *max = 3; + } +} + + object_t *getweapon(lifeform_t *lf) { object_t *o; o = getequippedob(lf->pack, BP_WEAPON); @@ -4952,7 +5080,7 @@ void givejob(lifeform_t *lf, enum JOB jobid) { int rollatt[MAXATTS]; int ignorenext; int ignoredprev; - int db = B_TRUE; + int db = B_FALSE; if (db) dblog("givejob() starting.\n"); @@ -5237,9 +5365,12 @@ int giveskill(lifeform_t *lf, enum SKILL id) { msg("You will no longer forget your surroundings."); } else if (f->val[1] == PR_EXPERT) { msg("Your map will now show the location of objects."); - } else if (f->val[1] == PR_MASTER) { } } + if (f->val[1] == PR_MASTER) { + newf = addflag(lf->flags, F_CANWILL, OT_S_MAPPING, 50, 50, "pw:1;"); + newf->lifetime = FROMJOB; + } } else if (id == SK_COOKING) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (f->val[1] == PR_NOVICE) { @@ -5310,6 +5441,22 @@ int giveskill(lifeform_t *lf, enum SKILL id) { } } } + } else if (id == SK_TRACKING) { + if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { + if (f->val[1] == PR_NOVICE) { + msg("You can now see footprints."); + } else if (f->val[1] == PR_BEGINNER) { + msg("You can now determine how recently footprints were made."); + } else if (f->val[1] == PR_ADEPT) { + msg("You can now identify creatures from their footprints."); + } else if (f->val[1] == PR_SKILLED) { + msg("You can now recognise the direction of footprints."); + } else if (f->val[1] == PR_EXPERT) { + msg("You can now partially obscure your own footprints."); + } else if (f->val[1] == PR_MASTER) { + msg("You can now move without leaving footprints."); + } + } } else if (id == SK_TWOWEAPON) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (f->val[1] == PR_NOVICE) { @@ -5387,9 +5534,15 @@ int giveskilllev(lifeform_t *lf, enum SKILL id, enum SKILLLEVEL slev) { void givestartobs(lifeform_t *lf, flagpile_t *fp) { object_t *o; flag_t *f; - char buf[BUFLEN]; + char buf[BUFLEN],buf2[BUFLEN]; int ignorenext = B_FALSE; int ignoredprev = B_FALSE; + int db = B_FALSE; + + if (db) { + sprintf(buf2, "calling givestartobs for %s",lf->race->name); + dbtimestart(buf2); + } // give start objects and id them ignorenext = B_FALSE; @@ -5429,18 +5582,43 @@ void givestartobs(lifeform_t *lf, flagpile_t *fp) { if (f->id == F_STARTOB) { assert(strlen(f->text) > 0); + if (db) dbtime(f->text); if (rnd(1,100) <= f->val[0]) { o = addob(lf->pack, f->text); } } else if (f->id == F_STARTOBDT) { if (rnd(1,100) <= f->val[0]) { - while (!getrandomobwithdt(lf->cell->map, f->val[1], buf)); + int counter = 0; + if (db) { + sprintf(buf2, "calling startobdt"); + dbtime(buf2); + } + while (!getrandomobwithdt(lf->cell->map, f->val[1], buf)) { + counter++; + } + if (db) { + sprintf(buf2, "finished startobdt (needed %d tries)", counter); + dbtime(buf2); + } //assert(strlen(buf) > 0); o = addob(lf->pack, buf); } } else if (f->id == F_STARTOBCLASS) { if (rnd(1,100) <= f->val[0]) { - while (!getrandomobwithclass(lf->cell->map, f->val[1], buf, f->val[2])); + int counter = 0; + if (db) { + sprintf(buf2, "calling startobclass"); + dbtime(buf2); + } + //obdb = B_TRUE; + while (!getrandomobwithclass(lf->cell->map, f->val[1], buf, f->val[2])) { + counter++; + } + //obdb = B_FALSE; + if (db) { + sprintf(buf2, "finished startobclass (needed %d tries)", counter); + dbtime(buf2); + } //if (strlen(buf) <= 0); o = addob(lf->pack, buf); } @@ -5450,7 +5628,7 @@ void givestartobs(lifeform_t *lf, flagpile_t *fp) { // added an object? if (o) { // undead can't have cursed objecst - if (lfhasflag(lf, F_UNDEAD)) { + if (isundead(lf)) { if (o->blessed == B_BLESSED) setblessed(o, B_CURSED); } // player knows the objects they start with @@ -5463,14 +5641,17 @@ void givestartobs(lifeform_t *lf, flagpile_t *fp) { } // now remove startob flags so we don't get them again! + if (db) dbtime("killing startflags"); killflagsofid(fp, F_STARTOB); killflagsofid(fp, F_STARTOBDT); killflagsofid(fp, F_STARTOBCLASS); + if (db) dbtime("finished killing startflags"); // make sure lf doesn't start off burdened! while (isburdened(lf)) { modattr(lf, A_STR, 1); // get stronger } + if (db) dbtimeend("done."); } void givestartskills(lifeform_t *lf, flagpile_t *fp) { @@ -5898,11 +6079,28 @@ int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) { } // solid cells stop lof - if ((cell->type->solid) && (loftype & LOF_WALLSTOP)) { - reason = E_NOLOF; - return B_FALSE; + if (loftype & LOF_WALLSTOP) { + if (cell->type->solid) { + reason = E_NOLOF; + return B_FALSE; + } + // certain objects block lof + blockob = hasobwithflag(cell->obpile, F_BLOCKSLOF); + if (blockob) { + int blocked = B_TRUE; // default + int isopen; + if (isdoor(blockob, &isopen)) { + if (isopen) { + // open doors don't block line of fire + blocked = B_FALSE; + } + } + if (blocked) { + reason = E_NOLOF; + return B_FALSE; + } + } } - // if lifeforms don't stop lof, this is now a valid cell. // if (!(loftype & LOF_LFSSTOP)) { if (newdest) *newdest = cell; @@ -5919,23 +6117,6 @@ int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) { } } } - // certain objects block lof - blockob = hasobwithflag(cell->obpile, F_BLOCKSTHROW); - if (blockob && (loftype & LOF_LFSSTOP)) { - int blocked = B_TRUE; // default - int isopen; - if (isdoor(blockob, &isopen)) { - if (isopen) { - // open doors don't block line of fire - blocked = B_FALSE; - } - } - - if (blocked) { - reason = E_NOLOF; - return B_FALSE; - } - } // cell is now valid if (newdest) *newdest = cell; } @@ -6116,7 +6297,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_SS_DIVINATION, PR_MASTER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SS_MENTAL, PR_MASTER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SS_SUMMONING, PR_MASTER, NA, NULL); - //addflag(lastjob->flags, F_HASPET, NA, NA, NA, "young wolf"); + addflag(lastjob->flags, F_HASPET, NA, NA, NA, "young wolf"); for (i = 1; i < MAXSKILLS; i++) { addflag(lastjob->flags, F_CANLEARN, i, NA, NA, NULL); } @@ -6195,6 +6376,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_ARMOUR, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SHIELDS, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LORE_HUMANOID, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_TRACKING, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); @@ -6234,6 +6416,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_FIRSTAID, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_ARMOUR, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_TECHUSAGE, PR_BEGINNER, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_TRACKING, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_BACKSTAB, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); @@ -6254,6 +6437,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LORE_NATURE, PR_ADEPT, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_STAVES, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_CARTOGRAPHY, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_TRACKING, PR_NOVICE, NA, NULL); // learnable skills addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); @@ -6261,9 +6445,9 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CLUBS, NA, NA, NULL); // gained skills - addflag(lastjob->flags, F_LEVFLAG, 3, F_DETECTMAGIC, B_TRUE, NULL); - addflag(lastjob->flags, F_LEVFLAG, 7, F_DETECTAURAS, B_TRUE, NULL); - addflag(lastjob->flags, F_LEVFLAG, 10, F_CONTROL, B_TRUE, NULL); + addflag(lastjob->flags, F_LEVSKILL, 5, SK_LORE_NATURE, NA, NULL); + addflag(lastjob->flags, F_LEVSKILL, 10, SK_LORE_NATURE, NA, NULL); + addflag(lastjob->flags, F_LEVSKILL, 15, SK_LORE_NATURE, NA, NULL); // abilities mayusespellschool(lastjob->flags, SS_NATURE, F_CANCAST); addflag(lastjob->flags, F_HASPET, NA, NA, NA, "young wolf"); @@ -6304,6 +6488,7 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LORE_ARCANA, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_POLEARMS, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_TRACKING, NA, NA, NULL); addjob(J_PIRATE, "Pirate"); // stats @@ -6335,6 +6520,7 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LORE_ARCANA, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_TRACKING, NA, NA, NULL); addjob(J_ROGUE, "Rogue"); addflag(lastjob->flags, F_STARTATT, A_STR, ST_WEAK, NA, NULL); @@ -6357,6 +6543,7 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_STAVES, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_UNARMED, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_TRACKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_TWOWEAPON, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SS_DIVINATION, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SS_GRAVITY, NA, NA, NULL); @@ -6535,6 +6722,7 @@ void initrace(void) { addflag(lastrace->flags, F_RARITY, H_DUNGEON, 63, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL); addflag(lastrace->flags, F_HITDICE, 4, 5, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "2d4+1"); @@ -6681,6 +6869,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_A_HEAVYBLOW, NA, NA, NULL); addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_GIANTFIRE, "fire giant", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); @@ -6711,6 +6900,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 4, NA, "bellows^a bellow"); addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_GIANTFIREFC, "fire giant forgecaller", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GIANTFIRE; @@ -6747,6 +6937,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_HASSKILL, SK_SS_FIRE, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_GIANTFIRETITAN, "fire titan", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 40, NA, NULL); @@ -6775,6 +6966,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_S_BURNINGWAVE, 3, 3, "pw:6;"); addflag(lastrace->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); // TODO: storm giant // TODO: storm titan @@ -6785,6 +6977,7 @@ void initrace(void) { addflag(lastrace->flags, F_NUMAPPEAR, 2, 3, NA, ""); addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 72, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HITDICE, 8, NA, NA, NULL); addflag(lastrace->flags, F_ARMOURRATING, 9, NA, NA, NULL); addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); @@ -6809,6 +7002,7 @@ void initrace(void) { addflag(lastrace->flags, F_RARITY, H_DUNGEON, 68, NA, NULL); addflag(lastrace->flags, F_HITDICE, 6, NA, NA, NULL); addflag(lastrace->flags, F_ARMOURRATING, 9, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); @@ -6826,6 +7020,7 @@ void initrace(void) { addflag(lastrace->flags, F_PACKATTACK, 3, NA, 2, NULL); addflag(lastrace->flags, F_MINIONS, 75, 1, 2, "gnoll"); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_GNOLLMR, "gnoll marauder", 130, 'h', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GNOLL; @@ -6835,6 +7030,7 @@ void initrace(void) { addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, NA, NULL); addflag(lastrace->flags, F_HITDICE, 10, NA, NA, NULL); addflag(lastrace->flags, F_ARMOURRATING, 9, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); @@ -6901,6 +7097,7 @@ void initrace(void) { addflag(lastrace->flags, F_PACKATTACK, 2, DT_SLASH, 3, NULL); addflag(lastrace->flags, F_MINIONS, 90, 1, 2, "goblin"); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_GOBLINSHOOTER, "goblin sharpshooter", 20, 'g', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GOBLIN; @@ -6929,6 +7126,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL); addflag(lastrace->flags, F_STARTHIDDENPCT, 75, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_GOBLINHEXER, "goblin hexer", 20, 'g', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GOBLIN; @@ -7090,6 +7288,7 @@ void initrace(void) { addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "spits"); addflag(lastrace->flags, F_CANWILL, OT_S_POISONBOLT, 5, 5, "pw:5;"); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_LURKINGHORROR, "lurking horror", 100, 'U', C_MAGENTA, MT_FLESH, RC_DEMON); addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); @@ -7173,6 +7372,7 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 20, NA, NA, NULL); addflag(lastrace->flags, F_MINIONS, 50, 1, 5, "orc"); addflag(lastrace->flags, F_MINIONS, 50, 1, 3, "orc warrior"); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_OGREWARHULK, "ogre warhulk", 160, 'O', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_OGRE; @@ -7315,6 +7515,8 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 4, NA, "screams in pain^screams of pain"); addflag(lastrace->flags, F_RESISTMAG, 5, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 25, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_POLTERGEIST, "poltergeist", 50, 'p', C_GREEN, MT_FLESH, RC_UNDEAD); // sPirit addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); @@ -7342,7 +7544,6 @@ void initrace(void) { addflag(lastrace->flags, F_HASSKILL, SK_SS_MENTAL, PR_EXPERT, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_XPMULTIPLY, 2, NA, NA, NULL); - addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL); addrace(R_SATYR, "satyr", 80, 'h', C_GREEN, MT_FLESH, RC_HUMANOID); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); @@ -7373,11 +7574,13 @@ void initrace(void) { addflag(lastrace->flags, F_HASSKILL, SK_SS_MENTAL, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL); addflag(lastrace->flags, F_STARTHIDDENPCT, 60, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); addrace(R_SHADOWCAT, "shadowcat", 5, 'f', C_BLUE, MT_FLESH, RC_MAGIC); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HITDICE, 2, 3, NA, NULL); addflag(lastrace->flags, F_EVASION, 40, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); @@ -7468,6 +7671,7 @@ void initrace(void) { addflag(lastrace->flags, F_HITDICE, 1, 0, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DIMWITTED, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_STR, ST_WEAK, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); @@ -7584,6 +7788,7 @@ void initrace(void) { addflag(lastrace->flags, F_HITDICE, 3, 3, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d3"); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d3"); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d6"); @@ -7603,6 +7808,7 @@ void initrace(void) { addflag(lastrace->flags, F_HITDICE, 5, 5, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6"); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6"); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d8"); @@ -7687,6 +7893,7 @@ void initrace(void) { addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, ""); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 77, NA, ""); addflag(lastrace->flags, F_RARITY, H_FOREST, 77, NA, ""); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HITDICE, 4, 0, NA, ""); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d6"); addflag(lastrace->flags, F_MAXATTACKS, 1, NA, NA, NULL); @@ -7720,6 +7927,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL); addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 2, NA, "whines in pain^whining"); addflag(lastrace->flags, F_HITCONFER, F_POISONED, SC_POISON, 24, "10-15"); @@ -7742,6 +7950,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL); @@ -7762,6 +7971,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d2"); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d2"); addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); @@ -7789,6 +7999,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d3"); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d3"); addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); @@ -7817,6 +8028,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d3+3"); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d4+3"); addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); @@ -7841,6 +8053,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6+5"); addflag(lastrace->flags, F_EXTRADAM, DT_COLD, NA, NA, "1d6"); addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); @@ -7923,6 +8136,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d2"); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d3"); addflag(lastrace->flags, F_MAXATTACKS, 1, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); @@ -8130,6 +8344,7 @@ void initrace(void) { addflag(lastrace->flags, F_STARTATT, A_CON, CN_HARDY, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 87, NA, ""); addflag(lastrace->flags, F_RARITY, H_FOREST, 97, NA, ""); addflag(lastrace->flags, F_HITDICE, 2, 2, NA, ""); @@ -8151,6 +8366,7 @@ void initrace(void) { addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_FAST, NA, NA, ""); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 80, NA, ""); addflag(lastrace->flags, F_RARITY, H_FOREST, 90, NA, ""); addflag(lastrace->flags, F_HITDICE, 3, 3, NA, ""); @@ -8309,7 +8525,6 @@ void initrace(void) { // undead addrace(R_ZOMBIE, "zombie", 50, 'Z', C_BLUE, MT_FLESH, RC_UNDEAD); - addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 85, NA, NULL); addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); @@ -8330,7 +8545,6 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addrace(R_SKELETON, "skeleton", 20, 'Z', C_GREY, MT_BONE, RC_UNDEAD); - addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL); addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "5-20 bones"); addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL); @@ -8350,7 +8564,6 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addrace(R_GHAST, "ghast", 50, 'Z', C_MAGENTA, MT_FLESH, RC_UNDEAD); - addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_SMART, NA, NULL); addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL); addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); @@ -8370,7 +8583,6 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addrace(R_GHOST, "ghost", 50, 'p', C_BLUE, MT_MAGIC, RC_UNDEAD); // p for sPirit - addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_SMART, NA, NULL); addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL); addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); @@ -8391,7 +8603,6 @@ void initrace(void) { // their previous corpse. use f_mycorpse->oid for this. addrace(R_GHOUL, "ghoul", 50, 'Z', C_BLUE, MT_FLESH, RC_UNDEAD); - addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL); addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL); addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); @@ -8522,7 +8733,7 @@ int ischarmable(lifeform_t *lf) { reason = E_LOWIQ; return B_FALSE; } - if (hasflag(lf->flags, F_UNDEAD)) { + if (isundead(lf)) { reason = E_UNDEAD; return B_FALSE; } @@ -8566,7 +8777,7 @@ void killlf(lifeform_t *lf) { // TODO: check on all maps? if ((gamemode == GM_GAMESTARTED)) { for (l = m->lf ; l ; l = l->next) { - f = lfhasflagval(l, F_TARGET, lf->id, NA, NA, NULL); + f = lfhasflagval(l, F_TARGETLF, lf->id, NA, NA, NULL); if (f) killflag(f); f = lfhasflagval(l, F_ATTACHEDTO, lf->id, NA, NA, NULL); if (f) killflag(f); @@ -8758,7 +8969,7 @@ int ispeaceful(lifeform_t *lf) { if (lfhasflag(lf, F_HOSTILE)) { return B_FALSE; } - if (lfhasflagval(lf, F_TARGET, player->id, NA, NA, NULL)) { + if (lfhasflagval(lf, F_TARGETLF, player->id, NA, NA, NULL)) { return B_FALSE; } return B_TRUE; @@ -8772,6 +8983,17 @@ int ispetof(lifeform_t *lf, lifeform_t *owner) { return B_FALSE; } +flag_t *ispetortarget(lifeform_t *lf, lifeform_t *ownertarget) { + flag_t *f; + if (!lf || !ownertarget) return B_FALSE; + f = lfhasflagval(lf, F_PETOF, ownertarget->id, NA, NA, NULL); + if (f) return f; + f = lfhasflagval(lf, F_TARGETLF, ownertarget->id, NA, NA, NULL); + if (f) return f; + + return NULL; +} + int isplayer(lifeform_t *lf) { if (lf && (lf->controller == C_PLAYER)) { return B_TRUE; @@ -8893,12 +9115,15 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { map_t *m; lifeform_t *a; int i; + int db = B_FALSE; assert(cell); if (cell->type != (celltype_t *) DUMMYCELLTYPE) { assert(!cell->type->solid); } + if (db) dbtimestart("startime addlf"); + m = cell->map; // add to the end of the list @@ -8977,20 +9202,27 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { // set race - this will inherit race flags a->race = NULL; + + if (db) dbtime("before setrace done."); setrace(a, rid, B_FALSE); + if (db) dbtime("setrace done."); + // update other things cell->lf = a; // give start objetcs if ((gamemode != GM_LOADING) && (gamemode != GM_VALIDATION)) { + if (db) dbtime("about to outfitlf."); outfitlf(a); + if (db) dbtime("done outfitlf."); } a->created = B_TRUE; a->born = B_TRUE; // now finished creating it. if ((gamemode == GM_GAMESTARTED) && cansee(player, a)) { needredraw = B_TRUE; } + if (db) dbtimeend("addlf"); return a; } @@ -9092,6 +9324,56 @@ skill_t *addskill(enum SKILL id, char *name, char *desc) { return a; } +void addtrail(lifeform_t *lf, int dir) { + object_t *footprint, *scent; + flag_t *fpflag; + + if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { + // no tracks at all + return; + } + // footprints first + if (!isairborne(lf)) { + int fpdir; + enum SKILLLEVEL slev; + + slev = getskill(lf, SK_TRACKING); + if (slev == PR_MASTER) { + // no footprints! + return; + } else if (slev == PR_EXPERT) { + fpdir = D_NONE; + } else { + fpdir = dir; + } + + + footprint = hastrailof(lf->cell->obpile, lf, OT_FOOTPRINT, &fpflag, NULL); + if (footprint) { + assert(fpflag); + fpflag->lifetime = getfootprinttime(lf); + fpflag->val[1] = fpdir; + } else { + char buf[BUFLENTINY]; + sprintf(buf, "%d", lf->id); + footprint = addob(lf->cell->obpile, "footprint"); + addtempflag(footprint->flags, F_TRAIL, lf->race->id, fpdir, S_SIGHT, buf, getfootprinttime(lf)); + } + } + + // now smell + scent = hastrailof(lf->cell->obpile, lf, OT_SCENT, &fpflag, NULL); + if (scent) { + assert(fpflag); + fpflag->lifetime = SCENTTIME; + } else { + char buf[BUFLENTINY]; + sprintf(buf, "%d", lf->id); + scent = addob(lf->cell->obpile, "scent"); + addtempflag(scent->flags, F_TRAIL, lf->race->id, dir, S_SMELL, buf, SCENTTIME); + } +} + void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype) { flag_t *f; if (isimmuneto(lf->flags, damtype)) { @@ -9126,6 +9408,13 @@ void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype) { (*amt) *= 2; } + if ((damtype == DT_ELECTRIC) && hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { + (*amt) *= 2; + } + if ((damtype == DT_COLD) && hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { + (*amt) = pctof(150, *amt); + } + if (isresistantto(lf->flags, damtype)) { (*amt) /= 2; } @@ -9164,7 +9453,7 @@ void makepeaceful(lifeform_t *who) { } killflagsofid(who->flags, F_HOSTILE); - killflagsofid(who->flags, F_TARGET); // stop targetting anyone + killflagsofid(who->flags, F_TARGETLF); // stop targetting anyone } @@ -9321,6 +9610,8 @@ void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o) { } int areallies(lifeform_t *lf1, lifeform_t *lf2) { +// UNCOMMENT THIS TO GO BACK TO ONLY HAVING MONSTERS +// OF THE SAME BASE RACE COUNTING AS ALLIES. if (getallegiance(lf1) == getallegiance(lf2)) { if (isplayer(lf1) || isplayer(lf2)) { // if one of them is the player @@ -9331,6 +9622,11 @@ int areallies(lifeform_t *lf1, lifeform_t *lf2) { } } } + /* + if (getallegiance(lf1) == getallegiance(lf2)) { + return B_TRUE; + } + */ return B_FALSE; } @@ -9448,6 +9744,26 @@ void autotarget(lifeform_t *lf) { } } +int isswimming(lifeform_t *lf) { + if (!isairborne(lf) && + hasobwithflag(lf->cell->obpile, F_DEEPWATER) && + getskill(lf, SK_SWIMMING)) { + return B_TRUE; + } + + return B_FALSE; +} + +int isundead(lifeform_t *lf) { + if (lf->race->raceclass->id == RC_UNDEAD) { + return B_TRUE; + } + if (lfhasflag(lf, F_UNDEAD)) { + return B_TRUE; + } + return B_FALSE; +} + flag_t *isvulnto(flagpile_t *fp, enum DAMTYPE dt) { flag_t *f; f = hasflagval(fp, F_DTVULN, dt, NA, NA, NULL); @@ -9788,16 +10104,16 @@ void makefriendly(lifeform_t *who, int howlong) { if (!hasflag(who->flags, F_FRIENDLY)) { addtempflag(who->flags, F_FRIENDLY, B_TRUE, NA, NA, NULL, howlong); } - killflagsofid(who->flags, F_TARGET); // stop targetting anyone + killflagsofid(who->flags, F_TARGETLF); // stop targetting anyone } int makenauseated(lifeform_t *lf, int amt, int howlong) { flag_t *f; - if (lfhasflag(lf, F_UNDEAD)) return B_TRUE; + if (isundead(lf)) return B_TRUE; + if (!hasbp(lf, BP_HEAD)) return B_TRUE; // can't smell with no head - if (lfhasflag(lf, F_UNDEAD)) return B_TRUE; if (!lfhasflag(lf, F_HUMANOID)) return B_TRUE; @@ -10134,13 +10450,13 @@ int noise(cell_t *c, lifeform_t *noisemaker, int volume, char *text, char *seete // sound will travel 2*volume cells sounddist = 2 * volume; - if (noisemaker && (noisemaker->race->id == R_BANDIT)) { - dblog("xxx"); - } - // if anything nearby hears it, it might respond for (l = c->map->lf; l ; l = l->next) { int dist; + + // ie. if this lf is in the process of swapping places + if (!l->cell) continue; + // if you make a noise while swapping position with // something, its ->cell will be null here! dist = getcelldist(l->cell, c); @@ -10148,9 +10464,6 @@ int noise(cell_t *c, lifeform_t *noisemaker, int volume, char *text, char *seete if ((l != noisemaker) && (l->cell)) { int difficulty; //if (canhear(l, c) && haslos(l, c)) { - if (isplayer(l)) { - dblog("xxx"); - } // listen check difficulty is based on sound distance vs max hearing distance difficulty = (int) ( ((float)getcelldist(l->cell, c) / (float)gethearingrange(l)) * 20); @@ -10289,12 +10602,19 @@ int noise(cell_t *c, lifeform_t *noisemaker, int volume, char *text, char *seete // give initial equiment / skills to a lifeform void outfitlf(lifeform_t *lf) { + int db = B_FALSE; + if (db) dbtimestart("outfitlf"); givestartobs(lf, lf->flags); + if (db) dbtime("finished giveobs"); givestartskills(lf, lf->flags); + if (db) dbtime("finished givestartskills"); autoskill(lf); + if (db) dbtime("finished autoskill"); // weild/wear stuff autoweild(lf); + if (db) dbtime("finished autoweild"); + if (db) dbtimeend("outfitlf done"); } @@ -10964,9 +11284,8 @@ int scare(lifeform_t *lf, lifeform_t *scarer, int howlong, int scarerbonus) { if (!scarer) return B_FALSE; // immune to fear? - if (lfhasflag(lf, F_UNDEAD)) { - return B_FALSE; - } + if (lfhasflag(lf, F_UNDEAD)) return B_FALSE; + if (lfhasflag(lf, F_ASLEEP)) { return B_FALSE; } @@ -11189,6 +11508,8 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { // inherit flags from race copyflags(lf->flags, lf->race->flags, FROMRACE); + // don't want certain race only flags... + killflagsofid(lf->flags, F_RARITY); // generate stats for (i = 0; i < MAXATTS; i++) { @@ -11232,7 +11553,7 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { // no pack? if (lfhasflag(lf, F_NOPACK)) { // drop everything - if (countobs(lf->pack)) { + if (countobs(lf->pack, B_FALSE)) { if (isplayer(lf)) { msg("Your possessions drop to the ground!"); } else if (cansee(player, lf)) { @@ -11311,7 +11632,9 @@ void initskills(void) { addskill(SK_SPELLCASTING, "Sorcery", "Determines your ability to cast spells from all schools."); addskill(SK_SPOTHIDDEN, "Searching", "Helps you to spot hidden traps or creatures."); addskill(SK_STEALTH, "Stealth", "Affects your ability to move silently."); + addskill(SK_SWIMMING, "Swimming", "Allows you to safely swim through deep water."); addskill(SK_TECHUSAGE, "Technology", "Determines your comprehension of modern technological items."); + addskill(SK_TRACKING, "Tracking", "Allows you to track enemies by their footprints."); addskill(SK_TRAPS, "Traps", "Affects your ability to locate and disarm traps."); addskill(SK_TWOWEAPON, "Dual Weilding", "Allows you to weild two melee weapons at once."); // knowledge @@ -11910,7 +12233,7 @@ int takeoff(lifeform_t *lf, object_t *o) { } void taketime(lifeform_t *lf, long howlong) { - int db = B_TRUE; + int db = B_FALSE; map_t *map; if (lfhasflag(lf, F_NOTIME)) { @@ -12056,7 +12379,9 @@ void turneffectslf(lifeform_t *lf) { if (isplayer(lf) && isdrunk(lf)) statdirty = B_TRUE; + // clear one-turn-only flags killflagsofid(lf->flags, F_DONELISTEN); + killflagsofid(lf->flags, F_NOSWAP); // update where player knows // (but without a map you will then slowly forget it) @@ -12077,6 +12402,13 @@ void turneffectslf(lifeform_t *lf) { //} } } + // drown? + o = hasobwithflag(lf->cell->obpile, F_DEEPWATER); + if (o) { + if (checkfordrowning(lf, o)) { + if (isdead(lf)) return; + } + } // get more hungry modhunger(lf, 1); @@ -12273,19 +12605,21 @@ void turneffectslf(lifeform_t *lf) { flag_t *f; int mod = 0; - if (hasflag(o->flags, F_TRAP)) { - mod += getskill(lf, SK_TRAPS); - } - f = hasflag(o->flags, F_SECRET); - if (skillcheck(lf, SC_SEARCH, f->val[0], mod)) { - char obname[BUFLEN]; - // reveal it - getobname(o, obname, o->amt); - msg("You notice %s!",obname); - killflag(f); - needredraw = B_TRUE; - drawscreen(); + if (f && (f->val[0] != NA)) { + if (hasflag(o->flags, F_TRAP)) { + mod += getskill(lf, SK_TRAPS); + } + + if (skillcheck(lf, SC_SEARCH, f->val[0], mod)) { + char obname[BUFLEN]; + // reveal it + getobname(o, obname, o->amt); + msg("You notice %s!",obname); + killflag(f); + needredraw = B_TRUE; + drawscreen(); + } } } } @@ -12459,37 +12793,41 @@ void turneffectslf(lifeform_t *lf) { // for flags which can occur multiple times for (f = o->flags->first ; f ; f = f->next) { - if (f->id == F_WALKDAMBP) { - object_t *armour; - int dam; - enum BODYPART bp; - enum DAMTYPE damtype; + if ((f->id == F_DEEPWATER) && !isairborne(lf)) { + checkfordrowning(lf, o); + if (isdead(lf)) return; + } else if (f->id == F_WALKDAMBP) { + if (!isairborne(lf)) { + object_t *armour; + int dam; + enum BODYPART bp; + enum DAMTYPE damtype; + bp = f->val[0]; + damtype = f->val[1]; + dam = roll(f->text); - bp = f->val[0]; - damtype = f->val[1]; - dam = roll(f->text); + getobname(o, buf, o->amt); - getobname(o, buf, o->amt); - - // some things get your armour too! - armour = getouterequippedob(lf, bp); - if (armour) { - takedamage(armour, dam, damtype); - } else { - if (f->val[2] == FALLTHRU) { - // certain combinations might give announcements... - switch (damtype) { - case DT_WATER: - if ((bp == BP_FEET) && isplayer(lf) && hasbp(lf, bp)) { - msg("Your %s get wet.", getbodypartname(bp)); - } - break; - default: - break; + // some things get your armour too! + armour = getouterequippedob(lf, bp); + if (armour) { + takedamage(armour, dam, damtype); + } else { + if (f->val[2] == FALLTHRU) { + // certain combinations might give announcements... + switch (damtype) { + case DT_WATER: + if ((bp == BP_FEET) && isplayer(lf) && hasbp(lf, bp)) { + msg("Your %s get wet.", getbodypartname(bp)); + } + break; + default: + break; + } + // apply damage to lf instead. + applywalkdam(lf, roll(f->text), f->val[1], o); } - // apply damage to lf instead. - applywalkdam(lf, roll(f->text), f->val[1], o); } } } @@ -12581,9 +12919,9 @@ void turneffectslf(lifeform_t *lf) { } } // remove invalid targets - if ((f->id == F_TARGET) || (f->id == F_TARGETCELL)) { + if ((f->id == F_TARGETLF) || (f->id == F_TARGETCELL)) { lifeform_t *targ = NULL; - if (f->id == F_TARGET) { + if (f->id == F_TARGETLF) { targ = findlf(lf->cell->map, f->val[0]); } else if (f->id == F_TARGETCELL) { cell_t *c; @@ -12644,7 +12982,7 @@ int touch(lifeform_t *lf, object_t *o) { } // undead and blesed objects? - if (lfhasflag(lf, F_UNDEAD) && isblessed(o)) { + if (isundead(lf) && isblessed(o)) { object_t *gloves; // not wearing gloves? gloves = getequippedob(lf->pack, BP_HANDS); diff --git a/lf.h b/lf.h index 2651a99..ff23b1a 100644 --- a/lf.h +++ b/lf.h @@ -6,6 +6,7 @@ job_t *addjob(enum JOB id, char *name); race_t *addrace(enum RACE id, char *name, float weight, char glyph, int glyphcolour, enum MATERIAL mat, enum RACECLASS raceclass); raceclass_t *addraceclass(enum RACECLASS id, char *name, char *pluralname, enum SKILL skill); skill_t *addskill(enum SKILL id, char *name, char *desc); +void addtrail(lifeform_t *lf, int dir); void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype); void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o); int areallies(lifeform_t *lf1, lifeform_t *lf2); @@ -35,7 +36,8 @@ int canwear(lifeform_t *lf, object_t *o, enum BODYPART where); int canweild(lifeform_t *lf, object_t *o); int cantakeoff(lifeform_t *lf, object_t *o); int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *targob, cell_t *targcell); -void checkxp(enum RACE rid); +int checkfordrowning(lifeform_t *lf, object_t *o); +//void checkxp(enum RACE rid); float comparelfs(lifeform_t *lf1, lifeform_t *lf2); int countinnateattacks(lifeform_t *lf); int countmoney(lifeform_t *lf); @@ -91,6 +93,7 @@ char *getbodypartname(enum BODYPART bp); char *getbodypartequipname(enum BODYPART bp); object_t *getequippedob(obpile_t *op, enum BODYPART bp); object_t *getfirearm(lifeform_t *lf); +int getfootprinttime(lifeform_t *lf); lifeform_t *getguntarget(lifeform_t *lf); int getguntargetid(lifeform_t *lf); //int gethealtime(lifeform_t *lf); @@ -160,6 +163,7 @@ char *getskilldesc(enum SKILL id ); char *getskillname(enum SKILL id ); char *getskilllevelname(enum SKILLLEVEL sl); int getthrowspeed(int str); +void getwantdistance(lifeform_t *lf, int *min, int *max, int attacking); object_t *getweapon(lifeform_t *lf); long getxpforlev(int level); void givejob(lifeform_t *lf, enum JOB jobid); @@ -206,6 +210,7 @@ int isloreskill(enum SKILL skid); int ismaxedskill(lifeform_t *lf, enum SKILL skid); int ispeaceful(lifeform_t *lf); int ispetof(lifeform_t *lf, lifeform_t *owner); +flag_t *ispetortarget(lifeform_t *lf, lifeform_t *ownertarget); int isplayer(lifeform_t *lf); flag_t *ispoisoned(lifeform_t *lf); flag_t *ispoisonedwith(lifeform_t *lf, enum POISONTYPE pt); @@ -214,6 +219,8 @@ int isprone(lifeform_t *lf); flag_t *isresistantto(flagpile_t *fp, enum DAMTYPE dt); flag_t *isresting(lifeform_t *lf); object_t *isstuck(lifeform_t *lf); +int isswimming(lifeform_t *lf); +int isundead(lifeform_t *lf); flag_t *isvulnto(flagpile_t *fp, enum DAMTYPE dt); void killjob(job_t *job); void killlf(lifeform_t *lf); diff --git a/log.txt b/log.txt index 5949e2f..ffd85ac 100644 --- a/log.txt +++ b/log.txt @@ -2,364 +2,183 @@ ====== NEW LOGFILE ==== -givejob() starting. - -xxx -xxx -xxx -xxx -lfid 175 (kobold) spending 20 time - -lfid 180 (kobold) spending 20 time - -xxx -xxx -lfid 181 (kobold) spending 20 time - -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 175 (kobold) spending 20 time - -xxx -lfid 180 (kobold) spending 20 time - -fireat(): dam = throwdam(3) * speed(2)/2 = 3 -lfid 181 (kobold) spending 20 time - -fireat(): dam = throwdam(2) * speed(3)/2 = 2 -xxx -xxx -xxx -lfid 195 (kobold) spending 20 time - -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -lfid 175 (kobold) spending 20 time - -xxx -lfid 178 (kobold) spending 20 time - -xxx -lfid 180 (kobold) spending 20 time - -fireat(): dam = throwdam(3) * speed(2)/2 = 3 -xxx -lfid 181 (kobold) spending 20 time - -xxx -xxx -xxx -lfid 195 (kobold) spending 20 time - -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -lfid 175 (kobold) spending 20 time - -xxx -lfid 178 (kobold) spending 20 time - -xxx -xxx -xxx -lfid 195 (kobold) spending 15 time - -xxx -xxx -lfid 198 (human) spending 20 time - -lfid 170 (giant rat) spending 15 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 178 (kobold) spending 15 time - -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -lfid 167 (xat) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 40 time - -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -lfid 198 (human) spending 20 time - -xxx -xxx -xxx -lfid 175 (kobold) spending 20 time - -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx -xxx +rollhitdice() for xat - rolling 1d4 + 0 +rollhitdice() - mod is +32% +rollhitdice() ---- die 1/1 == 3 +TOTAL: 3 + -> modified to: 3 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +0% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for xat - rolling 1d4 + 0 +rollhitdice() - mod is +54% +rollhitdice() ---- die 1/1 == 2 +TOTAL: 2 + -> modified to: 3 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +0% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +44% +rollhitdice() ---- die 1/1 == 3 +TOTAL: 3 + -> modified to: 4 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +-16% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 4 +rollhitdice() for xat - rolling 1d4 + 0 +rollhitdice() - mod is +76% +rollhitdice() ---- die 1/1 == 1 +TOTAL: 1 + -> modified to: 1 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +10% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 4 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +76% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 7 +rollhitdice() for xat - rolling 1d4 + 0 +rollhitdice() - mod is +-27% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 3 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +22% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +10% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +0% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +-5% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 4 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +88% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 7 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +-5% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 4 +rollhitdice() for troglodyte - rolling 1d4 + 0 +rollhitdice() - mod is +0% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 4 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +10% +rollhitdice() ---- die 1/1 == 1 +TOTAL: 1 + -> modified to: 1 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +0% +rollhitdice() ---- die 1/1 == 1 +TOTAL: 1 + -> modified to: 1 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +-11% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +76% +rollhitdice() ---- die 1/1 == 3 +TOTAL: 3 + -> modified to: 5 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +32% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +-11% +rollhitdice() ---- die 1/1 == 2 +TOTAL: 2 + -> modified to: 2 +rollhitdice() for xat - rolling 1d4 + 0 +rollhitdice() - mod is +66% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 6 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +76% +rollhitdice() ---- die 1/1 == 1 +TOTAL: 1 + -> modified to: 1 +rollhitdice() for xat - rolling 1d4 + 0 +rollhitdice() - mod is +32% +rollhitdice() ---- die 1/1 == 2 +TOTAL: 2 + -> modified to: 2 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +54% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +54% +rollhitdice() ---- die 1/1 == 1 +TOTAL: 1 + -> modified to: 1 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +10% +rollhitdice() ---- die 1/1 == 3 +TOTAL: 3 + -> modified to: 3 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +0% +rollhitdice() ---- die 1/1 == 3 +TOTAL: 3 + -> modified to: 3 +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +32% +rollhitdice() ---- die 1/1 == 3 +TOTAL: 3 + -> modified to: 3 +rollhitdice() for giant rat - rolling 0d4 + 1 +rollhitdice() - mod is +-16% +TOTAL: 1 + -> modified to: 1 +rollhitdice() for xat - rolling 1d4 + 0 +rollhitdice() - mod is +22% +rollhitdice() ---- die 1/1 == 2 +TOTAL: 2 + -> modified to: 2 +rollhitdice() for giant newt - rolling 1d4 + 0 +rollhitdice() - mod is +66% +rollhitdice() ---- die 1/1 == 4 +TOTAL: 4 + -> modified to: 6 +rollhitdice() for human - rolling 2d4 + 2 +rollhitdice() - mod is +0% +rollhitdice() ---- die 1/2 == 6 +rollhitdice() ---- die 2/2 == 4 +TOTAL: 10 + -> modified to: 10 +rollhitdice() for young wolf - rolling 2d4 + 2 +rollhitdice() - mod is +100% +rollhitdice() ---- die 1/2 == 4 +rollhitdice() ---- die 2/2 == 4 +TOTAL: 8 + -> modified to: 16 +xxx +rollhitdice() for kobold - rolling 1d4 + 0 +rollhitdice() - mod is +44% +rollhitdice() ---- die 1/1 == 3 +TOTAL: 3 + -> modified to: 4 xxx diff --git a/map.c b/map.c index 6292bc1..60a576f 100644 --- a/map.c +++ b/map.c @@ -112,11 +112,13 @@ map_t *addmap(void) { // when monsters are made during level generation, autogen will be true. otherwise false; -lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen) { +lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen, int *nadded) { lifeform_t *lf = NULL; race_t *r; int db = B_FALSE; + if (nadded) *nadded = 0; + // ie. don't create mosnters on closed doors! if (!cellwalkable(NULL, c, NULL)) { return NULL; @@ -133,13 +135,25 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto assert(r); - if (db) dblog("adding rand lf %s to cell %d,%d",r->name,c->x,c->y); + + if (db) { + char buf[BUFLEN]; + sprintf(buf, "start addmonster for %s",r->name); + dbtimestart(buf); + } + + //if (db) dblog("adding rand lf %s to cell %d,%d",r->name,c->x,c->y); if (r) { + if (db) dbtime("doing lf addition"); lf = addlf(c, r->id, getrandommonlevel(r, c->map)); + if (db) dbtime("finished lf addition"); if (lf) { flag_t *f; + + if (nadded) (*nadded)++; + if (db) dbtime("checking for job"); lf->born = B_FALSE; if (jobok) { for (f = lf->flags->first ; f ; f = f->next) { @@ -173,6 +187,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto } // appears in groups? + if (db) dbtime("handling groups"); if (autogen) { f = hasflag(lf->flags, F_NUMAPPEAR); if (f) { @@ -188,7 +203,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto lifeform_t *newlf; // find an adjacent cell to one of the newly added monsters, // starting with the first one - adjcell = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND); + adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL); // did we find one? if (!adjcell) break; @@ -196,6 +211,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto if (!newlf) { break; } + if (nadded) (*nadded)++; newlf->born = B_FALSE; // initial monster shoudl remember its minions @@ -215,6 +231,8 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto } // minons? + // appears in groups? + if (db) dbtime("handling minions"); if (autogen) { f = hasflag(lf->flags, F_MINIONS); if (f) { @@ -229,7 +247,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto lifeform_t *newlf; race_t *newr; - adjcell = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND); + adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL); if (!adjcell) break; newr = findracebyname(f->text); @@ -238,6 +256,8 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto newlf = addlf(adjcell, newr->id, getrandommonlevel(newr, adjcell->map)); if (!newlf) break; + if (nadded) (*nadded)++; + newlf->born = B_FALSE; if (lfhasflag(lf, F_ASLEEP)) addflag(newlf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL); newlf->born = B_TRUE; @@ -247,6 +267,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto } + if (db) dbtime("handling random objects"); // sometimes give the lf random objects (extra monsters through // 'numappears' don't get them. if (lfhasflag(lf, F_HUMANOID) && !lfhasflag(lf, F_NOPACK)) { @@ -266,6 +287,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto } } + if (db) dbtime("giving torches"); // humanoids on dark levels which can't see will probably have some // kind of light producing device if (!islit(c) && !hasflag(lf->flags, F_SEEINDARK) && !hasflag(lf->flags, F_TREMORSENSE)) { @@ -294,6 +316,8 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto } // end if lf } + if (db) dbtimeend("finished addmonster"); + return lf; } @@ -313,18 +337,21 @@ object_t *addrandomob(cell_t *c) { return o; } -int addrandomthing(cell_t *c, int obchance) { +int addrandomthing(cell_t *c, int obchance, int *nadded) { int rv = TT_NONE; // if there's already someone there, // then add an object. if (c->lf || (rnd(1,100) <= obchance)) { + object_t *o; // object - if (addrandomob(c)) { + o = addrandomob(c); + if (o) { + if (nadded) *nadded = o->amt; rv = TT_OBJECT; } } else { // monster - if (addmonster(c, R_RANDOM, B_TRUE, 1, B_TRUE)) { + if (addmonster(c, R_RANDOM, B_TRUE, 1, B_TRUE, nadded)) { rv = TT_MONSTER; } } @@ -486,7 +513,6 @@ void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { if (haslos(viewer, c)) { // show cell contents - //drawcellwithcontents(cell, x-viewx, y-viewy); if (c->lf && cansee(viewer, c->lf)) { // lifeform here which we can see // draw the lf's race glyph *g = *(getlfglyph(c->lf)); @@ -506,7 +532,7 @@ void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { } // objects here? - if ((countobs(c->obpile) > 0)) { + if ((countobs(c->obpile, B_FALSE) > 0)) { object_t *o; // draw highest object in sort order @@ -516,8 +542,6 @@ void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { *g = *(getglyph(o)); } else { // objects here, but we can't see them. draw the cell. - // otherwise just draw the cell - //*g = c->obpile->first->type->obclass->glyph; *g = c->type->glyph; if (!islit(c)) { g->colour = C_BLUE; @@ -618,6 +642,7 @@ object_t *gettopobject(cell_t *where) { flag_t *f; // ignore hidden traps, but not secret doors if (hasflag(o->flags, F_SECRET) && !isdoor(o, NULL)) { + } else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) { } else { f = hasflag(o->flags, F_IMPASSABLE); if (f && (f->val[0] > largest)) { @@ -638,7 +663,9 @@ object_t *gettopobject(cell_t *where) { // appear first. for (o = where->obpile->last ; o ; o = o->prev) { if (o->type->obclass->id == sortorder[c]) { - if (!hasflag(o->flags, F_SECRET)){ + if (hasflag(o->flags, F_SECRET)) { + } else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) { + } else { return o; } } @@ -1083,26 +1110,46 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { } - // add staircases - dungeons alway shave an up and down stairs + // add staircases - dungeons always have an up and down stairs for (i = 0; i < 3; i++) { // add up stairs c = NULL; - while (!c || !isempty(c) || countobs(c->obpile)) { + while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { c = getrandomroomcell(map, ANYROOM); } o = addob(c->obpile, "staircase going up"); linkstairs(o); + // first dungeon level has barriers over the exit stairs + if ((map->region == RG_FIRSTDUNGEON) && (map->depth == 1)) { + if (c->lf) killlf(c->lf); + addob(c->obpile, "magical barrier"); + /* + int d; + // surround stairs with barriers + for (d = DC_N; d <= DC_NW; d++) { + cell_t *newc; + newc = getcellindir(c, d); + + if (newc && !newc->type->solid) { + // kill lfs there + if (newc->lf) killlf(newc->lf); + // add a barrier + addob(newc->obpile, "magical barrier"); + } + } + */ + } c = NULL; - while (!c || !isempty(c) || countobs(c->obpile)) { + while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { c = getrandomroomcell(map, ANYROOM); } o = addob(c->obpile, "staircase going down"); linkstairs(o); } + // add pillars & objects & monsters to rooms if (wantrooms && (numrooms > 0)) { - // add pillars & objects & monsters to rooms for (i = 0; i < numrooms; i++) { int numobsmin,numobsmax,numobs,n; int maxpillars; @@ -1120,7 +1167,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { cell_t *c; c = getrandomroomcell(map, i); - if (c && isempty(c) && !countobs(c->obpile)) { + if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { setcelltype(cell, CT_WALL); } } @@ -1154,8 +1201,9 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { while (!done) { c = getrandomroomcell(map, i); // if nothing there - if (c && isempty(c) && !countobs(c->obpile)) { + if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { int obchance; + int nadded = 0; // limit # monster per room to depth+1 if (nmonsters >= (depth+1)) { @@ -1165,8 +1213,8 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { obchance = getobchance(map->habitat) + 10; } - if (addrandomthing(c,obchance) == TT_MONSTER) { - nmonsters++; + if (addrandomthing(c,obchance, &nadded) == TT_MONSTER) { + nmonsters += nadded; } done = B_TRUE; } else { @@ -1314,6 +1362,7 @@ void createmap(map_t *map, int depth, int region, int habitat, map_t *parentmap, for (i = depth-1; i <= depth+1; i += 2) { map_t *othermap; othermap = findregionmap(map->region, i); + // TODO: set upmap/downmap for use later on. if (othermap) { if (i == (depth-1)) { map->nextmap[D_UP] = othermap->id; @@ -1438,7 +1487,7 @@ void createmap(map_t *map, int depth, int region, int habitat, map_t *parentmap, } } - // join up any unlinked staircases + // join up any unlinked staircases in this map. for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { cell_t *c; @@ -1451,6 +1500,9 @@ void createmap(map_t *map, int depth, int region, int habitat, map_t *parentmap, } } + // TODO: search for unlinked pits/holes in roof in adjacent maps + // if we find any, add a matching end as close as we can in this map. + // add random objects and monsters for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { @@ -1458,7 +1510,7 @@ void createmap(map_t *map, int depth, int region, int habitat, map_t *parentmap, c = getcellat(map, x, y); if (c && isempty(c)) { if (rnd(1,100) <= getthingchance(map->habitat)) { - addrandomthing(c, getobchance(map->habitat)); + addrandomthing(c, getobchance(map->habitat), NULL); } } } @@ -1833,6 +1885,7 @@ cell_t *findmapentrypoint(map_t *m, int side, lifeform_t *lf) { cell_t *selection = NULL; cell_t *bestcell = NULL; int closest = 999; +// oooooo TODO handle side being diagonal switch (side) { case D_N: x = 0; @@ -2120,10 +2173,10 @@ int getthingchance(int habitat) { } cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand) { - return real_getrandomadjcell(c, wantempty, allowexpand, NULL); + return real_getrandomadjcell(c, wantempty, allowexpand, LOF_NEED, NULL); } -cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum OBTYPE *dontwantob) { +cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LOFTYPE needlof, enum OBTYPE *dontwantob) { int radius = 1; int x,y; cell_t *poss[MAXCANDIDATES]; @@ -2139,7 +2192,7 @@ cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum OB if (new && (new != c) && (getcelldist(c,new) == radius) && - haslof(c, new, LOF_WALLSTOP, NULL)) { + haslof(c, new, needlof, NULL)) { enum OBTYPE *badoid; int ok = B_FALSE; numwithlof++; @@ -2159,7 +2212,8 @@ cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum OB } } } else if (wantempty == WE_NOTWALL) { - if (!new->type->solid){ + if ((!new->type->solid) && !hasobwithflag(new->obpile, F_IMPASSABLE)) { + //if (!new->type->solid) { ok = B_TRUE; } } else { @@ -2372,13 +2426,33 @@ int hasobject(cell_t *c) { int hasknownobject(cell_t *c) { object_t *o; for (o = c->obpile->first ; o ; o = o->next) { - if (o && !hasflag(o->flags, F_SECRET)) { + if (o && canseeob(player, o)) { return B_TRUE; } } return B_FALSE; } +object_t *hastrailof(obpile_t *op, lifeform_t *lf, enum OBTYPE oid, flag_t **tflag, lifeform_t *viewer) { + object_t *o; + flag_t *f; + for (o = op->first ; o ; o = o->next) { + if (viewer && !canseeob(viewer, o)) continue; + if ((oid == NA) || (o->type->id == oid)) { + f = hasflag(o->flags, F_TRAIL); + // raceid and lfid must match + if (f && (f->val[0] == lf->race->id) && (atoi(f->text) == lf->id)) { + if (tflag) { + *tflag = f; + } + return o; + } + } + } + return NULL; +} + + int isadjacent(cell_t *src, cell_t *dst) { if (getcelldist(src, dst) == 1) { return B_TRUE; diff --git a/map.h b/map.h index 0dc43a8..bc7e9b6 100644 --- a/map.h +++ b/map.h @@ -3,9 +3,9 @@ cell_t *addcell(map_t *map, int x, int y); void addhomeobs(lifeform_t *lf); map_t *addmap(void); -lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen); +lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen, int *nadded); object_t *addrandomob(cell_t *c); -int addrandomthing(cell_t *c, int obchance); +int addrandomthing(cell_t *c, int obchance, int *nadded); int cellhaslos(cell_t *c1, cell_t *dest); cell_t *getcellat(map_t *map, int x, int y); int getcelldist(cell_t *src, cell_t *dst); @@ -43,7 +43,7 @@ int getnewdigdir(cell_t *cell, int lastdir, int turnpct, int *moved); int getobchance(int habitat); int getthingchance(int habitat); cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand); -cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum OBTYPE *dontwantob); +cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LOFTYPE needlof, enum OBTYPE *dontwantob); cell_t *getrandomcell(map_t *map); cell_t *getrandomcelloftype(map_t *map, int id); int getrandomdir(int dirtype); @@ -55,6 +55,7 @@ object_t *hasenterableobject(cell_t *c); lifeform_t *haslf(cell_t *c); int hasknownobject(cell_t *c); int hasobject(cell_t *c); +object_t *hastrailof(obpile_t *op, lifeform_t *lf, enum OBTYPE oid, flag_t **tflag, lifeform_t *viewer); int isadjacent(cell_t *src, cell_t *dst); int isdark(cell_t *c); int isdiggable(cell_t *c); diff --git a/move.c b/move.c index 6ee5911..4bfb4f7 100644 --- a/move.c +++ b/move.c @@ -29,66 +29,65 @@ extern WINDOW *gamewin, *msgwin; int canandwillmove(lifeform_t *lf, int dir, enum ERROR *error) { if (isplayer(lf)) { - if (canmove(lf, dir, error)) { + if (ispossiblemove(lf, dir)) { return B_TRUE; } } else { - if (canmove(lf, dir, error) && willmove(lf, dir, error)) { + if (ispossiblemove(lf, dir) && willmove(lf, dir, error)) { return B_TRUE; } } return B_FALSE; } -int canmove(lifeform_t *lf, int dir, enum ERROR *error) { - cell_t *cell; - flag_t *f; - - // default - if (error) { - *error = E_OK; - rdata = NULL; +int isorthogonal(int dir) { + switch (dir) { + case D_N: + case D_E: + case D_S: + case D_W: + case DC_N: + case DC_E: + case DC_S: + case DC_W: + return B_TRUE; } - - if (isburdened(lf) >= BR_OVERLOADED) { - if (error) *error = E_TOOHEAVY; - return B_FALSE; - } - - // check if we are paralyzed, frozen, etc - if (isimmobile(lf)) { - if (error) *error = E_CANTMOVE; - return B_FALSE; - } - - cell = getcellindir(lf->cell, dir); - if (!cell) { - if (error) *error = E_OFFMAP; - return B_FALSE; - } - - f = lfhasflag(lf, F_GRABBEDBY); - if (f) { - lifeform_t *lf2; - lf2 = findlf(NULL, f->val[0]); - if (lf2 && (lf2 != cell->lf)) { - if (error) { - rdata = lf2; - *error = E_GRABBEDBY; - } - return B_FALSE; - } - } - - // not attacking - if (lfhasflag(lf, F_DOESNTMOVE) && !cell->lf) { - *error = E_CANTMOVE; - return B_FALSE; - } - - return cellwalkable(lf, cell, error); + return B_FALSE; } +// ie. moving into a wall isn't possible +// moving into a lf/door IS possible since you will attack/open it +// moving while grabbed IS possible since you'll try to break free +int ispossiblemove(lifeform_t *lf, int dir) { + enum ERROR error; + if (moveclear(lf, dir, &error)) { + return B_TRUE; + } else { + object_t *inway = NULL; + switch (error) { + case E_OFFMAP: + if (lf->cell->map->region == RG_WORLDMAP) { + return B_TRUE; + } + break; + case E_DOORINWAY: + case E_CANTMOVE: + case E_GRABBEDBY: + case E_TOOHEAVY: + case E_LFINWAY: + return B_TRUE; + case E_OBINWAY: + inway = (object_t *)rdata; + if (inway && ispushable(inway)) { + return B_TRUE; + } + break; + default: + break; + } + } + return B_FALSE; +} // lf is the one moving, lf2 is the one who is being forced to swap int canswapwith(lifeform_t *lf, lifeform_t *lf2) { @@ -96,6 +95,12 @@ int canswapwith(lifeform_t *lf, lifeform_t *lf2) { if (isplayer(lf2)) { return B_FALSE; } + + // mosnters don't swap with people who have F_NOSWAP. + if (!isplayer(lf) && lfhasflag(lf2, F_NOSWAP)) { + return B_FALSE; + } + // cannot swap if lf's cell is dangerous to lf2 if (celldangerous(lf2, lf->cell, B_FALSE, NULL)) { return B_FALSE; @@ -133,6 +138,16 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err // obvious things that you can see if (!onlyifknown || (haslos(lf, cell) && !lfhasflag(lf, F_UNDEAD))) { for (o = cell->obpile->first ; o ; o = o->next) { + f = hasflag(o->flags, F_DEEPWATER); + if (f) { + if (!isairborne(lf) && (getobdepth(o, lf) >= DP_HEAD) && !getskill(lf, SK_SWIMMING)) { + if (error) { + *error = E_AVOIDOB; + rdata = o; + } + return B_TRUE; + } + } f = hasflag(o->flags, F_WALKDAM); if (f) { if ((f->val[0] != DT_WATER) || isvulnto(lf->flags, DT_WATER)) { @@ -205,6 +220,14 @@ int cellwalkable(lifeform_t *lf, cell_t *cell, enum ERROR *error) { // must check for lf before checking for impassable objects, // so that we are able to attack monsters embedded in walls. if (cell->lf && (cell->lf != lf)) { + // usually can't attack while swimming + if (isswimming(lf) && (getskill(lf, SK_SWIMMING) <= PR_EXPERT)) { + if (!lfhasflag(lf, F_AQUATIC)) { + if (error) *error = E_SWIMMING; + return B_FALSE; + } + } + if (error) *error = E_LFINWAY; return B_FALSE; } @@ -345,6 +368,12 @@ int getdiraway(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int d int nposs; enum ERROR error; + if (dirtype == DT_ORTH) { + maxdist = getcelldistorth(src, dst); + } else { + maxdist = getcelldist(src, dst); + } + for (d = DC_N; d <= DC_NW; d++) { dist[d - DC_N] = -1; } @@ -429,12 +458,19 @@ int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, in int nposs; enum ERROR error; + if (dirtype == DT_ORTH) { + mindist = getcelldistorth(src, dst); + } else { + mindist = getcelldist(src, dst); + } + for (d = DC_N; d <= DC_NW; d++) { dist[d - DC_N] = -1; } for (d = DC_N; d <= DC_NW; d++) { int ok = B_FALSE; + int thisdist; c = getcellindir(src, d); if (!c) continue; @@ -465,16 +501,20 @@ int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, in ok = B_TRUE; } } + + // get distance if (ok) { - int thisdist; if (dirtype == DT_ORTH) { thisdist = getcelldistorth(c, dst); } else { thisdist = getcelldist(c, dst); } - dist[d - DC_N] = thisdist; + if (thisdist < mindist) { + dist[d - DC_N] = thisdist; mindist = thisdist; + } else { + dist[d - DC_N] = -1; } } else { dist[d - DC_N] = -1; // ie. invalid @@ -536,7 +576,7 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc } for (i = 0; i < howfar; i++) { - if (canmove(lf, dir, &reason)) { + if (moveclear(lf, dir, &reason)) { if ((i == 0) && seen) { msg("%s %s knocked backwards!",lfname,is(lf)); } @@ -561,6 +601,7 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc // don't fall mightfall = B_FALSE; break; + case E_SWIMMING: case E_LFINWAY: newcell = getcellindir(lf->cell, dir); newlf = newcell->lf; @@ -619,6 +660,62 @@ int moveawayfrom(lifeform_t *lf, cell_t *dst, int dirtype ) { return rv; } +// ie. is the destination cell free? +int moveclear(lifeform_t *lf, int dir, enum ERROR *error) { + cell_t *cell; + flag_t *f; + + // default + if (error) { + *error = E_OK; + rdata = NULL; + } + + if (isburdened(lf) >= BR_OVERLOADED) { + if (error) *error = E_TOOHEAVY; + return B_FALSE; + } + + // check if we are paralyzed, frozen, etc + if (isimmobile(lf)) { + if (error) *error = E_CANTMOVE; + return B_FALSE; + } + + cell = getcellindir(lf->cell, dir); + if (!cell) { + if (error) *error = E_OFFMAP; + return B_FALSE; + } + + f = lfhasflag(lf, F_GRABBEDBY); + if (f) { + lifeform_t *lf2; + lf2 = findlf(NULL, f->val[0]); + if (lf2 && (lf2 != cell->lf)) { + if (error) { + rdata = lf2; + *error = E_GRABBEDBY; + } + return B_FALSE; + } + } + + if ((lf->race->id == RC_DEMON) && hasob(cell->obpile, OT_PENTAGRAM)) { + *error = E_PENTAGRAM; + return B_FALSE; + } + + + // not attacking + if (lfhasflag(lf, F_DOESNTMOVE) && !cell->lf) { + *error = E_CANTMOVE; + return B_FALSE; + } + + return cellwalkable(lf, cell, error); +} + // IMPORTANT: don't modify lf's flagpile during this code! // particularly don't remove flags... @@ -673,6 +770,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) { int changedlev = B_FALSE; int preroom = -1, postroom = -1; int prespeed = B_FALSE, postspeed = B_FALSE; + int prewater = B_FALSE; assert(newcell); @@ -689,8 +787,14 @@ int movelf(lifeform_t *lf, cell_t *newcell) { // update current cell + room id prespeed = getmovespeed(lf); preroom = lf->cell->roomid; - lf->cell->lf = NULL; + // getting out of water? + if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { + prewater = B_TRUE; + } + + // move out... + lf->cell->lf = NULL; // if required, relink lifeform to new map if (newcell->map != lf->cell->map) { @@ -756,10 +860,39 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } + if (isplayer(lf)) { + if (prewater && !hasobwithflag(newcell->obpile, F_DEEPWATER)) { + // getitng out of water? + statdirty = B_TRUE; + } + } + // check ground objects if (!isairborne(lf)) { for (o = newcell->obpile->first ; o ; o = nexto ) { nexto = o->next; + + f = hasflag(o->flags, F_DEEPWATER); + if (f) { + if (checkfordrowning(lf, o)) { + didmsg = B_TRUE; + if (isdead(lf)) return B_TRUE; + } + // did you just enter the water? + if (!prewater) { + if (getskill(lf, SK_SWIMMING)) { + if (isplayer(lf)) { + msg("You start swimming."); + didmsg = B_TRUE; + statdirty = B_TRUE; + } else if (cansee(player, lf)) { + msg("%s starts swimming.", lfname); + didmsg = B_TRUE; + } + } + } + } + f = hasflag(o->flags, F_SHARP); if (f && hasbp(lf, BP_FEET)) { object_t *boots; @@ -994,7 +1127,7 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { // see objects on ground if (isplayer(lf)) { int numobs; - numobs = countnoncosmeticobs(newcell->obpile); + numobs = countnoncosmeticobs(newcell->obpile, B_TRUE); if ((numobs == 0) && !newcell->writing) { // just clear the message buffer if (!didmsg) clearmsg(); @@ -1363,11 +1496,26 @@ int pullnextto(lifeform_t *lf, cell_t *c) { // do pre-move checks like slipping on stuff in your current cell, // webs, etc. // cell can be null if you're using stairs. + +// return true if something happened int initiatemove(lifeform_t *lf, cell_t *cell, int *didmsg) { object_t *o, *nexto; char buf[BUFLEN]; flag_t *f; + // demon in pentagram + if ((getraceclass(lf) == RC_DEMON) && hasob(lf->cell->obpile, OT_PENTAGRAM)) { + if (isplayer(lf)) { + msg("You cannot escape the pentagram!"); + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s struggles within a pentagram!", lfname); + } + reason = E_OK; + taketime(lf, getmovespeed(lf)); + return B_TRUE; + } // gravboosted if (lfhasflag(lf, F_GRAVBOOSTED)) { @@ -1544,6 +1692,11 @@ void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int onpurpose) { // move them... lf2->cell = cell1; cell1->lf = lf2; + + // remember that we just swapped + if (!isplayer(lf1)) { + addflag(lf1->flags, F_NOSWAP, B_TRUE, NA, NA, NULL); + } } // teleport somewhere, along with puffs of smoke etc @@ -1585,7 +1738,7 @@ int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) { // show any objects here, just like if we moved. // BUT don't let dolook() clear the msg bar if there are // no objects here. - if (isplayer(lf) && countnoncosmeticobs(lf->cell->obpile)) { + if (isplayer(lf) && countnoncosmeticobs(lf->cell->obpile, B_TRUE)) { dolook(lf->cell, B_FALSE); } return B_FALSE; @@ -1598,6 +1751,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { char buf[BUFLEN]; int dontclearmsg = B_FALSE; int moveok; + int drunk = B_FALSE; flag_t *f; f = isdrunk(lf); @@ -1606,19 +1760,36 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { if (rnd(1,6) <= ((f->lifetime/DRUNKTIME)+1)) { // randomize move dir = rnd(DC_N, DC_NW); + drunk = B_TRUE; // ie. you can walk into walls now. } } } cell = getcellindir(lf->cell, dir); - moveok = B_FALSE; - if (onpurpose) { - if (canandwillmove(lf, dir, &errcode)) { - moveok = B_TRUE; + if (celldangerous(lf, cell, B_TRUE, &errcode)) { + if ((errcode == E_AVOIDOB) && rdata) { + char obname[BUFLEN]; + char ques[BUFLEN]; + char ch; + object_t *avoidob; + avoidob = (object_t *)rdata; + getobname(avoidob, obname, avoidob->amt); + sprintf(ques, "Really %s into %s?", getmoveverb(lf), obname); + ch = askchar(ques, "yn","n", B_TRUE); + if (ch != 'y') { + return B_TRUE; + } } - } else { - if (canmove(lf, dir, &errcode)) { + } + + moveok = B_FALSE; + if (moveclear(lf, dir, &errcode)) { + if (onpurpose) { + if (canandwillmove(lf, dir, &errcode)) { + moveok = B_TRUE; + } + } else { moveok = B_TRUE; } } @@ -1627,27 +1798,36 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { lifeform_t *alf; if (initiatemove(lf, cell, &dontclearmsg)) { // failed? - return B_FALSE; + return B_TRUE; } - // move to new cell + reason = E_OK; // remember last dir we walked killflagsofid(lf->flags, F_LASTDIR); addflag(lf->flags, F_LASTDIR, dir, NA, NA, NULL); + // add footprints in our current cell. + addtrail(lf, dir); + // do your pets see you move? if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) { lifeform_t *l; for (l = lf->cell->map->lf ; l ; l = l->next) { - if (ispetof(l,lf) && cansee(l, lf)) { - killflagsofid(l->flags, F_OWNERLASTDIR); - addflag(l->flags, F_OWNERLASTDIR, dir, NA, NA, NULL); + if (cansee(l, lf)) { + flag_t *tf; + tf = ispetortarget(l, lf); + if (tf) { + // update text field + free(tf->text); + asprintf(&(tf->text), "%d", dir); + } } } } - moveto(lf, cell, onpurpose, dontclearmsg); + // now move to new cell + moveto(lf, cell, drunk ? B_FALSE : onpurpose, dontclearmsg); if (onpurpose) { taketime(lf, getmovespeed(lf)); } @@ -1662,7 +1842,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { newdir = getdirtowards(alf->cell, lf->cell, alf, B_FALSE, DT_ORTH); // do a manual canmove check here first, to avoid 'the stirge flies into a wall' // if the move fails. - if ((newdir != D_NONE) && canmove(alf, newdir, NULL)) { + if ((newdir != D_NONE) && moveclear(alf, newdir, NULL)) { trymove(alf, newdir, B_FALSE); } @@ -1675,11 +1855,11 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { reason = errcode; switch (errcode) { case E_OFFMAP: - if (lf->cell->map->region == RG_WORLDMAP) { + if ((lf->cell->map->region == RG_WORLDMAP) && (isorthogonal(dir))) { // we are allowed to walk off the edge - walkoffmap(lf, dir, B_TRUE); - } - break; + return walkoffmap(lf, dir, B_TRUE); + } + // otherwise fall through... case E_WALLINWAY: if (isplayer(lf)) { msg("Ouch! You %s into a wall.", getmoveverb(lf)); @@ -1718,7 +1898,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { opendoor(lf, inway); } else { msg("Ouch! You %s into a door.", getmoveverb(lf)); - setcellknown(cell, B_FALSE); + setcellknown(cell, B_FALSE); sprintf(buf, "%sing into a door", getmoveverb(lf)); losehp(lf, 1, DT_BASH, NULL, buf); if (onpurpose) taketime(lf, getmovespeed(lf)); @@ -1777,6 +1957,11 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { } } break; + case E_SWIMMING: + if (isplayer(lf)) { + msg("You can't attack while swimming!"); + } + break; case E_LFINWAY: if (canswapwith(lf, cell->lf)) { lifeform_t *lfinway; @@ -1828,6 +2013,12 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { } if (onpurpose) taketime(lf, getmovespeed(lf)); break; + case E_PENTAGRAM: + if (isplayer(lf)) { + msg("You cannot seem to enter the pentagram."); + } + if (onpurpose) taketime(lf, getmovespeed(lf)); + break; case E_TOOHEAVY: if (isplayer(lf)) { msg("Your load is too heavy to move with!"); @@ -1956,13 +2147,14 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { } cell = getcellindir(lf->cell, dir); - if (celldangerous(lf, cell, B_TRUE, error)) { + + if (cell && celldangerous(lf, cell, B_TRUE, error)) { if (error) *error = E_WONT; return B_FALSE; } // don't attack other monsters - if (cell->lf) { // if someone is in the way + if (cell && cell->lf) { // if someone is in the way if (lf->race->raceclass->id == RC_INSECT) { if (hasactivespell(cell->lf, OT_S_REPELINSECTS)) { if (error) *error = E_WONT; @@ -1993,33 +2185,35 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { } // look for avoided objects (because they are cursed). - for (o = cell->obpile->first ; o ; o = o->next) { - flag_t *f; - sprintf(buf, "%ld",o->id); - f = lfhasflagval(lf, F_AVOIDCURSEDOB, NA, NA, NA, buf); - if (f) { - // still cursed? - if (iscursed(o)) { - if (error) *error = E_WONT; - return B_FALSE; - } else { - // remove the flag. - killflag(f); - } - } - - if (hasflag(o->flags, F_TRAP)) { - if (hasflag(o->flags, F_SECRET)) { - // hidden traps? - if (iq >= IQ_SMART) { + if (cell) { + for (o = cell->obpile->first ; o ; o = o->next) { + flag_t *f; + sprintf(buf, "%ld",o->id); + f = lfhasflagval(lf, F_AVOIDCURSEDOB, NA, NA, NA, buf); + if (f) { + // still cursed? + if (iscursed(o)) { if (error) *error = E_WONT; return B_FALSE; + } else { + // remove the flag. + killflag(f); } - } else { - // non-hidden traps? - if (iq >= IQ_AVERAGE) { - if (error) *error = E_WONT; - return B_FALSE; + } + + if (hasflag(o->flags, F_TRAP)) { + if (hasflag(o->flags, F_SECRET)) { + // hidden traps? + if (iq >= IQ_SMART) { + if (error) *error = E_WONT; + return B_FALSE; + } + } else { + // non-hidden traps? + if (iq >= IQ_AVERAGE) { + if (error) *error = E_WONT; + return B_FALSE; + } } } } diff --git a/move.h b/move.h index 78a4c2a..c23964e 100644 --- a/move.h +++ b/move.h @@ -1,7 +1,6 @@ #include "defs.h" int canandwillmove(lifeform_t *lf, int dir, enum ERROR *error); -int canmove(lifeform_t *lf, int dir, enum ERROR *error); int canswapwith(lifeform_t *lf, lifeform_t *lf2); int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *error); int cellwalkable(lifeform_t *lf, cell_t *cell, enum ERROR *error); @@ -13,6 +12,7 @@ int getdiraway(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int d int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int dirtype); int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallcheckdiff); int moveawayfrom(lifeform_t *lf, cell_t *dst, int dirtype); +int moveclear(lifeform_t *lf, int dir, enum ERROR *error); int moveeffects(lifeform_t *lf); int movelf(lifeform_t *lf, cell_t *newcell); int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg); @@ -21,6 +21,8 @@ int opendoorat(lifeform_t *lf, cell_t *c); int opendoor(lifeform_t *lf, object_t *o); int pullnextto(lifeform_t *lf, cell_t *c); int initiatemove(lifeform_t *lf, cell_t *cell, int *didmsg); +int isorthogonal(int dir); +int ispossiblemove(lifeform_t *lf, int dir); void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int onpurpose); int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke); int trymove(lifeform_t *lf, int dir, int onpurpose); diff --git a/nexus.c b/nexus.c index 9861988..18e4024 100644 --- a/nexus.c +++ b/nexus.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include "ai.h" @@ -34,6 +35,9 @@ hiddenname_t *firsthiddenname = NULL, *lasthiddenname = NULL; glyph_t playerglyph,tempglyph; +double startticks,lastticks; +struct timeval starttv, tv,newtv; + // maintains unique lifeform ID numbers long nextlfid = 0; @@ -58,6 +62,8 @@ void *rdata; // globel for returning data lifeform_t *player = NULL; int gameover; +int obdb = B_FALSE; + enum GAMEMODE gamemode = GM_FIRST; long curtime = 0; @@ -171,7 +177,9 @@ int main(int argc, char **argv) { if (where->lf) { killlf(where->lf); } - // add player + + // add player nearby + where = real_getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL); real_addlf(where, R_HUMAN, 1, C_PLAYER); // this will assign 'player' user = getenv("USER"); @@ -464,6 +472,27 @@ void cleanup(void) { // free races } +void dbtimestart(char *text) { + gettimeofday(&tv, NULL); + starttv = tv; + dblog("START\t%s", text); +} + +void dbtime(char *text) { + double ticks; + gettimeofday(&newtv, NULL); + ticks = ((newtv.tv_sec - tv.tv_sec) * 1000000) + (newtv.tv_usec - tv.tv_usec); + dblog("+%f\t%s", ticks, text); + tv = newtv; +} + +void dbtimeend(char *text) { + double ticks; + gettimeofday(&newtv, NULL); + ticks = ((newtv.tv_sec - starttv.tv_sec) * 1000000) + (newtv.tv_usec - starttv.tv_usec); + dblog("FINISHED %s (total time %f)", text, ticks); +} + void dobresnham(int d, int xinc1, int yinc1, int dinc1, int xinc2, int yinc2, int dinc2, int *xinc, int *yinc, int *dinc) { if (d < 0) { *xinc = xinc1; @@ -953,7 +982,7 @@ int rollhitdice(lifeform_t *lf) { int roll = 0; int i; float mod; - int db = B_FALSE; + int db = B_TRUE; f = hasflag(lf->flags, F_HITDICE); if (f) { @@ -964,11 +993,11 @@ int rollhitdice(lifeform_t *lf) { ndice = 1; plus = 0; } - if (db && isplayer(lf)) dblog("rollhitdice() - rolling %dd4 + %d",ndice,plus); + if (db) dblog("rollhitdice() for %s - rolling %dd4 + %d",lf->race->name,ndice,plus); mod = getstatmod(lf, A_CON); if (mod > 0) mod *= 2; - if (db && isplayer(lf)) dblog("rollhitdice() - mod is +%0.0f%%",mod); + if (db) dblog("rollhitdice() - mod is +%0.0f%%",mod); if (ndice == 0) { int thisroll; @@ -983,14 +1012,14 @@ int rollhitdice(lifeform_t *lf) { int thisroll; thisroll = rolldie(1, 4) + plus; if (thisroll < 1) thisroll = 1; - if (db && isplayer(lf)) dblog("rollhitdice() ---- die %d/%d == %d",i+1,ndice,thisroll); + if (db) dblog("rollhitdice() ---- die %d/%d == %d",i+1,ndice,thisroll); roll += thisroll; } } - if (db && isplayer(lf)) dblog("TOTAL: %d",roll); + if (db) dblog("TOTAL: %d",roll); roll = roll + (int)((float)roll * (mod/100)); - if (db && isplayer(lf)) dblog(" -> modified to: %d",roll); + if (db) dblog(" -> modified to: %d",roll); return roll; } diff --git a/nexus.h b/nexus.h index 7b5205c..5af2d12 100644 --- a/nexus.h +++ b/nexus.h @@ -5,6 +5,9 @@ command_t *addcommand(enum COMMAND id, char c, char *desc); void checkdeath(void); void checkendgame(void); void cleanup(void); +void dbtime(char *text); +void dbtimeend(char *text); +void dbtimestart(char *text); void dobresnham(int d, int xinc1, int yinc1, int dinc1, int xinc2, int yinc2, int dinc2, int *xinc, int *yinc, int *dinc); void donextturn(map_t *map); char *getdirname(int dir); diff --git a/objects.c b/objects.c index 61da7a3..425b712 100644 --- a/objects.c +++ b/objects.c @@ -44,6 +44,8 @@ extern int reason; extern int needredraw; extern int statdirty; +extern int obdb; + char letorder[MAXPILEOBS] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', @@ -658,6 +660,8 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { // inherit flags from objecttype copyflags(o->flags, ot->flags, NA); + // don't want certain objecttype only flags... + killflagsofid(o->flags, F_RARITY); // random flags... f = hasflag(o->flags, F_RNDCHARGES); @@ -1146,6 +1150,8 @@ int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fr if (!where) return 0; + redrawpause(); + if (dirtype == DT_ORTH) { distfunc = getcelldistorth; } else { @@ -1168,6 +1174,7 @@ int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fr } } + redrawresume(); return nadded; } @@ -1413,6 +1420,11 @@ void adjustdamob(object_t *o, unsigned int *dam, enum DAMTYPE damtype) { } } + if (hasflag(o->flags, F_INVULNERABLE)) { + *dam = 0; + return; + } + // immune? if (isimmuneto(o->flags, damtype)) { *dam = 0; @@ -1750,6 +1762,54 @@ int canbepoisoned(enum OBTYPE oid) { return B_FALSE; } +int canseeob(lifeform_t *lf, object_t *o) { + flag_t *f; + if (gamemode != GM_GAMESTARTED) { + return B_TRUE; + } + + if (hasflag(o->flags, F_SECRET) && isplayer(lf)) { + // can't see + return B_FALSE; + } + f = hasflag(o->flags, F_TRAIL); + if (f) { + if (f->val[2] == S_SIGHT) { + enum SKILLLEVEL slev; + int cutoffpct; + int cutoff; + slev = getskill(lf, SK_TRACKING); + switch (slev) { + case PR_NOVICE: cutoffpct = 80; break; + case PR_BEGINNER: cutoffpct = 65; break; + case PR_ADEPT: cutoffpct = 50; break; + case PR_SKILLED: cutoffpct = 35; break; + case PR_EXPERT: cutoffpct = 20; break; + case PR_MASTER: cutoffpct = 0; break; + default: + case PR_INEPT: cutoffpct = 200; break; + } + + cutoff = pctof(cutoffpct, FOOTPRINTTIME); + + if (f->lifetime >= cutoff) { + return B_TRUE; + } else { + return B_FALSE; + } + } else { + // ie. SCENT + // can't smell your own race... + if ((f->val[0] != lf->race->id) && lfhasflag(lf, F_ENHANCESMELL)) { + return B_TRUE; + } else { + return B_FALSE; + } + } + } + return B_TRUE; +} + // does the pile "op" have an object we can // stack "match" with object_t *canstackob(obpile_t *op, object_t *match) { @@ -1929,23 +1989,35 @@ int countnames(char **list) { return count; } -int countobs(obpile_t *op) { +int countobs(obpile_t *op, int onlyifknown) { object_t *o; int count = 0; for (o = op->first ; o ; o = o->next) { - count++; + if (onlyifknown) { + if (canseeob(player, o)) { + count++; + } + } else { + count++; + } } return count; } -int countnoncosmeticobs(obpile_t *op) { +int countnoncosmeticobs(obpile_t *op, int onlyifknown) { object_t *o; int count = 0; for (o = op->first ; o ; o = o->next) { if (!hasflag(o->flags, F_COSMETIC) && !hasflag(o->flags, F_SECRET)) { - count++; + if (onlyifknown) { + if (canseeob(player, o)) { + count++; + } + } else { + count++; + } } } return count; @@ -1993,11 +2065,17 @@ int curseob(object_t *o) { return rv; } -void damageallobs(object_t *exception, obpile_t *op, int howmuch, int damtype) { +void damageallobs(object_t *srcob, obpile_t *op, int howmuch, int damtype) { object_t *o, *nexto; for (o = op->first ; o ; o = nexto) { nexto = o->next; - if ((o != exception) && !hasflag(o->flags, F_DEAD)) { + + // special case to stop steam from hurting water + if (srcob && (srcob->material->id == MT_GAS)) { + continue; + } + + if ((o != srcob) && !hasflag(o->flags, F_DEAD)) { takedamage(o, howmuch, damtype); } } @@ -3023,6 +3101,22 @@ int getnutrition(object_t *o) { return (int)nutrition; } +enum DEPTH getobdepth(object_t *o, lifeform_t *lf) { + int depth = DP_NONE; + flag_t *f; + f = hasflag(o->flags, F_DEEPWATER); + if (f) { + depth = f->val[0]; + if (lf) { + int mod; + mod = SZ_HUMAN - getlfsize(lf); + depth += mod; + limit(&depth, DP_NONE, DP_HEAD); + } + } + return depth; +} + char *getobdesc(object_t *o, char *buf) { if (isknown(o)) { if (o->type->id == OT_CORPSE) { @@ -3197,10 +3291,88 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan showall = B_TRUE; } - if (showall) { - strcpy(basename,o->type->name); + f = hasflag(o->flags, F_TRAIL); + if (f) { + race_t *r = NULL; + enum SKILLLEVEL slev; + slev = getskill(player, SK_TRACKING); + r = findrace(f->val[0]); + assert(r); + + if (f->val[2] == S_SMELL) { + char buf[BUFLEN]; + float pct; + char dname[BUFLEN]; + char adjective[BUFLEN]; + + lifeform_t *who = NULL; + pct = ((float)f->lifetime / (float)SCENTTIME)*100; + if (pct >= 66) { + strcpy(adjective, "strong "); + } else if (pct >= 33) { + strcpy(adjective, ""); + } else { + strcpy(adjective, "weak "); + } + + strcpy(basename, ""); + + if (strlen(f->text)) { + who = findlf(where->map, atoi(f->text)); + } + if (who) { + char lfname[BUFLEN]; + real_getlfname(who, lfname, B_FALSE); + sprintf(buf, "%s%s %sscent",lfname,getpossessive(lfname), adjective); + } else { + sprintf(buf, "%s %sscent",r->name, adjective); + } + strcat(basename, buf); + + strcat(basename, " leading "); + sprintf(dname, "%s", getdirname(f->val[1])); + dname[0] = tolower(dname[0]); + strcat(basename, dname); + } else { + char buf[BUFLEN]; + // adept and upwards gets depth + if (slev >= PR_BEGINNER) { + float pct; + pct = ((float)f->lifetime / (float)FOOTPRINTTIME)*100; + if (pct >= 66) { + strcpy(basename, "fresh "); + } else if (pct >= 33) { + strcpy(basename, ""); + } else { + strcpy(basename, "faint "); + } + } else { + strcpy(basename, ""); + } + + // adept and upwards get "monstername footprints" + if (slev >= PR_ADEPT) { + sprintf(buf, "%s %s",r->name, o->type->name); + strcat(basename, buf); + } else { + sprintf(basename, "%s", o->type->name); + } + + // skilled and upwards get the direction + if (slev >= PR_SKILLED) { + char dname[BUFLEN]; + strcat(basename, " leading "); + sprintf(dname, "%s", getdirname(f->val[1])); + dname[0] = tolower(dname[0]); + strcat(basename, dname); + } + } // end if sight/smell } else { - strcpy(basename,gethiddenname(o)); + if (showall) { + strcpy(basename,o->type->name); + } else { + strcpy(basename,gethiddenname(o)); + } } if (o->type->obclass->id == OC_BOOK) { @@ -3306,7 +3478,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan strcpy(localbuf, ""); // figure out pluralname - if ((count == 1) && !hasflag(o->flags, F_NO_A)) { + if (count == 1) { pluralname = strdup(basename); } else { // multiple objects? @@ -3399,7 +3571,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan int bonus; bonus = f->val[0]; if (bonus != 0) { - sprintf(buf2, "%s%d ", (bonus < 0) ? "" : "+", bonus); + sprintf(buf2, "%s%d ", (bonus < 0) ? "-" : "+", abs(bonus)); strcat(localbuf, buf2); } } @@ -3513,23 +3685,32 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } // apply prefix now! - if ((count == 1) && !hasflag(o->flags, F_NO_A)) { - if (hasflag(o->flags, F_UNIQUE)) { // TODO: && o->identified - strcpy(prefix, "The"); - } else { - if (needan(localbuf)) { - strcpy(prefix, "an"); + if (count == 1) { + if (hasflag(o->flags, F_NO_A)) { + if (o->type->id == OT_GOLD) { + sprintf(prefix, "%d ",count); } else { - strcpy(prefix, "a"); + // nothing. + strcpy(prefix, ""); + } + } else { + if (hasflag(o->flags, F_UNIQUE)) { // TODO: && o->identified + strcpy(prefix, "The "); + } else { + if (needan(localbuf)) { + strcpy(prefix, "an "); + } else { + strcpy(prefix, "a "); + } } } } else { // multiple objects? - sprintf(prefix, "%d",count); + sprintf(prefix, "%d ",count); } // prepend prefix on to buf - sprintf(buf, "%s %s", prefix, localbuf); + sprintf(buf, "%s%s", prefix, localbuf); return buf; } @@ -3688,8 +3869,10 @@ char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth flag_t *omposs[MAXCANDIDATES]; int noms = 0; + db = obdb; - if (forcedepth != NA) { + //if (forcedepth != NA) { + if (forcedepth >= 0) { depth = forcedepth; } else { depth = getmapdifficulty(map); @@ -3751,7 +3934,7 @@ char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth } if (rarok && condok) { - //if (db) dblog("-> possibility: %s, rarity=%d",ot->name, rarflag->val[1]); + if (db) dblog("-> possibility: %s, rarity=%d",ot->name, rarflag->val[1]); poss[nposs] = ot; nposs++; if (nposs >= MAXRANDOMOBCANDIDATES) break; @@ -3901,6 +4084,7 @@ char *getrandomobwithdt(map_t *map, enum DAMTYPE damtype, char *buf) { char *getrandomobwithclass(map_t *map, enum OBCLASS cid, char *buf, int depthmod) { //return real_getrandomob(map, buf, RO_OBCLASS, cid, map->depth + depthmod); + if (depthmod == NA) depthmod = 0; return real_getrandomob(map, buf, RO_OBCLASS, cid, getmapdifficulty(map) + depthmod); } @@ -4060,6 +4244,25 @@ int getthrowdam(object_t *o) { return (int)dam; } +char *gettopobname(cell_t *c, char *retbuf) { + char buf[BUFLEN]; + object_t *o; + o = gettopobject(c); + if (o) { + int nother; + getobname(o, buf, o->amt); + strcat(retbuf, buf); + // other obs here too? + nother = countnoncosmeticobs(c->obpile, B_TRUE) - 1; + if (nother >= 1) { + sprintf(buf, " (+%d other thing%s)", nother, (nother == 1) ? "" : "s"); + strcat(retbuf, buf); + } + return retbuf; + } + return NULL; +} + enum BODYPART getweildloc(object_t *o, enum BODYPART *otherloc, int *twohanded) { enum BODYPART weildloc; if (o) { @@ -4570,7 +4773,7 @@ void initobjects(void) { addflag(lastot->flags, F_DOOR, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_IMPASSABLE, SZ_MAX, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LOCKABLE, B_TRUE, NA, NA, NULL); @@ -4583,7 +4786,7 @@ void initobjects(void) { addflag(lastot->flags, F_DOOR, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_IMPASSABLE, SZ_MAX, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LOCKABLE, B_TRUE, NA, NA, NULL); @@ -4599,7 +4802,7 @@ void initobjects(void) { addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL); addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); // addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); @@ -4611,7 +4814,7 @@ void initobjects(void) { addflag(lastot->flags, F_GLYPH, C_CYAN, NA, NA, "'"); addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 80, 80, NA, NULL); @@ -4621,7 +4824,7 @@ void initobjects(void) { addflag(lastot->flags, F_GLYPH, NA, NA, NA, "'"); addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL); addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 80, 80, NA, NULL); @@ -4643,16 +4846,56 @@ void initobjects(void) { addot(OT_VENDINGMACHINE, "vending machine", "A gold-operated vending machine.", MT_METAL, 500, OC_DFEATURE); addflag(lastot->flags, F_RARITY, H_ALL, 70, NA, ""); - addflag(lastot->flags, F_GLYPH, C_WHITE, NA, NA, "_"); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "_"); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addot(OT_HOLYCIRCLE, "holy circle", "A consecrated area imbued with holy power.", MT_NOTHING, 0, OC_DFEATURE); + addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, ""); + addflag(lastot->flags, F_GLYPH, C_CYAN, NA, NA, "_"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_REPELBLESSED, B_CURSED, NA, NA, NULL); + + addot(OT_PENTAGRAM, "pentagram", "A area imbued with evil.", MT_NOTHING, 0, OC_DFEATURE); + addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, ""); + addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "_"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_REPELBLESSED, B_BLESSED, NA, NA, NULL); + addot(OT_PORTAL, "magic portal", "A magical portal to a different place...", MT_MAGIC, 0, OC_DFEATURE); addflag(lastot->flags, F_GLYPH, C_BOLDGREEN, NA, NA, "&"); addflag(lastot->flags, F_CLIMBABLE, D_IN, NA, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addot(OT_WATERSHALLOW, "shallow water", "Waist-deep water.", MT_WATER, 150, OC_DFEATURE); + addflag(lastot->flags, F_NO_A, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_BLUE, NA, NA, "{"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DEEPWATER, DP_WAIST, NA, NA, NULL); + addflag(lastot->flags, F_DTCONVERT, DT_COLD, NA, NA, "sheet of ice"); + addflag(lastot->flags, F_DTCREATEOB, DT_FIRE, 1, DT_COMPASS, "cloud of steam"); + addflag(lastot->flags, F_DRINKABLE, B_TRUE, NA, B_DONTKILL, NULL); + addflag(lastot->flags, F_LINKOB, OT_POT_WATER, NA, NA, NULL); + addflag(lastot->flags, F_REDUCEMOVEMENT, 3, NA, NA, NULL); + + addot(OT_WATERDEEP, "deep water", "Very deep water.", MT_WATER, 300, OC_DFEATURE); + addflag(lastot->flags, F_NO_A, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_BOLDBLUE, NA, NA, "{"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DTCONVERT, DT_COLD, NA, NA, "sheet of ice"); + addflag(lastot->flags, F_DTCREATEOB, DT_FIRE, 1, DT_COMPASS, "cloud of steam"); + addflag(lastot->flags, F_DRINKABLE, B_TRUE, NA, B_DONTKILL, NULL); + addflag(lastot->flags, F_LINKOB, OT_POT_WATER, NA, NA, NULL); + addflag(lastot->flags, F_DEEPWATER, DP_HEAD, NA, NA, NULL); + addflag(lastot->flags, F_REDUCEMOVEMENT, 4, NA, NA, NULL); + // traps addot(OT_TRAPTRIP, "tripwire", "A thin wire at ankle height.", MT_NOTHING, 0, OC_MISC); addflag(lastot->flags, F_TRAP, 10, B_FALSE, 20, NULL); @@ -4805,7 +5048,7 @@ void initobjects(void) { addflag(lastot->flags, F_GLYPH, C_GREEN, NA, NA, "#"); addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 80, 80, NA, NULL); @@ -5143,15 +5386,18 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 addot(OT_S_WEAKEN, "weaken", "Temporarily lowers the target's muscle strength.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_PARALYZE, "paralyze", "Disables the target's muscles, leaving them unable to move.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_ANIMATEDEAD, "animate dead", "Imbues nearby corpses with life, creating an undead zombie.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); @@ -5162,6 +5408,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l7 addot(OT_S_POSSESSION, "possession", "Completely possess an enemy, moving your consciousness into their body.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); @@ -5457,6 +5704,7 @@ void initobjects(void) { addot(OT_S_DIG, "dig", "Blasts away earth to create passages.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_S_ENTANGLE, "entangle", "Causes magical vines to hold enemies.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); @@ -5708,11 +5956,13 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); // l5 addot(OT_S_DISPERSAL, "dispersal", "Scatters everything in the target cell around the area.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l6 addot(OT_S_GATE, "gate", "Creates a portal to a different dungeon level.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); @@ -6487,7 +6737,7 @@ void initobjects(void) { addot(OT_ICESHEET, "sheet of ice", "A large sheet of slippery ice.", MT_ICE, 0.5, OC_MISC); addflag(lastot->flags, F_STACKABLE, NA, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "_"); + addflag(lastot->flags, F_GLYPH, C_WHITE, NA, NA, "_"); addflag(lastot->flags, F_DTCONVERT, DT_FIRE, NA, NA, "large puddle of water"); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "large puddle of water"); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "melts"); @@ -6584,7 +6834,7 @@ void initobjects(void) { addot(OT_PUDDLEOIL, "puddle of oil", "A slippery puddle of oil.", MT_OIL, 0, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, ","); // should really be dark grey addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_FLAMMABLE, 5, NA, NA, "medium fire"); @@ -6612,9 +6862,9 @@ void initobjects(void) { addflag(lastot->flags, F_LINKOB, OT_POT_WATER, NA, NA, NULL); addot(OT_MUDPOOL, "pool of mud", "A large puddle of wet mud.", MT_WATER, 0, OC_MISC); - addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, ","); + addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, ","); addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL); addflag(lastot->flags, F_RARITY, H_FOREST, 90, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); @@ -6627,7 +6877,7 @@ void initobjects(void) { addot(OT_PUDDLEWATER, "small puddle of water", "A small puddle of water.", MT_WATER, 0, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, C_BLUE, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_BLUE, NA, NA, ","); addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL); addflag(lastot->flags, F_RARITY, H_FOREST, 90, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); @@ -6641,7 +6891,7 @@ void initobjects(void) { addot(OT_PUDDLEWATERL, "large puddle of water", "A large pool of water.", MT_WATER, 0, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, C_BLUE, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_BLUE, NA, NA, ","); addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL); addflag(lastot->flags, F_RARITY, H_FOREST, 85, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); @@ -6651,6 +6901,7 @@ void initobjects(void) { addflag(lastot->flags, F_LINKOB, OT_POT_WATER, NA, NA, NULL); addflag(lastot->flags, F_WALKDAMBP, BP_FEET, DT_WATER, FALLTHRU, "0d1+2"); + addot(OT_BLOODSTAIN, "blood stain", "A dried stain of blood.", MT_BLOOD, 0, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); @@ -6746,9 +6997,27 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); + // trail objects + addot(OT_FOOTPRINT, "footprints", "Footprints which show the passage of some kind of creature.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_NO_A, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NO_PLURAL, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "."); // ie not really visible + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + // NOTE: must add F_TRAIL when creating this object. + addot(OT_SCENT, "scent", "The scent of a creature, only perceivable to those with an enhanced sense of smell.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_NO_A, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NO_PLURAL, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "."); // ie not really visible + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + // NOTE: must add F_TRAIL when creating this object. + // effects addot(OT_FIRELARGE, "large fire", "A large, roaring inferno.", MT_FIRE, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_ORANGE, NA, NA, "}"); + addflag(lastot->flags, F_GLYPH, C_ORANGE, NA, NA, "^"); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dies down a little"); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "medium fire"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); @@ -6759,7 +7028,7 @@ void initobjects(void) { addflag(lastot->flags, F_PRODUCESLIGHT, 3, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_FIREMED, "medium fire", "A medium-sized roaring fire.", MT_FIRE, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "}"); + addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "^"); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "small fire"); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dies down a little"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); @@ -6770,7 +7039,7 @@ void initobjects(void) { addflag(lastot->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_FIRESMALL, "small fire", "A small blaze.", MT_FIRE, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "}"); + addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "^"); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "goes out"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6781,7 +7050,7 @@ void initobjects(void) { addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_STEAMCLOUD, "cloud of steam", "A thick cloud of scalding steam.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "}"); addflag(lastot->flags, F_NODIECONVERTTEXT, NA, NA, NA, NULL); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "puff of steam"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); @@ -6793,7 +7062,7 @@ void initobjects(void) { addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_STEAMPUFF, "puff of steam", "A small puff of scalding steam.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "}"); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "disperses"); addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6805,7 +7074,7 @@ void initobjects(void) { addot(OT_SLEETSTORM, "storm of sleet", "An intense storm of sleet. Hampers movement", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_CYAN, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_CYAN, NA, NA, "}"); addflag(lastot->flags, F_NODIECONVERTTEXT, NA, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6813,7 +7082,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSVIEW, 3, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_REDUCEMOVEMENT, 2, NA, NA, NULL); + addflag(lastot->flags, F_REDUCEMOVEMENT, 3, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addflag(lastot->flags, F_WALKDAMBP, BP_HEAD, DT_WATER, NA, "1d2"); addflag(lastot->flags, F_WALKDAMBP, BP_SHOULDERS, DT_WATER, NA, "1d2"); @@ -6823,7 +7092,7 @@ void initobjects(void) { addflag(lastot->flags, F_WALKDAMBP, BP_FEET, DT_WATER, NA, "1d2"); addot(OT_MIST, "thick mist", "A thick cloud of obscuring mist.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "}"); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "clears"); addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6833,7 +7102,7 @@ void initobjects(void) { addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_SMOKECLOUD, "cloud of smoke", "A thick cloud of black smoke.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "}"); addflag(lastot->flags, F_NODIECONVERTTEXT, NA, NA, NA, NULL); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "puff of smoke"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); @@ -6846,7 +7115,7 @@ void initobjects(void) { addot(OT_SMOKEPUFF, "puff of smoke", "A small puff of black smoke.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "}"); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "disperses"); addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6857,7 +7126,7 @@ void initobjects(void) { addflag(lastot->flags, F_CAUSESCOUGH, 18, NA, NA, NULL); addot(OT_POISONCLOUD, "cloud of poison gas", "A thick cloud of poisonous gas.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_GREEN, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_GREEN, NA, NA, "}"); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "thins out a little"); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "puff of gas"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); @@ -6869,7 +7138,7 @@ void initobjects(void) { addflag(lastot->flags, F_BLOCKSVIEW, 2, NA, NA, NULL); addot(OT_POISONPUFF, "puff of poison gas", "A small puff of poisonous gas.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_GREEN, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_GREEN, NA, NA, "}"); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "disperses"); addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6879,7 +7148,7 @@ void initobjects(void) { addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_HAILSTORM, "hail storm", "An intense storm of damaging hail.", MT_GAS, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_WHITE, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_WHITE, NA, NA, "}"); addflag(lastot->flags, F_NODIECONVERTTEXT, NA, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6906,8 +7175,7 @@ void initobjects(void) { addot(OT_MAGICBARRIER, "magical barrier", "A glowing, impassable barrier of magical energy.", MT_MAGIC, 0, OC_EFFECT); addflag(lastot->flags, F_GLYPH, C_YELLOW, NA, NA, "#"); addflag(lastot->flags, F_IMPASSABLE, SZ_MAX, NA, NA, NULL); - addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "vanishes"); addflag(lastot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL); @@ -7292,6 +7560,7 @@ void initobjects(void) { addot(OT_TEETH, "teeth", "teeth object", MT_BONE, 0, OC_WEAPON); addflag(lastot->flags, F_DAM, DT_BITE, NA, NA, "1d2"); addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); + addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL); addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL); @@ -8178,6 +8447,11 @@ int isimpassableob(object_t *o, lifeform_t *lf) { return B_TRUE; } } + if (lf && (lf->race->raceclass->id == RC_UNDEAD)) { + if (hasflagval(o->flags, F_REPELBLESSED, B_CURSED, NA, NA, NULL)) { + return B_TRUE; + } + } return B_FALSE; } @@ -9088,7 +9362,7 @@ void obdie(object_t *o) { } int obfits(object_t *o, obpile_t *op) { - if (countobs(op) >= MAXPILEOBS) { + if (countobs(op, B_FALSE) >= MAXPILEOBS) { return B_FALSE; } return B_TRUE; @@ -9279,10 +9553,10 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { if (isplayer(lf)) { if (strlen(f->text) > 0) { - where = askcoords(f->text, ttype, lf, UNLIMITED); + where = askcoords(f->text, ttype, lf, UNLIMITED, LOF_NEED, B_TRUE); } else { sprintf(buf, "Where will you aim %s?",obname); - where = askcoords(buf, ttype, lf, UNLIMITED); + where = askcoords(buf, ttype, lf, UNLIMITED, LOF_NEED, B_TRUE); if (!haslos(lf, where)) { // exception - wand of digging doesn't need los if (isknown(o) && (o->type->id == OT_WAND_DIGGING)) { @@ -9440,7 +9714,7 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { case 0: // butterflies around user willid = B_TRUE; where = getrandomadjcell(lf->cell, WE_WALKABLE, B_ALLOWEXPAND); - addmonster(where, R_BUTTERFLY, B_FALSE, rnd(10,20), B_FALSE); + addmonster(where, R_BUTTERFLY, B_FALSE, rnd(10,20), B_FALSE, NULL); if (haslos(player, where)) { msg("A swarm of butterflies appears!"); } @@ -10147,6 +10421,8 @@ void quaff(lifeform_t *lf, object_t *o) { int playercansee; int forcedrop = B_FALSE; int seen; + int killobwhendone = B_TRUE; + flag_t *drinkflag; getobname(o, obname, 1); @@ -10210,14 +10486,21 @@ void quaff(lifeform_t *lf, object_t *o) { if (o->type->obclass->id == OC_POTION) { potioneffects(lf, o->type->id, o->blessed, &seen); - } else if (hasflag(o->flags, F_DRINKABLE)) { - // drinkable thing which isn't a potion? - flag_t *f; - f = hasflag(o->flags, F_LINKOB); - if (f) { - potioneffects(lf, f->val[0], o->blessed, NULL); - } else { - eat(lf, o); + } else { + drinkflag= hasflag(o->flags, F_DRINKABLE); + if (drinkflag) { + // drinkable thing which isn't a potion? + flag_t *f; + f = hasflag(o->flags, F_LINKOB); + if (f) { + potioneffects(lf, f->val[0], o->blessed, NULL); + } else { + eat(lf, o); + } + // + if (drinkflag->val[2] == B_DONTKILL) { + killobwhendone = B_FALSE; + } } } @@ -10233,8 +10516,11 @@ void quaff(lifeform_t *lf, object_t *o) { // try to add an empty container to our pack addemptyob(lf->pack, o); } - // lose the potion - removeob(o, 1); + + if (killobwhendone) { + // lose the potion + removeob(o, 1); + } } @@ -10544,11 +10830,15 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, enum BLESSTYPE potblessed, i if (c && isempty(c)) { object_t *newob; newob = addob(c->obpile, "magical barrier"); - addflag(newob->flags, F_OBHP, i, i, NA, NULL); if (first && haslos(player, c)) { msg("A magical barrier appears!"); first = B_FALSE; } + + // it will disappear eventually + addflag(newob->flags, F_OBHP, i, i, NA, NULL); + addflag(newob->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(newob->flags, F_OBHPDRAIN, 1, NA, NA, NULL); } } break; @@ -11056,7 +11346,7 @@ void shatter(object_t *o, int hitlf, char *damstring, lifeform_t *fromlf) { capitalise(obcaps); obcaps = strrep(obcaps, "An ", "The ", NULL); obcaps = strrep(obcaps, "A ", "The ", NULL); - msg("%s shatters!",obcaps); + msg("%s shatter%s!",obcaps, (o->amt == 1) ? "s" : ""); free(obcaps); seen = B_TRUE; @@ -11332,6 +11622,29 @@ int real_takedamage(object_t *o, unsigned int howmuch, int damtype, int wantanno } } + + // damage type creates other objects? + f = hasflagval(o->flags, F_DTCREATEOB, damtype, NA, NA, NULL); + if (f) { + cell_t *loc; + int radius; + int dirtype; + objecttype_t *ot; + + loc = getoblocation(o); + ot = findotn(f->text); + if (ot && !hasob(loc->obpile, ot->id)) { + if (f->val[1] == NA) { + radius = 0; + dirtype = NA; + } else { + radius = f->val[1]; + dirtype = f->val[2]; + } + addobburst(loc, radius, dirtype, f->text, NULL, LOF_WALLSTOP); + } + } + // damage type converts this into something else? f = hasflagval(o->flags, F_DTCONVERT, damtype, NA, NA, NULL); if (f && !hasflag(o->flags, F_NODTCONVERT)) { @@ -11348,6 +11661,7 @@ int real_takedamage(object_t *o, unsigned int howmuch, int damtype, int wantanno adjustdamob(o, &howmuch, damtype); + // effects which have to happen before damage is applied... // explodes? f = hasflag(o->flags, F_EXPLODEONDAM); @@ -11445,6 +11759,11 @@ int real_takedamage(object_t *o, unsigned int howmuch, int damtype, int wantanno // object dies! addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); + + // if we didn't want this announced, don't say that it died either + if (!wantannounce) { + addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); + } } else if (hpflag) { // object was just damaged getobconditionname(o, postdamname); @@ -11967,7 +12286,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, shatter(newob, youhit, dambuf, thrower); } else { // don't announce damage to the thrown object - real_takedamage(newob, speed, DT_BASH, B_FALSE); + real_takedamage(newob, speed-1, DT_BASH, B_FALSE); } return B_FALSE; @@ -11977,24 +12296,42 @@ void timeeffectsob(object_t *o) { flag_t *f, *nextf; cell_t *location; lifeform_t *owner; - char obname[BUFLEN]; + object_t *sg; + char obname[BUFLEN],ownername[BUFLEN]; if (hasflag(o->flags, F_DEAD)) return; getobname(o, obname, o->amt); + location = getoblocation(o); + /* if (o->pile->where) { location = o->pile->where; } else { location = NULL; } + */ if (o->pile->owner) { owner = o->pile->owner; + getlfname(owner, ownername); } else { owner = NULL; } + // special case for trail flags + f = hasflag(o->flags, F_TRAIL); + if (f) { + if (f->lifetime > 0) { + f->lifetime--; + if (f->lifetime <= 0) { + // object dies. + killob(o); + return; + } + } + } + // expire flags timeeffectsflags(o->flags); @@ -12073,6 +12410,55 @@ void timeeffectsob(object_t *o) { } } + // sacred ground? + sg = hasobwithflagval(location->obpile, F_REPELBLESSED, o->blessed, NA, NA, NULL); + if (sg) { + char sgname[BUFLEN]; + cell_t *newc; + int canseeloc = B_FALSE; + + if (haslos(player, location)) { + getobname(sg, sgname, sg->amt); + canseeloc = B_FALSE; + msg("The %s pulses %s!", noprefix(sgname), + (o->blessed == B_CURSED) ? "white" : "black"); + } + + // object gets thrown away + newc = getrandomadjcell(location, WE_NOTWALL, B_ALLOWEXPAND); + if (newc) { + //flag_t *inv; + + // make sure the object doesn't take damage + //inv = addflag(o->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL); + + fireat(NULL, o, o->amt, newc, 1, NULL); + if (canseeloc) { + // player now knows that this is blessed + o->blessknown = B_TRUE; + } + //if (needredraw) drawlevelfor(player); + // update location + + //if (inv) killflag(inv); + + location = newc; + } else { + // object can't go anywhere - it disintegrates. + if (owner && cansee(player, owner)) { + msg("%s%s vanish%s in a surge of %s power!", ownername, getpossessive(ownername), + obname, (o->amt == 1) ? "es" : "", + (o->blessed = B_CURSED) ? "holy" : "evil"); + } else if (haslos(player, location)) { + msg("%s vanish%s in a surge of %s power!", obname, (o->amt == 1) ? "es" : "", + (o->blessed = B_CURSED) ? "holy" : "evil"); + } + removeob(o, o->amt); + return; + } + } + + if (location) { // does object's material change cell type? if (o->material->id == MT_FIRE) { @@ -12331,7 +12717,9 @@ void timeeffectsob(object_t *o) { object_t *splash; ourcell = getoblocation(o); // drip - splash = addob(ourcell->obpile, "splash of water"); + if (!hasobwithflag(ourcell->obpile, F_DEEPWATER)) { + splash = addob(ourcell->obpile, "splash of water"); + } } } diff --git a/objects.h b/objects.h index 2be56b5..e6e4ff0 100644 --- a/objects.h +++ b/objects.h @@ -25,6 +25,7 @@ void applyobmod(object_t *o, obmod_t *om); int blessob(object_t *o); void brightflash(cell_t *centre, int range, lifeform_t *immunelf); int canbepoisoned(enum OBTYPE oid); +int canseeob(lifeform_t *lf, object_t *o); object_t *canstackob(obpile_t *op, object_t *match); object_t *canstacknewot(obpile_t *op, objecttype_t *match); int changemat(object_t *o, enum MATERIAL mat); @@ -32,10 +33,10 @@ objecttype_t *checkobnames(char *haystack, char *needle); void colourmatchob(object_t *o, lifeform_t *lf); void copyobprops(object_t *dst, object_t *src); int countnames(char **list); -int countobs(obpile_t *op); -int countnoncosmeticobs(obpile_t *op); +int countobs(obpile_t *op, int onlyifknown); +int countnoncosmeticobs(obpile_t *op, int onlyifknown); int curseob(object_t *o); -void damageallobs(object_t *exception, obpile_t *op, int howmuch, int damtype); +void damageallobs(object_t *srcob, obpile_t *op, int howmuch, int damtype); void dumprandomobs(int amt); void explodeob(object_t *o, flag_t *f, int bigness); void extinguish(object_t *o); @@ -80,6 +81,7 @@ char getnextletter(obpile_t *op, char *wantletter); int getnumshards(object_t *o); int getnutritionbase(object_t *o); int getnutrition(object_t *o); +enum DEPTH getobdepth(object_t *o, lifeform_t *lf); char *getobdesc(object_t *o, char *buf); char *getobequipinfo(object_t *o, char *buf); char *getobextrainfo(object_t *o, char *buf); @@ -104,6 +106,7 @@ char *getschoolnameshort(enum SPELLSCHOOL sch); int getshatterdam(object_t *o); enum SKILLLEVEL gettechlevel(object_t *o); int getthrowdam(object_t *o); +char *gettopobname(cell_t *c, char *retbuf); enum BODYPART getweildloc(object_t *o, enum BODYPART *otherloc, int *twohanded); int hasedibleob(obpile_t *op); object_t *hasknownob(obpile_t *op, enum OBTYPE oid); diff --git a/spell.c b/spell.c index b03976c..b5fb352 100644 --- a/spell.c +++ b/spell.c @@ -107,7 +107,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef if (isplayer(user)) { sprintf(buf, "Charge who (max range %d)?",range); // TODO: ask for direction - targcell = askcoords(buf, TT_MONSTER, user, range); + targcell = askcoords(buf, TT_MONSTER, user, range, LOF_NEED, B_TRUE); if (!targcell) { msg("Cancelled."); return TRUE; @@ -254,7 +254,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } else { sprintf(buf, "Darkwalk to where?"); } - targcell = askcoords(buf, TT_NONE, user, range); + targcell = askcoords(buf, TT_NONE, user, range, LOF_DONTNEED, B_FALSE); } else { return B_TRUE; } @@ -538,7 +538,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef sprintf(buf, "Jump where (max distance 2)?"); while (!targcell) { // ask where - targcell = askcoords(buf, TT_NONE, user, 2); + targcell = askcoords(buf, TT_NONE, user, 2, LOF_DONTNEED, B_TRUE); if (!targcell) { return B_TRUE; } else if (getcelldist(user->cell, targcell) > 2) { @@ -801,7 +801,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef if (isplayer(user)) { sprintf(buf, "Swoop who (max range %d)?",srange); // TODO: ask for direction - targcell = askcoords(buf, TT_MONSTER, user, srange); + targcell = askcoords(buf, TT_MONSTER, user, srange, LOF_NEED, B_TRUE); if (!targcell) { msg("Cancelled."); return TRUE; @@ -935,13 +935,13 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef return B_FALSE; } else if (abilid == OT_A_DEBUG) { cell_t *where; - where = askcoords("Debug who?", TT_MONSTER, user, UNLIMITED); + where = askcoords("Debug who?", TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf) { debug(where->lf); } } else if (abilid == OT_A_EMPLOY) { cell_t *where; - where = askcoords("Assign job to who?", TT_MONSTER, user, UNLIMITED); + where = askcoords("Assign job to who?", TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf) { char question[BUFLEN]; char lfname[BUFLEN]; @@ -965,7 +965,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } } else if (abilid == OT_A_ENHANCE) { cell_t *where; - where = askcoords("Enhance stats of who?", TT_MONSTER, user, UNLIMITED); + where = askcoords("Enhance stats of who?", TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf) { char ch; enum ATTRIB att; @@ -1483,7 +1483,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (spellid == OT_S_BLINDNESS) { int failed = B_FALSE; // ask for target - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if (isblind(target)) { fizzle(caster); @@ -1547,6 +1548,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // create a line of fire towards the target cell calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell); + redrawpause(); // don't set caster's cell on fire! for (i = 1; i < nretcell; i++) { cell_t *c; @@ -1558,24 +1560,31 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (o) { setobcreatedby(o, caster); - if (c->lf) { - char buf[BUFLEN]; - char damstring[BUFLEN]; - char realcname[BUFLEN]; - getlfname(c->lf,buf); - if (!isimmuneto(c->lf->flags, DT_FIRE)) { - msg("%s burn%s!",buf,isplayer(c->lf) ? "" : "s"); - } - real_getlfname(caster, realcname, B_FALSE); - sprintf(damstring, "%s%s wave of fire", realcname, getpossessive(realcname)); - losehp(c->lf, rolldie(2,10), DT_FIRE, caster, damstring); - } } } + redrawresume(); + // now burn things + for (i = 1; i < nretcell; i++) { + cell_t *c; + c = retcell[i]; + + if (c->lf) { + char buf[BUFLEN]; + char damstring[BUFLEN]; + char realcname[BUFLEN]; + getlfname(c->lf,buf); + if (!isimmuneto(c->lf->flags, DT_FIRE)) { + msg("%s burn%s!",buf,isplayer(c->lf) ? "" : "s"); + } + real_getlfname(caster, realcname, B_FALSE); + sprintf(damstring, "%s%s wave of fire", realcname, getpossessive(realcname)); + losehp(c->lf, rolldie(2,10), DT_FIRE, caster, damstring); + } + } + if (cansee(player, caster)) { needredraw = B_TRUE; } - } else if (spellid == OT_S_CALLLIGHTNING) { int failed = B_FALSE; // ask for a target cell @@ -1617,13 +1626,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (targcell->obpile->first) { // select object from cell... - if (countobs(targcell->obpile) == 1) { - targob = targcell->obpile->first; - } else { - targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE); - if (!targob) { - failed = B_TRUE; - } + targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE); + if (!targob) { + failed = B_TRUE; } } else { failed = B_TRUE; @@ -2045,7 +2050,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // add the monster - newlf = addmonster(targcell, r->id, randomjobsok, 1, B_FALSE); + newlf = addmonster(targcell, r->id, randomjobsok, 1, B_FALSE, NULL); if (newlf) { // assign job if required if (forcejob) { @@ -2152,9 +2157,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_DIG) { int numseen = 0; - int maxrange; cell_t *retcell[MAXRETCELLS]; int nretcell,i; + int ndigs; // don't need line of fire OR sight! if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power)) return B_TRUE; @@ -2163,25 +2168,47 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell); - maxrange = power; + ndigs = 0; // get rid of rock in the cells... - for (i = 0; i < nretcell && (i <= maxrange); i++) { + for (i = 0; i < nretcell && (ndigs <= power) ; i++) { + int seenthiscell = B_FALSE; + if (haslos(player, retcell[i])) seenthiscell = B_TRUE; if (retcell[i]->type->solid) { setcelltype(retcell[i], getemptycelltype(retcell[i]->map->habitat)); - if (haslos(player, retcell[i])) numseen++; + if (seenthiscell) { + ndigs++; + numseen++; + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + } else { + object_t *o; + for (o = retcell[i]->obpile->first ; o ; o = o->next) { + if (hasflag(o->flags, F_IMPASSABLE)) { + char obname[BUFLEN]; + // destroy this object then stop. + addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); + if (seenthiscell) { + if (!hasflag(o->flags, F_OBDIETEXT)) { + getobname(o, obname, o->amt); + msg("%s crumble%s to dust!", obname, (o->amt == 1) ? "s" : ""); + } + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + ndigs++; + } + } } } - - // announce if any seen + // announce destruction of any walls seen if (numseen) { msg("The wall%s crumble%s to dust!", (numseen == 1) ? "" : "s", (numseen == 1) ? "s" : "" ); if (seenbyplayer) *seenbyplayer = B_TRUE; - } else { + } else if (ndigs == 0) { if (isplayer(caster)) nothinghappens(); } } else if (spellid == OT_S_DETECTLIFE) { @@ -2313,15 +2340,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ object_t *o, *nexto; int donesomething = B_FALSE; + if (!validatespellcell(caster, &targcell,TT_MONSTER|TT_OBJECT, spellid, power)) return B_TRUE; if (!targcell) { if (isplayer(caster)) { - sprintf(buf, "Where will you target your dispersal?"); - targcell = askcoords(buf, TT_MONSTER|TT_OBJECT, caster, UNLIMITED); - if (!targcell) { // no cell - fizzle(caster); - return B_TRUE; - } - } else { fizzle(caster); return B_TRUE; } @@ -2494,7 +2515,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_FEEBLEMIND) { // ask for target - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if ((getattr(target, A_IQ) <= 3) || skillcheck(target, SC_RESISTMAG, 20 + power, 0)) { if (cansee(player, target)) { @@ -2588,6 +2610,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ anim(caster->cell, targcell, '^', C_RED); + redrawpause(); // add fires as follows (3 = medium, 2 = medium, 1 = smell) // // 1 @@ -2642,6 +2665,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } + + redrawresume(); } else { failed = B_TRUE; @@ -3529,21 +3554,19 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); } } else if (spellid == OT_S_MINDSCAN) { - cell_t *where; int failed = B_FALSE; if (isplayer(caster)) { - // ask for a target cell - sprintf(buf, "Whose mind will you scan?"); - where = askcoords(buf, TT_MONSTER, caster, UNLIMITED); - if (where && haslos(caster, where) && haslf(where)) { + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE; + + if (targcell && haslos(caster, targcell) && haslf(targcell)) { char targname[BUFLEN]; lifeform_t *oldplayer; // temporarily change player pointer... oldplayer = player; - player = where->lf; + player = targcell->lf; // - getlfname(where->lf, targname); + getlfname(targcell->lf, targname); sprintf(buf, "Mindscanning %s, ESC to quit.", targname); doexplain(buf); @@ -3585,7 +3608,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (spellid == OT_S_PARALYZE) { int howlong; int saved = B_FALSE; - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if (lfhasflag(target, F_PARALYZED)) { fizzle(caster); @@ -3619,7 +3643,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (spellid == OT_S_PAIN) { int failed = B_FALSE; // ask for target - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if (lfhasflag(target, F_PAIN)) { fizzle(caster); @@ -3654,7 +3679,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); } } else if (spellid == OT_S_PETRIFY) { - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; // some thigns can't be stoned if (!lfcanbestoned(target)) { @@ -3933,11 +3959,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (targcell->obpile->first) { // no lifeform there targob = NULL; // select object from cell... - if (countobs(targcell->obpile) == 1) { - targob = targcell->obpile->first; - } else { - targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE); - } + targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE); if (targob) { if (ismetal(targob->material->id)) { @@ -4122,23 +4144,37 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS); knockback(target, dir, 2, caster, 0); } else { - o = hasobwithflag(targcell->obpile, F_DOOR); - if (o) { - int dooropen; - isdoor(o, &dooropen); // just check whether it's open, we know it's a door - if (dooropen) { - fizzle(caster); - } else { - if (haslos(player, targcell)) { - getobname(o, buf, o->amt); - msg("%s %s!",isplayer(caster) ? "You blast" : "Something blasts", buf); + int donesomething = B_FALSE; + object_t *nexto; + for (o = targcell->obpile->first ; o ; o = nexto) { + nexto = o->next; + if (hasflag(o->flags, F_IMPASSABLE)) { + int gotit = B_FALSE; + int dooropen; + if (isdoor(o, &dooropen)) { + if (!dooropen) { + gotit = B_TRUE; + } + } else { + gotit = B_TRUE; + } + + if (gotit) { + if (haslos(player, targcell)) { + getobname(o, buf, o->amt); + msg("%s %s!",isplayer(caster) ? "You blast" : "Something blasts", buf); + } + addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); + if (seenbyplayer) *seenbyplayer = B_TRUE; + donesomething = B_TRUE; } - takedamage(o, 999, DT_DIRECT); - if (seenbyplayer) *seenbyplayer = B_TRUE; } - } else { + } + + if (!donesomething) { fizzle(caster); } + } } else if (spellid == OT_S_LEVITATION) { flag_t *f; @@ -4190,7 +4226,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } // undead will flee from light - if (lfhasflag(l, F_UNDEAD)) { + if (isundead(l)) { // runs away from caster addtempflag(l->flags, F_FLEEFROM, caster->id, NA, NA, NULL, 20); } @@ -4254,7 +4290,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ for (i = 0; i < caster->nlos; i++) { target = caster->los[i]->lf; - if (target) { + if (target && areenemies(caster, target)) { poss[nposs++] = target; } } @@ -4422,18 +4458,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_GRAVBOOST) { // ask for target - if (!target) { - if (isplayer(caster)) { - cell_t *where; - sprintf(buf, "Where will you target your spell?"); - where = askcoords(buf, TT_MONSTER, caster, UNLIMITED); - if (where && haslos(caster, where) && haslf(where)) { - target = haslf(where); - } else { - fizzle(caster); - } - } - } + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE; if (target) { int howlong = 15; @@ -4615,7 +4640,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int donesomething = B_FALSE; // stop targetting anybody - if (killflagsofid(target->flags, F_TARGET)) { + if (killflagsofid(target->flags, F_TARGETLF)) { donesomething = B_TRUE; } if (killflagsofid(target->flags, F_TARGETCELL)) { @@ -4649,16 +4674,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (spellid == OT_S_POLYMORPH) { race_t *r = NULL; - // ask for target if required - if (!target && isplayer(caster)) { - cell_t *where; - where = askcoords("Who will you polymorph?", TT_MONSTER, caster, UNLIMITED); - if (haslos(caster, where)) { - target = where->lf; - } else { - target = NULL; - } - } + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if (!target) { fizzle(caster); @@ -4968,7 +4985,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f->obfrom = spellid; } else if (spellid == OT_S_SLEEP) { int howlong; - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if (lfhasflag(target, F_ASLEEP) || !cansleep(target)) { fizzle(caster); @@ -5023,7 +5041,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_SLOW) { int howlong = 15; - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) { if (isplayer(target)) { @@ -5064,7 +5083,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ badoid[0] = OT_MUDPOOL; badoid[1] = OT_NONE; for (i = 0; i < powerleft; i++) { - c = real_getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND, badoid); + c = real_getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND, LOF_DONTNEED, badoid); if (c) { o = addob(c->obpile, "pool of mud"); if (o) { @@ -5296,7 +5315,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ sprintf(buf, "Where will you teleport to?"); while (!c) { int ch; - c = askcoords(buf, TT_NONE, caster, UNLIMITED); + c = askcoords(buf, TT_NONE, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); if (!c->known) { // confirm ch = askchar("Are you sure to want to teleport into the unknown?", "yn", "n", B_TRUE); @@ -5452,17 +5471,13 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!targob) { // ask for a target cell (to take objects from) sprintf(buf, "Where will you focus your telekinetic power?"); - where = askcoords(buf, TT_OBJECT | TT_DOOR, caster, UNLIMITED); + where = askcoords(buf, TT_OBJECT | TT_DOOR, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && haslos(caster, where)) { if (where->obpile->first) { // select object from cell... - if (countobs(where->obpile) == 1) { - targob = where->obpile->first; - } else { - targob = askobject(where->obpile, "Target which object", NULL, AO_NONE); - if (!targob) { - failed = B_TRUE; - } + targob = askobject(where->obpile, "Target which object", NULL, AO_NONE); + if (!targob) { + failed = B_TRUE; } // TODO: check object weight! } else { @@ -5489,7 +5504,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ char obname[BUFLEN]; getobname(targob, obname, 1); sprintf(buf, "Where will you move %s to?", obname); - targcell = askcoords(buf, TT_MONSTER | TT_PLAYER, caster, UNLIMITED); + // TODO: start trail from the object + targcell = askcoords(buf, TT_MONSTER | TT_PLAYER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); } // not liftable? @@ -5554,7 +5570,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ for (i = 0; i < caster->nlos; i++) { targcell = caster->los[i]; target = targcell->lf; - if (target && hasflag(target->flags, F_UNDEAD)) { + if (target && isundead(target)) { int howlong = 10; int worked = B_TRUE; // TODO: does it work? depends on caster level & intelligence & power @@ -5879,7 +5895,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_WEAKEN) { // ask for target - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) { if (cansee(player, target)) { @@ -5969,7 +5986,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; // ask for target if (spellid == OT_S_GIFT) { - if (!validatespelllf(caster, &target)) return B_TRUE; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; } else { target = caster; } @@ -6406,6 +6424,9 @@ int getspellrange(enum OBTYPE spellid, int power) { case OT_S_CALLWIND: range = (power*2); break; + case OT_S_DIG: + range = power; + break; case OT_S_LIGHTNINGBOLT: range = (power*3); break; @@ -6612,7 +6633,7 @@ int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, i r = findrace(poss[rnd(0,nposs-1)]); if (r) { // add it! - newlf = addmonster(c, r->id, B_FALSE, 1, B_FALSE); + newlf = addmonster(c, r->id, B_FALSE, 1, B_FALSE, NULL); // not worth any xp killflagsofid(newlf->flags, F_XPVAL); addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL); @@ -6644,13 +6665,12 @@ lifeform_t *validateabillf(lifeform_t *user, enum OBTYPE aid, lifeform_t **targe maxrange = UNLIMITED; } - // ask for a target lifeform if (isplayer(user)) { cell_t *where; char buf[BUFLEN]; sprintf(buf, "Where will you target your %s?",ot->name); - where = askcoords(buf, TT_MONSTER, user, maxrange); + where = askcoords(buf, TT_MONSTER, user, maxrange, LOF_DONTNEED, B_FALSE); if (where) { if (!haslf(where)) { msg("There is nobody there!"); @@ -6781,7 +6801,7 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, e } else { sprintf(buf, "Where will you target your spell [max range %d]?",maxrange); } - where = askcoords(buf, targtype, caster, maxrange); + where = askcoords(buf, targtype, caster, maxrange, needlof, needlof ? B_TRUE : B_FALSE); if (!where) { int ch; ch = askchar("Abandon your spell?","yn","n", B_TRUE); @@ -6862,6 +6882,7 @@ int getmpcost(lifeform_t *lf, enum OBTYPE oid) { } +/* lifeform_t *validatespelllf(lifeform_t *caster, lifeform_t **target) { if (!caster) { return *target; @@ -6888,3 +6909,4 @@ lifeform_t *validatespelllf(lifeform_t *caster, lifeform_t **target) { } return *target; } +*/ diff --git a/spell.h b/spell.h index 4e79d07..2632ba8 100644 --- a/spell.h +++ b/spell.h @@ -28,7 +28,7 @@ void stopallspells(lifeform_t *lf); int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, int howmany, int lifetime); lifeform_t *validateabillf(lifeform_t *user, enum OBTYPE aid, lifeform_t **target); cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, enum OBTYPE spellid, int power); -lifeform_t *validatespelllf(lifeform_t *caster, lifeform_t **lf); +//lifeform_t *validatespelllf(lifeform_t *caster, lifeform_t **lf); #endif