diff --git a/Makefile b/Makefile index fc1d193..bb95f24 100644 --- a/Makefile +++ b/Makefile @@ -1,58 +1,61 @@ #all: Makefile defs.h nexus.c nexus.h ai.c ai.h attack.c attack.h data.c data.h flag.c flag.h god.c god.h io.c io.h lf.c lf.h map.c map.h move.c move.h objects.c objects.h text.c text.h save.c save.h shops.c shops.h spell.c spell.h vault.c vault.h # gcc -Wall -g -o nexus nexus.c ai.c attack.c data.c flag.c god.c io.c lf.c map.c move.c objects.c text.c save.c spell.c shops.c vault.c -lncurses -lsqlite3 -nexus: ai.o attack.o data.o findleak.o flag.o god.o io.o lf.o map.o move.o nexus.o objects.o save.o shops.o spell.o text.o vault.o ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h nexus.h objects.h save.h shops.h spell.h text.h vault.h - gcc -g -Wall -o nexus ai.o attack.o data.o findleak.o flag.o god.o io.o lf.o map.o move.o nexus.o objects.o save.o shops.o spell.o text.o vault.o -lncurses -lsqlite3 +nexus: ai.o astar.o attack.o data.o findleak.o flag.o god.o io.o lf.o map.o move.o nexus.o objects.o save.o shops.o spell.o text.o vault.o ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h nexus.h objects.h save.h shops.h spell.h text.h vault.h + gcc -g -Wall -o nexus ai.o astar.o attack.o data.o findleak.o flag.o god.o io.o lf.o map.o move.o nexus.o objects.o save.o shops.o spell.o text.o vault.o -lncurses -lsqlite3 -ai.o: ai.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h ai.h objects.h save.h shops.h spell.h text.h vault.h +ai.o: ai.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h ai.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o ai.o ai.c -attack.o: attack.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h attack.h objects.h save.h shops.h spell.h text.h vault.h +attack.o: attack.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h attack.h astar.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o attack.o attack.c -data.o: data.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h data.h objects.h save.h shops.h spell.h text.h vault.h +astar.o: astar.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h attack.h astar.h objects.h save.h shops.h spell.h text.h vault.h + gcc -Wall -c -g -o astar.o astar.c + +data.o: data.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h data.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o data.o data.c -findleak.o: findleak.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h findleak.h objects.h save.h shops.h spell.h text.h vault.h +findleak.o: findleak.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h findleak.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o findleak.o findleak.c -flag.o: flag.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h flag.h objects.h save.h shops.h spell.h text.h vault.h +flag.o: flag.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h flag.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o flag.o flag.c -god.o: god.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h god.h objects.h save.h shops.h spell.h text.h vault.h +god.o: god.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h god.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o god.o god.c -io.o: io.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h io.h objects.h save.h shops.h spell.h text.h vault.h +io.o: io.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h io.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o io.o io.c -lf.o: lf.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h lf.h objects.h save.h shops.h spell.h text.h vault.h +lf.o: lf.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h lf.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o lf.o lf.c -map.o: map.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h map.h objects.h save.h shops.h spell.h text.h vault.h +map.o: map.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h map.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o map.o map.c -move.o: move.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h move.h objects.h save.h shops.h spell.h text.h vault.h +move.o: move.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h move.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o move.o move.c -nexus.o: nexus.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h nexus.h objects.h save.h shops.h spell.h text.h vault.h +nexus.o: nexus.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h nexus.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o nexus.o nexus.c -objects.o: objects.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h objects.h objects.h save.h shops.h spell.h text.h vault.h +objects.o: objects.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h objects.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o objects.o objects.c -save.o: save.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h save.h objects.h save.h shops.h spell.h text.h vault.h +save.o: save.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h save.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o save.o save.c -shops.o: shops.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h shops.h objects.h save.h shops.h spell.h text.h vault.h +shops.o: shops.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h shops.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o shops.o shops.c -spell.o: spell.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h spell.h objects.h save.h shops.h spell.h text.h vault.h +spell.o: spell.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h spell.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o spell.o spell.c -text.o: text.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h text.h objects.h save.h shops.h spell.h text.h vault.h +text.o: text.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h text.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o text.o text.c -vault.o: vault.c ai.h attack.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h vault.h objects.h save.h shops.h spell.h text.h vault.h +vault.o: vault.c ai.h attack.h astar.h data.h defs.h findleak.h flag.h god.h io.h lf.h map.h move.h vault.h objects.h save.h shops.h spell.h text.h vault.h gcc -Wall -c -g -o vault.o vault.c diff --git a/ai.c b/ai.c index 7314711..1646c30 100644 --- a/ai.c +++ b/ai.c @@ -3,6 +3,7 @@ #include #include #include "ai.h" +#include "astar.h" #include "attack.h" #include "defs.h" #include "flag.h" @@ -152,79 +153,6 @@ int aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit) { return B_FALSE; } -int calcheuristic(cell_t *c, cell_t *end) { - int h,xd,yd; - xd = abs(c->x - end->x); - yd = abs(c->y - end->y); - if (xd > yd) { - h = (14*yd) + 10*(xd - yd); - } else { - h = (14*xd) + 10*(yd - xd); - } - return h; -} - -// inserts node 'n' into list 'list' (sorted by cost) -// returns index of insert position. -int insert(node_t *n, node_t *list, int *listcount) { - int pos,i; - // add it to open list, with parent = node[cur] - pos = -1; - for (pos = 0; pos < *listcount; pos++) { - if (n->cost <= list[pos].cost) { - // add here. - break; - } - } - - // shuffle others up - for (i = *listcount; i > pos; i--) { - list[i] = list[i-1]; - } - (*listcount)++; - // fill in . - list[pos] = *n; - return pos; -} - -int calcg(lifeform_t *lf, cell_t *thiscell, node_t *parent, int dirfromparent) { - int fromstart; - object_t *o; - int dooropen; - enum ATTRBRACKET wis = AT_EXHIGH; - if (lf) { - wis = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL); - } - if (isorthogonal(dirfromparent)) { - fromstart = parent->fromstart + 10; - } else { - fromstart = parent->fromstart + 14; - } - - // closed doors count for more. - if (wis >= AT_AVERAGE) { - o = hasdoor(thiscell); - if (o && isdoor(o, &dooropen) && !dooropen) { - fromstart += 10; - } - } - - // lf's wisdom will affect what is the "best" path - if (wis >= AT_GTAVERAGE) { - // avoid mud, etc - o = hasobwithflag(thiscell->obpile, F_REDUCEMOVEMENT); - if (o) { - int howmuch; - sumflags(o->flags, F_REDUCEMOVEMENT, &howmuch, NULL, NULL); - if (howmuch > 0) { - fromstart += (10*howmuch); - } - } - } - - return fromstart; -} - // A* algorithm. Returns F_AIPATH flag. flag_t *ai_createpathto(lifeform_t *lf, cell_t *targcell) { char *pathbuf; diff --git a/ai.h b/ai.h index 97d1881..66e5590 100644 --- a/ai.h +++ b/ai.h @@ -34,6 +34,7 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG void aiturn(lifeform_t *lf); int aiwants(lifeform_t *lf, object_t *o, int *covets); int aiwants_real(lifeform_t *lf, object_t *o, int *covets, int *noids, enum OBTYPE *oid, int *oidcovet,int *nwantflags, enum FLAG *wantflag, int *wantflagcovet); +void clearnode(node_t *n;); lifeform_t *gettargetlf(lifeform_t *lf); object_t *hasbetterarmour(lifeform_t *lf, obpile_t *op); object_t *hasbetterweapon(lifeform_t *lf, obpile_t *op); diff --git a/attack.c b/attack.c index ea763f6..2f7d187 100644 --- a/attack.c +++ b/attack.c @@ -2551,8 +2551,8 @@ int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE d // for a given ArmourRating (AR), return range of damage which it might reduce void getarrange(int arating, int *min, int *max) { - *min = pctof(20,arating); - *max = pctof(60, arating); + *min = pctof(50,arating); + *max = pctof(80, arating); } /* @@ -3392,7 +3392,7 @@ void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam, int isu readuntil(frombuf, p, '^'); } - poison(victim, howlong, ptype, ppower, frombuf, owner ? owner->race->id : R_NONE); + poison(victim, howlong, ptype, ppower, frombuf, owner ? owner->race->id : R_NONE, B_FALSE); } else { // flag values if (valflag) { @@ -3415,7 +3415,7 @@ void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam, int isu int pct; pct = f->val[0] * 10; if (pctchance(pct)) { - poison(victim, PERMENANT, P_TETANUS, 1, frombuf, owner ? owner->race->id : R_NONE); + poison(victim, PERMENANT, P_TETANUS, 1, frombuf, owner ? owner->race->id : R_NONE, B_FALSE); } } // end if (fid == xxx) diff --git a/data.c b/data.c index 74894ca..0e66696 100644 --- a/data.c +++ b/data.c @@ -2020,6 +2020,7 @@ void initobjects(void) { addmaterial(MT_SLIME, "slime", 9); addmaterial(MT_STONE, "stone", 10); addflag(lastmaterial->flags, F_HARDNESS, 4, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTIMMUNE, DT_BASH, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL); addmaterial(MT_SILVER, "silver", 11); @@ -3470,12 +3471,14 @@ void initobjects(void) { addflag(lastot->flags, F_GLYPH, C_MAGENTA, '%', NA, NULL); addflag(lastot->flags, F_EDIBLE, B_TRUE, 1, NA, ""); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_VENOMSAC, B_TRUE, NA, NA, ""); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); addot(OT_POISONSACBL, "blue venom sac", "A small sac of flesh, filled with blindness-inducing venom. A skilled Chemist could use it to create a blindness potion.", MT_FLESH, 0.2, OC_FOOD, SZ_TINY); // weight normally comes from corpse type addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "You can 'o'perate this to mix it into a poiton."); addflag(lastot->flags, F_GLYPH, C_BLUE, '%', NA, NULL); addflag(lastot->flags, F_EDIBLE, B_TRUE, 1, NA, ""); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_VENOMSAC, B_TRUE, NA, NA, ""); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); addot(OT_PSITRUFFLE, "psitruffle", "Psitruffles are extremely rare forms of truffle which can unlock potentials within a living brain. Eating one will double all experience earned for a short period thereafter.", MT_FOOD, 0.5, OC_FOOD, SZ_TINY); addflag(lastot->flags, F_GLYPH, C_MAGENTA, '%', NA, NULL); @@ -3635,7 +3638,7 @@ void initobjects(void) { addflag(lastot->flags, F_VALUE, 75, NA, NA, NULL); addot(OT_POT_BLOOD, "potion of blood", "A small quantity of blood.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_UNCOMMON, NULL); - addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "splash of blood"); + addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "5 splashes of blood"); addflag(lastot->flags, F_VALUE, 15, NA, NA, NULL); addot(OT_POT_SANCTUARY, "potion of sanctuary", "Creates a temporary magical barrier abour the drinker.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_RARE, NULL); @@ -3650,9 +3653,9 @@ void initobjects(void) { addflag(lastot->flags, F_AIBOOSTITEM, B_TRUE, 40, NA, NULL); addflag(lastot->flags, F_VALUE, 410, NA, NA, NULL); - addot(OT_POT_BLOODC, "potion of cockatrice blood", "A small quantity of cockatrice blood.", MT_GLASS, 1, OC_POTION, SZ_TINY); + addot(OT_POT_BLOODC, "potion of antistoning", "A small quantity of bottled blood from a cockatrice, able to reverse the effects of petrification.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_RARE, NULL); - addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "splash of cockatrice blood"); + addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "5 splashes of cockatrice blood"); addflag(lastot->flags, F_VALUE, 35, NA, NA, NULL); addot(OT_POT_COMPETENCE, "potion of competence", "Permemantly increases the drinker's base attribites (Strength, IQ, etc).", MT_GLASS, 1, OC_POTION, SZ_TINY); @@ -6923,7 +6926,7 @@ void initobjects(void) { addflag(lastot->flags, F_FILLPOT, 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, SZ_TINY); + addot(OT_BLOODSTAIN, "blood stain", "A dried stain of blood.", MT_BLOOD, 0, OC_MISC, SZ_MINI); 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_RED, ',', NA, NULL); @@ -6934,24 +6937,8 @@ void initobjects(void) { addflag(lastot->flags, F_NOFEEL, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_PURIFIESTO, OT_SPLASHWATER, NA, NA, NULL); - addot(OT_BLOODCSPLASH, "splash of cockatrice blood", "A small pool of cockatrice blood.", MT_BLOOD, 0, OC_MISC, SZ_SMALL); - 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, NA, ',', NA, NULL); - addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, RR_UNCOMMON, NULL); - addflag(lastot->flags, F_RARITY, H_CAVE, 50, RR_UNCOMMON, NULL); - addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_SLIPPERY, 10, NA, NA, NULL); - addflag(lastot->flags, F_OBDIETEXT, NA, NA, NA, "evaporates"); - addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); - addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); - addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_DRINKABLE, B_TRUE, 0, NA, NULL); - addflag(lastot->flags, F_LINKOB, OT_POT_BLOODC, NA, NA, NULL); - addflag(lastot->flags, F_NOFEEL, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_PURIFIESTO, OT_SPLASHWATER, NA, NA, NULL); - addot(OT_BLOODSPLASH, "splash of blood", "A small pool of blood.", MT_BLOOD, 0, OC_MISC, SZ_SMALL); + addot(OT_BLOODSPLASH, "splash of blood", "A small pool of blood.", MT_BLOOD, 0, OC_MISC, SZ_MINI); 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, NA, ',', NA, NULL); @@ -6959,7 +6946,7 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL); addflag(lastot->flags, F_RARITY, H_CAVE, 75, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_SLIPPERY, 10, NA, NA, NULL); + addflag(lastot->flags, F_SLIPPERY, 5, NA, NA, NULL); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "blood stain"); //addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dries up"); //addflag(lastot->flags, F_DIECONVERTTEXTPL, NA, NA, NA, "dry up"); @@ -6973,14 +6960,14 @@ void initobjects(void) { addflag(lastot->flags, F_PURIFIESTO, OT_SPLASHWATER, NA, NA, NULL); addflag(lastot->flags, F_FILLPOT, OT_POT_BLOOD, 5, NA, NULL); - addot(OT_BLOODPOOL, "pool of blood", "A large pool of blood.", MT_BLOOD, 0, OC_MISC, SZ_MEDIUM); + addot(OT_BLOODPOOL, "pool of blood", "A large pool of blood.", MT_BLOOD, 0, OC_MISC, SZ_SMALL); 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, NA, '~', NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL); addflag(lastot->flags, F_RARITY, H_CAVE, 65, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_SLIPPERY, 50, NA, NA, NULL); + addflag(lastot->flags, F_SLIPPERY, 25, NA, NA, NULL); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "blood stain"); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dries up"); addflag(lastot->flags, F_DIECONVERTTEXTPL, NA, NA, NA, "dry up"); @@ -7269,6 +7256,9 @@ void initobjects(void) { addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "roaring flames."); addflag(lastot->flags, F_SHRINKSTO, OT_FIREMED, VT_OB, NA, NULL); + addflag(lastot->flags, F_GENERATES, 10, 0, B_INADJCELL, "cloud of smoke"); + addflag(lastot->flags, F_GENERATES, 50, 0, B_INADJCELL, "puff of smoke"); + addflag(lastot->flags, F_GENERATES, 50, 0, B_INADJCELL, "puff of smoke"); addot(OT_FIREMED, "medium fire", "A medium-sized roaring fire.", MT_FIRE, 0, OC_EFFECT, SZ_MEDIUM); addflag(lastot->flags, F_GLYPH, C_RED, '}', NA, NULL); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "small fire"); @@ -7283,6 +7273,8 @@ void initobjects(void) { addflag(lastot->flags, F_MAKESNOISE, 33, 2, NA, "roaring flames."); addflag(lastot->flags, F_GROWSTO, OT_FIRELARGE, VT_OB, NA, NULL); addflag(lastot->flags, F_SHRINKSTO, OT_FIRESMALL, VT_OB, NA, NULL); + addflag(lastot->flags, F_GENERATES, 50, 0, B_INADJCELL, "puff of smoke"); + addflag(lastot->flags, F_GENERATES, 50, 0, B_INADJCELL, "puff of smoke"); addot(OT_FIRESMALL, "small fire", "A small blaze.", MT_FIRE, 0, OC_EFFECT, SZ_SMALL); addflag(lastot->flags, F_GLYPH, C_RED, '}', NA, NULL); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "goes out"); @@ -7295,6 +7287,7 @@ void initobjects(void) { addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addflag(lastot->flags, F_MAKESNOISE, 33, 1, NA, "crackling flames."); addflag(lastot->flags, F_GROWSTO, OT_FIREMED, VT_OB, NA, NULL); + addflag(lastot->flags, F_GENERATES, 50, 0, B_INADJCELL, "puff of smoke"); addot(OT_STEAMCLOUD, "cloud of steam", "A thick cloud of scalding steam.", MT_GAS, 0, OC_EFFECT, SZ_HUMAN); addflag(lastot->flags, F_GLYPH, C_WHITE, UNI_SHADEMED, NA, NULL); @@ -7360,6 +7353,7 @@ void initobjects(void) { addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addflag(lastot->flags, F_CAUSESCOUGH, 125, NA, NA, NULL); + addflag(lastot->flags, F_OBMOVESRANDOMLY, NA, NA, NA, NULL); addot(OT_SMOKEPUFF, "puff of smoke", "A small puff of black smoke.", MT_GAS, 0, OC_EFFECT, SZ_MEDIUM); addflag(lastot->flags, F_GLYPH, C_GREY, UNI_SHADELIGHT, NA, NULL); @@ -7371,6 +7365,7 @@ void initobjects(void) { addflag(lastot->flags, F_BLOCKSVIEW, 2, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); addflag(lastot->flags, F_CAUSESCOUGH, 80, NA, NA, NULL); + addflag(lastot->flags, F_OBMOVESRANDOMLY, NA, NA, NA, NULL); addot(OT_POISONCLOUD, "cloud of poison gas", "A thick cloud of poisonous gas.", MT_GAS, 0, OC_EFFECT, SZ_LARGE); addflag(lastot->flags, F_GLYPH, C_GREEN, UNI_SHADEMED, NA, NULL); @@ -7634,12 +7629,16 @@ void initobjects(void) { addflag(lastot->flags, F_OBHP, 120, 120, NA, NULL); addflag(lastot->flags, F_CRITPROTECTION, 100, NA, NA, NULL); - addot(OT_FLAKJACKET, "flak jacket", "Heavy metal vest, worn over the shoulders. Designed to stop a bullet, but ineffective against melee attacks.", MT_METAL, 30, OC_ARMOUR, SZ_MEDIUM); + addot(OT_FLAKJACKET, "flak jacket", "Heavy metal vest, worn over the shoulders. Designed protect against bullets and explosions, but less effective against melee attacks.", MT_METAL, 30, OC_ARMOUR, SZ_MEDIUM); addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_RARE, NULL); addflag(lastot->flags, F_MULTISIZE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_GOESON, BP_SHOULDERS, NA, NA, NULL); addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL); addflag(lastot->flags, F_EQUIPCONFER, F_ARMOURPENALTY, 0, 5, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_DTRESIST, DT_EXPLOSIVE, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_DTRESIST, DT_PROJECTILE, NA, NULL); + addflag(lastot->flags, F_DTRESIST, DT_EXPLOSIVE, NA, NA, NULL); + addflag(lastot->flags, F_DTRESIST, DT_PROJECTILE, NA, NA, NULL); addflag(lastot->flags, F_ATTREQ, A_STR, 50, NA, NULL); addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL); addflag(lastot->flags, F_CRITPROTECTION, 100, NA, NA, NULL); @@ -10054,7 +10053,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HITCONFER, F_POISONED, SC_POISON, 100, NULL); addflag(lastrace->flags, F_HITCONFERVALS, P_LYCANTHROPY, -1, PERMENANT, "10^dire rat"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, BLOODFORPOT, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addrace(R_SEBASTIAN, "Sebastian", 75, '@', C_WHITE, MT_FLESH, RC_HUMANOID, "An impeccably-dressed man with long sideburns."); @@ -11294,7 +11293,7 @@ void initrace(void) { } } addflag(lastrace->flags, F_GODPOISON, B_FALSE, 25, NA, NULL); - addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "destroying the undead"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "destroying the undead or robots"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "casting certain holy spells"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "natural healing"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the destruction of healing potions"); @@ -11487,6 +11486,7 @@ void initrace(void) { addflag(lastrace->flags, F_GENDER, G_FEMALE, NA, NA, NULL); addflag(lastrace->flags, F_MPREGEN, NA, NA, NA, "100"); // ie. basically infinite addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "the use of magic"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "destroying robots"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the use of missile or ranged weapons"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the use of technology"); // sacrifices @@ -11616,7 +11616,7 @@ void initrace(void) { addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CASTCHANCE, 50, NA, NA, NULL); addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_LEVITATION, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_LEVITATION, BLOODFORPOT, NA, NULL); addrace(R_BOGGART, "bogle", 15, 'n', C_BROWN, MT_WOOD, RC_MAGIC, "An evil household fairy made of wood. They delight in sowing disease and misfortune."); setbodytype(lastrace, BT_HUMANOID); @@ -11789,7 +11789,7 @@ void initrace(void) { addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_RARE, NULL); addflag(lastrace->flags, F_RARITY, H_ANTNEST, NA, RR_VERYRARE, NULL); addflag(lastrace->flags, F_COLDBLOOD, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, "splash of cockatrice blood"); + //addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, "splash of cockatrice blood"); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HITDICE, 5, NA, NA, NULL); addflag(lastrace->flags, F_TR, 5, NA, NA, NULL); @@ -12029,6 +12029,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_FLY, 1, NA, "^flapping wings"); addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_LEVITATION, BLOODFORPOT, NA, NULL); addrace(R_TRICLOPS, "triclops", 160, 'H', C_BROWN, MT_FLESH, RC_HUMANOID, "The three-eyes triclops race are blessed with extraordiny perceptive and are nearly impossible to surprise. They commonly use their third eye to stun enemies, then finish them off with their huge weapons."); setbodytype(lastrace, BT_HUMANOID); @@ -12277,7 +12278,7 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_ATTRMOD, A_STR, 5, "50"); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_FIRE, NA, "50"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, BLOODFORPOT, NA, NULL); addrace(R_GIANTFIREFC, "flame giant firemaster", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID, "A subspecies of flame giant who have developed the ability to command the primal volcanic fires around them."); setbodytype(lastrace, BT_HUMANOID); @@ -12321,7 +12322,7 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 15, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_ATTRMOD, A_STR, 5, "50"); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_FIRE, NA, "50"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, BLOODFORPOT, NA, NULL); // TODO: storm giant @@ -12855,7 +12856,7 @@ void initrace(void) { setbodytype(lastrace, BT_QUADRAPED); addbodypart(lastrace, BP_TAIL, NULL); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, BLOODFORPOT, NA, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_HUGE, NA, NA, NULL); addflag(lastrace->flags, F_RARITY, H_ALL, NA, RR_VERYRARE, NULL); @@ -12890,7 +12891,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, BLOODFORPOT, NA, NULL); addrace(R_KOBOLD, "kobold", 18, 'k', C_BROWN, MT_FLESH, RC_HUMANOID, "An evil humanoid race with doglike features, kobolds are known for their cowardace and prefer to attack from a distance if at all possible."); @@ -12966,7 +12967,7 @@ void initrace(void) { addflag(lastrace->flags, F_DIURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 2, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, BLOODFORPOT, NA, NULL); addrace(R_LEPRECHAUN, "leprechaun", 35, 'n', C_BOLDGREEN, MT_FLESH, RC_HUMANOID, "Leprechauns are tiny Irish humans, with a love for gold and practical jokes. Known for their supernatural luck."); setbodytype(lastrace, BT_HUMANOID); @@ -14167,7 +14168,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_FIRE, NA, "25"); addflag(lastrace->flags, F_WANTSOBFLAG, F_GEM, B_COVETS, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, BLOODFORPOT, NA, NULL); addrace(R_SPRITEGRAVE, "grave sprite", 5, 'n', C_GREY, MT_FLESH, RC_MAGIC, "A small magical creature made from corpse dust."); setbodytype(lastrace, BT_HUMANOID); @@ -14246,7 +14247,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_COLD, NA, "25"); addflag(lastrace->flags, F_WANTSOBFLAG, F_GEM, B_COVETS, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, BLOODFORPOT, NA, NULL); addrace(R_SPRITEWEED, "weed sprite", 5, 'n', C_ORANGE, MT_FLESH, RC_MAGIC, "The bright colour of weed sprites belies their evil nature."); setbodytype(lastrace, BT_HUMANOID); @@ -14297,9 +14298,9 @@ void initrace(void) { addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_COMMON, NULL); addflag(lastrace->flags, F_RARITY, H_MASTERVAULTS, NA, RR_COMMON, NULL); addflag(lastrace->flags, F_HITDICE, 5, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMIN, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALING, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMAJ, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMIN, BLOODFORPOT, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALING, BLOODFORPOT, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMAJ, BLOODFORPOT, NA, NULL); addflag(lastrace->flags, F_TR, 5, NA, NA, NULL); addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); @@ -14369,9 +14370,9 @@ void initrace(void) { addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMIN, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALING, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMAJ, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMIN, BLOODFORPOT, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALING, BLOODFORPOT, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMAJ, BLOODFORPOT, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL); addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_UNCOMMON, NULL); addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_COMMON, NULL); @@ -14413,9 +14414,9 @@ void initrace(void) { addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMIN, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALING, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMAJ, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMIN, BLOODFORPOT, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALING, BLOODFORPOT, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_HEALINGMAJ, BLOODFORPOT, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL); addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_COMMON, NULL); addflag(lastrace->flags, F_HITDICE, 5, NA, NA, NULL); @@ -14572,7 +14573,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_WALK, 2, NA, "^gurgling"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 4, NA, "gurgles loudly^a loud gurgle"); addflag(lastrace->flags, F_EATCONFER, F_MUTABLE, B_TRUE, NA, "100"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_FISHLUNG, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_FISHLUNG, BLOODFORPOT, NA, NULL); addrace(R_PIRANHA, "piranha", 0.5, ';', C_GREEN, MT_FLESH, RC_AQUATIC, "A vicious, flesh-eating fish"); setbodytype(lastrace, BT_FISH); @@ -14829,7 +14830,7 @@ void initrace(void) { addflag(lastrace->flags, F_TR, 1, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTIMMUNE, DT_BASH, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_FURY, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_FURY, BLOODFORPOT, NA, NULL); addrace(R_GLUON, "gluon", 1, 'F', C_YELLOW, MT_PLANT, RC_PLANT, "A walking plant-based monster whose body is covered with a thick glue-like secretion."); addbodypart(lastrace, BP_BODY, "stalk"); @@ -15059,7 +15060,7 @@ void initrace(void) { addflag(lastrace->flags, F_ENHANCESMELL, 4, NA, NA, NULL); addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 6, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_MAGIC, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_MAGIC, BLOODFORPOT, NA, NULL); addrace(R_BATVAMPIRE, "vampire bat", 6, 'B', C_BLUE, MT_FLESH, RC_ANIMAL, "Bats which suck the blood of their victims."); setbodytype(lastrace, BT_BIRD); addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_COMMON, NULL); @@ -15960,7 +15961,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTIMMUNE, DT_COLD, B_TRUE, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_COLD, NA, "20"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_ELEMENTIMMUNE, BLOODFORPOT, NA, NULL); addrace(R_ELEPHANT, "elephant", 4000, 'Q', C_GREY, MT_LEATHER, RC_ANIMAL, "A massive grey mammal with a long trunk and sharp tusks."); setbodytype(lastrace, BT_QUADRAPED); @@ -16282,7 +16283,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^squeaking"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 1, NA, "squeaks^squeaking"); addflag(lastrace->flags, F_POISONCORPSE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, BLOODFORPOT, NA, NULL); addflag(lastrace->flags, F_GERMS, NA, NA, NA, NULL); addrace(R_ROC, "roc", 1, 'A', C_MAGENTA, MT_FLESH, RC_ANIMAL, "Rocs are unbelievably gargantuan birds or prey, large enough to carry a fully-grown elephant. They are generally peaceful, but deadly once provoked."); // 'A' for Avian @@ -16699,7 +16700,7 @@ void initrace(void) { addflag(lastrace->flags, F_FLEEONHPPCT, 25, NA, NA, ""); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_POISON, NA, "15"); addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "creep"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, BLOODFORPOT, NA, NULL); addrace(R_SPIDERREDBACK, "giant redback", 5, 'S', C_RED, MT_FLESH, RC_ANIMAL, "A version of a giant spider with a highly painful bite."); setbodytype(lastrace, BT_SPIDER); lastrace->baseid = R_SPIDER; @@ -16739,7 +16740,7 @@ void initrace(void) { addflag(lastrace->flags, F_FLEEONHPPCT, 25, NA, NA, ""); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_POISON, NA, "10"); addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "creep"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_POISON, BLOODFORPOT, NA, NULL); addrace(R_SPIDERTOMB, "tomb spider", 5, 'S', C_BLUE, MT_FLESH, RC_ANIMAL, "Tomb spiders are truly nightmarish beings. Their skin can absorb light itself, and they can boost their own life force by consuming the flesh of their victims."); setbodytype(lastrace, BT_SPIDER); addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_RARE, NULL); @@ -17794,6 +17795,7 @@ void initrace(void) { addflag(lastrace->flags, F_TR, 3, NA, NA, NULL); addflag(lastrace->flags, F_ARMOURRATING, 10, NA, NA, NULL); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 1, NA, NULL); + addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^scuttling"); addflag(lastrace->flags, F_GERMS, NA, NA, NA, NULL); addrace(R_FIREBUG, "firebug", 2, 'x', C_RED, MT_FLESH, RC_INSECT, "Constantly burning insects which attack their victims with fire."); setbodytype(lastrace, BT_QUADRAPED); @@ -18813,7 +18815,7 @@ void initrace(void) { addflag(lastrace->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HITCONFER, F_POISONED, SC_POISON, 100, NULL); addflag(lastrace->flags, F_HITCONFERVALS, P_LYCANTHROPY, -1, PERMENANT, "10^grizzly bear"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, BLOODFORPOT, NA, NULL); addrace(R_WERERAT, "wererat", 50, '@', C_BROWN, MT_FLESH, RC_HUMANOID, "Weedy humans with shifty eyes and whiskers, wererats are known for their extreme cunning."); setbodytype(lastrace, BT_HUMANOID); @@ -18856,7 +18858,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HITCONFER, F_POISONED, SC_POISON, 100, NULL); addflag(lastrace->flags, F_HITCONFERVALS, P_LYCANTHROPY, -1, PERMENANT, "10^dire rat"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, BLOODFORPOT, NA, NULL); addrace(R_WEREWOLF, "werewolf", 100, '@', C_BROWN, MT_FLESH, RC_HUMANOID, "Shaggy humans with the uncanny ability to shapeshift into a ferocious wolf."); setbodytype(lastrace, BT_HUMANOID); @@ -18898,7 +18900,7 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 6, NA, NA, NULL); addflag(lastrace->flags, F_HITCONFER, F_POISONED, SC_POISON, 100, NULL); addflag(lastrace->flags, F_HITCONFERVALS, P_LYCANTHROPY, -1, PERMENANT, "10^dire wolf"); - addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, NA, NA, NULL); + addflag(lastrace->flags, F_FILLPOT, OT_POT_LYCANTHROPY, BLOODFORPOT, NA, NULL); // special monsters addrace(R_GASCLOUD, "cloud of gas", 0.1, 'y', C_GREY, MT_GAS, RC_OTHER, "A large cloud of gas which seems to move with a life of its own..."); @@ -19580,8 +19582,14 @@ void initskills(void) { snprintf(buf, BUFLEN, "^gYou now have common knowledge about %s.^n", rc->pluralname); addskilldesc(sk->id, PR_NOVICE, buf, B_TRUE); + snprintf(buf, BUFLEN, "^gYou now know about the abilities of %s.^n", rc->pluralname); addskilldesc(sk->id, PR_BEGINNER, buf, B_TRUE); + if (raceclassbleeds(rc->id)) { + snprintf(buf, BUFLEN, "^gYou can now recognise the blood of %s.^n", rc->pluralname); + addskilldesc(sk->id, PR_BEGINNER, buf, B_TRUE); + } + snprintf(buf, BUFLEN, "^gYou have now comprehensively studied %s.^n", rc->pluralname); addskilldesc(sk->id, PR_ADEPT, buf, B_TRUE); snprintf(buf, BUFLEN, "^gYou can now anticipate how %s will react.^n", rc->pluralname); diff --git a/data/hiscores.db b/data/hiscores.db index 3a519f6..c86d086 100644 Binary files a/data/hiscores.db and b/data/hiscores.db differ diff --git a/defs.h b/defs.h index 7356f4b..375dd4a 100644 --- a/defs.h +++ b/defs.h @@ -19,6 +19,12 @@ // skill check difficulty #define D_ALWAYSFAIL -99 +// how many blood splashes you need before you +// can fill a flask with it. +#define BLOODFORPOT 2 + +#define TEMPVAULTLOCK "temporary vaultcell lock" + // Text #define TEXT_WARN_ATTACK_NOXP "You will not gain experience until you train. Really attack?" #define TEXT_WARN_CLIMB "Really attempt to climb without Climbing skill?" @@ -50,8 +56,10 @@ // Map building defaults #define DEF_LOOPPCT 95 -#define DEF_SPARSENESS 25 -#define DEF_TURNPCT 40 +//#define DEF_SPARSENESS 25 +#define DEF_SPARSENESS 35 +//#define DEF_TURNPCT 40 +#define DEF_TURNPCT 30 #define DEF_WINDOWPCT 5 #define DEF_WARNINGTIME 20 @@ -88,6 +96,8 @@ #define B_TRUE (-1) #define B_MAYBE (-2) +#define B_INADJCELL (-1) + #define B_DODAM (-1) #define B_NODAM (0) @@ -152,6 +162,9 @@ #define B_COVETS (-1) #define B_ANY (0) +#define B_FIRSTONE (0) +#define B_ANYONE (-1) + #define B_SINGLE (0) #define B_MULTI (-1) #define B_MULTIPLE (-1) @@ -2163,7 +2176,6 @@ enum OBTYPE { OT_BLOODSTAIN, OT_BLOODSPLASH, OT_BLOODPOOL, - OT_BLOODCSPLASH, OT_METALCHUNK, OT_MELTEDWAX, OT_SOGGYPAPER, @@ -2654,8 +2666,6 @@ enum FLAG { F_ENCHANTABLE, // object can get +1/-1 ect F_FILLPOT, // can fill empty flasks with this object to create // a potion of obtype v0. - // if v1 != NA, then object count must be >= v1 to - // fill from this ob. F_GEM, // this object is a gem. F_JEWELERY, // this object counts as jewelery F_GODGIFT, // this was a gift form god with race v0. @@ -3122,6 +3132,12 @@ enum FLAG { // optional v1 = minrange (for spells only) F_RELOADTURNS, // # actions it takes to reload this gun // end gun flags + F_GENERATES, // this object has a v0 pct chance of generating + // the object 'text' each turn. + // v1 = radius to generate obejcts within (including + // centre) + // if v2 is set, obs will be created in a random + // adjacent cell. F_FLAMESTRIKE, // causes fires where you hit F_BALANCE, // heals target if their maxhp < your maxhp F_HOMING, // missile always hits @@ -3403,6 +3419,8 @@ enum FLAG { // calculation // optional text = prefix for ob name. // eg "good" "branded" "cursed" etc + // if this text is a rarity (ie. common) then + // it will be specially handled. F_CONTAINER, // this object is a container - you can use 'o' // to take stuff out or put it in. F_HOLDING, // this container is a xxx of holding and makes objects @@ -3582,6 +3600,8 @@ enum FLAG { // this is cleared at start of turn. F_TARGETCELL, // lf will go towards this place. val0=x,val1=y // optional: v2 = mr_lf or mr_ob. text=lf or ob id. + F_TURNSINPEACE, // v0 is a counter for how many turns this ai + // mosnter has gone without being in battle. F_PHANTASM, // this lf is a phantasm - can deal no damage, and // uses v0 as a fake hp counter. //F_STABBEDBY, // lf has been stabbed by lfid v0. can't be stabbed @@ -3685,6 +3705,7 @@ enum FLAG { F_NAMED, // text = lf's name. ie. lfname = "xat named Fred" F_XPMOD, // add/subtract this much from calculated xpval F_BLOODOB, // text = type of object to drop for blood + // corpses will inherit the FIRST one of these only. F_UNSUMMONOB, // text = type of object to drop when this creature // uis unsummoned. F_DIESPLATTER, // this lf will splatter objcets of type 'text' @@ -3870,6 +3891,8 @@ enum FLAG { // obfrom = if you ate a corpse, this records its race // otherwise, NA. // text = power^fromwhat .eg'a bad egg' + F_VENOMSAC, // this object is a venom sac, used to create poison + // potions F_POISONED, // has poisoning. v0 = poison type, // v1 = power // v2 = if you ate a corpse, this records its race @@ -4704,6 +4727,7 @@ typedef struct cell_s { int hp; char *reason; + char *lockedreason; char *writing; int writinglifetime; @@ -5181,6 +5205,7 @@ typedef struct prompt_s { typedef struct node_s { struct cell_s *c; struct node_s *parent; + int dirfromparent; int fromstart, heur, cost; } node_t; diff --git a/flag.c b/flag.c index bf94733..3eff40a 100644 --- a/flag.c +++ b/flag.c @@ -90,6 +90,18 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, if ((id == F_PARTVEGETARIAN) && hasflag(fp, F_CARNIVORE)) return NULL; if ((id == F_CARNIVORE) && (hasflag(fp, F_VEGETARIAN) || hasflag(fp, F_PARTVEGETARIAN)) ) return NULL; + // no dupes allowed + if (id == F_LINKRACE) { + if (hasflagval(fp, F_LINKRACE, val1, val2, val3, NULL)) { + return NULL; + } + } + if (id == F_FILLPOT) { + if (hasflagval(fp, F_FILLPOT, val1, NA, NA, NULL)) { + return NULL; + } + } + // overrite NA rarity if ((id == F_RARITY) && (val3 == NA)) { val3 = RR_COMMON; @@ -566,15 +578,19 @@ void copyflags(flagpile_t *dst, flagpile_t *src, int lifetime) { } newf = addflag_real(dst, f->id, f->val[0], f->val[1], f->val[2], f->text, (lifetime == NA) ? f->lifetime : lifetime, f->known, -1); - newf->condition = f->condition; - newf->origlifetime = f->origlifetime; - newf->chance = f->chance; - if (f->altval) { - addaltval(newf, f->altval->id, - f->altval->val[0], - f->altval->val[1], - f->altval->val[2], - f->altval->text); + // newf might not be set, if (for example) we are coyping a duplicate + // f_linkrace flag. + if (newf) { + newf->condition = f->condition; + newf->origlifetime = f->origlifetime; + newf->chance = f->chance; + if (f->altval) { + addaltval(newf, f->altval->id, + f->altval->val[0], + f->altval->val[1], + f->altval->val[2], + f->altval->text); + } } } } diff --git a/god.c b/god.c index 4956097..6132033 100644 --- a/god.c +++ b/god.c @@ -754,7 +754,7 @@ void dooffer(void) { pleasegod(god->race->id, pietyplus); if (strlen(splatterob)) { addob(player->cell->obpile, splatterob); - addobsinradius(player->cell, 1, DT_COMPASS, splatterob, B_TRUE, NULL); + addobsinradius(player->cell, 1, DT_COMPASS, splatterob, B_TRUE, B_NOCENTRE, NULL, NULL, NULL, NULL); } if (god->race->id == R_GODFIRE) { dospelleffects(player, OT_S_FLAMEBURST, 1, NULL, NULL, player->cell, B_UNCURSED, NULL, B_FALSE, NULL); @@ -1085,11 +1085,11 @@ lifeform_t *godappears(enum RACE rid, cell_t *where) { switch (god->race->id) { case R_GODBATTLE: // bloodsplatter addob(god->cell->obpile, "splash of blood"); - addobsinradius(god->cell, 2, DT_COMPASS, "splash of blood", B_TRUE, NULL); + addobsinradius(god->cell, 2, DT_COMPASS, "splash of blood", B_TRUE, B_INCLUDECENTRE, NULL, NULL, NULL, NULL); break; case R_GODLIFE: //light addob(god->cell->obpile, "bright light"); - addobsinradius(god->cell, 2, DT_COMPASS, "bright light", B_TRUE, NULL); + addobsinradius(god->cell, 2, DT_COMPASS, "bright light", B_TRUE, B_INCLUDECENTRE, NULL, NULL, NULL, NULL); break; case R_GODFIRE: // fire addob(god->cell->obpile, "large fire"); @@ -1477,7 +1477,7 @@ int godgiftmaybe(enum RACE rid, int fromtemple, int announce) { if (getskill(player, SK_RANGED)) { char obname[BUFLEN]; if (real_getrandomob(NULL, obname, 25, NA, getlfsize(player), - SK_RANGED, B_TRUE, OC_WEAPON, OC_NONE, DT_NONE, F_NONE)) { + SK_RANGED, RR_NONE, B_TRUE, OC_WEAPON, OC_NONE, DT_NONE, F_NONE)) { snprintf(obtogive, BUFLEN, "excellent %s", obname); } else { rollagain = B_TRUE; diff --git a/io.c b/io.c index af56035..a1b08ce 100644 --- a/io.c +++ b/io.c @@ -4304,12 +4304,11 @@ void describespell(objecttype_t *ot) { restoregamewindows(); } -void doattackcell(char dirch) { - int dir = D_NONE; +void doattackcell(int dir) { flag_t *f; cell_t *c; - if (dirch == '\0') { + if (dir == D_NONE) { dir = askdir("Attack in which direction (- to cancel)", B_TRUE, B_TRUE); } @@ -4326,7 +4325,7 @@ void doattackcell(char dirch) { // update last cmd f = hasflag(player->flags, F_LASTCMD); if (f) { - f->val[2] = dirch; + f->val[2] = dir; } @@ -10683,7 +10682,7 @@ void handleinput(void) { if (wantrepeat) { doattackcell(repeatflag.val[2]); } else { - doattackcell('\0'); + doattackcell(D_NONE); } break; case CMD_GO: // go somewhere (pathfind) @@ -14407,11 +14406,11 @@ void tombstone(lifeform_t *lf) { cls(); y = 0; - if (rank == -1) { + if (rank == -1) { // -1 means 'cheating' mvwprintw(mainwin, y, 0, "Since you were cheating, your score will not be listed."); y++; minrank = 1; maxrank = 10; - } else if (rank > 100) { + } else if ((rank == -2) || (rank > 100)) { // -2 means 'no points' mvwprintw(mainwin, y, 0, "Your score of %ld didn't make the top 100 list.", playerscore); y++; minrank = 1; maxrank = 10; diff --git a/io.h b/io.h index fb366a7..71b038c 100644 --- a/io.h +++ b/io.h @@ -47,7 +47,7 @@ void describeob(object_t *o); void describerace(enum RACE rid); void describeskill(enum SKILL skid, enum SKILLLEVEL levhilite); void describespell(objecttype_t *ot); -void doattackcell(char dirch); +void doattackcell(int dir); void doclose(void); void docomms(lifeform_t *target); void docomms_areainfo(char *who, flagpile_t *fp, lifeform_t *lf); diff --git a/lf.c b/lf.c index b589f6e..e3f9bbc 100644 --- a/lf.c +++ b/lf.c @@ -218,48 +218,26 @@ void awardxpfor(lifeform_t *killed, float pct) { } void bleed(lifeform_t *lf, int splatter) { - flag_t *f; char obname[BUFLEN]; - flag_t *retflag[MAXCANDIDATES]; - int nretflags; if (lf->cell->type->solid) return; if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) return; - if (hasequippedobid(lf->pack, OT_AMU_BLOOD)) return; - getflags(lf->flags, retflag, &nretflags, F_BLOODOB, F_NONE); - if (nretflags) { - // pick a random one - f = retflag[rnd(0,nretflags-1)]; - if (f->text) { - strcpy(obname, f->text); - } else { - strcpy(obname, ""); - } - } else { - strcpy(obname, "splash of blood"); - } + getbloodobname(lf->flags, obname, B_ANYONE); if (strlen(obname) > 0) { - int nfillpot = 0; object_t *o; o = addob(lf->cell->obpile, obname); - - if (hasflag(lf->flags, F_FILLPOT)) { - // override object flags - killflagsofid(o->flags, F_FILLPOT); - } - nfillpot = copyflag(o->flags, lf->flags, F_FILLPOT); - + setbloodfrom(o, lf); if (splatter) { - addobsinradius(lf->cell, 1, DT_COMPASS, obname, B_TRUE, NULL); - if (nfillpot) { - int i; - for (i = 0; i < nretobs; i++) { - copyflag(retobs[i]->flags, lf->flags, F_FILLPOT); - } + object_t *bloodob[MAXCANDIDATES]; + int i,nbloodobs = 0; + // this call will populate retobs[]... + addobsinradius(lf->cell, 1, DT_COMPASS, obname, B_TRUE, B_NOCENTRE, NULL, bloodob, NULL, &nbloodobs); + for (i = 0; i < nbloodobs; i++) { + setbloodfrom(bloodob[i], lf); } } } @@ -3512,6 +3490,10 @@ void die(lifeform_t *lf) { case RC_PLANT: pleasegodmaybe(R_GODFIRE, 10); break; + case RC_ROBOT: + pleasegodmaybe(R_GODLIFE, rnd(2,3)); + pleasegodmaybe(R_GODMAGIC, rnd(2,3)); + break; default: break; } @@ -3642,7 +3624,7 @@ void die(lifeform_t *lf) { msg("%s explodes into flames!", lfname); } // explode into flames - addobsinradius(corpsecell, 1, DT_COMPASS, "medium fire", B_TRUE, NULL); + addobsinradius(corpsecell, 1, DT_COMPASS, "medium fire", B_TRUE, B_INCLUDECENTRE, NULL, NULL, NULL, NULL); } else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) { // shattered fragments(corpsecell, "chunk of ice", 2, UNLIMITED); @@ -3732,6 +3714,33 @@ void die(lifeform_t *lf) { // some corpses will regenerate... if (copyflag(corpse->flags, lf->flags, F_REVIVETIMER)) { killflagsofid(corpse->flags, F_OBHPDRAIN); + } else { + // non-regenerating corpses might bleed... + if (damtypecausesbleed(lf->lastdamtype, NULL)) { + char bname[BUFLEN]; + if (getbloodobname(lf->flags, bname, B_FIRSTONE)) { + int howlong; + char *locbname; + locbname = strdup(bname); + // insert race name. for example, + // replace 'splash of blood' with 'splash of xat blood' + if (strends(locbname, " of blood")) { + char replacewith[BUFLEN]; + sprintf(replacewith, " of %s blood",lf->race->name); + strrep(&locbname, " of blood", replacewith, NULL); + } + // amount of time blood will appear for depends + // on size. + howlong = getlfsize(lf)*4; + if (howlong > 0) { + flag_t *bf; + bf = addtempflag(corpse->flags, F_GENERATES, + 80, 0, NA, locbname, howlong); + dblog("xx"); + } + free(locbname); + } + } } // special case if (lf->race->id == R_BLASTBUG) { @@ -4925,7 +4934,7 @@ int eat(lifeform_t *lf, object_t *o) { if (corpserace && (corpserace->id == lf->race->baseid)) { ppower = 3; } - poison(lf, rnd(20,40), ptid, ppower, dambuf, cf ? cf->val[0] : R_NONE); + poison(lf, rnd(20,40), ptid, ppower, dambuf, cf ? cf->val[0] : R_NONE, B_FALSE); } else if (!drinking) { // raw meat? if (corpserace && israwmeat(o) && !lfhasflag(lf, F_CANEATRAW)) { @@ -4961,7 +4970,7 @@ int eat(lifeform_t *lf, object_t *o) { ppower = 3; } if (!skillcheck(lf, SC_POISON, checkdiff, 0)) { - poison(lf, rnd(timemin,timemax), ptid, ppower, dambuf, corpserace ? corpserace->id : R_NONE); + poison(lf, rnd(timemin,timemax), ptid, ppower, dambuf, corpserace ? corpserace->id : R_NONE, B_FALSE); } } } @@ -5083,7 +5092,7 @@ int eat(lifeform_t *lf, object_t *o) { addtempflag(lf->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, rnd(20,40)); } else if (o->type->id == OT_POISONSAC) { // very bad! - poison(lf, rnd(10,20), P_VENOM, 4, "eating a venom sac", R_NONE); + poison(lf, rnd(10,20), P_VENOM, 4, "eating a venom sac", R_NONE, B_TRUE); } if (isplayer(lf)) makeknown(o->type->id); @@ -7090,6 +7099,42 @@ int getadjenemies(lifeform_t *who, lifeform_t **adjlf, int *nadjlfs) { return nadj; } +// populates (and returns) buf with object name for this flagpile's blood. +// if no bloodob, buf will be set to "" and NULL returned. +// +// 'userandom' determines the behaviour if the flagpile has multiple F_BLOODOB flags. +// B_FIRSTONE means just return the first flag. +// B_ANYONE means return a random flag +char *getbloodobname(flagpile_t *fp, char *buf, int userandom) { + flag_t *retflag[MAXCANDIDATES],*f = NULL; + int nretflags; + getflags(fp, retflag, &nretflags, F_BLOODOB, F_NONE); + if (nretflags) { + if (nretflags == 1) { + f = retflag[0]; + } else { + if (userandom) { + // pick a random one + f = retflag[rnd(0,nretflags-1)]; + } else { + // pick first one + f = retflag[0]; + } + } + if (f->text) { + strcpy(buf, f->text); + } else { + strcpy(buf, ""); + return NULL; + } + } else { + // default + strcpy(buf, "splash of blood"); + } + return buf; +} + + // include allies or enemies which will follow you up/down stairs etc // ie. allies within LOS // ie. adjacent enemies @@ -12184,7 +12229,7 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { default: break; } depthmod += depthmod2; - if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, SK_NONE, B_TRUE, OC_NONE, DT_NONE, F_NONE)) { + if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, SK_NONE, RR_NONE, B_TRUE, OC_NONE, DT_NONE, F_NONE)) { if (isshop) apply_shopob_restrictions(buf); o = addob(op, buf); } @@ -12203,7 +12248,8 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { default: break; } depthmod += depthmod2; - if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, SK_NONE, B_TRUE, OC_NONE, val[1], DT_NONE, F_NONE)) { + if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, SK_NONE, + RR_NONE, B_TRUE, OC_NONE, val[1], DT_NONE, F_NONE)) { if (db) snprintf(buf2, BUFLEN, "finished startobdt successfuly."); if (isshop) apply_shopob_restrictions(buf); o = addob(op, buf); @@ -12226,7 +12272,8 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { default: break; } depthmod += depthmod2; - if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, val[1], B_TRUE, OC_NONE, DT_NONE, F_NONE)) { + if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, val[1], + getrarityval(text), B_TRUE, OC_NONE, DT_NONE, F_NONE)) { char buf3[BUFLEN]; if (db) snprintf(buf2, BUFLEN, "finished startobwepsk successfuly."); sprintf(buf3, "%s%s%s",text,strlen(text) ? " " : "",buf); @@ -12258,7 +12305,7 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { //obdb = B_TRUE; if (real_getrandomob(targmap, buf, getmapdifficulty(targmap) + depthmod, - NA, maxobsize, SK_NONE, B_TRUE, + NA, maxobsize, SK_NONE, RR_NONE, B_TRUE, val[1], OC_NONE, DT_NONE, wantflagid, F_NONE)) { if (db) snprintf(buf2, BUFLEN, "finished startobclass, success."); if (isshop) apply_shopob_restrictions(buf); @@ -16407,6 +16454,11 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml // if they died if (lf->hp <= 0) { + // bleed some more. + if (damtypecausesbleed(damtype, fromob)) { + bleed(lf, B_NOSPLATTER); + } + if (fromlf && (getallegiance(fromlf) == AL_FRIENDLY)) { addflag(lf->flags, F_KILLEDBYPLAYER, B_TRUE, NA, NA, NULL); } @@ -16522,7 +16574,7 @@ void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fr } // catch a cold? if (!skillcheck(lf, SC_CON, (dam/2) + (getexposedlimbs(lf)*20), 0)) { - poison(lf, 20+(dam*3), P_COLD, 0, "the cold", fromlf ? fromlf->race->id : R_NONE); + poison(lf, 20+(dam*3), P_COLD, 0, "the cold", fromlf ? fromlf->race->id : R_NONE, B_FALSE); } // cold will heal bruised limbs getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); @@ -16570,7 +16622,7 @@ void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fr } else if (damtype == DT_POISONGAS) { if (dam > 0) { if (!skillcheck(lf, SC_POISON, 170, 0)) { // HARD. - poison(lf, rnd(20,40), P_GAS, 2, "poison gas", fromlf ? fromlf->race->id : R_NONE); + poison(lf, rnd(20,40), P_GAS, 2, "poison gas", fromlf ? fromlf->race->id : R_NONE, B_TRUE); } } } @@ -18096,6 +18148,7 @@ int pickup(lifeform_t *lf, object_t *what, int howmany, int fromground, int want // picked up something our god doesn't like? if (isplayer(lf)) { if ( hasflagknown(o->flags, F_POISONED) || + hasflag(o->flags, F_VENOMSAC) || hasflagvalknown(o->flags, F_HITCONFER, F_POISONED, NA, NA, NULL)) { int i,nretgods; lifeform_t *retgod[MAXGODS]; @@ -18117,7 +18170,7 @@ int pickup(lifeform_t *lf, object_t *what, int howmany, int fromground, int want } // returns true on failure -int poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat, enum RACE srcraceid) { +int poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat, enum RACE srcraceid, int immediate) { flag_t *f; int found = B_FALSE,i; enum POISONSEVERITY psev; @@ -18187,7 +18240,7 @@ int poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char * if (!found) { // incubation period? - if (pt->incubationtime && isplayer(lf)) { + if (!immediate && pt->incubationtime && isplayer(lf)) { flag_t *ii; int multiplier = 0; int tempmult; @@ -18604,6 +18657,7 @@ void preparecorpse(lifeform_t *lf, object_t *corpse) { // add flag _before_ getting the name addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); + killflagsofid(corpse->flags, F_GENERATES); // stop making blood if (!hasflag(corpse->flags, F_TAINTED)) { flag_t *f; // it lasts a lot longer now. @@ -18622,6 +18676,7 @@ void preparecorpse(lifeform_t *lf, object_t *corpse) { msg("You cook %s.", obname); addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); + killflagsofid(corpse->flags, F_GENERATES); // stop making blood } else if (cansee(player, lf)) { char lfname[BUFLEN]; // add flag after getting the name @@ -18630,9 +18685,11 @@ void preparecorpse(lifeform_t *lf, object_t *corpse) { msg("%s cooks %s.", obname); addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); + killflagsofid(corpse->flags, F_GENERATES); // stop making blood } else { addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); + killflagsofid(corpse->flags, F_GENERATES); // stop making blood } } @@ -18706,6 +18763,21 @@ int racecantalk(enum RACE rid) { return B_FALSE; } +// returns wheter, in general, creatures of this raceclass will bleed. +// (individual creatures may override this though) +int raceclassbleeds(enum RACECLASS id) { + switch (id) { + case RC_ANIMAL: + case RC_AQUATIC: + case RC_DRAGON: + case RC_HUMANOID: + case RC_INSECT: + return B_TRUE; + default: break; + } + return B_FALSE; +} + int racemeetscondition(race_t *r, enum CELLCONDITION cond, int arg, int val) { int ok = B_FALSE; flag_t *f; @@ -19279,6 +19351,20 @@ int safetorest(lifeform_t *lf) { } } } + + // extra checks for monsters + if (!isplayer(lf)) { + int timeneeded; + flag_t *f; + // must have gone at lesat 10 turns without being in battle + timeneeded = 16 - (getmorale(lf) / 5); + limit(&timeneeded, 5, NA); + + f = lfhasflag(lf, F_TURNSINPEACE); + if (!f || (f->val[0] < timeneeded)) { + return B_FALSE; + } + } return B_TRUE; } @@ -19869,6 +19955,40 @@ void setattr(lifeform_t *lf, enum ATTRIB attr, int val) { } } +// if object o is a single object, mark that it came from lf. +// +// if it is more than one object, then maybe turn it into 'mixed blood' +// if there was already blood from a different kind of lf here. +// this will also make it unusable for filling flasks (ie. removes +// the F_FILLPOT flag) +// +// returns TRUE if blood is pure, FALSE if we now have mixed blood +int setbloodfrom(object_t *o, lifeform_t *lf) { + int pure = B_TRUE; + if (o->amt == 1) { + // ie. not joining a stack + addflag(o->flags, F_LINKRACE, lf->race->id, NA, NA, NULL); + if (hasflag(lf->flags, F_FILLPOT)) { + // override object flags + killflagsofid(o->flags, F_FILLPOT); + copyflag(o->flags, lf->flags, F_FILLPOT); + } + } else { + // if object isn't already linked to this lf, do so. + if (!hasflagval(o->flags, F_LINKRACE, lf->race->id, NA, NA, NULL)) { + addflag(o->flags, F_LINKRACE, lf->race->id, NA, NA, NULL); + } + // if there are now more than a single F_LINKRACE, it means that + // this is mixed blood. + if (countflagsofid(o->flags, F_LINKRACE) > 1) { + // mixed blood - can't use it for potions anymore + killflagsofid(o->flags, F_FILLPOT); + pure = B_FALSE; + } + } + return pure; +} + void setbodypartname(race_t *r, enum BODYPART bp, char *name) { int i; for (i = 0; i < r->nbodyparts; i++) { @@ -21511,11 +21631,13 @@ void startlfturn(lifeform_t *lf) { movedlastturn += killflagsofid(lf->flags, F_HASBEENMOVED); movedlastturn += killflagsofid(lf->flags, F_MOVED); + // if we didn't turn lsat move, kill our turncounter if (!killflagsofid(lf->flags, F_TURNED)) { lf->turncounter = 0; } + // update where player knows // (but without a map you will then slowly forget it) if (isplayer(lf)) { @@ -21526,6 +21648,21 @@ void startlfturn(lifeform_t *lf) { // ai start of turn code killflagsofid(lf->flags, F_IGNORECELL); + // track how long we have gone without fighitng. + // this is used to determine when it is safe to + // rest. + if (isinbattle(lf, B_INCLUDEDISTANT, B_FALSE)) { + killflagsofid(lf->flags, F_TURNSINPEACE); + } else { + f = lfhasflag(lf, F_TURNSINPEACE); + if (f) { + f->val[0]++; + } else { + addflag(lf->flags, F_TURNSINPEACE, 1, NA, NA, NULL); + } + } + + // cope with hecta's "sacrifices" escaping from the player f = lfhasflag(lf, F_HECTAESCAPEE); if (f) { @@ -21971,7 +22108,7 @@ void startlfturn(lifeform_t *lf) { for (i = 1 ; i < lf->nlos; i++) { lifeform_t *otherlf; otherlf = lf->los[i]->lf; - if (otherlf && !areallies(lf, otherlf) && cansee(lf, otherlf)) { + if (otherlf && !areallies(lf, otherlf) && cansee(lf, otherlf) && !isasleep(otherlf)) { int dist; dist = getcelldist(lf->cell, lf->los[i]); if (dist == (f->val[0] + 1) || (dist == (f->val[0] + 2))) { @@ -22030,7 +22167,7 @@ void startlfturn(lifeform_t *lf) { addob(c->obpile, f->text); } } else { - addobsinradius(lf->cell, f->val[0], DT_COMPASS, f->text, B_FALSE, lf); + addobsinradius(lf->cell, f->val[0], DT_COMPASS, f->text, B_FALSE, B_INCLUDECENTRE, lf, NULL, NULL, NULL); } } } diff --git a/lf.h b/lf.h index 66b3f06..7e4e2ed 100644 --- a/lf.h +++ b/lf.h @@ -150,6 +150,7 @@ int get_adjacent_quadrants(int dir, enum QUADRANT *start, enum QUADRANT *end); int get_circular_fov_endpoints(lifeform_t *lf, int maxvisrange, int *endx, int *endy, int *nendcells); int getactspeed(lifeform_t *lf); int getadjenemies(lifeform_t *lf, lifeform_t **adjlf, int *nadjlfs); +char *getbloodobname(flagpile_t *fp, char *buf, int userandom); void getwhowillfollow(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *seen, int *nadjallies); enum ALIGNMENT getalignment(lifeform_t *lf); int getalignmod(lifeform_t *lf); @@ -449,7 +450,7 @@ enum NOISECLASS noisetypetoclass(enum NOISETYPE nt); void outfitlf(lifeform_t *lf); void petify(lifeform_t *lf, lifeform_t *owner); int pickup(lifeform_t *lf, object_t *what, int howmany, int fromground, int antannounce); -int poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat, enum RACE srcraceid); +int poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat, enum RACE srcraceid, int immediate); int poisoneffects(lifeform_t *lf, enum POISONTYPE ptid, int power); int poisoncausesvomit(enum POISONTYPE ptype); int poisonthreatenslife(lifeform_t *lf, flag_t *f); @@ -460,6 +461,7 @@ void precalclos(lifeform_t *lf); void preparecorpse(lifeform_t *lf, object_t *corpse); int push(lifeform_t *lf, object_t *o, int dir); int racecantalk(enum RACE rid); +int raceclassbleeds(enum RACECLASS id); int racemeetscondition(race_t *r, enum CELLCONDITION cond, int arg, int val); int racemeets(enum RACE rid, condset_t *cs); int readytotrain(lifeform_t *lf); @@ -482,6 +484,7 @@ int scare(lifeform_t *lf, lifeform_t *scarer, int howlong, int scarerbonus); //int setammo(lifeform_t *lf, object_t *o); void setalignment(lifeform_t *lf, enum ALIGNMENT al); void setattr(lifeform_t *lf, enum ATTRIB attr, int val); +int setbloodfrom(object_t *o, lifeform_t *lf); void setbodypartname(race_t *r, enum BODYPART bp, char *name); void setbodytype(race_t *r, enum BODYTYPE bt); int setfacing(lifeform_t *lf, int dir); diff --git a/map.c b/map.c index 2249fc9..424d04e 100644 --- a/map.c +++ b/map.c @@ -5,6 +5,7 @@ #include #include #include +#include "astar.h" #include "attack.h" #include "defs.h" #include "flag.h" @@ -93,6 +94,7 @@ cell_t *addcell(map_t *m, int x, int y) { cell->filled = B_FALSE; cell->isroomwall = D_NONE; cell->reason = NULL; + cell->lockedreason = NULL; return cell; } @@ -297,14 +299,14 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok if (db) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "addmonster for '%s'->%s",racename, r->name); - dbtimestart(buf); + dblog(buf); } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging //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"); + if (db) dblog("doing lf addition"); if (hasflag(r->flags, F_UNIQUE)) { // does it already exist? @@ -320,14 +322,14 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok //if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging lf = addlf(c, r->id, getrandommonlevel(r, c->map)); - if (db) dbtime("finished lf addition"); + if (db) dblog("finished lf addition"); //if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging if (lf) { flag_t *f; if (nadded) (*nadded)++; - if (db) dbtime("checking for job"); + if (db) dblog("checking for job"); lf->born = B_FALSE; // special case for vaults @@ -444,7 +446,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok //if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging // appears in groups? - if (db) dbtime("handling groups"); + if (db) dblog("handling groups"); if (autogen) { f = hasflag(lf->flags, F_NUMAPPEAR); if (f) { @@ -495,7 +497,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok // minons? // appears in groups? - if (db) dbtime("handling minions"); + if (db) dblog("handling minions"); if (autogen) { f = hasflag(lf->flags, F_MINIONS); if (f) { @@ -536,7 +538,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - if (db) dbtime("handling random objects"); + if (db) dblog("handling random objects"); // sometimes give the lf random objects (extra monsters through // 'numappears' don't get them). if (onein(3)) { @@ -550,7 +552,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok /* // XXX: temporarily disbled due to slow light code - if (db) dbtime("giving torches"); + if (db) dblog("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)) { @@ -588,7 +590,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok killflagpile(wantflags); //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - if (db) dbtimeend("finished addmonster"); + if (db) dblog("finished addmonster"); return lf; } @@ -602,7 +604,7 @@ object_t *addrandomob(cell_t *c) { return NULL; } - if (real_getrandomob(c->map, buf, NA, c->habitat->id, SZ_MAX, SK_NONE, B_FALSE, OC_NONE, DT_NONE, F_NONE)) { + if (real_getrandomob(c->map, buf, NA, c->habitat->id, SZ_MAX, SK_NONE, RR_NONE, B_FALSE, OC_NONE, DT_NONE, F_NONE)) { if (db) dblog("adding rand obj %s to cell %d,%d",buf,c->x,c->y); o = addob(c->obpile, buf); } @@ -1129,6 +1131,7 @@ void breakwall(cell_t *c, char *why, ...) { } } + int cellhaslos(cell_t *c1, cell_t *dest) { int deltax, deltay; int numpixels; @@ -1286,9 +1289,10 @@ int adjcellokforreachability(cell_t *c, int srcroomid, int dir, int wantfilled) proomid = getroomid(c); if ( ((srcroomid == -1) || (proomid != srcroomid)) && cellwalkable(NULL, c, NULL) && !c->locked) { - if ((proomid >= 0) && (c->isroomwall != diropposite(dir))) { + if ((proomid >= 0) && (dir != D_NONE) && (c->isroomwall != D_NONE) && + (c->isroomwall != diropposite(dir))) { // different room and hits wrong wall. - } else if (cellisfixedvaultwall(c)) { + } else if (cellisfixedvaultwall(c) && (!streq(c->lockedreason, TEMPVAULTLOCK))) { } else { if (!wantfilled || c->filled) { return B_TRUE; @@ -1481,12 +1485,16 @@ int cellmeets(cell_t *c, condset_t *cs) { } // returns B_TRUE, B_FALSE or B_MAYBE -int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom) { - int db = B_FALSE; +int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom, char *why) { + int db = B_FALSE,d; + cell_t *c2; + if (why) strcpy(why, "ok."); if (c->locked) { + //db = B_TRUE; // locked cell. invalied. if (insameroom) *insameroom = B_FALSE; - if (db) dblog(" going %s hits locked cell. invalid.", getdirname(dir)); + if (db) dblog(" going %s hits locked cell(%s). invalid.", getdirname(dir),c->lockedreason); + if (why) sprintf(why, " going %s hits locked cell(%s). invalid.", getdirname(dir),c->lockedreason); return B_FALSE; } else if ((srcroomid >= 0) && (getroomid(c) == srcroomid) && c->type->solid && startcell->type->solid) { // hits a wall of the same room, @@ -1495,39 +1503,59 @@ int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, // invalid if (insameroom) *insameroom = B_TRUE; if (db) dblog(" going %s hits wall of same room. invalid.", getdirname(dir)); + if (why) sprintf(why," going %s hits wall of same room. invalid.", getdirname(dir)); return B_FALSE; - } else if (isroom(c) && (getroomid(c) != srcroomid) && (c->isroomwall != diropposite(dir))) { + } else if (isroom(c) && (getroomid(c) != srcroomid) && (c->isroomwall != D_NONE) && (dir != D_NONE) && + (c->isroomwall != diropposite(dir))) { // cell is in a different room, but not the correct edge // invalid if (insameroom) *insameroom = B_FALSE; if (db) dblog(" going %s hits wrong wall (%s) of different room. invalid.", getdirname(dir), getdirname(c->isroomwall)); + if (why) sprintf(why," going %s hits wrong wall (%s) of different room. invalid.", getdirname(dir), getdirname(c->isroomwall)); return B_FALSE; - } else if (cellisfixedvaultwall(c)) { + } else if (cellisfixedvaultwall(c) && !streq(c->lockedreason, TEMPVAULTLOCK)) { // cell is a wall of a maintain_edge vault, and not an exit cell // invalid if (insameroom) *insameroom = B_FALSE; if (db) dblog(" going %s hits non-exit wall maintain_edge vault. invalid.", getdirname(dir)); + if (why) sprintf(why," going %s hits non-exit wall maintain_edge vault. invalid.", getdirname(dir)); return B_FALSE; } else if (isroom(c) && (getroomid(c) != srcroomid) && c->type->solid && countadjdoors(c) ) { // cell is the wall of a different room, and adjacent to a door. // invalid if (insameroom) *insameroom = B_FALSE; if (db) dblog(" going %s hits wall adjacent to door. invalid.", getdirname(dir)); + if (why) sprintf(why," going %s hits wall adjacent to door. invalid.", getdirname(dir)); return B_FALSE; } else if (cellwalkable(NULL, c, NULL)) { if (getroomid(c) == srcroomid) { // invalid if (insameroom) *insameroom = B_TRUE; if (db) dblog(" going %s hits empty cell of same room. invalid.", getdirname(dir)); + if (why) sprintf(why," going %s hits empty cell of same room. invalid.", getdirname(dir)); return B_FALSE; } else { if (!wantfilled || c->filled) { // walkable and not in this vault. finished. // valid. + if (why) sprintf(why,"VALID"); return B_TRUE; } } } + + // adjacent to wall of current room? + for (d = D_N; d <= D_W; d++) { + c2 = getcellindir(c, d); + if (c2 && !cellwalkable(NULL, c2, NULL) && (getroomid(c2) == srcroomid) && !isadjacent(c2, c)) { + if (insameroom) *insameroom = B_TRUE; + if (db) dblog(" going %s ends up adjacent to wall of start room. invalid.", getdirname(dir)); + if (why) sprintf(why," going %s ends up adjacent to wall of start room. invalid.", getdirname(dir)); + return B_FALSE; + } + } + + if (why) sprintf(why,"maybe..."); return B_MAYBE; } @@ -1738,7 +1766,7 @@ int delve(map_t *map, int neighbourmin, int neighbourmax, int connchance, int ch dblog(" wanted cells: %d / %d (%d%%)", wantcells, totcells, (int) (((double)wantcells / (double)totcells)*(double)100) ); dblog(" dug cells: %d", cellsdone); dblog("generated map:"); - dumpmap(map, B_FALSE); + dumpmap(map, B_FALSE, NULL); dblog("END DELVE DEBUG:"); return cellsdone; @@ -2308,24 +2336,29 @@ int fix_reachability(map_t *m) { if (!m->cell[i]->type->solid && !m->cell[i]->filled && ((m->cell[i]->room && m->cell[i]->room->prevault) || !m->cell[i]->locked ) ) { vault_t *v; + // cell is unreachable v = getcellvault(m->cell[i]); if (v && hasflag(v->flags, F_VAULTNOLINK)) { // don't need to link it. + } else if (!countcellexits(c, DT_COMPASS) && !hasobwithflag(c->obpile, F_STAIRS)) { + // completely surrounded by walls. + clearcell(c); + setcelltype(c,getmapsolid(m)); } else { unreachcell[nunreach++] = m->cell[i]; } - } else { - if (!m->cell[i]->type->solid) { - // reachable. - reachcell[nreach++] = m->cell[i]; - } + } else if (!m->cell[i]->type->solid) { + // cell is ok - reachable. + reachcell[nreach++] = m->cell[i]; } } // try to fix unreachable areas. if (nunreach) { int nadded = 0,nportals = 0, ndoors = 0; cell_t *ucell; + ucell = unreachcell[0]; + if (db) dblog(" attempting to fix unreachable area at %d,%d.", ucell->x, ucell->y); @@ -2342,15 +2375,14 @@ int fix_reachability(map_t *m) { if (firstidx == -1) firstidx = idx; ucell = unreachcell[idx]; // try to link it back to a filled cell. - if (db) dblog(" looking for fixes at %d,%d...", - ucell->x, ucell->y); + //if (db) dblog(" looking for fixes at %d,%d...", ucell->x, ucell->y); if (!linkexit(ucell, B_TRUE, &nadded)) { // fixed. if (db) dblog(" successfully fixed by digging tunnels."); break; } else { - if (db) dblog("failed, trying new cell."); + //if (db) dblog("failed, trying new cell."); ntries++; // loop around to start if (++idx == nunreach) idx = 0; @@ -2358,9 +2390,9 @@ int fix_reachability(map_t *m) { } } /* - if (ntries >= maxtries) { + if (!ndoors) { cell_t *rcell; - int ptries = 0; + int ptries = 0,maxtries = 5; // failed! try to link via portals. if (db) dblog(" couldn't link via tunnels. trying to link via portals."); // select random REACHABLE cell @@ -2382,6 +2414,7 @@ int fix_reachability(map_t *m) { } else { // didn't add anything - fail! if (db) dblog(" fix_reachability failed."); + //raise(SIGINT); return B_TRUE; } @@ -2991,7 +3024,7 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int y = coord[i].y; // would the room fit here? if ( ((x + w) <= (map->w - xmargin)) && - ((y + h) <= (map->h - ymargin))) { + ((y + h) <= (map->h - ymargin))) { int valid = B_TRUE; int rx,ry; score = 0; @@ -3043,7 +3076,6 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int } } - if (valid) { // calculate score if we placed the room with its top left corner here. for (ry = y; (ry < y+h) && valid; ry++) { @@ -3055,11 +3087,7 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int // - be on top of the player (normally this can't happen, // but debugging via 'create vault' could do it) if (cell->lf && isplayer(cell->lf)) { - valid = B_FALSE; - } - // - be on top of an existing staircase - if (hasobwithflag(cell->obpile, F_CLIMBABLE)) { - valid = B_FALSE; + valid = B_FALSE; continue; } /* // - overlap the inside of an existing room @@ -3073,17 +3101,23 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int */ // - overlap any part of an existing room/vault if (cell->room) { - valid = B_FALSE; + valid = B_FALSE; continue; } // - overlap a 'locked' cell if (cell->locked) { - valid = B_FALSE; + valid = B_FALSE; continue; + } + // - be on top of an existing staircase + if (hasobwithflag(cell->obpile, F_CLIMBABLE)) { + valid = B_FALSE; continue; } // is this cell adjacent to an empty cell and not a // corner (ie. a valid door location) if (countcellexits(cell, DT_ORTH)) { score++; + if (score >= bestscore) continue; + if ( ((ry == y) && (rx == x)) || ((ry == y) && (rx == (x+w-1))) || ((ry == y+h-1) && (rx == x)) || @@ -3096,25 +3130,34 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int // not adjacent to any empty cells. ok. includethiscell = B_TRUE; } + if (includethiscell) { // is this cell empty itself? - if (!cell->type->solid) score += 3; + if (!cell->type->solid) { + score += 3; + if (score >= bestscore) continue; + } // avoid being adjacent to other room walls - if (countcellexits(cell, DT_ORTH)) score++; + if (countcellexits(cell, DT_ORTH)) { + score++; + if (score >= bestscore) continue; + } score += (countadjrooms(cell, DT_ORTH)*3); + if (score >= bestscore) continue; // overlapping another room? if (isroom(cell)) { if (force) { score += 10; + if (score >= bestscore) continue; } else { - valid = B_FALSE; + valid = B_FALSE; continue; } } - } - } - } + } // end if includethiscell + } // end for rx... + } // end for ry... } if (valid) { @@ -3126,7 +3169,7 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int } else { score = 9999; } - } else { + } else { // end if room would fit here if (db) dblog("cell %d,%d - a %dx%d room would not fit here",x,y,w,h); } coordscore[i] = score; @@ -3396,24 +3439,24 @@ void createborder(map_t *map, enum CELLTYPE solidtype) { c = getcellat(map, x, 0); clearcell(c); setcelltype(c,solidtype); - c->locked = B_TRUE; + setcelllocked(c, "border"); // s c = getcellat(map, x, map->h-1); clearcell(c); setcelltype(c,solidtype); - c->locked = B_TRUE; + setcelllocked(c, "border"); } for (y = 1; y < map->h-1; y++) { // w c = getcellat(map, 0, y); clearcell(c); setcelltype(c,solidtype); - c->locked = B_TRUE; + setcelllocked(c, "border"); // e c = getcellat(map, map->w-1, y); clearcell(c); setcelltype(c,solidtype); - c->locked = B_TRUE; + setcelllocked(c, "border"); } } @@ -3583,13 +3626,25 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ enum CELLTYPE emptycell,solidcell; //char buf[BUFLEN]; + + dbtimestart("Creating dungeon"); // select dungeon shape. + dbtime("Starting shape selection."); if (onein(3)) { shape = rnd(0,MAXMAPSHAPES-1); } else { shape = MS_NORMAL; } + + + + + + // xxxxxxxxxxxxxxxxx + shape = MS_NORMAL; + + switch (shape) { case MS_NORMAL:// normal break; @@ -3601,12 +3656,14 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ // ###### for (y = 0; y < map->h/4; y++) { for (x = 0; x < map->w; x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "horzbar"); } } for (y = (map->h/4)*3; y < map->h; y++) { for (x = 0; x < map->w; x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "horzbar"); } } break; @@ -3618,12 +3675,14 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ // ## ## for (x = 0; x < map->w/4; x++) { for (y = 0; y < map->h; y++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "vertbar"); } } for (x = (map->w/4)*3; x < map->w; x++) { for (y = 0; y < map->h; y++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "vertbar"); } } break; @@ -3635,18 +3694,22 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ // ## ## for (y = 0; y < map->h/3; y++) { for (x = 0; x < map->w/3; x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "cross"); } for (x = map->w - (map->w/3); x < map->w; x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "cross"); } } for (y = map->h-(map->h/3); y < map->h; y++) { for (x = 0; x < map->w/3; x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "cross"); } for (x = map->w - (map->w/3); x < map->w; x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "cross"); } } break; @@ -3662,17 +3725,20 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ // ########## for (y = (map->h/4); y < map->h/3; y++) { for (x = map->w/3; x < map->w - (map->w/3); x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "turret"); } } for (y = map->h/3; y < map->h - (map->h/3); y++) { for (x = map->w/4; x < map->w - (map->w/4); x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "turret"); } } for (y = map->h-(map->h/3); y < map->h - (map->h/4); y++) { for (x = map->w/3; x < map->w - (map->w/3); x++) { - c = getcellat(map, x, y); c->locked = B_TRUE; + c = getcellat(map, x, y); + setcelllocked(c, "turret"); } } break; @@ -3687,7 +3753,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ (pow(y - centre->y, 2) / pow(h/2, 2)); if (val > 1) { c = getcellat(map, x, y); - c->locked = B_TRUE; + setcelllocked(c, "circle"); } } } @@ -3703,6 +3769,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } } } + dbtime("Finished shape selection."); /* @@ -3734,6 +3801,9 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } } + // xxxxxxxxxxxxxxxxx + shape = CDT_SIMPLE; + // is the map lit? if (depth < 5) { map->illumination = IL_FULLLIT; @@ -3772,6 +3842,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ dir = D_NONE; + dbtime("Starting initial delve."); while (!done) { // get random direction based on turnpct dir = D_NONE; @@ -3847,14 +3918,20 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ //getchar(); } + dbtime("Finished initial delve."); + dbtime("Started removing small rooms."); // remove 2x2 dead-end rooms remove_smallrooms(map); + dbtime("Finsihed small rooms."); // use sparseness to cut down dead ends + dbtime("Started removing dead ends."); remove_deadends(map, sparseness); + dbtime("Finished removing dead ends."); // introduce loops + dbtime("Starting loop introduction."); for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x, y); @@ -3914,6 +3991,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } } } + dbtime("Finished loop introduction."); } // adjust min/maxrooms based on fixed vaults @@ -3922,6 +4000,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ limit(&minrooms, 0, NA); limit(&maxrooms, 0, NA); + dbtime("Starting room creation."); // create rooms if (wantrooms && (maxrooms > 0)) { numrooms = rnd(minrooms, maxrooms); @@ -3933,24 +4012,32 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ //roomvault[i] = NULL; if (rnd(1,100) <= map->habitat->randvaultpct) { vault_t *v; + dbtime("Starting create vault"); v = getvaulttype(map); if (!createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { // success + dbtime("Finished createvault (success)"); continue; - } + } else { + dbtime("Finished createvault (failed)"); + } } // just do a normal room calcposandmakeroom(map, map->nrooms, NA, NA, NA, NA, DEF_VAULTMARGIN, DEF_VAULTMARGIN, NULL, NULL, NULL, NULL, 50, 25, B_FALSE, 0); //roomvault[i] = B_FALSE; } } + dbtime("Finished room creation."); if (corridortype == CDT_NORMAL) { + dbtime("Starting 2nd deadend removal."); // now clear up dead ends again. remove_deadends(map, sparseness); + dbtime("Finished 2nd deadend removal."); } // link up room exits + dbtime("Starting room exit linking."); for (i = 0; i < map->nrooms; i++) { int wantlink = B_FALSE; if (!map->room[i].exitslinked) { @@ -3963,8 +4050,10 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ linkexits(map, map->room[i].id); } } + dbtime("Finished room exit linking."); // add pillars & objects & monsters to rooms + dbtime("Starting pillars/objects/monsters"); if (wantrooms && (numrooms > 0)) { for (i = 0; i < map->nrooms; i++) { if (!map->room[i].vault || hasflag(map->room[i].vault->flags, F_AUTOPOPULATE)) { @@ -4045,15 +4134,21 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } // end if !vault } // end foreach room } // end if wantrooms & nrooms>0 - if (db) dblog("Finished adding stuff to rooms."); + dbtime("Finished pillars/objects/monsters"); // river? if ((depth >= 4) && pctchance(20)) { + dbtime("Starting river creation"); createriver(map); + dbtime("Finished river creation"); } // now do a border + dbtime("Starting border"); createborder(map, solidcell); + dbtime("Finished border"); + + dbtimeend("Finished dungeon"); } void createfakes(map_t *map, cell_t *cell) { @@ -4301,6 +4396,8 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex int nthings = 0,failed,nstairslinked = 0; int nprevaults = 0; int db = B_TRUE; + char dbtag[BUFLEN],dbbuf[BUFLEN]; + // don't redraw screen during level change calculations noredraw = B_TRUE; @@ -4321,8 +4418,8 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex map->depth = depth; map->region = region; + getregionname(buf, map, NULL, RF_SHORT); if (db) { - getregionname(buf, map, NULL, RF_SHORT); dblog("Creating new map of region '%s'",buf); } map->habitat = findhabitat(habitat); @@ -4464,11 +4561,17 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex snprintf(buf, BUFLEN, "%s (id #%d)",buf2, map->id); map->name = strdup(buf); + sprintf(dbbuf, "%s -about to check outline",dbtag); + dblog(dbbuf); + // get a list of what things are here based on the region's outline map->nfixedrooms = 0; nthings = 0; if (region->outline) { - if (db) dblog(" checking region outline for things..."); + if (db) { + dblog(" checking region outline for things..."); + dblog(" checking region outline for things..."); + } for (i = 0; i < region->outline->nthings; i++) { int matched = B_FALSE; if (db) { @@ -4529,182 +4632,221 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex failed = B_TRUE; while (failed) { - failed = B_FALSE; + while (failed) { + failed = B_FALSE; - selectcelltypes(map); + selectcelltypes(map); - // create initial map cells (they will be solid) - for (y = 0; y < map->h; y++) { - for (x = 0; x < map->w; x++) { - addcell(map, x, y); - } - } - map->nrooms = 0; // reset room counts - - // place forced vaults. - if (db) dblog(" adding forced things first..."); - - for (i = 0; i < nthings ;i++) { - vault_t *v = NULL; - //cell_t *c; - switch (thing[i]->whatkind) { - case RT_VAULT: - if (db) dblog(" adding vault"); - v = findvault(thing[i]->what); - assert(v); - if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { - dblog("ERROR - couldn't create vault %s on map %s", v->id, map->name); - failed = B_TRUE; - } - break; - case RT_RNDVAULTWITHFLAG: - if (db) dblog(" adding rndvaultwithflag"); - v = findvaultwithflag(thing[i]->value); - assert(v); - if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { - dblog("ERROR - couldn't create rndvaultwithflag %s on map %s", v->id, map->name); - failed = B_TRUE; - } - break; - case RT_RNDVAULTWITHTAG: - if (db) dblog(" adding rndvaultwithtag"); - v = findvaultwithtag(thing[i]->what); - assert(v); - if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { - dblog("ERROR - couldn't create rndvaultwithtag %s on map %s", v->id, map->name); - failed = B_TRUE; - } - break; - default: - break; - } - - if (failed) break; - - // did we make a vault? - if (v) { - room_t *r; - int x,y; - // lock all vault's cells. BUT we'll unlock them - // again when doing fix_reachability - r = &map->room[map->nrooms-1]; - for (y = r->y1; y <= r->y2; y++) { - for (x = r->x1; x <= r->x2; x++) { - cell_t *c; - c = getcellat(map, x, y); - c->locked = B_TRUE; - } + // create initial map cells (they will be solid) + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + addcell(map, x, y); } - // remember that this room was made before the rest of the map. - // it will counteract the fact that we locked this room's cells - // when we call fix_reachability(). - r->prevault = B_TRUE; - nprevaults++; } - } // for each remembered thing + map->nrooms = 0; // reset room counts - if (failed) { - dblog("********* got errors during forced vault placement - restarting map creation. *********"); - unmakemap(map); - continue; + // place forced vaults. + if (db) { + dblog(" adding forced things first..."); + dblog(" adding forced things first..."); + } + + for (i = 0; i < nthings ;i++) { + vault_t *v = NULL; + //cell_t *c; + switch (thing[i]->whatkind) { + case RT_VAULT: + if (db) dblog(" adding vault"); + v = findvault(thing[i]->what); + assert(v); + if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { + dblog("ERROR - couldn't create vault %s on map %s", v->id, map->name); + failed = B_TRUE; + } + break; + case RT_RNDVAULTWITHFLAG: + if (db) dblog(" adding rndvaultwithflag"); + v = findvaultwithflag(thing[i]->value); + assert(v); + if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { + dblog("ERROR - couldn't create rndvaultwithflag %s on map %s", v->id, map->name); + failed = B_TRUE; + } + break; + case RT_RNDVAULTWITHTAG: + if (db) dblog(" adding rndvaultwithtag"); + v = findvaultwithtag(thing[i]->what); + assert(v); + if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { + dblog("ERROR - couldn't create rndvaultwithtag %s on map %s", v->id, map->name); + failed = B_TRUE; + } + break; + default: + break; + } + + if (failed) break; + + // did we make a vault? + if (v) { + room_t *r; + int x,y; + // lock all vault's cells so that map generation doesn't + // overwrite it. BUT we'll unlock them again just before + // doing fix_reachability. + r = &map->room[map->nrooms-1]; + for (y = r->y1; y <= r->y2; y++) { + for (x = r->x1; x <= r->x2; x++) { + cell_t *c; + c = getcellat(map, x, y); + // this exact text is important!! used later. + setcelllocked(c, TEMPVAULTLOCK); + } + } + // remember that this room was made before the rest of the map. + // it will counteract the fact that we locked this room's cells + // when we call fix_reachability(). + r->prevault = B_TRUE; + nprevaults++; + } + } // for each remembered thing + + if (failed) { + dblog("********* got errors during forced vault placement - restarting map creation. *********"); + unmakemap(map); + continue; + } + + if (db) { + dblog(" finished forced vault creation (%d placed).", nprevaults); + } + // build it... + //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging + + if (db) dblog(" creating map habitat."); + createhabitat(map, depth, parentmap, exitdir, entryob); + if (db) dblog(" finished map habitat."); + //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging + + // add home objects + if (db) dblog(" adding home objects."); + for (lf = map->lf ; lf ; lf = lf->next) { + addhomeobs(lf, B_TRUE); + } + if (db) dblog(" finished home objects."); + //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging + + // add outline things + if (db) dblog(" adding remembered region outline things..."); + for (i = 0; i < nthings ;i++) { + //vault_t *v; + cell_t *c; + // add this thing + switch (thing[i]->whatkind) { + case RT_HABITAT: // already handled above + break; + case RT_OBJECT: + if (db) dblog(" adding forced regionthing object: %s", thing[i]->what); + //c = getrandomroomcell(map, ANYROOM, WE_WALKABLE); + c = getcell_cond(map, &ccwalkableroom); + if (!c) c = getrandomcell(map); + c = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); + addob(c->obpile, thing[i]->what); + break; + case RT_LF: + if (db) dblog(" adding forced regionthing lifeform: %s", thing[i]->what); + //c = getrandomroomcell(map, ANYROOM, WE_WALKABLE); + c = getcell_cond(map, &ccwalkableroom); + if (!c) c = getrandomcell(map); + c = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); + addmonster(c, R_SPECIFIED, thing[i]->what, B_FALSE, thing[i]->value, B_TRUE, NULL); + break; + case RT_BRANCHLINK: + if (db) dblog(" adding branchlink"); + createbranchlink(map, NULL, NULL, thing[i]->what, thing[i]->value, map->region); + // ... don't need to do this since we know there won't be anywhere to link to. + //linkstairs(o); + break; + case RT_NONE: + break; + default: + break; + } + } + if (db) dblog(" finished remembered region outline things."); + //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging + + // ensure that starting level has a player start position + if ((gamemode == GM_CHARGEN) && (region->rtype->id == BH_MAINDUNGEON)) { + cell_t *startpos; + startpos = findobinmap(map, OT_PLAYERSTART); + if (!startpos) { + dblog("ERROR - first level of main dungeon missing player start position."); + failed = B_TRUE; + } + } + + if (db) dblog(" removing bad doors..."); + if (!failed) { + remove_baddoors(map); + //remove_deadends(map, 5); + } + if (db) dblog(" finished bad doors."); + + // unlock temporary locked cells. + for (i = 0; i < map->w * map->h; i++) { + cell_t *c; + c = map->cell[i]; + if (c->lockedreason && streq(c->lockedreason, TEMPVAULTLOCK)) { + free(c->lockedreason); + c->locked = B_FALSE; + } + } + + if (db) dblog(" starting reachability fix..."); + // ensure there are no unreachable areas + if (!failed) { + if (fix_reachability(map)) { + failed = B_TRUE; + } + } + if (db) dblog(" finished reachability fix."); + //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging + + if (failed) { + dblog("********* got errors - restarting map creation. *********"); + unmakemap(map); + continue; + } + // + } // end while failed) + + // at this point, we know failed == false. + + //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging + + if (map->habitat->id == H_CAVE) { + // expand the cave a little more now that we've fixed reachability. + // this will help make any new corridors look more 'cave-like'. + expand_cave(map, 2); } - if (db) dblog(" finished forced vault creation (%d placed).", nprevaults); - // build it... - if (db) dblog(" starting map build."); - //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - - if (db) dblog(" creating map habitat."); - createhabitat(map, depth, parentmap, exitdir, entryob); - //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - - // add home objects - if (db) dblog(" adding home objects."); - for (lf = map->lf ; lf ; lf = lf->next) { - addhomeobs(lf, B_TRUE); + if (db) { + dblog("About to finalise map"); } - //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - - // add outline things - if (db) dblog(" adding remembered region outline things..."); - for (i = 0; i < nthings ;i++) { - //vault_t *v; - cell_t *c; - // add this thing - switch (thing[i]->whatkind) { - case RT_HABITAT: // already handled above - break; - case RT_OBJECT: - if (db) dblog(" adding forced regionthing object: %s", thing[i]->what); - //c = getrandomroomcell(map, ANYROOM, WE_WALKABLE); - c = getcell_cond(map, &ccwalkableroom); - if (!c) c = getrandomcell(map); - c = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); - addob(c->obpile, thing[i]->what); - break; - case RT_LF: - if (db) dblog(" adding forced regionthing lifeform: %s", thing[i]->what); - //c = getrandomroomcell(map, ANYROOM, WE_WALKABLE); - c = getcell_cond(map, &ccwalkableroom); - if (!c) c = getrandomcell(map); - c = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); - addmonster(c, R_SPECIFIED, thing[i]->what, B_FALSE, thing[i]->value, B_TRUE, NULL); - break; - case RT_BRANCHLINK: - if (db) dblog(" adding branchlink"); - createbranchlink(map, NULL, NULL, thing[i]->what, thing[i]->value, map->region); - // ... don't need to do this since we know there won't be anywhere to link to. - //linkstairs(o); - break; - case RT_NONE: - break; - default: - break; + // add any required stairs, fix doors, etc. + if (finalisemap(map, entryob, exitdir)) { + failed = B_TRUE; + dblog("********* Map finalisation failed. Testarting map creation. *********"); + dblog("********* Map finalisation failed. Testarting map creation. *********"); + } else { + if (db) { + dblog("Finalisation finished."); } } - //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - - // ensure that starting level has a player start position - if ((gamemode == GM_CHARGEN) && (region->rtype->id == BH_MAINDUNGEON)) { - cell_t *startpos; - startpos = findobinmap(map, OT_PLAYERSTART); - if (!startpos) { - dblog("ERROR - first level of main dungeon missing player start position."); - failed = B_TRUE; - } - } - - if (!failed) { - remove_baddoors(map); - //remove_deadends(map, 5); - } - // ensure there are no unreachable areas - if (!failed) { - if (fix_reachability(map)) { - failed = B_TRUE; - } - } - //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - - if (failed) { - dblog("********* got errors - restarting map creation. *********"); - unmakemap(map); - continue; - } - // - } // end while failed) - - //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - - if (map->habitat->id == H_CAVE) { - // expand the cave a little more now that we've fixed reachability. - // this will help make any new corridors look more 'cave-like'. - expand_cave(map, 2); } - // add any required stairs, fix doors, etc. - finalisemap(map, entryob, exitdir); - // special cases // village - add town walls and clear it out /* @@ -4841,6 +4983,10 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex } */ + if (db) { + dblog("About to autolink holes."); + } + // link up holes - this will create NEW holes in THIS map connecting to // EXISTING unlinked holes in adjacent maps // @@ -4851,14 +4997,18 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex // which isn't meant to happen. i = linkholes(map); if (db) { - if (db) dblog(" autolinked to %d holes in adjacent maps.",i); + dblog(" autolinked to %d holes in adjacent maps.",i); + dblog(" autolinked to %d holes in adjacent maps.",i); } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging // try to join up any unlinked staircases in this map. - if (db) dblog(" joining unlinked stairs..."); + if (db) { + dblog(" joining unlinked stairs..."); + dblog(" joining unlinked stairs..."); + } nstairslinked = 0; for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { @@ -4885,7 +5035,10 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex } } } - if (db) dblog(" linked %d stairs.", nstairslinked); + if (db) { + dblog(" linked %d stairs.", nstairslinked); + dblog(" linked %d stairs.", nstairslinked); + } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging @@ -4962,7 +5115,9 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex noredraw = B_FALSE; map->beingcreated = B_FALSE; - if (db) dblog(" Map creation finished."); + if (db) { + dblog(" Map creation finished."); + } } void createsewer(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { @@ -5201,6 +5356,7 @@ void createsewer(map_t *map, int depth, map_t *parentmap, int exitdir, object_t f = hasflag(o->flags, F_CLIMBABLE); assert(f); f->val[1] = map->region->parentregion->id; + //linkstairs(o, entryob); } @@ -5347,6 +5503,7 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *r map->nrooms++; // now make it + dbtime("start actually making vault %s", v->id); if (db) dblog("making vault %s at pos %d,%d on map %s", v->id, minx, miny, map->name); for (y = miny; y <= maxy; y++) { for (x = minx; x <= maxx; x++) { @@ -5365,8 +5522,11 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *r addvaultcellcontents(cell, v, x-minx,y-miny, rotation); } } + dbtime("finished actually making vault %s", v->id); + dbtime("start markroomwalls - vault %s", v->id); markroomwalls(map, thisroom); + dbtime("finished markroomwalls - vault %s", v->id); } if (retw) *retw = w; @@ -5374,32 +5534,40 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *r if (retx) *retx = minx; if (rety) *rety = miny; + dbtime("start addvaultcontents - vault %s", v->id); // add other stuff to the vault based on flags // this will also set cell->vault for all cells. addvaultcontents(map, v, minx, miny, maxx, maxy, rotation); + dbtime("finished addvaultcontents - vault %s", v->id); // auto add doors if required f = hasflag(v->flags, F_AUTODOORS); if (f) { + dbtime("start autodoors - vault %s", v->id); // when adding autodoors to a vault, they are ALWAYS closed. autodoors(map, roomid, minx, miny, maxx, maxy, f->val[0], B_NODOORS); + dbtime("finished autodoors - vault %s", v->id); } // link up exits from this vault if (!hasflag(v->flags, F_VAULTNOLINK)) { + dbtime("start linkexits - vault %s", v->id); linkexits(map, roomid); + dbtime("finished linkexits - vault %s", v->id); } // lock cells if required + dbtime("start locking cells - vault %s", v->id); if (hasflag(v->flags, F_MAINTAINEDGE)) { for (y = miny; y <= maxy; y++) { for (x = minx; x <= maxx; x++) { cell_t *c; c = getcellat(map, x, y); - c->locked = B_TRUE; + setcelllocked(c, "maintainedge vaultcell"); } } } + dbtime("finished locking cells - vault %s", v->id); // remove bones vault files after creation. if (hasflagval(v->flags, F_VAULTTAG, NA, NA, NA, "bones")) { @@ -5503,6 +5671,8 @@ void killregion(region_t *r) { } } + + // link a single cell up to the rest of the map. // make sure it links to an empty cell of a DIFFERENT roomid. // if 'wantfilled' is set, only link to "filled" cells. @@ -5512,6 +5682,7 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { int d, roomid,startd,endd; int poss2[MAXCANDIDATES],nposs2; int dist[MAXDIR_ORTH],hitsedge[MAXDIR_ORTH], sameroom[MAXDIR_ORTH]; + int baddir[MAXDIR_ORTH]; cell_t *directendcell[MAXDIR_ORTH]; int mindist = 999,maxdist = -1; cell_t *c; @@ -5521,9 +5692,13 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { roomid = getroomid(startcell); if (db) dblog(" calling linkexit() for cell at %d,%d in roomid %d", startcell->x, startcell->y, roomid); + if (db) { + dumpmap(startcell->map, -1, NULL); + } for (d = D_N; d <= D_W; d++) { hitsedge[d] = B_TRUE; + baddir[d] = B_FALSE; directendcell[d] = NULL; } @@ -5534,8 +5709,10 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { forcedir = startcell->isroomwall; startd = forcedir; endd = forcedir; + dblog(" force start dir = %s", getdirname(forcedir)); } else { startd = D_N; endd = D_W; + dblog(" checking all start dirs"); } // otherwise, starting from the door, count the number of cells in @@ -5551,16 +5728,20 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { int rv; dist[d]++; - rv = cellokforreachability(startcell, c, roomid, d, wantfilled, &(sameroom[d])); + rv = cellokforreachability(startcell, c, roomid, d, wantfilled, &(sameroom[d]), NULL); if (rv == B_FALSE) { + if (db) dblog(" %d,%d going %s, cell NOT ok for reachability.", c->x, c->y, getdirname(d)); dist[d] = 999; + baddir[d] = B_TRUE; break; } else if (rv == B_TRUE) { directendcell[d] = c; - if (db) dblog(" can make %s path (hits empty cell at dist %d)", getdirname(d), dist[d]); + if (db) dblog(" %d,%d can make %s path (hits empty cell at dist %d)", c->x, c->y, getdirname(d), dist[d]); + break; } else { // ie. rv == B_MAYBE int perpdir[2],n; cell_t *pcell = NULL; + if (db) dblog(" %d,%d going %s maybe ok...", c->x, c->y, getdirname(d)); perpdir[0] = d - 1; if (perpdir[0] < D_N) perpdir[0] = D_W; perpdir[1] = d + 1; if (perpdir[1] > D_W) perpdir[1] = D_N; // is there an adjacent walkable cell in a perpendicular direction @@ -5576,7 +5757,7 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { } } // check next cell - c = getcellindir(c, d); // getting the same cell! + c = getcellindir(c, d); } if (!c) { if (db) dblog(" going %s hits edge of map.", getdirname(d)); @@ -5584,6 +5765,7 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { } if (dist[d] != 999) { if (!hitsedge[d]) { + if (db) dblog(" going %s we can walk %d steps",getdirname(d), dist[d]); if (dist[d] < mindist) mindist = dist[d]; } if (dist[d] > maxdist) maxdist = dist[d]; @@ -5591,6 +5773,7 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { } if (mindist == 999) { + /* cell_t *turncell = NULL,*endcell = NULL; cell_t *perpcell[MAX_MAPW*MAX_MAPH]; cell_t *perpturncell1[MAX_MAPW*MAX_MAPH]; @@ -5615,23 +5798,32 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { // if start cell is inside a room, find longest distance which // doesn't go through the same room + if (db) dblog(" Selecting startdir to find turns."); for (d = D_N; d <= D_W; d++) { - if (isroom(startcell)) { + if (baddir[d]) { + if (db) dblog(" %s bad (baddir is set)", getdirname(d)); + } else if (isroom(startcell)) { if (!sameroom[d] && (dist[d] > maxdist2)) { + if (db) dblog(" %s ok (diff room and %d is best distance so far)", + getdirname(d), dist[d]); maxdist2 = dist[d]; } } else { + if (db) dblog(" %s ok (start not room and %d is best distance so far)", + getdirname(d), dist[d]); maxdist2 = dist[d]; } } // pick one randomly for (d = D_N; d <= D_W; d++) { - if (dist[d] == maxdist2) { + if ((dist[d] == maxdist2) && !baddir[d]) { startposs[nstartposs++] = d; } } + if (db) dblog(" %d possibilities found (maxdist2=%d).",nstartposs,maxdist2); if (nstartposs) { startdir = startposs[rnd(0,nstartposs-1)]; + if (db) dblog(" Selected %s.",getdirname(startdir)); } } @@ -5663,6 +5855,11 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { // check left/right from this cell for rooms for (n = 0; n <= 1; n++) { int turndist = 0; + + if (db) { + dblog("Travelling %s, checking %s from %d,%d.", getdirname(startdir), + getdirname(perpdir[n]), c->x, c->y); + } c2 = getcellindir(c, perpdir[n]); while (c2) { @@ -5670,11 +5867,15 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { int rv; turndist++; + assert((n >= 0) && (n <= 1)); perpcell[nperpcells] = c2; // this will be used if we need to make 2 turns perpturncell1[nperpcells] = c; perpturndir1[nperpcells] = perpdir[n]; nperpcells++; + if (db) { + dblog_nocr("(%d,%d)",c2->x, c2->y); + } rv = cellokforreachability(startcell, c2, roomid, perpdir[n], wantfilled, NULL); if (rv == B_FALSE) { @@ -5837,14 +6038,262 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { } else { if (db) dblog(" Cannot find a way to link up."); // debugging - a failure here during fix_reachability is fatal. - /* - if (wantfilled) { - assert(0 == 1); - } - */ + //if (wantfilled) { + // assert(0 == 1); + //} return B_TRUE; } } + */ + + int roomidx[MAXROOMS]; + cell_t *midcell[MAXROOMS]; + int donesomething,a,i,foundpath; + map_t *m; + node_t *solnode = NULL; + + if (db) dblog("Using modified a* algorithm."); + + // use modified a* instead. + m = startcell->map; + + // get all other rooms, in order of distance. + for (i = 0; i < m->nrooms; i++) { + roomidx[i] = i; + midcell[i] = getroommidcell(m, i); + } + // bubblesort + donesomething = B_TRUE; + while (donesomething) { + donesomething = B_FALSE; + for (i = 0; i < m->nrooms-1; i++) { + if (getcelldist(startcell, midcell[i]) > getcelldist(startcell, midcell[i+1])) { + int tempidx; + cell_t *tempcell; + // swap + tempcell = midcell[i+1]; + tempidx = roomidx[i+1]; + midcell[i+1] = midcell[i]; + roomidx[i+1] = roomidx[i]; + midcell[i] = tempcell; + roomidx[i] = tempidx; + donesomething = B_TRUE; + } + } + } + if (db) { + dblog("Other room distances from room %d:", roomid); + for (i = 0;i < m->nrooms; i++) { + room_t *r; + r = &(m->room[roomidx[i]]); + dblog(" Room %d (distance %d)%s", r->id, getcelldist(startcell, midcell[i]), + (r->id == roomid) ? " [start room]" : ""); + } + } + + // try pathfinding to each room, in order. + foundpath = B_FALSE; + for (a = 0 ; (a < m->nrooms) && !foundpath; a++) { + int destroomid; + int done = B_FALSE,i,n; + node_t open[MAX_PATHFIND_ADJ]; + node_t closed[MAX_PATHFIND_ADJ]; + node_t *cur; + int nopen = 0,nclosed = 0; + int okforreach; + + destroomid = m->room[roomidx[a]].id; + if (destroomid == roomid) continue; + + // clear open and closed lists. + for (i = 0; i < MAX_PATHFIND_ADJ; i++) { + clearnode(&open[i]); + clearnode(&closed[i]); + } + nopen = 0; + nclosed = 0; + + if (db) dblog("linkexit() pathfind - finding path from %d,%d (room %d) to room %d.", + startcell->x, startcell->y, roomid, destroomid); + + // add starting cell to open list + open[0].c = startcell; + open[0].fromstart = 0; + open[0].heur = calch_map(startcell, destroomid); + open[0].cost = open[0].fromstart + open[0].cost; + open[0].parent = NULL; + open[0].dirfromparent = D_NONE; + nopen = 1; + + while (!done) { + char why[BUFLEN]; + // if open list empty? + if (!nopen) { + // if so, there is NO path. fail. + if (db) dblog("linkexit() pathfind - open list is empty. FAILED."); + foundpath = B_FALSE; + break; + } + // find lowest COST in open list. this is cur. + // move node[cur] to closed list + closed[nclosed] = open[0]; + cur = &closed[nclosed]; + nclosed++; + for (i = 0; i < nopen-1; i++) { + open[i] = open[i+1]; + } + nopen--; + okforreach = cellokforreachability(startcell, cur->c, roomid, D_NONE, wantfilled, NULL, NULL); + // is node[cur] in the target room? xxxxxxxx not sure about dirfromparent here. + if ((getroomid(cur->c) == destroomid) && okforreach) { + if (db) dblog("success - found a cell in the dest room!."); + // if so, we've found a path. now need to populate + //if (db) dblog("%s pathfind - at target cell - success!",lfname); + foundpath = B_TRUE; + done = B_TRUE; + solnode = cur; + } else if (wantfilled && cur->c->filled && okforreach) { + if (db) dblog("success - stumbled upon a filled cell."); + foundpath = B_TRUE; + done = B_TRUE; + solnode = cur; + } else if (cellwalkable(NULL, cur->c, NULL) && okforreach) { + if (db) dblog("success - stumbled upon a walkable cell."); + foundpath = B_TRUE; + done = B_TRUE; + solnode = cur; + } else { + if (db) dblog("checking around (%d,%d): ", cur->c->x, cur->c->y); + // for "adjcell" in 4 squares around it: + for (i = D_N; i <= D_W; i++) { + cell_t *adjcell; + int found = B_FALSE, ok = B_FALSE; + adjcell = getcellindir(cur->c, i); + if (!adjcell) continue; + + //if (db) dblog("%s pathfind - checking %s",lfname, getdirnameshort(i)); + // already on closed list? + for (n = 0; n < nclosed; n++) { + if (closed[n].c == adjcell) { + found = B_TRUE; + break; + } + } + if (found) { + // already on closed list. ignore. + if (db) dblog(" %s = on closed list.", getdirname(i)); + //if (db) dblog(" %s (%d,%d): already on closed list. ignore.",getdirnameshort(i), + // adjcell->x, adjcell->y); + continue; + } + + //walkable = cellwalkable(lf, adjcell, &whynot); + ok = cellokforreachability(startcell, adjcell, roomid, i, wantfilled, NULL, why); + if (ok) { + int openpos = -1; + // adjcell is walkable. + for (n = 0; n < nopen; n++) { + if (open[n].c == adjcell) { + openpos = n; + break; + } + } + if (openpos != -1) { + int newfromstart; + node_t temp; + // adjcell is already on open list + if (db) dblog(" %s = already on open list. recalcing.", getdirname(i)); + //if (db) dblog(" %s (%d,%d): on open list (position %d).",getdirnameshort(i), + // adjcell->x, adjcell->y, openpos); + + // recalc fromstart of adjcell using node[cur] as a parent. + newfromstart = calcg_map(open[openpos].c, cur, i); + if (newfromstart < open[openpos].fromstart) { + // path through node[cur] is better. + //if (db) dblog(" %s (%d,%d): better cost - recalcing.", + // getdirnameshort(i), + // adjcell->x, adjcell->y); + + // change parent of adjcell to node[cur] + // and recalc new costings (and re-sort list) + temp = open[openpos]; + temp.parent = cur; + temp.dirfromparent = i; + assert(isadjacent(temp.c, temp.parent->c)); + temp.fromstart = calcg_map(temp.c, temp.parent, i); + temp.heur = calch_map(temp.c, destroomid); + temp.cost = temp.fromstart + temp.heur; + + // remove adjcell from open list. + for (n = openpos; n < nopen; n++) { + open[n] = open[n+1]; + } + nopen--; + // re-add adjcell at correct pos; + insert(&temp, open, &nopen); + } + } else { + if (db) dblog(" %s = not on open list. adding.", getdirname(i)); + // not on open list + node_t temp; + + //if (db) dblog(" %s (%d,%d): not on openlist. inserting.", + // getdirnameshort(i), + // adjcell->x, adjcell->y); + // calc costs + temp.c = adjcell; + temp.parent = cur; + temp.dirfromparent = i; + assert(isadjacent(temp.c, temp.parent->c)); + temp.fromstart = calcg_map(temp.c, temp.parent, i); + temp.heur = calch_map(adjcell, destroomid); + temp.cost = temp.fromstart + temp.heur; + + insert(&temp, open, &nopen); + } + } else { + // !walkable - ignore it. + if (db) dblog(" %s = not usable (%s). ignoring.", getdirname(i), why); + //if (db) dblog(" %s (%d,%d): not walkable - ignoring.", + // getdirnameshort(i), + // adjcell->x, adjcell->y); + } + } // end for each adj cell + }// end "have we reached target room" + } + } + + + if (foundpath) { + int pathlen = 0; + char reasontext[BUFLEN]; + // work backwards from node[cur] (ie. targcell) following parents. + // populate path and return + if (db) dblog("linkexit() - found path!\n"); + + sprintf(reasontext, "astar pathfind link (from roomid %d)",roomid); + while (solnode) { + pathlen++; + if (pathlen >= MAX_PATHFIND_STEPS) { + assert("pathfind - path > MAX_PATHFIND_STEPS - failed." == 0); + } + // clear solnode->c + breakwall(solnode->c, reasontext); + if (ncellsadded) (*ncellsadded)++; + + solnode = solnode->parent; + } + if (db) dblog("linkexit() - SUCCESS. Path length is %d steps.\n", pathlen); + if (db) dumpmap(m, B_TRUE, reasontext); + } else { + if (db) dblog(" Cannot find a way to link up."); + // debugging - a failure here during fix_reachability is fatal. + //if (wantfilled) { + // assert(0 == 1); + //} + //raise(SIGINT); + return B_TRUE; + } } else { // we found a way to go without needing to turn. int whichway,sel; int mindist2 = 999; @@ -5977,6 +6426,12 @@ int linkexits(map_t *m, int roomid) { if (db) dblog(" Need to link."); if (!linkexit(poss[i], B_FALSE, NULL)) { nadded++; + } else { + // linkexit failed! + if (db) dblog(" Linkexits() - linkexit() failed."); + //if (db) { + // raise(SIGINT); + //} } } } // end for each door @@ -6303,10 +6758,12 @@ int calcposandmakeroom(map_t *map, int roomid, int overrideminw, int overridemin if (reth) *reth = h; // find room position + dbtime("start calcroompos"); if (calcroompos(map, w, h, xmargin, ymargin, &minx, &miny, B_FALSE, stayclose)) { dblog("** couldn't make room!\n"); return B_TRUE; } + dbtime("end calcroompos"); if (retx) *retx = minx; if (rety) *rety = miny; @@ -6319,11 +6776,15 @@ int calcposandmakeroom(map_t *map, int roomid, int overrideminw, int overridemin maxy = miny + (h-1); // actually make the room now. + dbtime("start createroom"); createroom(map, roomid, minx,miny,maxx,maxy, forcewalls); + dbtime("end createroom"); // add doors if (doorpct) { + dbtime("start autodoors"); autodoors(map, roomid, minx, miny, maxx, maxy, doorpct, dooropenchance); + dbtime("end autodoors"); } return B_FALSE; @@ -6415,7 +6876,7 @@ int doorisvalid(object_t *o) { return B_FALSE; } -void dumpmap(map_t *map, int showrooms) { +void dumpmap(map_t *map, int showrooms, char *hilitereason) { int x,y; cell_t *cell; char ch; @@ -6432,17 +6893,21 @@ dblog("dump of map '%s' (%d x %d):\n",map->name, map->w, map->h); dblog_nocr("%d",y % 10); for (x = 0; x < map->w; x++) { cell = getcellat(map, x, y); - ch = cell->type->glyph.ch; - if (ch == '.') { - if (showrooms && isroom(cell)) { - ch = '0' + getroomid(cell); - } - + if (hilitereason && streq(cell->reason, hilitereason)) { + ch = '@'; + } else { + ch = cell->type->glyph.ch; if (ch == '.') { - if (cell->filled) { - ch = 'o'; + if (showrooms && isroom(cell)) { + ch = '0' + getroomid(cell); + } + + if (ch == '.') { + if (cell->filled) { + ch = 'o'; + } } - } + } else ch = '#'; } dblog_nocr("%c",ch); } @@ -6598,7 +7063,8 @@ void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *c } } -void finalisemap(map_t *map, object_t *entryob, int exitdir) { +//returns true on failure +int finalisemap(map_t *map, object_t *entryob, int exitdir) { enum OBTYPE upstairtype, downstairtype; int i,d,x,y; int linkedentry = B_FALSE; @@ -6678,7 +7144,7 @@ void finalisemap(map_t *map, object_t *entryob, int exitdir) { // up stairs on all other levels for (i = 0; i < nupstairsneeded; i++) { c = NULL; - while (!c || countobs(c->obpile, B_TRUE)) { + if (!c || countobs(c->obpile, B_TRUE)) { //c = getrandomroomcell(map, ANYROOM, WE_EMPTY); c = getcell_cond(map, &okforstairs); /* @@ -6694,7 +7160,13 @@ void finalisemap(map_t *map, object_t *entryob, int exitdir) { } } */ - trytokillobs(c->obpile); + if (c) { + trytokillobs(c->obpile); + } else { + dblog("ERROR - couldnt find pos for up stairs while making habitat %s.", map->habitat->name); + msg("ERROR - couldnt find pos for up stairs while making habitat %s.", map->habitat->name); + return B_TRUE; + } } o = addobfast(c->obpile, upstairtype); assert(o); @@ -6717,10 +7189,16 @@ void finalisemap(map_t *map, object_t *entryob, int exitdir) { if ((downstairtype != OT_NONE) && (map->depth < map->region->rtype->maxdepth)) { for (i = 0; i < ndownstairsneeded; i++) { c = NULL; - while (!c || countobs(c->obpile, B_TRUE)) { + if (!c || countobs(c->obpile, B_TRUE)) { //c = getrandomroomcell(map, ANYROOM, WE_EMPTY); c = getcell_cond(map, &okforstairs); - trytokillobs(c->obpile); + if (c) { + trytokillobs(c->obpile); + } else { + dblog("ERROR - couldnt find pos for down stairs while making habitat %s.", map->habitat->name); + msg("ERROR - couldnt find pos for down stairs while making habitat %s.", map->habitat->name); + return B_TRUE; + } } o = addobfast(c->obpile, downstairtype); assert(o); @@ -6849,6 +7327,8 @@ void finalisemap(map_t *map, object_t *entryob, int exitdir) { } } } + + return B_FALSE; } void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, enum BEHAVIOUR wantbehaviour, int idx) { @@ -7968,18 +8448,18 @@ object_t *hastrailof(obpile_t *op, lifeform_t *lf, enum OBTYPE oid, flag_t **tfl void initmap(void) { // habitats // thingchance, obchance, vaultchance, maxvisrange, upstiartype, downstairtype - addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 3, 50, 30, 6, OT_STAIRSUP, OT_STAIRSDOWN); - addhabitat(H_CAVE, "cave", CT_DIRT, CT_WALLDIRT, 5, 45, 20, 6, OT_TUNNELUP, OT_TUNNELDOWN); - addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALLTREE, 3, 75, 0, MAXVISRANGE, OT_TREEUP, OT_TREEDOWN); + addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 5, 60, 30, 6, OT_STAIRSUP, OT_STAIRSDOWN); + addhabitat(H_CAVE, "cave", CT_DIRT, CT_WALLDIRT, 10, 45, 20, 6, OT_TUNNELUP, OT_TUNNELDOWN); + addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALLTREE, 5, 75, 0, MAXVISRANGE, OT_TREEUP, OT_TREEDOWN); addhabitat(H_HEAVEN, "heaven", CT_CORRIDOR, CT_WALLGLASS, 5, 0, 0, MAXVISRANGE, OT_NONE, OT_NONE); addhabitat(H_PIT, "pit", CT_CORRIDOR, CT_WALL, 0, 0, 0, 5, OT_NONE, OT_NONE); - addhabitat(H_VILLAGE, "village", CT_GRASS, CT_WALL, 3, 70, 0, MAXVISRANGE, OT_NONE, OT_NONE); - addhabitat(H_SEWER, "sewer", CT_MOSSROCK, CT_WALL, 5, 50, 0, MAXVISRANGE, OT_GRATINGROOF, OT_NONE); + addhabitat(H_VILLAGE, "village", CT_GRASS, CT_WALL, 5, 70, 0, MAXVISRANGE, OT_NONE, OT_NONE); + addhabitat(H_SEWER, "sewer", CT_MOSSROCK, CT_WALL, 10, 50, 0, MAXVISRANGE, OT_GRATINGROOF, OT_NONE); addhabitat(H_STOMACH, "stomach", CT_FLOORFLESH, CT_WALLFLESH, 5, 80, 0, MAXVISRANGE, OT_NONE, OT_NONE); - addhabitat(H_SWAMP, "swamp", CT_CORRIDOR, CT_WALL, 3, 50, 0, MAXVISRANGE, OT_STAIRSUP, OT_STAIRSDOWN); + addhabitat(H_SWAMP, "swamp", CT_CORRIDOR, CT_WALL, 5, 50, 0, MAXVISRANGE, OT_STAIRSUP, OT_STAIRSDOWN); addhabitat(H_BYHUT, "babayaga's hut", CT_FLOORWOOD, CT_WALLDWOOD, 0, 0, 0, MAXVISRANGE, OT_BYHUTDOOR, OT_NONE); - addhabitat(H_ANTNEST, "ant nest", CT_DIRT, CT_WALLDIRT, 5, 40, 0, MAXVISRANGE, OT_STAIRSUP, OT_STAIRSDOWN); - addhabitat(H_MASTERVAULTS, "master vaults", CT_FLOORDURANITE, CT_WALLDURANITE, 5, 0, 0, MAXVISRANGE, OT_VSTAIRSUP, OT_VSTAIRSDOWN); + addhabitat(H_ANTNEST, "ant nest", CT_DIRT, CT_WALLDIRT, 10, 40, 0, MAXVISRANGE, OT_STAIRSUP, OT_STAIRSDOWN); + addhabitat(H_MASTERVAULTS, "master vaults", CT_FLOORDURANITE, CT_WALLDURANITE, 10, 0, 0, MAXVISRANGE, OT_VSTAIRSUP, OT_VSTAIRSDOWN); // cell types - solid // floorheight, hp, volmod, absorbant @@ -8466,6 +8946,17 @@ int isnewcellok(cell_t *cell, char *err) { return B_TRUE; } +cell_t *getroommidcell(map_t *m, int roomid) { + int midx,midy; + cell_t *c; + midx = m->room[roomid].x1 + + ((m->room[roomid].x2 - m->room[roomid].x1)/2); + midy = m->room[roomid].y1 + + ((m->room[roomid].y2 - m->room[roomid].y1)/2); + c = getcellat(m, midx, midy); + assert(c); + return c; +} enum TIMEPHASE gettimephase(void) { int h,m,s; @@ -9492,6 +9983,22 @@ void setcellknownradius(cell_t *centre, int forcelev, int radius, int dirtype) { } } +void setcelllocked(cell_t *c, char *why, ...) { + char buf[BUFLEN]; + va_list args; + + c->locked = B_TRUE; + + if (c->lockedreason) free(c->lockedreason); + + va_start(args, why); + vsnprintf( buf, BUFLEN, why, args ); + va_end(args); + + c->lockedreason = strdup(buf); +} + + void setcellreason(cell_t *c, char *why, ...) { char buf[BUFLEN]; va_list args; diff --git a/map.h b/map.h index 8cefcc3..c841cb2 100644 --- a/map.h +++ b/map.h @@ -19,7 +19,7 @@ int cellisfixedvaultwall(cell_t *c); //int cellmatchescondition(cell_t *c, int wecond); int cellmeetscondition(cell_t *c, enum CELLCONDITION cond, int arg, int value); int cellmeets(cell_t *c, condset_t *cs); -int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom); +int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom, char *why); void clearcell(cell_t *c); void clearcell_exceptflags(cell_t *c, ...); int delve(map_t *map, int neighbourmin, int neighbourmax, int connchance, int chancepct, int newneighbourmin, int newneighbourmax, int newconnchance, enum CELLTYPE empty, enum CELLTYPE solid); @@ -61,6 +61,7 @@ int getmapmaxvisrange(map_t *m); void getradiuscells(cell_t *centre, int radius, int dirtype, int outsideonly, enum LOFTYPE needlof, int wantcentre, cell_t **retcell, int *ncells, int scatterdensity); int getroomid(cell_t *c); void getroomedge(map_t *m, int roomid, int minx, int miny, int maxx, int maxy, int whichside, cell_t **retcell, int *ncells, int onlywantsolid, int includefixed); +cell_t *getroommidcell(map_t *m, int roomid); enum TIMEPHASE gettimephase(void); object_t *gettopobject(cell_t *where, int forglyph); //void calclight(map_t *map); @@ -102,12 +103,12 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *r int dirtox(int dt, int dir); int dirtoy(int dt, int dir); int doorisvalid(object_t *o); -void dumpmap(map_t *map, int showrooms); +void dumpmap(map_t *map, int showrooms, char *reasontext); void expand_cave(map_t *map, int numpasses); void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *centre, lifeform_t *fromwho, int doknockback); void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int dirtype, int wantannounce, lifeform_t *fromwho); void explosion_knockback(cell_t *c, int radius, int dirtype, int dam, lifeform_t *fromwho); -void finalisemap(map_t *map, object_t *entryob, int exitdir); +int finalisemap(map_t *map, object_t *entryob, int exitdir); void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, enum BEHAVIOUR wantbehaviour, int idx); celltype_t *findcelltype(enum CELLTYPE cid); celltype_t *findcelltypebyname(char *name); @@ -203,6 +204,7 @@ void selectcelltypes(map_t *map); void set_scanned_glyph(int targettype, void *what, char *descappend, char *desc, glyph_t *glyph); void setcellknown(cell_t *cell, int forcelev); void setcellknownradius(cell_t *centre, int forcelev, int radius, int dirtype); +void setcelllocked(cell_t *c, char *why, ...); void setcellreason(cell_t *c, char *why, ...); void setcelltype(cell_t *cell, enum CELLTYPE id); int shattercell(cell_t *c, lifeform_t *fromlf, char *damstring); diff --git a/move.c b/move.c index 07e93a2..e529ee0 100644 --- a/move.c +++ b/move.c @@ -3796,10 +3796,14 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { // is there a lf in sight, who is within our territorial // radius of the cell we are testing? for (i = 0; i < lf->nlos; i++) { - if (lf->los[i]->lf && !areallies(lf, lf->los[i]->lf) && - (getcelldist(cell, lf->los[i]) <= tdist)) { - if (error) *error = E_WONT; - return B_FALSE; + lifeform_t *otherlf; + otherlf = lf->los[i]->lf; + if (otherlf && !areallies(lf, otherlf) && + (getcelldist(cell, otherlf->cell) <= tdist)) { + if (!isasleep(otherlf)) { + if (error) *error = E_WONT; + return B_FALSE; + } } } } diff --git a/nexus.c b/nexus.c index 7954987..31248ca 100644 --- a/nexus.c +++ b/nexus.c @@ -890,25 +890,46 @@ void dbtimeendlf(lifeform_t *lf) { dbtimeend(buf); } -void dbtimestart(char *text) { +void dbtimestart(char *format, ...) { + char buf[BUFLEN]; + va_list args; gettimeofday(&tv, NULL); starttv = tv; - dblog("START\t%s", text); + + va_start(args, format); + vsnprintf( buf, BUFLEN, format, args ); + va_end(args); + + dblog("START\t%s", buf); } -void dbtime(char *text) { +void dbtime(char *format, ...) { double ticks; + char buf[BUFLEN]; + va_list args; + + va_start(args, format); + vsnprintf( buf, BUFLEN, format, args ); + va_end(args); + gettimeofday(&newtv, NULL); ticks = ((newtv.tv_sec - tv.tv_sec) * 1000000) + (newtv.tv_usec - tv.tv_usec); - dblog("+%f\t%s", ticks, text); + dblog("+%f\t%s", ticks, buf); tv = newtv; } -void dbtimeend(char *text) { +void dbtimeend(char *format, ...) { double ticks; + char buf[BUFLEN]; + va_list args; + + va_start(args, format); + vsnprintf( buf, BUFLEN, format, args ); + va_end(args); + 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); + dblog("FINISHED %s (total time %f)", buf, ticks); } void dobresnham(int d, int xinc1, int yinc1, int dinc1, int xinc2, int yinc2, int dinc2, int *xinc, int *yinc, int *dinc) { diff --git a/nexus.h b/nexus.h index a219694..eb233fd 100644 --- a/nexus.h +++ b/nexus.h @@ -6,10 +6,10 @@ warning_t *addwarning(char *text, int lifetime); void checkdeath(void); void checkendgame(void); void cleanup(void); -void dbtime(char *text); -void dbtimeend(char *text); +void dbtime(char *format, ...); +void dbtimeend(char *format, ...); void dbtimeendlf(lifeform_t *lf); -void dbtimestart(char *text); +void dbtimestart(char *format, ...); void dbtimestartlf(lifeform_t *lf); 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); diff --git a/objects.c b/objects.c index 8f2798f..422f295 100644 --- a/objects.c +++ b/objects.c @@ -509,6 +509,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum cell_t *targetcell = NULL; // for portals int donesomething; object_t *addedob[MAXPILEOBS]; + race_t *wantbloodrace = NULL; int nadded = 0; flagpile_t *wantflags; // for doors @@ -716,6 +717,37 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum wantblessed = B_CURSED; p += strlen("cursed "); donesomething = B_TRUE; + // blood + } else if (strends(p, " blood")) { + char *spaceblood,*pp,rname[BUFLEN],*rp,replacefrom[BUFLEN]; + race_t *r = NULL; + spaceblood = strends(p, " blood"); + pp = strstr(p, " of "); + if (pp) { + pp += 4; + } + rp = rname; + for (;pp < spaceblood; pp++, rp++) { + *rp = *pp; + } + *rp = '\0'; + if (strlen(rname)) { + r = findracebyname(rname, NULL); + if (r) { + wantbloodrace = r; + } + } + + if (r) { + // now replace the end of the string. + // initially we'll have (eg) 'splash of xxx blood' + // change it to 'splash of blood' + sprintf(replacefrom, "%s blood", r->name); + + pp = strstr(p, replacefrom); + assert(pp); + strcpy(pp, "blood"); + } // food flags } else if (strstarts(p, "bruised ")) { addflag(wantflags, F_BRUISED, B_TRUE, NA, NA, NULL); @@ -1432,11 +1464,27 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum } } - if (linkrace && (linkrace->id != R_NONE)) { - addflag(o->flags, F_LINKRACE, linkrace->id, NA, NA, NULL); - } - if (gamemode != GM_LOADING) { + if (linkrace && (linkrace->id != R_NONE)) { + addflag(o->flags, F_LINKRACE, linkrace->id, NA, NA, NULL); + } + + if (wantbloodrace && (wantbloodrace->id != R_NONE)) { + flag_t *retflag[MAXCANDIDATES]; + int nretflags = 0,i; + // don't duplicate this as then it will count as 'mixed blood' + if (!hasflagval(o->flags, F_LINKRACE, wantbloodrace->id, NA, NA, NULL)) { + addflag(o->flags, F_LINKRACE, wantbloodrace->id, NA, NA, NULL); + } + // not using copyflag, in order to avoid duplicates + getflags(wantbloodrace->flags, retflag, &nretflags, F_FILLPOT, F_NONE); + for (i = 0; i < nretflags; i++) { + addflag(o->flags, retflag[i]->id, retflag[i]->val[0], retflag[i]->val[1], + retflag[i]->val[2], retflag[i]->text); + //copyflag(o->flags, wantbloodrace->flags, F_FILLPOT); + } + } + if (hasflag(o->flags, F_NOBLESS)) { setblessed(o, B_UNCURSED); } else { @@ -2379,18 +2427,19 @@ void addomprefix(enum OBMOD id, char *altprefix) { om->naltprefix++; } -void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes, lifeform_t *creator) { +void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes, int includecentre, lifeform_t *creator, object_t **retob, int **retobcount, int *nretobs) { cell_t *cell[MAXCANDIDATES],*c; int ncells,i; objecttype_t *ot; object_t *o; + int nmade = 0; ot = findotn(name); if (!ot) return; - getradiuscells(centre, radius, DT_ORTH, B_FALSE, LOF_WALLSTOP, (radius == 0) ? B_TRUE : B_FALSE, cell, &ncells, B_FALSE); + getradiuscells(centre, radius, DT_ORTH, B_FALSE, LOF_WALLSTOP, + (radius == 0) || includecentre ? B_TRUE : B_FALSE, cell, &ncells, B_FALSE); - // global return values - nretobs = 0; + if (nretobs) *nretobs = 0; for (i = 0; i < ncells; i++) { c = cell[i]; @@ -2401,10 +2450,10 @@ void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int all if (creator) { setobcreatedby(o, creator); } - // global return values - retobs[nretobs] = o; - retobscount[nretobs] = o->amt; - nretobs++; + if (retob) retob[nmade] = o; + if (retobcount) *(retobcount[nmade]) = o->amt; + nmade++; + if (nretobs) (*nretobs) = nmade; } } } @@ -5496,6 +5545,29 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } else { sprintf(basename, "abandoned temple"); } + } else if ((o->type->id == OT_BLOODSPLASH) || (o->type->id == OT_BLOODPOOL)) { + race_t *brace = NULL; + enum SKILLLEVEL lorelev = PR_INEPT; + char *p,xblood[BUFLEN]; + if (countflagsofid(o->flags, F_LINKRACE) > 1) { + sprintf(xblood, "mixed blood"); + } else { + // you might recognise the type of blood + f = hasflag(o->flags, F_LINKRACE); + if (f) { + brace = findrace(f->val[0]); + if (brace) lorelev = getlorelevel(player, brace->raceclass->id); + } + if (brace && (lorelev >= PR_BEGINNER)) { + sprintf(xblood, "%s blood", brace->name); + } else { + sprintf(xblood, "unidentifiable blood"); + } + } + p = strdup(o->type->name); + strrep(&p, "blood", xblood, NULL); + snprintf(basename, BUFLEN, "%s", p); + free(p); } else if (hasflag(o->flags, F_DEEPWATER)) { snprintf(basename, BUFLEN, "%s %s", getwaterdepthname(getobdepth(o, player)), o->type->name); } else { @@ -6314,7 +6386,7 @@ objecttype_t *getoppositestairs(objecttype_t *ot) { // OC_CLASS1, OC_CLAS2, OC_NONE, ..., DT_DAMTYPE1, DT_DAMTYPE2, .., DT_NONE, F_WANTFLAG1, F_WANTFLAG2, ..., F_NONE // // if no objectclass given then it will be picked randomly, or (if wepsk isn't sk_none) set to oc_weapon. -objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, enum LFSIZE maxsize, enum SKILL wepsk, int forpickup, ... ) { +objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, enum LFSIZE maxsize, enum SKILL wepsk, enum RARITY forcerr, int forpickup, ... ) { va_list args; objecttype_t *ot; objecttype_t *poss[MAXRANDOMOBCANDIDATES]; @@ -6396,7 +6468,11 @@ objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forceh getrarityrange(depth, &raritymin, &raritymax, RARITYVARIANCEOB, B_TRUE); // pick rr... - wantrr = pickrr(TT_OBJECT); + if (forcerr == RR_NONE) { + wantrr = pickrr(TT_OBJECT); + } else { + wantrr = forcerr; + } origwantrr = wantrr; // no obclass given? pick one randomly. @@ -6407,10 +6483,10 @@ objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forceh } else if (!nwantflag) { wantclass[0] = getrandomobclass(hab->id); nwantclass = 1; - if (wantclass[0] == OC_WAND) { - dblog("random WAND picked!"); - partdb = B_TRUE; - } + //if (wantclass[0] == OC_WAND) { + //dblog("random WAND picked!"); + //partdb = B_TRUE; + //} } } @@ -6740,25 +6816,25 @@ objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forceh } objecttype_t *getrandomob(map_t *map, char *buf) { - return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE, B_FALSE, OC_NONE, DT_NONE, F_NONE); + return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE, RR_NONE, B_FALSE, OC_NONE, DT_NONE, F_NONE); } objecttype_t *getrandomobofsize(map_t *map, char *buf, enum LFSIZE maxsize) { - return real_getrandomob(map, buf, NA, NA, maxsize, SK_NONE, B_FALSE, OC_NONE, DT_NONE, F_NONE); + return real_getrandomob(map, buf, NA, NA, maxsize, SK_NONE,RR_NONE, B_FALSE, OC_NONE, DT_NONE, F_NONE); } objecttype_t *getrandomobwithdt(map_t *map, enum DAMTYPE damtype, char *buf) { - return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE, B_TRUE, OC_NONE, damtype, DT_NONE, F_NONE); + return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE,RR_NONE, B_TRUE, OC_NONE, damtype, DT_NONE, F_NONE); } objecttype_t *getrandomobwithflag(map_t *map, enum FLAG fid, char *buf) { - return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE, B_TRUE, OC_NONE, DT_NONE, fid, F_NONE); + return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE,RR_NONE, B_TRUE, OC_NONE, DT_NONE, fid, F_NONE); } objecttype_t *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, getmapdifficulty(map) + depthmod, NA, SZ_MAX, SK_NONE, B_FALSE, cid, OC_NONE, DT_NONE, F_NONE); + return real_getrandomob(map, buf, getmapdifficulty(map) + depthmod, NA, SZ_MAX, SK_NONE, RR_NONE, B_FALSE, cid, OC_NONE, DT_NONE, F_NONE); } enum OBCLASS getrandomobclass(enum HABITAT hab) { @@ -10506,16 +10582,27 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { } } else if ((o->type->id == OT_EMPTYFLASK) || (o->type->id == OT_EMPTYVIAL)) { object_t *oo,*nextoo; + object_t *notenough = NULL; if (isplayer(lf)) { int donedip = B_FALSE; + int anydippable = B_FALSE; // anything here to fill with? for (oo = lf->cell->obpile->first ; (oo && !donedip); oo = nextoo) { flag_t *fillflag; int dippable = B_FALSE; nextoo = oo->next; fillflag = hasflag(oo->flags, F_FILLPOT); - if (fillflag && ((fillflag->val[1] == NA) || (oo->amt >= fillflag->val[1]))) { - dippable = B_TRUE; + if (fillflag) { + if (getobsize(oo) < getobsize(o)) { + // not dippable, but display a different message + if (!notenough) { + notenough = oo; + } + } else { + dippable = B_TRUE; + anydippable = B_TRUE; + notenough = NULL; + } } if (dippable) { @@ -10593,7 +10680,16 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { } if (!donedip) { - msg("There is nothing here to fill your %s from.",noprefix(obname)); + if (anydippable) { + msg("There is nothing else here to fill your %s from.",noprefix(obname)); + } else if (notenough) { + char poolname[BUFLEN]; + getobname(notenough, poolname, notenough->amt); + msg("%s %s not sufficient to fill your %s.",poolname, + (notenough->amt == 1) ? "is" : "are", noprefix(obname)); + } else { + msg("There is nothing here to fill your %s from.",noprefix(obname)); + } } } } else if (o->type->id == OT_MISTLETOE) { @@ -11710,7 +11806,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE if (isplayer(lf)) msg("Yuck, this tastes like blood!"); // find random lycanthrope type r = getrandomracewithflag(F_LYCANTHROPE); - poison(lf, PERMENANT, P_LYCANTHROPY, 1, r->name, R_NONE); + poison(lf, PERMENANT, P_LYCANTHROPY, 1, r->name, R_NONE, B_FALSE); break; case OT_POT_MAGIC: if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, -10)) { @@ -11746,7 +11842,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE if (isplayer(lf)) { msg("^%cThis tastes like poison!", getlfcol(lf, CC_BAD)); } - if (poison(lf, rnd(10,20), P_VENOM, 1, "a potion of poison", R_NONE)) { + if (poison(lf, rnd(10,20), P_VENOM, 1, "a potion of poison", R_NONE, B_TRUE)) { if (isplayer(lf)) msg("Luckily, it has no effect."); } break; @@ -13923,7 +14019,7 @@ int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int sp } radius = o->amt * 2; if (radius > 10) radius = 10; - addobsinradius(thrower->cell, radius, DT_COMPASS, "cloud of smoke", B_FALSE, thrower); + addobsinradius(thrower->cell, radius, DT_COMPASS, "cloud of smoke", B_FALSE, B_INCLUDECENTRE, thrower, NULL, NULL, NULL); } else if (o->type->id == OT_ASHEXPLODE) { char diebuf[BUFLEN]; // explosion! @@ -14610,8 +14706,12 @@ void timeeffectsob(object_t *o) { newoid = f->val[1]; } // some flags will be inherited by the new object + + // remember certain flags from the objects which are combining. copyflag(fp, o->flags, F_FILLPOT); + copyflag(fp, o->flags, F_LINKRACE); killflagsofid(o->flags, F_FILLPOT); + killflagsofid(o->flags, F_LINKRACE); // remove some instances of the original object. removeob(o, newamt * f->val[0]); @@ -14624,6 +14724,9 @@ void timeeffectsob(object_t *o) { newob = addobfast(op, newoid); } if (newob) { + killflagsofid(newob->flags, F_FILLPOT); + killflagsofid(newob->flags, F_LINKRACE); + // restore remembered flags copyflags(newob->flags, fp, NA); } } @@ -14632,6 +14735,12 @@ void timeeffectsob(object_t *o) { return; } + // mixed blood loses other properties. + // shoudl never happen - but check just in case + if (countflagsofid(o->flags, F_LINKRACE) > 1) { + killflagsofid(o->flags, F_FILLPOT); + } + // expire flags timeeffectsflags(o->flags); checkflagpile(o->flags); @@ -14874,7 +14983,6 @@ void timeeffectsob(object_t *o) { } if (location && onground) { - if ((o->type->material->id == MT_WATER) || (o->type->id == OT_SPLASHWATER)) { if (location->type->absorbent) { if (haslos(player, location)) { @@ -14885,6 +14993,29 @@ void timeeffectsob(object_t *o) { return; } } + getflags(o->flags, retflag, &nretflags, F_GENERATES, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if (pctchance(f->val[0])) { + cell_t *where; + if (f->val[2] == NA) { + where = location; + } else { + condset_t cs; + initcondv(&cs, CC_IMPASSABLE, B_FALSE, NA, CC_NONE); + where = getrandomadjcell(location, &cs, B_NOEXPAND); + } + + if (where) { + if (f->val[1] == 0) { + addob(where->obpile, f->text); + } else { + addobsinradius(where, f->val[1], DT_COMPASS, f->text, B_TRUE, B_INCLUDECENTRE, + NULL, NULL, NULL, NULL); + } + } + } + } } @@ -15359,13 +15490,21 @@ void timeeffectsob(object_t *o) { // now move other adjacent objects in the same direction (if we can). for (i = 0; i < nadjobs; i++) { + int dead = B_FALSE; c = getcellindir(adjob[i]->pile->where, dir); if (c) { if (c->type->solid) { - // this should always kill it! - damagecell(c, c->hp, DT_DIRECT, NULL); + if (f->val[0] == B_TRUE) { + // this should always kill it! + damagecell(c, c->hp, DT_DIRECT, NULL); + } else { + killob(adjob[i]); + dead = B_TRUE; + } + } + if (!dead) { + moveob(adjob[i], c->obpile, adjob[i]->amt); } - moveob(adjob[i], c->obpile, adjob[i]->amt); } } } @@ -15506,7 +15645,7 @@ void trapeffects(object_t *trapob, enum OBTYPE oid, cell_t *c, object_t *trapped } } if (!avoided) { - poison(lf, rnd(10,20), P_VENOM, 1, "a needle trap", R_NONE); + poison(lf, rnd(10,20), P_VENOM, 1, "a needle trap", R_NONE, B_TRUE); } } else { if (haslos(player, c)) { diff --git a/objects.h b/objects.h index 66998f5..aef49bf 100644 --- a/objects.h +++ b/objects.h @@ -16,7 +16,7 @@ int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fr obmod_t *addobmod(enum OBMOD id, char *prefix); obpile_t *addobpile(lifeform_t *owner, cell_t *where, object_t *parentob); void addomprefix(enum OBMOD id, char *altprefix); -void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes, lifeform_t *creator); +void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes, int includecentre, lifeform_t *creator, object_t **retob, int **retobcount, int *nretobs); objecttype_t *addot(enum OBTYPE id, char *name, char *description, int material, float weight, int obclassid, enum LFSIZE size); recipe_t *addrecipe(enum OBTYPE result, ...); void adjustdamhardness(int *dam, enum DAMTYPE damtype, enum MATERIAL mat); @@ -146,7 +146,7 @@ char *getobhurtname(object_t *o, enum DAMTYPE damtype); float getobweight(object_t *o); float getobunitweight(object_t *o); objecttype_t *getoppositestairs(objecttype_t *ot); -objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, enum LFSIZE maxsize, enum SKILL wepsk, int forpickup, ... ); +objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, enum LFSIZE maxsize, enum SKILL wepsk, enum RARITY forcerr, int forpickup, ... ); objecttype_t *getrandomob(map_t *map, char *buf); objecttype_t *getrandomobofsize(map_t *map, char *buf, enum LFSIZE maxsize); objecttype_t *getrandomobwithdt(map_t *map, enum DAMTYPE damtype, char *buf); diff --git a/save.c b/save.c index 86ce2db..5e56b2f 100644 --- a/save.c +++ b/save.c @@ -1282,6 +1282,7 @@ int showhiscores(lifeform_t *lf, int min, int max) { if (lf && !lfhasflag(lf, F_NOSCORE)) { hilitescore = calcscore(lf); + if (hilitescore == 0) hilitescore = -1; } else { hilitescore = -1; } @@ -1301,9 +1302,9 @@ int showhiscores(lifeform_t *lf, int min, int max) { snprintf(hilitescoretext, BUFLEN, "%ld",hilitescore); rc = sqlite3_exec(db, cmd, showhiscoreline, hilitescoretext, &errmsg); if (rc != SQLITE_OK) { - msg("error writing hiscores: '%s'", errmsg); + msg("error readin hiscores: '%s'", errmsg); dblog("query was: '%s'", cmd); - dblog("error writing hiscores: '%s'\n sql command: [%s]", errmsg, cmd); + dblog("error readin hiscores: '%s'\n sql command: [%s]", errmsg, cmd); sqlite3_free(errmsg); } free(cmd); @@ -1351,7 +1352,7 @@ int writehiscore(lifeform_t *lf, int *rank) { escaped = strdup(killedby); strrep(&escaped, "'", "''", NULL); - if (!lfhasflag(lf, F_NOSCORE)) { + if (!lfhasflag(lf, F_NOSCORE) && (score > 0)) { asprintf(&cmd, "insert into 'hiscores' (score,name,job,killedby) VALUES (%ld, '%s', '%s', '%s')", score, pname, jobname, escaped); rc = sqlite3_exec(db, cmd, NULL, NULL, &errmsg); free(cmd); @@ -1367,6 +1368,8 @@ int writehiscore(lifeform_t *lf, int *rank) { if (rank) { if (lfhasflag(lf, F_NOSCORE)) { *rank = -1; + } else if (score == 0) { + *rank = -2; } else { // find out the player's rank asprintf(&cmd, "select (select count(*) from hiscores b where b.score > a.score) + 1 as rank from hiscores a where score = %ld;",score); diff --git a/spell.c b/spell.c index f939b8d..761e123 100644 --- a/spell.c +++ b/spell.c @@ -1197,11 +1197,11 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef slev = getskill(user, SK_ENGINEERING); - dir = askdir("Dismantle obstacle in which direction (- to cancel)", B_TRUE, B_FALSE); - if (dir == D_NONE) { + dirch = askdir("Dismantle obstacle in which direction (- to cancel)", B_TRUE, B_FALSE); + if (dirch == D_NONE) { msg("Cancelled."); return B_TRUE; - } else if (dir == D_MYSELF) { + } else if (dirch == D_MYSELF) { c = user->cell; } else { dir = chartodir(dirch); @@ -2093,7 +2093,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } else { object_t *oo; for (oo = targcell->obpile->first; oo ; oo = oo->next) { - if (aiwants(user, oo, NULL)) { + if (canpickup(user, oo, 1) && aiwants(user, oo, NULL)) { o = oo; break; } @@ -4054,7 +4054,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // destroy WORN metal objects by the caster (not CARRIED ones) for (o = caster->pack->first ; o ; o = o->next) { - if (isequipped(o) && ismetal(o->material->id) && isweapon(o)) { + if (isequipped(o) && ismetal(o->material->id)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobweight(o); @@ -6171,10 +6171,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } - addobsinradius(c, 1, DT_COMPASS, "5-10 stones", B_TRUE, NULL); + addobsinradius(c, 1, DT_COMPASS, "5-10 stones", B_TRUE, B_NOCENTRE, NULL, NULL, NULL, NULL); addob(c->obpile, "5-10 stones"); } else if (c->type->material->id == MT_BLOOD) { - addobsinradius(c, 1, DT_COMPASS, "pool of blood", B_TRUE, NULL); + addobsinradius(c, 1, DT_COMPASS, "pool of blood", B_TRUE, B_NOCENTRE, NULL, NULL, NULL, NULL); addob(c->obpile, "pool of blood"); } @@ -7726,7 +7726,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ radius = power / 2; if (radius < 1) radius = 1; - addobsinradius(targcell, radius, DT_ORTH, "hail storm", B_FALSE, caster); + addobsinradius(targcell, radius, DT_ORTH, "hail storm", B_FALSE, B_INCLUDECENTRE, + caster, NULL, NULL, NULL); // replace damage per sec with power d4 asprintf(&dambuf, "%dd4", power); @@ -9381,7 +9382,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("^%cA glob of venom hits %s.",getlfcol(target, CC_BAD), lfname); } if (!isimmuneto(target->flags, DT_POISON, B_FALSE)) { - poison(target, power*3, P_VENOM, (power/4)+1, "a glob of venom", caster ? caster->race->id : R_NONE); + poison(target, power*3, P_VENOM, (power/4)+1, "a glob of venom", + caster ? caster->race->id : R_NONE, B_TRUE); } } } @@ -11597,7 +11599,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ radius = power / 2; if (radius < 1) radius = 1; - addobsinradius(targcell, radius, DT_ORTH, "storm of sleet", B_FALSE, caster); + addobsinradius(targcell, radius, DT_ORTH, "storm of sleet", B_FALSE, B_INCLUDECENTRE, + caster, NULL, NULL, NULL); } else { failed = B_TRUE; } diff --git a/text.c b/text.c index ad12d0d..1489078 100644 --- a/text.c +++ b/text.c @@ -1644,6 +1644,23 @@ char *getrarityname(enum RARITY rr) { return "?unknownrarity?"; } +// converts text to rarity value +enum RARITY getrarityval(char *name) { + enum RARITY wantrarity = RR_NONE; + if (streq(name,"frequent")) { + wantrarity = RR_FREQUENT; + } else if (streq(name,"common")) { + wantrarity = RR_COMMON; + } else if (streq(name,"uncommon")) { + wantrarity = RR_UNCOMMON; + } else if (streq(name,"rare")) { + wantrarity = RR_RARE; + } else if (streq(name,"very rare")) { + wantrarity = RR_VERYRARE; + } + return wantrarity; +} + // pass in EITHER m or r, not both. // // if how is anything other than RF_SHORT, "m" should be passed. diff --git a/text.h b/text.h index 4f44ee7..8b1573c 100644 --- a/text.h +++ b/text.h @@ -42,6 +42,7 @@ char *getinjuryname(enum DAMTYPE dt); char *getinjurydesc(enum BODYPART bp, enum DAMTYPE dt); char *getobmodprefix(object_t *o, obmod_t *om); char *getrarityname(enum RARITY rr); +enum RARITY getrarityval(char *name); char *getregionname(char *buf, map_t *m, region_t *r, enum REGIONNAMEFORMAT how); char *getreldirname(int reldir); char *getsizetext(enum LFSIZE sz); diff --git a/vault.c b/vault.c index c37d23d..7d96701 100644 --- a/vault.c +++ b/vault.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -409,7 +410,9 @@ int addvaultthing(cell_t *c, vault_t *v, enum VAULTTHING vt, char *what) { case VT_LF: lf = addmonster(c, R_SPECIFIED, what, B_TRUE, 1, B_TRUE, NULL); if (!lf) { - dblog("invalid racename '%s' in vault", what); + dblog("invalid racename '%s' in vault %s", what, v->id); + msg("invalid racename '%s' in vault %s", what, v->id); + raise(SIGINT); rv = B_TRUE; } // first lifeform in a shop is the shopkeeper