diff --git a/ai.c b/ai.c index 0a798c3..256f1d9 100644 --- a/ai.c +++ b/ai.c @@ -562,6 +562,26 @@ int aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim // aim at an adjacent wall cell if (spellcell) *spellcell = poss[rnd(0,nposs-1)]; + } else if (spelltype->id == OT_S_PLANTWALK) { + cell_t *cell[MAXCANDIDATES]; + int ncells,i; + cell_t *poss[MAX_MAPW*MAX_MAPH]; + int nposs = 0; + + getradiuscells(lf->cell, 1, DT_COMPASS, B_TRUE, LOF_DONTNEED, B_TRUE, cell, &ncells, 0); + // any plants within range 1? + for (i = 0; i < ncells; i++) { + if (hasobofclass(cell[i]->obpile, OC_FLORA)) { + poss[nposs++] = cell[i]; + } + } + + // should always be true since we check this in aispellok + if (nposs > 0) { + if (spellcell) *spellcell = poss[rnd(0,nposs-1)]; + if (spelllf) *spelllf = NULL; + if (spellob) *spellob = NULL; + } } else if (spelltype->id == OT_S_TELEKINESIS) { float maxweight; object_t *poss[MAXPILEOBS]; @@ -596,6 +616,7 @@ int aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim // cast spell at the victim if (spelllf) *spelllf = victim; if (spellcell) *spellcell = victim->cell; + /* } else if (spelltype->id == OT_S_CHARM) { lifeform_t *l; l = getnearbypeaceful(lf); @@ -604,6 +625,7 @@ int aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim if (spellcell) *spellcell = l->cell; if (spellob) *spellob = NULL; } + */ } else if (spelltype->id == OT_S_SUPERHEAT) { // get all potions object_t *poss[MAXPILEOBS],*o; @@ -963,10 +985,39 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { int ai_handle_emergencies(lifeform_t *lf, enum ATTRBRACKET iqb) { int db = B_FALSE; int moveawayfromcell = B_FALSE; + flag_t *f; if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; if (lfhasflag(lf, F_RAGE)) return B_FALSE; + // if we are not near our life ob, move towards it! + f = lfhasflag(lf, F_LIFEOB); + if (f) { + if (!findnearbylifeob(lf->cell, f, NULL)) { + cell_t *poss[8]; + int nposs = 0; + int dir; + if (db) dblog(".oO { not near my lifeob! }"); + for (dir = DC_N; dir <= DC_NW; dir++) { + cell_t *c; + c = getcellindir(lf->cell, dir); + if (c && findnearbylifeob(c, f, NULL)) { + poss[nposs++] = c; + } + } + if (nposs) { + if (aigoto(lf, poss[rnd(0,nposs-1)], MR_OTHER, NULL, PERMENANT)) { + if (db) dblog(".oO { moving close to lifeob }"); + // success + return B_TRUE; + } else { + if (db) dblog(".oO { couldnt find an adjacent cell near lifeob }"); + // TODO: search all in los + } + } + } + } + // if our cell is dangerous, move away! if (iqb >= AT_AVERAGE) { if (celldangerous(lf, lf->cell, B_TRUE, NULL)) { @@ -1565,7 +1616,6 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { // spell failed. we will keep going through aiturn. } else { // spell succesful - if ((spell == OT_A_STEAL) && !lfhasflag(lf, F_NOFLEE)) { if (!isgod(lf)) { // run away for a while @@ -2235,6 +2285,16 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG } } } + } else if (ot->id == OT_S_PLANTWALK) { + cell_t *cell[MAXCANDIDATES]; + int ncells,i; + getradiuscells(lf->cell, 1, DT_COMPASS, B_TRUE, LOF_DONTNEED, B_TRUE, cell, &ncells, 0); + // any plants within range 1? + for (i = 0; i < ncells; i++) { + if (hasobofclass(cell[i]->obpile, OC_FLORA)) { + ok = B_TRUE; + } + } } else if (ot->id == OT_S_PYROMANIA) { int i; for (i = 0; i < lf->nlos; i++) { @@ -2262,10 +2322,6 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG } } } - } else if (ot->id == OT_S_CHARM) { - if (getnearbypeaceful(lf)) { - ok = B_TRUE; - } } else if (ot->id == OT_S_SUPERHEAT) { // got a potion? if (victim && hasobwithflag(lf->pack, F_DRINKABLE) && haslof(lf->cell, victim->cell, LOF_NEED, NULL)) { diff --git a/data.c b/data.c index 67501b5..702f582 100644 --- a/data.c +++ b/data.c @@ -445,6 +445,7 @@ void initjobs(void) { addflag(lastjob->flags, F_HASPET, NA, NA, NA, "young wolf"); addflag(lastjob->flags, F_PARTVEGETARIAN, B_TRUE, NA, NA, NULL); addflag(lastjob->flags, F_LEVSKILL, 5, SK_LORE_NATURE, NA, NULL); + addflag(lastjob->flags, F_LEVSPELL, 7, OT_S_PLANTWALK, NA, NULL); addflag(lastjob->flags, F_LEVSKILL, 10, SK_LORE_NATURE, NA, NULL); addflag(lastjob->flags, F_LEVSKILL, 15, SK_LORE_NATURE, NA, NULL); addflag(lastjob->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); @@ -3109,6 +3110,13 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_ADJSELF, NA, NA, NULL); + addot(OT_S_WHIRLWIND, "conjure whirlwind", "Creates a spinning whirlwind at a specified location.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the whirlwind will last."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER|TT_OBJECT, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); // l4 addot(OT_S_CLOUDKILL, "cloudkill", "Creates a cloud of poisonous gas. The cloud's size is 1-3 cells, depending on the spell power.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); @@ -3135,12 +3143,6 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); - - - - - - // l5 addot(OT_S_CHAINLIGHTNING, "chain lightning", "Electricity arcs up to 5 times between all nearby enemies. The initial arc deals 3d6 damage, the next deals 3d5 damage, etc.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's range is based on its power."); @@ -3149,6 +3151,12 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_NEED, NA, NULL); + addot(OT_S_ETHEREALSTEED, "ethereal steed", "Raises the caster onto a spinning whirlwind, granting them levitation and speed. Smaller whirlwinds are left in a trail behind the caster, deterring pursuit.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long its effects will last."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_S_HASTE, "haste", "Increases the speed of the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long its effects will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); @@ -3156,12 +3164,26 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_ALLY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_TORNADO, "conjure tornado", "Creates a lethal tornado at a specified location. The tornado will move about, causing damage to everything in its path.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the tornado will last."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER|TT_OBJECT, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); // l6 addot(OT_S_FLIGHT, "fly", "Allows the caster to fly.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); + addot(OT_S_HURRICANE, "conjure hurricane", "Creates a devastating hurricane at a specified location. The hurricane will move about, obliterating all in its path.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the hurricane will last."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER|TT_OBJECT, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); /////////////////// // elemental - fire magic @@ -3264,6 +3286,15 @@ void initobjects(void) { addflag(lastot->flags, F_RANGE, 3, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 5, NA, NULL); + addot(OT_S_WALLOFFIRE, "wall of fire", "Creates an roaring wall of flames.", MT_ICE, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the size of the flames."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 5, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 5, NA, NULL); // l6 addot(OT_S_METEOR, "meteor", "Launches a white-hot meteorite towards the target location, dealing up to ^bpower^nd6+30 damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The damage is lower for enemies further away from the ball's centre."); @@ -3596,6 +3627,16 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_PLANTWALK, "plantwalk", "Instantly travel through a plant to another nearby plant.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power level I, caster may only travel to plants of the same type."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power level II, caster may travel to any other plant."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 2, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_OBJECT, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 1, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_SPECIAL, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l6 addot(OT_S_LIGHTNINGSTORM, "lightning storm", "Blasts all visible enemies bolts of lightning from the sky, dealing 3d6 damage (4d6 if outdoors).", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how many bolts will appear."); @@ -3821,7 +3862,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); - addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); /////////////////// // modification @@ -3859,16 +3900,18 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); // l2 - addot(OT_S_SIZEUP, "unnatural growth", "Causes the target's body to grow in size. They will become easier to hit, but deal less damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_SIZEUP, "unnatural growth", "Causes the target's body to grow in size. They will become easier to hit, but deal more damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addot(OT_S_SIZEDOWN, "unnatural shrinkage", "Causes the target's body to shrink in size. They will deal less damage, but become harder to hit.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addot(OT_S_MIRRORIMAGE, "mirror image", "Creates ^bpower^n illusionary clones of the caster to distract enemies. These clones cannot deal damage, and vanish when hit.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how many clones are created, and how many hits they can sustain."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); @@ -3933,6 +3976,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addot(OT_S_QUICKENSTONE, "quicken stone", "Crafts nearby stone into powerful stone primalities.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how many creatures will be created."); @@ -5673,6 +5717,17 @@ void initobjects(void) { addflag(lastot->flags, F_WALKDAMBP, BP_LEGS, DT_WATER, NA, "1d2"); addflag(lastot->flags, F_WALKDAMBP, BP_FEET, DT_WATER, NA, "1d2"); + addot(OT_HURRICANE, "hurricane", "A massive column of devestating air currents.", MT_GAS, 0, OC_EFFECT, SZ_HUMAN); + addflag(lastot->flags, F_GLYPH, C_GREY, UNI_SPIRAL, NA, NULL); + addflag(lastot->flags, F_OBHP, 12, 12, NA, NULL); + addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); + addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_KNOCKAWAY, 4, 40, 12, "4d6"); + addflag(lastot->flags, F_OBMOVESRANDOMLY, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); + addot(OT_ICEWALL, "wall of ice", "A wall made of solid ice.", MT_ICE, 0, OC_EFFECT, SZ_LARGE); addflag(lastot->flags, F_GLYPH, C_CYAN, '#', NA, NULL); addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_MAX, NA, NULL); @@ -5691,6 +5746,17 @@ void initobjects(void) { addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "vanishes"); addflag(lastot->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL); + addot(OT_TORNADO, "tornado", "A large column of incredibly fast spinning air.", MT_GAS, 0, OC_EFFECT, SZ_HUMAN); + addflag(lastot->flags, F_GLYPH, C_GREY, UNI_SPIRAL, NA, NULL); + addflag(lastot->flags, F_OBHP, 8, 8, NA, NULL); + addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); + addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_KNOCKAWAY, 2, 35, 10, "3d6"); + addflag(lastot->flags, F_OBMOVESRANDOMLY, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); + addot(OT_VIBCLOUD, "vibrating cloud", "A cloud of unstable molecules.", MT_GAS, 0, OC_EFFECT, SZ_LARGE); addflag(lastot->flags, F_GLYPH, C_ORANGE, UNI_SHADEMED, NA, NULL); addflag(lastot->flags, F_NODIECONVERTTEXT, NA, NA, NA, NULL); @@ -5731,6 +5797,16 @@ void initobjects(void) { addflag(lastot->flags, F_DTVULN, DT_ACID, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); + addot(OT_WHIRLWIND, "whirlwind", "A large column of very rapidly spinning air.", MT_GAS, 0, OC_EFFECT, SZ_HUMAN); + addflag(lastot->flags, F_GLYPH, C_GREY, UNI_SPIRAL, NA, NULL); + addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL); + addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); + addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_KNOCKAWAY, 1, 30, 8, "2d6"); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); + // armour objects // armour - multipart addot(OT_WETSUIT, "wetsuit", "Full-body rubber suit which provides good insulation from cold.", MT_RUBBER, 6, OC_ARMOUR, SZ_MEDIUM); @@ -8767,6 +8843,113 @@ void initrace(void) { addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_NIGHTVISRANGEMOD, 1, NA, "75"); + addrace(R_DRYAD, "dryad", 65, 'n', C_BROWN, MT_WOOD, RC_MAGIC, "Dryads are gentle forest spirits. These peaceful creatures prefer the tranquility of nature, and will avoid fighting if possible."); + setbodytype(lastrace, BT_HUMANOID); + addflag(lastrace->flags, F_ALIGNMENT, AL_GOOD, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_RARE, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_COMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "2d4"); + addflag(lastrace->flags, F_TR, 2, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_LIFEOB, OT_TREE, 2, 3, NULL); + addflag(lastrace->flags, F_HOMEOB, 100, NA, NA, "tree"); + addflag(lastrace->flags, F_HOMEOB, 100, NA, NA, "random gem"); + addflag(lastrace->flags, F_HOMEOB, 66, NA, NA, "random gem"); + addflag(lastrace->flags, F_HOMEOB, 33, NA, NA, "random gem"); + addflag(lastrace->flags, F_HOMEOB, 50, NA, NA, "10-50 gold"); + addflag(lastrace->flags, F_HOMELEVOB, 2, 4, NA, "tree"); + addflag(lastrace->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_FISTS, 4, NA, NULL); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "knife"); + addflag(lastrace->flags, F_CANCAST, OT_S_PLANTWALK, NA, NA, NULL); + addflag(lastrace->flags, F_CANCAST, OT_S_CHARM, NA, NA, "pw:1;"); + //addflag(lastrace->flags, F_CANCAST, OT_S_SLEEP, 10, 10, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_PLANTWALK, NA, NA, NULL); + addflag(lastrace->flags, F_WANTSOBFLAG, F_GEM, B_COVETS, NA, NULL); + addflag(lastrace->flags, F_WANTS, OT_GOLD, B_COVETS, NA, NULL); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "shouts^a shout"); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_SS_NATURE, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_THIEVERY, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_STAYINROOM, NA, NA, NA, NULL); // stay in our starting room + addflag(lastrace->flags, F_MORALE, 2, NA, NA, NULL); + + addrace(R_DJINNI, "genie", 65, 'Y', C_YELLOW, MT_FLESH, RC_MAGIC, "Genies are powerful air spirits. They resemble richly dressed humans floating on a cone of whirling air."); + setbodytype(lastrace, BT_HUMANOID); + addflag(lastrace->flags, F_ALIGNMENT, AL_GOOD, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_RARE, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "7d4+3"); + addflag(lastrace->flags, F_TR, 7, NA, NA, NULL); + addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_DTIMMUNE, DT_PROJECTILE, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_FISTS, 7, NA, NULL); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "2 scimitars"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "silk shirt"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "riding trousers"); + addflag(lastrace->flags, F_NATURALFLIGHT, B_TRUE, NA, NA, ""); + addflag(lastrace->flags, F_CANWILL, OT_S_FLIGHT, NA, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_FLIGHT, NA, NA, NULL); + addflag(lastrace->flags, F_AUTOCREATEOB, 0, NA, NA, "whirlwind"); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "shouts^a shout"); + addflag(lastrace->flags, F_CANCAST, OT_S_AIRBLAST, NA, NA, "pw:3;"); + addflag(lastrace->flags, F_CANCAST, OT_S_GUSTOFWIND, NA, NA, "pw:8;"); + addflag(lastrace->flags, F_CANCAST, OT_S_INVISIBILITY, 20, 20, "pw:1;"); + addflag(lastrace->flags, F_CANCAST, OT_S_GASEOUSFORM, NA, NA, "pw:1;"); + addflag(lastrace->flags, F_CANCAST, OT_S_MIRRORIMAGE, 10, 10, "pw:2;"); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_TWOWEAPON, PR_ADEPT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_SS_AIR, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); + + addrace(R_EFREETI, "efreeti", 65, 'Y', C_RED, MT_FLESH, RC_MAGIC, "Efreeti are evil, fire-based cousins of genies. They resemble richly dressed humans surrounded by crackling flames."); + setbodytype(lastrace, BT_HUMANOID); + addflag(lastrace->flags, F_ALIGNMENT, AL_GOOD, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_RARE, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "5d4+3"); + addflag(lastrace->flags, F_TR, 7, NA, NA, NULL); + addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 5, NA, NULL); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming scimitar"); + addflag(lastrace->flags, F_NATURALFLIGHT, B_TRUE, NA, NA, ""); + addflag(lastrace->flags, F_CANWILL, OT_S_FLIGHT, NA, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_FLIGHT, NA, NA, NULL); + addflag(lastrace->flags, F_AUTOCREATEOB, 0, NA, NA, "medium fire"); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "shouts^a shout"); + addflag(lastrace->flags, F_CANCAST, OT_S_WALLOFFIRE, 10, 10, "pw:5;"); + addflag(lastrace->flags, F_CANCAST, OT_S_FLAMEPILLAR, NA, NA, "pw:7;"); + addflag(lastrace->flags, F_CANCAST, OT_S_GASEOUSFORM, NA, NA, "pw:1;"); + addflag(lastrace->flags, F_CANCAST, OT_S_SIZEUP, 20, 20, "pw:1;"); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_TWOWEAPON, PR_ADEPT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_SS_FIRE, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); + addrace(R_EYEBAT, "eyebat", 5, 'e', C_BLUE, MT_FLESH, RC_MAGIC, "A smaller cousin to the beholder, an eyebat is a single oversized eyeball suspended between bat-like wings."); addbodypart(lastrace, BP_BODY, NULL); addbodypart(lastrace, BP_WINGS, NULL); @@ -9304,7 +9487,7 @@ void initrace(void) { addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL); - addflag(lastrace->flags, F_CANCAST, OT_S_GASEOUSFORM, NA, NA, "pw:3;"); + addflag(lastrace->flags, F_CANCAST, OT_S_GASEOUSFORM, NA, NA, "pw:1;"); // gremlins also cause tech to fail around them. addrace(R_HOBGOBLIN, "hobgoblin", 90, 'g', C_YELLOW, MT_FLESH, RC_HUMANOID, "A larger, stronger, smarter and more menacing form of a goblin."); @@ -9930,7 +10113,7 @@ void initrace(void) { addflag(lastrace->flags, F_ENHANCESMELL, 3, NA, NA, NULL); addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); - addrace(R_POLTERGEIST, "poltergeist", 50, 'p', C_GREEN, MT_FLESH, RC_UNDEAD, "An evil ghostly spirit who telekinetically throws objects at its enemies."); // sPirit + addrace(R_POLTERGEIST, "poltergeist", 50, 'P', C_GREEN, MT_FLESH, RC_UNDEAD, "An evil ghostly spirit who telekinetically throws objects at its enemies."); // sPirit addbodypart(lastrace, BP_BODY, NULL); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); @@ -11415,7 +11598,7 @@ void initrace(void) { addflag(lastrace->flags, F_DTVULN, DT_POISONGAS, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); - addrace(R_SLUG, "acid slug", 150, 'P', C_GREY, MT_FLESH, RC_ANIMAL, "While acid slugs lack the protective shell of their snail cousings, their rubbery flesh is extremely resilient. Their acid-based attacks also make them much more dangerous."); + addrace(R_SLUG, "acid slug", 150, 'p', C_GREY, MT_FLESH, RC_ANIMAL, "While acid slugs lack the protective shell of their snail cousings, their rubbery flesh is extremely resilient. Their acid-based attacks also make them much more dangerous."); addbodypart(lastrace, BP_BODY, NULL); addbodypart(lastrace, BP_HEAD, NULL); addbodypart(lastrace, BP_EYES, NULL); @@ -11445,7 +11628,7 @@ void initrace(void) { addflag(lastrace->flags, F_AQUATIC, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_BREATHWATER, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); - addrace(R_SNAIL, "mottled snail", 150, 'P', C_BROWN, MT_FLESH, RC_ANIMAL, "An enormous snail, protected by a hard, scaled shell and gifted with long, sharp fangs."); + addrace(R_SNAIL, "mottled snail", 150, 'p', C_BROWN, MT_FLESH, RC_ANIMAL, "An enormous snail, protected by a hard, scaled shell and gifted with long, sharp fangs."); addbodypart(lastrace, BP_BODY, NULL); addbodypart(lastrace, BP_HEAD, NULL); addbodypart(lastrace, BP_EYES, NULL); @@ -11680,7 +11863,7 @@ void initrace(void) { addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_POISONCORPSE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HOMEOB, NA, NA, NA, "web"); + addflag(lastrace->flags, F_HOMEOB, 100, NA, NA, "web"); addflag(lastrace->flags, F_HOMELEVOB, NA, NA, NA, "1-10 webs"); addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_FLEEONHPPCT, 25, NA, NA, ""); @@ -11710,7 +11893,7 @@ void initrace(void) { addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_POISONCORPSE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HOMEOB, NA, NA, NA, "web"); + addflag(lastrace->flags, F_HOMEOB, 100, NA, NA, "web"); addflag(lastrace->flags, F_HOMELEVOB, NA, NA, NA, "20-30 webs"); addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_FLEEONHPPCT, 25, NA, NA, ""); @@ -11741,7 +11924,7 @@ void initrace(void) { addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_POISONCORPSE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HOMEOB, NA, NA, NA, "web"); + addflag(lastrace->flags, F_HOMEOB, 100, NA, NA, "web"); addflag(lastrace->flags, F_HOMELEVOB, NA, NA, NA, "10-20 webs"); addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_FLEEONHPPCT, 25, NA, NA, ""); @@ -11771,7 +11954,7 @@ void initrace(void) { addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_POISONCORPSE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HOMEOB, NA, NA, NA, "web"); + addflag(lastrace->flags, F_HOMEOB, 100, NA, NA, "web"); addflag(lastrace->flags, F_HOMELEVOB, NA, NA, NA, "1-10 webs"); addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_FLEEONHPPCT, 25, NA, NA, ""); @@ -12972,7 +13155,7 @@ void initrace(void) { addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); - addrace(R_GHOST, "ghost", 50, 'p', C_BLUE, MT_MAGIC, RC_UNDEAD, "Wispy spirits formed when a soul refuses to depart the earthly realm after death, ghosts exist part way between dimensions. The sight of a ghost can cause fear in all who behold it, and their ethereal nature makes them immune to most attacks."); // p for sPirit + addrace(R_GHOST, "ghost", 50, 'P', C_BLUE, MT_MAGIC, RC_UNDEAD, "Wispy spirits formed when a soul refuses to depart the earthly realm after death, ghosts exist part way between dimensions. The sight of a ghost can cause fear in all who behold it, and their ethereal nature makes them immune to most attacks."); // p for sPirit setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, AT_GTAVERAGE, NA, NULL); @@ -13148,7 +13331,7 @@ void initrace(void) { addflag(lastrace->flags, F_TR, 9, NA, NA, NULL); addflag(lastrace->flags, F_ARMOURRATING, 5, NA, NA, NULL); addflag(lastrace->flags, F_EVASION, -10, NA, NA, NULL); - addflag(lastrace->flags, F_HOMEOB, NA, NA, NA, "coffin"); + addflag(lastrace->flags, F_HOMEOB, 100, NA, NA, "coffin"); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 10, NA, NULL); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 7, NA, NULL); addflag(lastrace->flags, F_NATURALFLIGHT, B_TRUE, NA, NA, ""); diff --git a/defs.h b/defs.h index d3b0a77..07ccb1d 100644 --- a/defs.h +++ b/defs.h @@ -58,6 +58,7 @@ #define UNI_SHADELIGHT 0x2591 #define UNI_SHADEMED 0x2592 #define UNI_SHADEDARK 0x2593 +#define UNI_SPIRAL 0x2202 //#define UNI_SOLID '#' // getrandomemptycell() params @@ -228,6 +229,7 @@ #define AO_DRINKABLE 16384 // askcoords target types +#define TT_SPECIFIED -1 #define TT_NONE 0 #define TT_MONSTER 1 #define TT_OBJECT 2 @@ -979,6 +981,9 @@ enum RACE { R_CREEPINGCLAW, R_CRYMIDIA, R_DARKMANTLE, + R_DJINNI, + R_DRYAD, + R_EFREETI, R_EYEBAT, R_GIANTHILL, R_GIANTFIRE, @@ -1470,10 +1475,14 @@ enum OBTYPE { OT_S_AIRBLAST, OT_S_CHAINLIGHTNING, OT_S_CLOUDKILL, + OT_S_ETHEREALSTEED, OT_S_GUSTOFWIND, + OT_S_HURRICANE, OT_S_MIST, OT_S_SHATTER, OT_S_TAILWIND, + OT_S_TORNADO, + OT_S_WHIRLWIND, OT_S_WINDSHIELD, // -- elemental - fire OT_S_BLADEBURN, @@ -1488,6 +1497,7 @@ enum OBTYPE { OT_S_PYROMANIA, OT_S_SPARK, OT_S_SUPERHEAT, + OT_S_WALLOFFIRE, // -- elemental - ice OT_S_ABSOLUTEZERO, OT_S_CHILL, @@ -1586,6 +1596,7 @@ enum OBTYPE { OT_S_HAILSTORM, OT_S_LIGHTNINGBOLT, OT_S_LIGHTNINGSTORM, + OT_S_PLANTWALK, OT_S_PURIFYFOOD, OT_S_QUENCH, OT_S_LESSENPOISON, @@ -1817,6 +1828,7 @@ enum OBTYPE { OT_FIREMED, OT_FIRESMALL, OT_HAILSTORM, + OT_HURRICANE, OT_ICEWALL, OT_MAGICBARRIER, OT_STEAMCLOUD, @@ -1828,9 +1840,11 @@ enum OBTYPE { OT_METHANEPUFF, OT_POISONCLOUD, OT_POISONPUFF, + OT_TORNADO, OT_VIBCLOUD, OT_VINE, OT_WEB, + OT_WHIRLWIND, // armour - multipart OT_WETSUIT, // armour - body @@ -2230,6 +2244,13 @@ enum FLAG { F_REPELBLESSED, // v0 = b_blessed or b_cursed. repels other obejcts // of this blesstype. // if v1 == b_blessed or b_cursed, will ID these blessings/curses + F_KNOCKAWAY, // this obejct will cause a knockback() effect on + // other objects of lifeforms in its space. + // v0 is the distance to knock lfs. + // v1 = skillcheck difficulty to avoid falling + // v2 = speed to fire objects + // text = "xdx" (damage to deal) + F_TRAIL, // this object denotes the trail left by a lf. // v0 = raceid of lf who left it // v1 = direction the lf moved out of this cell @@ -2285,6 +2306,9 @@ enum FLAG { F_THEREISHERE, // announce "there is xx here!", not "you see xx here" // text[0] is punctuation to use. F_OBDIETEXT, // text when the object dies + F_OBMOVESRANDOMLY, // object will randomly move around + // if v0 is true, it can destroy + // walls F_DIECONVERTTEXT, // text when the object converts. eg. "melts" F_DIECONVERTTEXTPL, // text when the object converts, if there are more than 1. eg. "melt" F_DIECONVERT, // text = what this turns into when dying @@ -2821,8 +2845,13 @@ enum FLAG { F_EXTRACORPSE, // text field specifies what additional corpse // obtype to leave // v0 = pct change for this to happen. NA = 100. - F_MYCORPSE, // text field contains obid of my corpse. - // (for ghosts) + F_LIFEOB, // v0 = obtype of life object + // OR + // text field contains obid of life object + // (text overrise v0) + // if lf is not witihn v1 dist of this object(s), + // they lose v2 hp + // (used by ghosts) F_NOCORPSE, // monster's body crumbles to dust after death F_NOCTURNAL, // monster sleeps during the day F_DIURNAL, // monster sleeps at night diff --git a/doc/glyphs.txt b/doc/glyphs.txt index 24c3ad3..d753c0f 100644 --- a/doc/glyphs.txt +++ b/doc/glyphs.txt @@ -31,8 +31,8 @@ n = small humanoid / nymph / sprite N = necron o = orc O = monstrous humanoid (ie. ogre) -p = sPirit -P = gastroPod +p = gastropod +P = sPirit q = quadraped Q = large quadraped r = rodent @@ -41,6 +41,7 @@ s = snake S = spider T = walkingtree-like monster (dryad, treant) U = unearthly/horrific creature +v = ? V = vampire w = small wyrm W = large wyrm diff --git a/flag.c b/flag.c index a031979..2515d3a 100644 --- a/flag.c +++ b/flag.c @@ -562,6 +562,7 @@ int flagcausesinterrupt(flag_t *f, enum GAINORLOSS gol ) { break; case F_BEINGSTONED: case F_CONFUSED: + case F_CHARMEDBY: case F_FEIGNINGDEATH: case F_FLYING: case F_LEVITATING: diff --git a/god.c b/god.c index 5987265..435c208 100644 --- a/god.c +++ b/god.c @@ -619,7 +619,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); + addobsinradius(player->cell, 1, DT_COMPASS, splatterob, B_TRUE, NULL); } if (god->race->id == R_GODFIRE) { dospelleffects(player, OT_S_FLAMEBURST, 1, NULL, NULL, player->cell, B_UNCURSED, NULL, B_FALSE); diff --git a/io.c b/io.c index 8c104be..dec3a4c 100644 --- a/io.c +++ b/io.c @@ -541,8 +541,11 @@ char askchar(char *prompt, char *validchars, char *def, int showchars, int mayca return ch; } -// "func" is a pointer to a function which returns an added description for a highlighted cell -cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *srclf, int maxrange, enum LOFTYPE LOFTYPE, int wanttrail) { +cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *srclf, int maxrange, enum LOFTYPE loftype, int wanttrail) { + return real_askcoords(prompt, subprompt, targettype, srclf, maxrange, loftype, wanttrail, NULL, 0); +} + +cell_t *real_askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *srclf, int maxrange, enum LOFTYPE loftype, int wanttrail, cell_t **spectarg, int nspectargs) { static int startlf = -1; int finished = B_FALSE; int moved = B_FALSE; @@ -570,7 +573,13 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src } // build list of targets if required - if (targettype != TT_NONE) { + if (targettype == TT_SPECIFIED) { + int i; + for (i = 0; i < nspectargs; i++) { + target[i] = spectarg[i]; + } + ntargets = nspectargs; + } else if (targettype != TT_NONE) { for (y = 0; y < player->cell->map->h; y++) { for (x = 0; x < player->cell->map->w; x++) { c = getcellat(player->cell->map, x, y); @@ -607,7 +616,6 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src if (valid) { target[ntargets] = c; - if (curtarget == -1) curtarget = ntargets; ntargets++; } } @@ -615,6 +623,10 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src } } + if ((curtarget == -1) && ntargets) { + //curtarget = ntargets; + curtarget = 0; + } // start prompting if (curtarget == -1) { @@ -855,16 +867,32 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src if (wep && (c->lf->race->id != R_DANCINGWEAPON)) { object_t *secwep; char obname[BUFLEN]; + char obname2[BUFLEN]; char buf2[BUFLEN]; + int donesec = B_FALSE; getobname(wep, obname, wep->amt); - snprintf(buf2, BUFLEN, "weilding %s",obname); + secwep = getequippedob(c->lf->pack, BP_SECWEAPON); + if (secwep) { + getobname(secwep, obname2, secwep->amt); + } else { + strcpy(obname2, ""); + } + + // two different weapons with the same name? + if (secwep && (secwep != wep) && streq(obname, obname2)) { + char *plur; + plur = makeplural(noprefix(obname)); + snprintf(buf2, BUFLEN, "weilding two %s",plur); + free(plur); + donesec = B_TRUE; + } else { + snprintf(buf2, BUFLEN, "weilding %s",obname); + } if (strlen(extrainfo)) strcat(extrainfo, ", "); - secwep = getequippedob(c->lf->pack, BP_SECWEAPON); - if (secwep && (secwep != wep)) { - getobname(secwep, obname, secwep->amt); + if (!donesec && secwep && (secwep != wep) ) { strcat(buf2, " and "); - strcat(buf2, obname); + strcat(buf2, obname2); } strcat(extrainfo, buf2); } else { @@ -958,15 +986,15 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src } trailtarg = c; - if (LOFTYPE != LOF_DONTNEED) { + if (loftype != LOF_DONTNEED) { cell_t *newcell; int bad = B_FALSE; if (srclf) { - if (!haslof_real(srclf->cell, c, LOFTYPE, &newcell, srclf, B_TRUE)) { + if (!haslof_real(srclf->cell, c, loftype, &newcell, srclf, B_TRUE)) { bad = B_TRUE; } } else { - if (!haslof(srclf->cell, c, LOFTYPE, &newcell)) { + if (!haslof(srclf->cell, c, loftype, &newcell)) { bad = B_TRUE; } } @@ -1418,7 +1446,7 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { lf2 = findlf(NULL, f->val[0]); if (lf2) { getlfname(lf2, buf); - msg("%s %s now under %s%s power!",lfname, is(lf), buf,getpossessive(buf)); + msg("^%c%s %s now under %s%s power!^n",getlfcol(lf, CC_BAD), lfname, is(lf), buf,getpossessive(buf)); donesomething = B_TRUE; } break; @@ -2099,7 +2127,8 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { if (lf2) { char buf[BUFLEN]; getlfname(lf2, buf); - msg("%s break%s free of %s%s control!",lfname, isplayer(lf) ? "" : "s", buf,getpossessive(buf)); + msg("^%c%s break%s free of %s%s control!^n",getlfcol(lf, CC_GOOD), + lfname, isplayer(lf) ? "" : "s", buf,getpossessive(buf)); if (isplayer(lf2)) { more(); } @@ -7455,6 +7484,8 @@ void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2, mpcost[nposs] = cost; deactspell[nposs] = B_FALSE; // default power = getspellpower(lf, ot->id); + + if (power > 0) { if (hasactivespell(lf, ot->id)) { cost = 0; @@ -7465,8 +7496,20 @@ void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2, getspellcosttext(lf, ot->id, power, mpdesc[nposs]); } if (mpcost[nposs] <= mpcutoff) { - validspell[nposs] = B_TRUE; - err[nposs] = E_OK; + if (f->val[2] == NA) { + validspell[nposs] = B_TRUE; + err[nposs] = E_OK; + } else { + if (f->val[1] == f->val[2]) { + snprintf(mpdesc[nposs], BUFLEN, "(ready)"); + validspell[nposs] = B_TRUE; + err[nposs] = E_OK; + } else { + snprintf(mpdesc[nposs], BUFLEN, "(%d/%d)",f->val[1],f->val[2]); + validspell[nposs] = B_FALSE; + err[nposs] = E_NOTREADY; + } + } } else { validspell[nposs] = B_FALSE; err[nposs] = E_NOMP; diff --git a/io.h b/io.h index 86a4e9f..42d2f8d 100644 --- a/io.h +++ b/io.h @@ -25,6 +25,7 @@ object_t *doaskobject(obpile_t *op, char *title, int *count, int showlong, int f int askobjectmulti(obpile_t *op, char *prompt, long opts); char askchar(char *prompt, char *validchars, char *def, int showchars, int maycancel); cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *srclf, int maxrange, enum LOFTYPE loftype, int wanttrail); +cell_t *real_askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *srclf, int maxrange, enum LOFTYPE loftype, int wanttrail, cell_t **spectarg, int nspectargs); char *askstring(char *prompt, char punc, char *retbuf, int retbuflen, char *def); vault_t *askvault(char *prompttext); void centre(WINDOW *win, enum COLOUR col, int y, char *format, ... ); diff --git a/lf.c b/lf.c index 734c8c2..4519f3d 100644 --- a/lf.c +++ b/lf.c @@ -100,10 +100,12 @@ void autoweild(lifeform_t *lf) { weild(lf, bestfirearm); } - // weild armour if required, + // weild armour/2nd weapons if required, // and mark other weapons as secondary for (o = lf->pack->first ; o ; o = o->next) { - if (!donesecondary && isweapon(o) && !isequipped(o)) { + if (isweapon(o) && !isequipped(o) && getskill(lf, SK_TWOWEAPON) && !getequippedob(lf->pack, BP_SECWEAPON) && canweild(lf, o)) { + weild(lf, o); + } else if (!donesecondary && isweapon(o) && !isequipped(o)) { addflag(o->flags, F_SECONDARY, B_TRUE, NA, NA, NULL); donesecondary = B_TRUE; } else { @@ -211,7 +213,7 @@ void bleed(lifeform_t *lf, int splatter) { if (strlen(obname) > 0) { addob(lf->cell->obpile, obname); - if (splatter) addobsinradius(lf->cell, 1, DT_COMPASS, obname, B_TRUE); + if (splatter) addobsinradius(lf->cell, 1, DT_COMPASS, obname, B_TRUE, NULL); } } @@ -622,7 +624,7 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { return B_FALSE; } } - } else if (lfhasflagval(lf, F_CANCAST, oid, NA, NA, NULL)) { + } else if ((f = lfhasflagval(lf, F_CANCAST, oid, NA, NA, NULL)) != NULL) { int cost,power; // override! @@ -639,6 +641,14 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { } } + // for mosnters: spell ready? + if (f->val[1] == f->val[2]) { + castable = B_TRUE; + } else { + reason = E_NOTREADY; + return B_FALSE; + } + // how powerful is this spell? power = getspellpower(lf, oid); if (power <= 0) { @@ -2050,6 +2060,75 @@ int celltransparentfor(lifeform_t *lf, cell_t *c, int *xray, int *rangemod) { return B_TRUE; } +int charmedaction(lifeform_t *lf, flag_t *charmflag) { + lifeform_t *charmer; + char charmername[BUFLEN]; + charmer = findlf(lf->cell->map, charmflag->val[0]); + if (!charmer) { + killflag(charmflag); + return B_TRUE; + } + getlfname(charmer, charmername); + + if (isadjacent(lf->cell, charmer->cell)) { + object_t *o; + enum OBTYPE oid[MAXPILEOBS]; + int oidcovet[MAXPILEOBS]; + int noids = 0; + enum FLAG wantflag[MAXPILEOBS]; + int wantflagcovet[MAXPILEOBS]; + int nwantflags = 0; + int ndone = 0; + // hands over wantob/weapon/armour + makewantedoblist(charmer, &noids, oid, oidcovet, &nwantflags, wantflag, wantflagcovet); + for (o = lf->pack->first ; o ; o = o->next) { + if (iscursed(o)) continue; + if (isweapon(o) || aiwants_real(charmer, o, NULL, &noids, oid, oidcovet, &nwantflags, wantflag, wantflagcovet)) { + if (isequipped(o)) { + takeoff(lf, o); + } else { + char obname[BUFLEN]; + getobname(o, obname, o->amt); + msg("You hand over your %s to %s.", obname, + cansee(lf, charmer) ? charmername : "your new master"); + moveob(o, charmer->pack, o->amt); + } + ndone++; + break; + } + } + // nothing they want? just remove armour/weapons. + if (!ndone) { + for (o = lf->pack->first ; o ; o = o->next) { + if (isequipped(o)) { + takeoff(lf, o); + ndone++; + break; + } + } + } + if (!ndone) { + msg("You bask in the glory of %s!", cansee(lf, charmer) ? charmername : "your new master"); + taketime(lf, getactspeed(lf)); + } + } else { + int dir; + // walks towards charmer, regardless of anything in the way + turntoface(lf, charmer->cell); + dir = getdirtowards(lf->cell, charmer->cell, lf, B_FALSE, DT_ORTH); + if (dir == D_NONE) { + msg("You try to %s towards %s, but fail.", getmoveverb(lf), + cansee(lf, charmer) ? charmername : "your new master"); + } else { + msg("You mindlessly %s towards %s.", getmoveverb(lf), + cansee(lf, charmer) ? charmername : "your new master"); + trymove(lf, dir, B_FALSE, B_TRUE); + } + taketime(lf, getactspeed(lf)); + } + return B_FALSE; +} + int checkburdened(lifeform_t *lf, int preburdened) { int postburdened; @@ -3101,7 +3180,7 @@ void die(lifeform_t *lf) { if (corpse) { char cid[BUFLEN]; snprintf(cid, BUFLEN, "%ld",corpse->id); - addflag(lf->flags, F_MYCORPSE, NA, NA, NA, cid); + addflag(lf->flags, F_LIFEOB, NA, 5, 2, cid); } } else { // Will the dead lf get reanimated up as a monster ? @@ -3265,14 +3344,22 @@ void dumplf(void) { void dumpmonsters(void) { race_t *r; flag_t *f; - int count = 0,wanthd; + int wanthd; + int totcount = 0; dblog("START MONSTER DUMP:"); for (wanthd = 0; wanthd <= maxmonhitdice ; wanthd++) { - dblog("MONSTERS WITH THREAT RATING %d:",wanthd); + int count = 0; + // count them + for (r = firstrace ; r ; r = r->next) { + if (gettrrace(r) == wanthd) { + count++; + totcount++; + } + } + dblog("MONSTERS WITH THREAT RATING %d (%d found):",wanthd, count); for (r = firstrace ; r ; r = r->next) { int thishd; - thishd = gettrrace(r); if (thishd == wanthd) { int ndice,nsides,bonus,min,max; @@ -3282,11 +3369,10 @@ void dumpmonsters(void) { min = ndice + bonus; max = (ndice*nsides) + bonus; dblog("\t%s (%d hp)",r->name, max); - count++; } } } - dblog("END MONSTER DUMP (%d found)",count); + dblog("END MONSTER DUMP (%d found)",totcount); } void genareaknowledge(flagpile_t *fp, int chancemod) { @@ -5151,6 +5237,57 @@ lifeform_t *findlfunique(enum RACE rid) { return NULL; } +cell_t *findnearbylifeob(cell_t *src, flag_t *lifeobflag, object_t **retlifeob) { + object_t *corpse = NULL; + int maxdist; + maxdist = lifeobflag->val[1]; + + if (retlifeob) { + *retlifeob = NULL; + } + + if (strlen(lifeobflag->text)) { + long corpseid; + // find the corpse + corpseid = atol(lifeobflag->text); + corpse = findobidinmap(src->map, corpseid); + if (getcelldist(src, corpse->pile->where) > maxdist) { + corpse = NULL; + } + } else { + // find closest location with corpse... + cell_t *retcell[MAXCANDIDATES]; + int nretcells,i; + enum OBTYPE oid; + int mindist = 9999; + + oid = lifeobflag->val[0]; + getradiuscells(src, maxdist, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_FALSE, retcell, &nretcells, 0); + for (i = 0; i < nretcells; i++) { + object_t *o; + cell_t *c; + int thisdist; + c = retcell[i]; + o = hasob(c->obpile, oid); + if (o) { + thisdist = getcelldist(src, c); + if (thisdist < mindist) { + mindist = thisdist; + corpse = o; + } + } + } + } + + if (corpse) { + if (retlifeob) { + *retlifeob = corpse; + } + return corpse->pile->where; + } + return NULL; +} + poisontype_t *findpoisontype(enum POISONTYPE id) { poisontype_t *pt; for (pt = firstpoisontype; pt ; pt = pt->next) { @@ -11803,6 +11940,10 @@ int ischarmable(lifeform_t *lf) { reason = E_DRUNK; return B_FALSE; } + if (lfhasflag(lf, F_CHARMEDBY)) { + reason = E_ALREADYUSING; + return B_FALSE; + } if (hasflag(lf->flags, F_UNIQUE)) { reason = E_NOEFFECT; // generic error return B_FALSE; @@ -14287,10 +14428,10 @@ void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fr if (postlowhp && !prelowhp) { f = lfhasflag(lf, F_LOWHPABIL); if (f) { - flag_t *notime; - notime = addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL); + flag_t *ntm; + ntm = addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL); abilityeffects(lf, f->val[0], lf->cell, lf, NULL); - killflag(notime); + killflag(ntm); } } @@ -18370,25 +18511,25 @@ void startlfturn(lifeform_t *lf) { if (f) { int radius; radius = f->val[0]; - addobsinradius(lf->cell, f->val[0], DT_COMPASS, f->text, B_FALSE); + addobsinradius(lf->cell, f->val[0], DT_COMPASS, f->text, B_FALSE, lf); } - // handle ghosts - if (lf->race->id == R_GHOST) { - f = lfhasflag(lf, F_MYCORPSE); - if (f) { - long corpseid; - cell_t *corpseloc = NULL; - object_t *corpse; + // handle life objects + f = lfhasflag(lf, F_LIFEOB); + if (f) { + cell_t *corpseloc = NULL; + object_t *corpse; + char corpsename[BUFLEN]; - corpseid = atol(f->text); - - corpse = findobidinmap(lf->cell->map, corpseid); - if (corpse) { - corpseloc = corpse->pile->where; + corpseloc = findnearbylifeob(lf->cell, f, &corpse); + if (corpse) { + // did we find a corpse in range ? + if (corpse->type->id == OT_CORPSE) { + strcpy(corpsename, "corpse"); + } else { + strcpy(corpsename, corpse->type->name); } - // can we see our corpse? - if (corpseloc && haslos(lf, corpseloc)) { + if (lf->race->id == R_GHOST) { // give possession ability if (!lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL)) { char pwbuf[BUFLEN]; @@ -18400,14 +18541,22 @@ void startlfturn(lifeform_t *lf) { f = addflag(lf->flags, F_CANWILL, OT_S_POSSESSION, NA, NA, pwbuf); f->lifetime = FROMRACE; } - } else { - // can't see corpse + } + } else { + // can't see corpse + if (lf->race->id == R_GHOST) { f = lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL); if (f) killflag(f); - // drain life - if (isplayer(lf)) msg("^BWithout your corpse, you feel yourself fading into nothingness."); - losehp(lf, 2, DT_DIRECT, NULL, "fading into nothingness"); } + // drain life + if (isplayer(lf)) { + msg("^BWithout your %s, you feel yourself fading into nothingness.", corpsename); + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s seems to be fading from view!", lfname); + } + losehp(lf, f->val[2], DT_DIRECT, NULL, "fading into nothingness"); } } @@ -18843,7 +18992,7 @@ void startlfturn(lifeform_t *lf) { if (isdead(lf)) return; // effects for/on your own flags - getflags(lf->flags, retflag, &nretflags, F_ANTICIPATE, F_ATTACHEDTO, F_CANWILL, F_CHARMEDBY, F_CLIMBING, F_FEIGNFOOLEDBY,F_FLEEFROM, + getflags(lf->flags, retflag, &nretflags, F_ANTICIPATE, F_ATTACHEDTO, F_CANCAST, F_CANWILL, F_CHARMEDBY, F_CLIMBING, F_FEIGNFOOLEDBY,F_FLEEFROM, F_GRABBEDBY, F_GRABBING, F_HIDING, F_BOOSTSPELL, F_FEIGNINGDEATH, F_HPDRAIN, F_INJURY, F_NOFLEEFROM, F_PETOF, F_SIZETIMER, F_SPOTTED, F_STRIKETOKO, F_TARGETCELL, F_TARGETLF, F_NONE); for (i = 0; i < nretflags; i++) { @@ -18990,7 +19139,7 @@ void startlfturn(lifeform_t *lf) { } // recharge abilities - if (f->id == F_CANWILL) { + if ((f->id == F_CANWILL) || (f->id == F_CANCAST)) { if (f->val[2] != NA) { if (f->val[1] < f->val[2]) { f->val[1]++; @@ -19538,6 +19687,7 @@ void timeeffectslf(lifeform_t *lf) { if (isdead(lf)) { killflagsofid(lf->flags, F_NOTIME); + notime = B_FALSE; return; } diff --git a/lf.h b/lf.h index 10ef4e0..0a97092 100644 --- a/lf.h +++ b/lf.h @@ -67,6 +67,7 @@ int cantalk(lifeform_t *lf); int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *targob, cell_t *targcell, object_t *fromob, int *seen); int celllitfor(lifeform_t *lf, cell_t *c, int maxvisrange, int nightvisrange); int celltransparentfor(lifeform_t *lf, cell_t *c, int *xray, int *rangemod); +int charmedaction(lifeform_t *lf, flag_t *charmflag); int checkburdened(lifeform_t *lf, int preburdened); int checkfordrowning(lifeform_t *lf, object_t *o); int check_rest_ok(lifeform_t *lf); @@ -107,6 +108,7 @@ job_t *findjob(enum JOB jobid); job_t *findjobbyname(char *name); lifeform_t *findlf(map_t *m, int lfid); lifeform_t *findlfunique(enum RACE rid); +cell_t *findnearbylifeob(cell_t *src, flag_t *lifeobflag, object_t **retlifeob); poisontype_t *findpoisontype(enum POISONTYPE id); race_t *findrace(enum RACE id); race_t *findracebyname(char *name); diff --git a/map.c b/map.c index 449c404..f61f2c0 100644 --- a/map.c +++ b/map.c @@ -119,12 +119,23 @@ habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum return a; } -void addhomeobs(lifeform_t *lf) { +void addhomeobs(lifeform_t *lf, int dolevelobs) { flag_t *f; + cell_t *homeobloc; + homeobloc = lf->cell; for (f = lf->flags->first ; f ; f = f->next) { - if (f->id == F_HOMEOB) { - addob(lf->cell->obpile, f->text); - } else if (f->id == F_HOMELEVOB) { + if ((f->id == F_HOMEOB) && pctchance(f->val[0])) { + cell_t *c; + object_t *o; + o = addob(homeobloc->obpile, f->text); + if (o && (homeobloc == lf->cell) && isimpassableob(o, lf, SZ_ANY)) { + c = real_getrandomadjcell(lf->cell, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); + if (c) { + homeobloc = c; // future obs will go here too. + moveob(o, homeobloc->obpile, o->amt); + } + } + } else if ((f->id == F_HOMELEVOB) && dolevelobs) { int i,amt; amt = rnd(f->val[0],f->val[1]); for (i = 0; i < amt; i++) { @@ -3369,7 +3380,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex // add home objects if (db) dblog(" adding home objects."); for (lf = map->lf ; lf ; lf = lf->next) { - addhomeobs(lf); + addhomeobs(lf, B_TRUE); } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging @@ -4355,7 +4366,12 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { } } - assert((startdir >= D_N) && (startdir <= D_W)); + //assert((startdir >= D_N) && (startdir <= D_W)); + if ((startdir < D_N) || (startdir > D_W)) { + // couldn't find any valid start dirs! + if (db) dblog(" Cannot find a valid startdir."); + return B_TRUE; + } // figure out perpendicular dirs perpdir[0] = startdir - 1; if (perpdir[0] < D_N) perpdir[0] = D_W; @@ -5445,6 +5461,17 @@ void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, if (f) { autospells(lf, f->val[0]); } + + addhomeobs(lf, B_TRUE); + + /* + getflags(lf->flags, retflag, &nretflags, F_LIFEOB, F_NONE); + for (i = 0; i < nretflags; i++) { + if (retflag[i]->val[0] != NA) { + addobfast(lf->cell->obpile, retflag[i]->val[0]); + } + } + */ } celltype_t *findcelltype(enum CELLTYPE cid) { diff --git a/map.h b/map.h index 1d9be2c..ae7a2dc 100644 --- a/map.h +++ b/map.h @@ -2,7 +2,7 @@ cell_t *addcell(map_t *map, int x, int y); habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell, int thingchance, int obchance, int vaultchance, int maxvisrange, enum OBTYPE upstairtype, enum OBTYPE downstairtype); -void addhomeobs(lifeform_t *lf); +void addhomeobs(lifeform_t *lf, int dolevelobs); map_t *addmap(void); lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok, int amt, int autogen, int *nadded); object_t *addrandomob(cell_t *c); diff --git a/move.c b/move.c index d3f42c8..4736b0c 100644 --- a/move.c +++ b/move.c @@ -909,13 +909,15 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc } if (seen) msg("^%c%s slam%s into %s!^n",getlfcol(lf, CC_BAD), lfname,isplayer(lf) ? "" : "s", thing); snprintf(buf, BUFLEN, "slamming into %s", thing); - dam = roll("1d6"); + // 1d6 dam per cell pushed + dam = rolldie(howfar, 6); losehp(lf, dam, DT_BASH, pusher, buf); // stop moving i = howfar; // don't fall mightfall = B_FALSE; - if (onein(3)) criticalhit(NULL, lf, getrandomcorebp(lf, NULL), dam, DT_BASH); + // 20% chance per cell pushed + if (pctchance(howfar*20)) criticalhit(NULL, lf, getrandomcorebp(lf, NULL), dam, DT_BASH); break; case E_SWIMMING: case E_LFINWAY: @@ -1131,6 +1133,7 @@ int moveeffects(lifeform_t *lf, int moved) { // returns TRUE if something happened int movelf(lifeform_t *lf, cell_t *newcell) { object_t *o,*nexto; + cell_t *precell; char obname[BUFLEN],lfname[BUFLEN],buf[BUFLEN]; lifeform_t *l; int didmsg = B_FALSE; @@ -1215,6 +1218,8 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } + precell = lf->cell; + // move out... lf->cell->lf = NULL; @@ -1560,9 +1565,11 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } */ if (preseenbyplayer && !cansee(player, lf) && !changedlev) { - if (areenemies(player, lf)) { - real_getlfnamea(lf, lfname, B_FALSE, B_FALSE); - msg("%s moves out of view.", lfname); + if (isadjacent(lf->cell, precell)) { // ie don't say this if we teleported/jumped + if (areenemies(player, lf)) { + real_getlfnamea(lf, lfname, B_FALSE, B_FALSE); + msg("%s %ss out of view.", lfname, getmoveverb(lf)); + } } } @@ -2671,6 +2678,21 @@ int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) { if (isplayer(lf) && countnoncosmeticobs(lf->cell->obpile, B_TRUE, B_TRUE)) { dolook(lf->cell, B_FALSE); } + + // for enemies, if we teleported and now can't see the person(s) we were fleeing + // from, stop fleeing. + if (!isplayer(lf)) { + flag_t *retflag[MAXCANDIDATES]; + int nretflags,i; + getflags(lf->flags, retflag, &nretflags, F_FLEEFROM, F_NONE); + for (i = 0; i < nretflags; i++) { + lifeform_t *thisone; + thisone = findlf(lf->cell->map, retflag[i]->val[0]); + if (thisone && !haslof(lf->cell, thisone->cell, LOF_WALLSTOP, NULL)) { + killflag(retflag[i]); + } + } + } return B_FALSE; } @@ -3370,6 +3392,17 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { return B_FALSE; } + // dont move out of range of lifeobs, but attacking out of range lfs is ok + if (!cell->lf) { + f = lfhasflag(lf, F_LIFEOB); + if (f) { + if (!findnearbylifeob(cell, f, NULL)) { + if (error) *error = E_WONT; + return B_FALSE; + } + } + } + // glyph of warding? if (cell->writing && strstr(cell->writing, "*WARD")) { char buf[BUFLEN]; diff --git a/nexus.c b/nexus.c index 7acad4b..07073de 100644 --- a/nexus.c +++ b/nexus.c @@ -724,11 +724,22 @@ void donextturn(map_t *map) { // skip turn taketime(who, SPEED_DEAD); } else { + // do we need to run away from something? if (!flee(who)) { int donormalmove = B_TRUE; flag_t *f; + // charmed ? + if (donormalmove) { + f = lfhasflag(who, F_CHARMEDBY); + if (f) { + if (!charmedaction(who, f)) { + donormalmove = B_FALSE; + } + } + } + // casting a spell? if (donormalmove) { f = lfhasflag(who, F_CASTINGSPELL); diff --git a/objects.c b/objects.c index 683c2f2..c57d753 100644 --- a/objects.c +++ b/objects.c @@ -1026,6 +1026,23 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum } if (found) break; } + // cope with "random gem" + if (!found) { + if (strstarts(p, "random gem")) { + int minrarity,maxrarity; + // want a specific rarity? + rrtorarity(wantrarity, &minrarity, &maxrarity); + + ot = getrandomobofclass(OC_ROCK, minrarity, maxrarity, NULL); + if (ot) { + // make sure it's not just a rock. + while (!hasflag(ot->flags, F_GEM)) { + ot = getrandomobofclass(OC_ROCK, minrarity, maxrarity, NULL); + } + found = B_TRUE; + } + } + } if (!found) { // look up the object name @@ -2112,10 +2129,11 @@ void addomprefix(enum OBMOD id, char *altprefix) { om->naltprefix++; } -void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes) { +void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes, lifeform_t *creator) { cell_t *cell[MAXCANDIDATES],*c; int ncells,i; objecttype_t *ot; + object_t *o; ot = findotn(name); if (!ot) return; @@ -2125,7 +2143,10 @@ void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int all c = cell[i]; if (!c->type->solid) { if (allowdupes || !hasob(c->obpile, ot->id)) { - addob(c->obpile, name); + o = addob(c->obpile, name); + if (o && creator) { + setobcreatedby(o, creator); + } } } } @@ -6961,6 +6982,7 @@ int isidentified(object_t *o) { int isimpassableob(object_t *o, lifeform_t *lf, enum LFSIZE forcesize) { flag_t *f; + f = hasflag(o->flags, F_IMPASSABLE); if (f) { enum LFSIZE lfsize; @@ -6986,6 +7008,7 @@ int isimpassableob(object_t *o, lifeform_t *lf, enum LFSIZE forcesize) { return B_TRUE; } } + return B_FALSE; } @@ -11785,7 +11808,6 @@ int real_takedamage(object_t *o, int howmuch, int damtype, int wantannounce) { // initial effects based on damage type if (damtype == DT_FIRE) { - if ( ((o->type->id == OT_CANDLE) || (o->type->id == OT_TORCH) || (o->type->id == OT_CANDELABRUM)) && !isactivated(o)) { cell_t *c; @@ -12301,7 +12323,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); + addobsinradius(thrower->cell, radius, DT_COMPASS, "cloud of smoke", B_FALSE, thrower); } else if (o->type->id == OT_ASHEXPLODE) { char diebuf[BUFLEN]; // explosion! @@ -13097,9 +13119,8 @@ void timeeffectsob(object_t *o) { } } - // check each flag for this object... - getflags(o->flags, retflag, &nretflags, F_ACTIVATED, F_EDIBLE, F_EXPLODEONDEATH, F_MATCONVERT, F_HOT, F_OBHPDRAIN, F_ONFIRE, + getflags(o->flags, retflag, &nretflags, F_ACTIVATED, F_EDIBLE, F_EXPLODEONDEATH, F_MATCONVERT, F_HOT, F_KNOCKAWAY, F_OBHPDRAIN, F_ONFIRE, F_RECHARGE, F_REVIVETIMER, F_WALKDAM, F_WET, F_NONE); for (i = 0; i < nretflags; i++) { object_t *oo,*nextoo; @@ -13204,6 +13225,52 @@ void timeeffectsob(object_t *o) { } } + if ((f->id == F_KNOCKAWAY) && (o->pile->where)) { + lifeform_t *lf,*creator; + object_t *oo; + lf = o->pile->where->lf; + creator = getobcreatedby(o); + if (lf && (lf != creator) ) { + char damstring[BUFLEN]; + if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("^%dPowerful winds pummel %s!^n", getlfcol(lf, CC_BAD), lfname); + } + // lfs here take damage + if (creator) { + char cname[BUFLEN]; + getlfname(creator, cname); + // ie. "the djinn's whirlwind" + sprintf(damstring, "%s%s %s", cname, getpossessive(cname), noprefix(obname)); + } else { + // ie. "a whirlwind" + sprintf(damstring, "%s", obname); + } + losehp(lf, roll(f->text), DT_PROJECTILE, creator, damstring); + // lfs then get knocked away (and might take further damage from hitting something) + knockback(lf, getrandomdir(DT_COMPASS), f->val[0], creator, f->val[1], B_TRUE); + } + // objects get knocked away + for (oo = o->pile->where->obpile->first ; oo ; oo = oo->next) { + if (oo == o) continue; + // note: f_nopickup obs will still get moved! + if (hasflag(oo->flags, F_COSMETIC) || hasflag(oo->flags, F_CLIMBABLE) || + (hasflag(oo->flags, F_TRAIL)) || + (hasflag(oo->flags, F_KNOCKAWAY)) || // don't affect other wind knockaway objects + (oo->type->obclass->id == OC_BUILDING)) { + } else { + cell_t *retcell[MAXRETCELLS],*c; + int nretcells; + getradiuscells(oo->pile->where, f->val[0], DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0); + if (nretcells) { + c = retcell[rnd(0,nretcells-1)]; + fireat(NULL, oo, oo->amt, c, f->val[2], NULL); + } + } + } + } + if (f->id == F_OBHPDRAIN) { enum DAMTYPE damtype; int doit = B_TRUE; @@ -13426,6 +13493,71 @@ void timeeffectsob(object_t *o) { } } // end for each object flag + + + // do this one after others. + if (o->pile->where) { + f = hasflag(o->flags, F_OBMOVESRANDOMLY); + if (f) { + cell_t *newcell,*c; + object_t *adjob[MAXPILEOBS*8]; + int nadjobs = 0; + int possdir[8]; + int npossdir = 0; + int dir; + + // before moving, get a list of other adjacent objects of the same type + for (dir = DC_N; dir <= DC_NW; dir++) { + c = getcellindir(o->pile->where, dir); + if (c) { + object_t *oo; + for (oo = c->obpile->first ; oo ; oo = oo->next) { + if (oo->type->id == o->type->id) { + adjob[nadjobs++] = oo; + } + } + } + } + + // now pick a direction to move. + for (dir = DC_N; dir <= DC_NW; dir++) { + c = getcellindir(o->pile->where, dir); + if (c) { + int ok = B_TRUE; + if (c->type->solid && (f->val[0] != B_TRUE)) { + ok = B_FALSE; + } + if (ok) { + possdir[npossdir++] = dir; + } + } + } + + if (npossdir) { + int i; + dir = possdir[rnd(0,npossdir-1)]; + newcell = getcellindir(o->pile->where, dir); + if (newcell->type->solid) { + // this should always kill it! + damagecell(newcell, newcell->hp, DT_DIRECT); + } + moveob(o, newcell->obpile, o->amt); + + // now move other adjacent objects in the same direction (if we can). + for (i = 0; i < nadjobs; i++) { + c = getcellindir(adjob[i]->pile->where, dir); + if (c) { + if (c->type->solid) { + // this should always kill it! + damagecell(c, c->hp, DT_DIRECT); + } + moveob(adjob[i], c->obpile, adjob[i]->amt); + } + } + } + } + } + checkflagpile(o->flags); } @@ -13983,6 +14115,19 @@ int validateobs(void) { } } + f = hasflag(ot->flags, F_HOMEOB); + if (f && (f->val[0] == NA)) { + printf("ERROR in object '%s' - has F_HOMEOB but missing pctchance in v0.\n", ot->name); + goterror = B_TRUE; + } + f = hasflag(ot->flags, F_HOMELEVOB); + if (f) { + if ((f->val[0] == NA) || (f->val[1] == NA)) { + printf("ERROR in object '%s' - has F_HOMELEVOB but missing count in v0/v1.\n", ot->name); + goterror = B_TRUE; + } + } + f = hasflag(ot->flags, F_TECHLEVEL); if (f && (f->val[0] != PR_INEPT)) { if (!hasflag(ot->flags, F_HASHIDDENNAME)) { diff --git a/objects.h b/objects.h index dbb6f41..5532696 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); +void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes, lifeform_t *creator); 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); diff --git a/spell.c b/spell.c index af237fb..1931b78 100644 --- a/spell.c +++ b/spell.c @@ -4525,12 +4525,27 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } getlfname(target, targetname); - // only animals/humanoids + // if the target is of a different raceclass, you usually + // can't charm them. if (getraceclass(target) != getraceclass(caster)) { - if (isplayer(caster)) { - msg("%s%s mind is too alien for you to charm.",targetname,getpossessive(targetname)); + int willfail = B_FALSE; + switch (getraceclass(target)) { + case RC_DEMON: + case RC_DRAGON: + case RC_GOD: + case RC_SLIME: + case RC_MAGIC: + case RC_PLANT: + willfail = B_TRUE; + break; + default: break; + } + if (willfail) { + if (isplayer(caster)) { + msg("%s%s mind is too alien for you to charm.",targetname,getpossessive(targetname)); + } + return B_FALSE; } - return B_FALSE; } if (!ischarmable(target)) { @@ -4545,6 +4560,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ case E_UNDEAD: msg("The undead are immune to charming."); break; + case E_ALREADYUSING: + msg("%s is already charmed by another!", targetname); + break; default: msg("You cannot charm %s.", targetname); break; @@ -4575,11 +4593,15 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } - howlong = getspellduration(20,30,blessed) + (power*10); + howlong = getspellduration(2,5,blessed) + power; - // - addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong); - addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong); + if (isplayer(target)) { + addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong); + } else { + // becaomes allied to caster + addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong); + addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong); + } } } else if (spellid == OT_S_CHARMANIMAL) { char targetname[BUFLEN]; @@ -5244,10 +5266,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } - addobsinradius(c, 1, DT_COMPASS, "5-10 stones", B_TRUE); + addobsinradius(c, 1, DT_COMPASS, "5-10 stones", B_TRUE, 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); + addobsinradius(c, 1, DT_COMPASS, "pool of blood", B_TRUE, NULL); addob(c->obpile, "pool of blood"); } @@ -5453,6 +5475,20 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f = addtempflag(caster->flags, F_REFLECTION, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (spellid == OT_S_ETHEREALSTEED) { + int howlong; + howlong = power*5; + addtempflag(caster->flags, F_AUTOCREATEOB, 0, NA, NA, "whirlwind", howlong); + if (isplayer(caster)) { + msg("A spinning whirlwind lifts you off the ground!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, caster)) { + msg("A spinning whirlwind lifts %s off the ground!", castername); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + addtempflag(caster->flags, F_LEVITATING, B_TRUE, NA, NA, NULL, howlong); + addtempflag(caster->flags, F_DTIMMUNE, DT_PROJECTILE, NA, NA, NULL, howlong); + } else if (spellid == OT_S_EVAPORATE) { cell_t *cell[MAXCANDIDATES]; int i,ncells; @@ -5729,7 +5765,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } losehp(target, roll("2d6"), DT_DIRECT, caster, "terrifying nightmares"); } else { - scare(target, caster, 5+rnd(1,power), 5+power); + scare(target, caster, 5+rnd(1,power), 6+power); } } else if (spellid == OT_S_FEEBLEMIND) { target = targcell->lf; @@ -6548,7 +6584,7 @@ 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); + addobsinradius(targcell, radius, DT_ORTH, "hail storm", B_FALSE, caster); // replace damage per sec with power d4 asprintf(&dambuf, "%dd4", power); @@ -7656,6 +7692,85 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); return B_TRUE; } + } else if (spellid == OT_S_PLANTWALK) { + int x,y; + cell_t *poss[MAX_MAPW*MAX_MAPH]; + object_t *srcob; + int nposs = 0; + srcob = hasobofclass(targcell->obpile, OC_FLORA); + if (!srcob) { + fizzle(caster); + return B_TRUE; + } + + // after this point the spell always counts as successful + // (ie. costs mana) + if (isplayer(caster)) { + char obname[BUFLEN]; + getobname(srcob, obname, 1); + msg("You touch %s.", obname); + } else if (cansee(player, caster)) { + char obname[BUFLEN]; + getobname(srcob, obname, 1); + msg("%s touches %s.", castername, obname); + } + + for (y = 0; y < caster->cell->map->h; y++) { + for (x = 0; x < caster->cell->map->w; x++) { + cell_t *c; + c = getcellat(caster->cell->map, x, y); + if (c && (c != targcell)) { + if ((power == 1) && hasob(c->obpile, srcob->type->id)) { + poss[nposs++] = c; + } else if ((power == 2) && hasobofclass(c->obpile, OC_FLORA)) { + poss[nposs++] = c; + } + } + } + } + + // no possible destinations? + if (!nposs) { + if (isplayer(caster) || cansee(player, caster)) { + nothinghappens(); + } + return B_FALSE; + } + + if (isplayer(caster)) { + int i; + // reveal all potential locations + for (i = 0; i < nposs; i++) { + setcellknown(poss[i], PR_EXPERT); + } + // then ask to select one + targcell = real_askcoords("Travel where?", "Plantwalk->", TT_SPECIFIED, player, UNLIMITED, LOF_DONTNEED, B_FALSE, poss, nposs); + if (!hasobofclass(targcell->obpile, OC_FLORA)) { + targcell = NULL; + } + } else { + targcell = poss[rnd(0,nposs-1)]; + } + + if (!targcell) { + fizzle(caster); + // purposely returning false here, since we've already revealed + // some information to the caster. + return B_FALSE; + } + + if (!cellwalkable(caster, targcell, NULL)) { + targcell = real_getrandomadjcell(targcell, WE_WALKABLE, B_NOEXPAND, LOF_DONTNEED, NULL, NULL); + if (!targcell) { + fizzle(caster); + return B_FALSE; + } + } + + // teleport to destination. + msg("orig cell: %d,%d.", caster->cell->x, caster->cell->y); + teleportto(caster, targcell, B_FALSE); + msg("new cell: %d,%d.", caster->cell->x, caster->cell->y); } else if (spellid == OT_S_PARALYZE) { int howlong; int saved = B_FALSE; @@ -8649,7 +8764,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ for (o = op->first ; o ; o = nexto) { nexto = o->next; - if (!hasflag(o->flags, F_NOSTEAL)) { + if (!hasflag(o->flags, F_NOSTEAL) && !hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) { if ((getobunitweight(o) <= 5) || ((isweapon(o)) && (isequipped(o))) ) { poss[nposs++] = o; @@ -9702,7 +9817,7 @@ 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); + addobsinradius(targcell, radius, DT_ORTH, "storm of sleet", B_FALSE, caster); } else { failed = B_TRUE; } @@ -10846,29 +10961,144 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ stopspell(caster, spellid); return B_TRUE; } + } else if (spellid == OT_S_WALLOFFIRE) { + object_t *o; + cell_t *c; + int vdist = 0,hdist = 0; + int vbad = B_FALSE, hbad = B_FALSE; + int walldir; + int seen = B_FALSE; + char firename[BUFLEN]; + + if (power < 5) { + sprintf(firename, "small fire"); + } else if (power < 8) { + sprintf(firename, "medium fire"); + } else { + sprintf(firename, "large fire"); + } + + // get vert distance + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) { + vdist++; + if (c->lf && (c->lf == caster)) { + vbad = B_TRUE; break; + } + } + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) { + vdist++; + if (c->lf && (c->lf == caster)) { + vbad = B_TRUE; break; + } + } + // get horz dist + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) { + hdist++; + if (c->lf && (c->lf == caster)) { + hbad = B_TRUE; break; + } + } + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) { + hdist++; + if (c->lf && (c->lf == caster)) { + hbad = B_TRUE; break; + } + } + + if (vbad) vdist = 0; + if (hbad) hdist = 0; + + if ((vdist == 0) && (hdist == 0)) { + fizzle(caster); + return B_TRUE; + } + + // select direction + if (vdist < hdist) { + walldir = D_N; + } else if (hdist < vdist) { + walldir = D_E; + } else { + if (onein(2)) { + walldir = D_N; + } else { + walldir = D_E; + } + } + + if (walldir == D_N) { + // vert wall + for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_N)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, firename); + } + for (c = getcellindir(targcell, D_S) ; + (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); + c = getcellindir(c, D_S)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, firename); + } + } else { + // horz wall + for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_E)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, firename); + } + for (c = getcellindir(targcell, D_W) ; + (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); + c = getcellindir(c, D_W)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, firename); + } + } + + if (seen) { + drawscreen(); + msg("A wall of fire appears!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + if (isplayer(caster)) { + angergodmaybe(R_GODNATURE, 25, GA_HERESY); + } } else if (spellid == OT_S_WALLOFICE) { object_t *o; int donesomething = B_FALSE; cell_t *c; int vdist = 0,hdist = 0; + int vbad = B_FALSE, hbad = B_FALSE; int walldir; int seen = B_FALSE; flag_t *f; // get vert distance for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) { vdist++; + if (c->lf && (c->lf == caster)) { + vbad = B_TRUE; break; + } } for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) { vdist++; + if (c->lf && (c->lf == caster)) { + vbad = B_TRUE; break; + } } // get horz dist for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) { hdist++; + if (c->lf && (c->lf == caster)) { + hbad = B_TRUE; break; + } } for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) { hdist++; + if (c->lf && (c->lf == caster)) { + hbad = B_TRUE; break; + } } + if (vbad) vdist = 0; + if (hbad) hdist = 0; + if ((vdist == 0) && (hdist == 0)) { fizzle(caster); return B_TRUE; @@ -11161,6 +11391,93 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("A positive gravity field forms around %s!", castername); } if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if ((spellid == OT_S_WHIRLWIND) || (spellid == OT_S_TORNADO) || (spellid == OT_S_HURRICANE)) { + int failed = B_FALSE; + char obname[BUFLEN],obdesc[BUFLEN]; + switch (spellid) { + case OT_S_HURRICANE: + strcpy(obname, "hurricane"); + strcpy(obdesc, "raging hurricane"); + break; + case OT_S_TORNADO: + strcpy(obname, "tornado"); + strcpy(obdesc, "towering tornado"); + break; + default: + case OT_S_WHIRLWIND: + strcpy(obname, "whirlwind"); + strcpy(obdesc, "spinning whirlwind"); + break; + } + if (targcell) { + cell_t *c[5]; + int ncells = 0,i,ndone = 0,doneannounce = B_FALSE; + if (spellid == OT_S_HURRICANE) { + c[0] = targcell; + c[1] = getcellindir(targcell, DC_N); + c[2] = getcellindir(targcell, DC_E); + c[3] = getcellindir(targcell, DC_S); + c[4] = getcellindir(targcell, DC_W); + ncells = 5; + } else { + c[0] = targcell; + ncells = 1; + } + for (i = 0; i < ncells; i++) { + if (c[i]) { + int ok = B_TRUE; + if (c[i]->type->solid) { + if (spellid == OT_S_HURRICANE) { + // this should always kill it! + damagecell(c[i], c[i]->hp, DT_DIRECT); + } else { + ok = B_FALSE; + } + } + if (ok) { + flag_t *f; + object_t *o; + // create flame there + if (haslos(player, c[i])) { + if (!doneannounce) { + msg("A %s appears!", obdesc); + doneannounce = B_TRUE; + } + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + o = addob(c[i]->obpile, obname); + // magically boost hp based on power + f = hasflag(o->flags, F_OBHP); + if (f) { + f->val[0] += (power*6); + // centre hurricane lasts a bit longer + if (i == 0) { + f->val[0] += 5; + } + f->val[1] = f->val[0]; + } + setobcreatedby(o, caster); + // for non-centre hurricanes, remove randommove flag + // (they will move with the original) + if (i != 0) { + killflagsofid(o->flags, F_OBMOVESRANDOMLY); + } + ndone++; + } + } + } + + if (!ndone) { + failed = B_TRUE; + } + } else { + failed = B_TRUE; + } + + if (failed) { + fizzle(caster); + return B_TRUE; + } } else if (spellid == OT_S_WINDSHIELD) { flag_t *f; // always targetted at caster