- [+] bug: notime being set in timeeffectslf() but NOT being UNSET!!!

- [+] air spells:
    - [+] create whirlwind - make a single whirlwind
        - [+] implement
    - [+] ethereal steed - move very fast and levitate
        - [+] f_autocreateob whirlwind behind you
        - [+] whirlwind object throws any lfs/objects around randomly
    - [+] tornado - creates a single tornado at a given location (more
          powerful). it moves around randomly, kills adjacent walls.
        - [+] F_OBMOVESRANDOMLY
        - [+] find an adjacent cell (walls ok)
        - [+] kill any walls there
        - [+] move there
    - [+] hurricane -  creates a cross cloud of whirlwinds (very
          powerful!). these move around randomly and kills adjacent
          walls.
        - [+] implement
        - [+] make them all move TOGETHER.
- [+] djinni / genie
    - [+] invisibility
    - [+] gaseous form
    - [+] illusion? mirror image ?
    - [+] permenant ethereal steed effects
    - [+] airblast
    - [+] gust of wind
- [+] efreeti
    - [+] wall of fire (new spell)
    - [+] gas form
    - [+] enlarge
    - [+] flame pillar
- [+] when summoning a monster with a lifeob, place it under them?
- [+] druid should learn canwill plantwalk at some point... level 7
- [+] allow f_cancast to have a timer too, like canwill. (so that
      monsters only cast certain spells sometimes)
- [+] is ai plantwalk working?
    - [+] seems to...
    - [+] but then i get "something casts a spell at you!"
    - [+] aigetspelltarget is setting targlf to player. FIXED.
    - [+] ai keeps on casting plantwalk after doing it once.
    - [+] ai needs to sto fleeing after casting plantwalk successfully!
        - [+] this is because after stealing, we are fleeing for a time
              limit, rather than PERMENANT
    - [+] once this is fixed, add spellcasttext plantwalk = null for
          dryad.
- [+] NEw code:  if you teleport, and you were fleeing, and you can no
      longer have LOF to the one uou're fleeing from, STOP.
- [+] The dryad vanishes!  A dryad moves out of view.--More--
    - [+] don't say both!
- [+] change CHARMEDBY code for player:
    - [+] walk towards charmer, then give them your stuff!
- [+] dryad brown 'T' (tree creature)
    - [+] knife
    - [+] must stay near oak tree
    - [+] can "plantwalk" between oak trees (or other plants).
        - [+] cast spell at cell with plant, warps you to a random one
            - [+] for player:  reveal all cells with trees, then ask
                  you which one.
        - [+] range 1
        - [+] maxpower 1
        - [+] ai casting:  must be a plant in range.
    - [+] emergency code: if not within lifeob range, move back!
    - [+] can cast sleep
    - [+] wants gold/gems/weapons
    - [+] home oak tree contains gems
    - [+] steal
    - [+] stayinroom
    - [+] charm 
    - [+] AI shouldn't ever walk away from life ob!
    - [+] ai homeob gems aren't appearing
This commit is contained in:
Rob Pearce 2012-03-07 11:46:55 +00:00
parent 899c185870
commit ea5d012876
17 changed files with 1111 additions and 112 deletions

66
ai.c
View File

@ -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)) {

219
data.c
View File

@ -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, "");

33
defs.h
View File

@ -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

View File

@ -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

1
flag.c
View File

@ -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:

2
god.c
View File

@ -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);

75
io.c
View File

@ -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;

1
io.h
View File

@ -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, ... );

220
lf.c
View File

@ -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;
}

2
lf.h
View File

@ -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);

39
map.c
View File

@ -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) {

2
map.h
View File

@ -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);

43
move.c
View File

@ -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];

11
nexus.c
View File

@ -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);

157
objects.c
View File

@ -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)) {

View File

@ -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);

345
spell.c
View File

@ -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