diff --git a/ai.c b/ai.c index b5fc136..9384243 100644 --- a/ai.c +++ b/ai.c @@ -1763,7 +1763,7 @@ int ai_inventory_mgt(lifeform_t *lf, int *canattack) { // is it red hot? if (curarm && hasflag(curarm->flags, F_HOT) && !isimmuneto(lf->flags, DT_FIRE, B_FALSE)) { if (db) dblog("%s o O { wearing a red-hot item. will try to remove it. } ", lf->race->name); - if (cantakeoff(lf, curarm)) { + if (cantakeoff(lf, curarm, NULL)) { if (!takeoff(lf, curarm)) { return B_TRUE; } @@ -2606,6 +2606,11 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG return B_FALSE; } + if (lfhasflag(lf, F_SILENCED)) { + if (db) dblog(".oO { can't cast spells, i am silenced }"); + return B_FALSE; + } + ot = findot(spellid); if (ot) { flag_t *f; @@ -3151,6 +3156,9 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG if ((ot->id == OT_S_SMITEEVIL) && (getalignment(victim) != AL_EVIL)) { specificcheckok = B_FALSE; } + if ((ot->id == OT_A_SONICBOLT) && lfhasflag(lf, F_SILENCED)) { + specificcheckok = B_FALSE; + } if (ot->id == OT_S_SPIKEVOLLEY) { if ((lf->race->id == R_MANTICORE) && lfhasflagval(lf, F_INJURY, IJ_TAILBROKEN, NA, NA, NULL)) { specificcheckok = B_FALSE; @@ -3219,6 +3227,9 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG specificcheckok = B_FALSE; } } + if ((ot->id == OT_A_WARCRY) && lfhasflag(lf, F_SILENCED)) { + specificcheckok = B_FALSE; + } if ((ot->id == OT_S_WARPWOOD)) { specificcheckok = B_FALSE; @@ -3661,7 +3672,7 @@ int useitemwithflag(lifeform_t *lf, enum FLAG whichflag) { quaff(lf, o); return B_FALSE; } - } else if (o->type->obclass->id == OC_SCROLL) { + } else if ((o->type->obclass->id == OC_SCROLL) && !lfhasflag(lf, F_SILENCED)) { if (!readsomething(lf, o)) { return B_FALSE; } diff --git a/attack.c b/attack.c index 5bd75dd..d4d9e0e 100644 --- a/attack.c +++ b/attack.c @@ -41,8 +41,8 @@ int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damty // special case - missiles always hit flak jacket if (damtype == DT_PROJECTILE) { object_t *o; - o = getequippedob(lf->pack, BP_BODY); - if (o && (o->type->id == OT_FLAKJACKET)) { + o = hasequippedobid(lf->pack, OT_FLAKJACKET); + if (o) { armour = o; } } @@ -1056,7 +1056,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) if (critpos == BP_NONE) { strcpy(victimbpname, victimname); } else { - armour = getequippedob(victim->pack, critpos); + armour = getouterequippedob(victim, critpos); if (armour) { char armname[BUFLEN]; real_getobname(armour, armname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); @@ -1152,7 +1152,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) dam[0] = getdamroll(wep, victim, damflag); if (isunarmed) { object_t *gloves; - gloves = getequippedob(lf->pack, BP_HANDS); + gloves = getouterequippedob(lf, BP_HANDS); if (gloves && hasflag(gloves->flags, F_HARDNESS)) { dam[0]++; } @@ -1318,12 +1318,13 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) // armour doesn't reduce damage for backstabs or critical hits. // BUT in the case of a critical hit, the armour might get // damaged during the call to criticalhit() later on. - if ((dam[i] > 0) && !backstab && !critical && ismeleedam(damtype[i])) { - // modify for defender's armour - // first figure out how much to reduce the damage by. - damreducedbyarmour = getarmourdamreduction(victim, wep, dam[i], damtype[i]); - // now actually reduce the damage amount - applyarmourdamreduction(victim, wep, damreducedbyarmour, &dam[i], damtype[i]); + + // + // normally armour would be handled by losehp() & losehpeffects(), but + // in this case we need to know whether the armour was hit beforehand, + // in order to construct the "you hit xxx" string. + if ((dam[i] > 0) && !backstab && !critical) { + damreducedbyarmour = handlearmour(victim, wep, &dam[i], damtype[i]); } // if damage has been reduced zero, it's not a critical hit anymore. @@ -1436,7 +1437,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) damreducedbyarmour = 0; } else if (lfhasflag(lf, F_QUIVERINGPALM)) { // victim explodes! - losehp_real(victim, victim->hp, DT_EXPLOSIVE, lf, "a quivering palm strike", B_FALSE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE); + losehp_real(victim, victim->hp, DT_EXPLOSIVE, lf, "a quivering palm strike", B_FALSE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE, B_NOCRIT); damreducedbyarmour = 0; } else { // actually deal the melee damage! @@ -1478,7 +1479,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) // don't adjust damage for resistences - we've already done that losehp_real(victim, dam[i], damtype[i], lf, buf, B_NODAMADJUST, wep, B_NORETALIATE, - &waskod, B_NODAMEFFECTS, critpos); + &waskod, B_NODAMEFFECTS, critpos, critical); } // was it fatal ? override previously calculated value. if ((victim->hp <= 0) && !waskod) { @@ -1556,41 +1557,8 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } } - - // now magic armour will pulse and maybe vanish. - if (magicarm) { - int damprevented; - // prevent all damage if possible - damprevented = dam[i]; - magicarm->val[0] -= damprevented; - if (magicarm->val[0] <= 0) { - // did magic armour come from a spell? - if (magicarm->lifetime == FROMSPELL) { - flag_t *spellflag; - spellflag = hasactivespell(victim, magicarm->obfrom); - if (spellflag) { - killflag(spellflag); - } - } - // magic armour vanishes now. - killflag(magicarm); - } else { - if (cansee(player, victim)) { - msg("^%d%s%s %s pulses!^n", - CC_GOOD, - victimname, getpossessive(victimname), - magicarm->text); - } - } - } else { - // victim's armour loses hp. note that it loses the full - // amount of damage dealt, not just what it reduced. - if (damreducedbyarmour && !critical) { - applyarmourdamage(victim, wep, dam[i] + damreducedbyarmour, damtype[i], lf); - // train armour - practice(victim, SK_ARMOUR, 1); - } - } + + /// ... used to apply armour damage here... // make noise // UNLESS this fighting involved the player. @@ -1606,8 +1574,8 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } // now handle the extra hp loss effects which we postponed above. - losehpeffects(victim, dam[i], damtype[i], lf, wep, B_NORETALIATE, waskod, &waskod, prebleed, - BP_NONE); + losehpeffects(victim, dam[i], damtype[i], lf, wep, B_NORETALIATE, waskod, + &waskod, prebleed, BP_NONE, damreducedbyarmour, critical); if (fatal || waskod || dodged || stopnow) break; // stop now, don't process further damtypes! } // end foreach damtype @@ -1722,7 +1690,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) attackername); } snprintf(damstring, BUFLEN, "%s%s %s", victimname, getpossessive(victimname), noprefix(obname)); - losehp_real(lf, rdam, f->val[0], victim, damstring, B_TRUE, NULL, B_TRUE, NULL, B_TRUE, critpos); + losehp_real(lf, rdam, f->val[0], victim, damstring, B_TRUE, NULL, B_TRUE, NULL, B_TRUE, critpos, critical); free(loctext); } } @@ -1975,7 +1943,7 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { if ((i == 0) && (wep->type->id == OT_FISTS) && hasflag(o->flags, F_HARDNESS)) { object_t *gloves; - gloves = getequippedob(lf->pack, BP_HANDS); + gloves = getouterequippedob(lf, BP_HANDS); if (gloves && hasflag(gloves->flags, F_HARDNESS)) { // ok } else if ((o->material->id == MT_WOOD) && (getskill(lf, SK_UNARMED) >= PR_ADEPT)) { @@ -2174,7 +2142,7 @@ int attackwall(lifeform_t *lf, cell_t *c, object_t *wep, flag_t *damflag) { if ((i == 0) && (wep->type->id == OT_FISTS) && hasflag(c->type->material->flags, F_HARDNESS)) { object_t *gloves; - gloves = getequippedob(lf->pack, BP_HANDS); + gloves = getouterequippedob(lf, BP_HANDS); if (gloves && hasflag(gloves->flags, F_HARDNESS)) { // ok } else if ((c->type->material->id == MT_WOOD) && (getskill(lf, SK_UNARMED) >= PR_ADEPT)) { @@ -2563,12 +2531,10 @@ int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE d int ar,min,max; //int pctrange; object_t *o; - flag_t *pierce = NULL; - - if (hasflag(wep->flags, F_ARMOURIGNORE)) { - reduceamt = 0; - } + if (wep && hasflag(wep->flags, F_ARMOURIGNORE)) { + return 0; + } ar = getarmourrating(lf, NULL, NULL, NULL, NULL); // between 25% and 75% of AR. @@ -2582,22 +2548,25 @@ int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE d // special case if (damtype == DT_PROJECTILE) { - o = getequippedob(lf->pack, BP_BODY); - if (o && (o->type->id == OT_FLAKJACKET)) { + o = hasequippedobid(lf->pack, OT_FLAKJACKET); + if (o) { // stop ALL missile damage reduceamt = dam; } } - // and if weapon is armour piercing, you always take at least its - // armourpiercing valut. - pierce = hasflag(wep->flags, F_ARMOURPIERCE); - if (pierce) { - if (pierce->val[0] < 0) reduceamt = 0; - else reduceamt -= pierce->val[0]; + if (wep && reduceamt) { + flag_t *pierce = NULL; + // and if weapon is armour piercing, you always take at least its + // armourpiercing valut. + pierce = hasflag(wep->flags, F_ARMOURPIERCE); + if (pierce) { + if (pierce->val[0] < 0) reduceamt = 0; + else reduceamt -= pierce->val[0]; + } } + limit(&reduceamt, 0, dam); - if (reduceamt < 0) reduceamt = 0; return reduceamt; } @@ -2934,6 +2903,26 @@ int getstrdammod(lifeform_t *lf) { } +int armourcanstopdam(enum DAMTYPE damtype) { + switch (damtype) { + case DT_PIERCE: + case DT_SLASH: + case DT_BASH: + case DT_BITE: + case DT_CHOP: + case DT_PROJECTILE: + case DT_UNARMED: + case DT_ACID: + case DT_EXPLOSIVE: + case DT_MAGIC: + case DT_CRUSH: + return B_TRUE; + default: + break; + } + return B_FALSE; +} + // ie. caused by hitting something with a melee weapon int ismeleedam(enum DAMTYPE damtype) { switch (damtype) { diff --git a/attack.h b/attack.h index 3c0e81b..c630345 100644 --- a/attack.h +++ b/attack.h @@ -26,6 +26,7 @@ int getdamroll(object_t *o, lifeform_t *victim, flag_t *damflag); //int getunarmeddamroll(flag_t *f); int getstrdammod(lifeform_t *lf); //obpile_t *getunarmedweapon(lifeform_t *lf, flag_t **uflag); +int armourcanstopdam(enum DAMTYPE damtype); int iskineticdam(enum DAMTYPE damtype); int ismeleedam(enum DAMTYPE damtype); int isphysicaldam(enum DAMTYPE damtype); diff --git a/data.c b/data.c index 1361512..1998b45 100644 --- a/data.c +++ b/data.c @@ -4365,6 +4365,13 @@ void initobjects(void) { addflag(lastot->flags, F_VARPOWER, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); + addot(OT_S_SILENCE, "silence", "Prevents vibration of air molecules around the target's mouth, preventing them from uttering a sound.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the silence 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, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addot(OT_S_SLOW, "slowness", "Causes the air around the target to thicken to a water-like consistency, greatly decreasing their speed.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the slowness will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); @@ -5162,6 +5169,9 @@ void initobjects(void) { addflag(lastot->flags, F_RANGE, 3, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); addot(OT_S_STUN, "stun", "Stuns the target, preventing them from taking agressive action for a few seconds.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power I-IV, target is stunned for 2 turns."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power V-IX, target is stunned for 3 turns."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power X, target is stunned for 4 turns."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); @@ -5233,6 +5243,12 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_SLOWMISSILES, "dampen missiles", "Creates a weak wall of outward psionic force around you, slowing down incoming projectiles and making them easier to dodge.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, 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_ONGOING, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); // l3 addot(OT_S_ANTICIPATE, "anticipate action", "Allows the caster to automatically dodge the target's attacks.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power detemines the number of attacks dodged."); @@ -5267,7 +5283,7 @@ void initobjects(void) { addot(OT_S_BAFFLE, "baffle", "Confuses the target, causing them to lose control of their movement.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The target will be confused for ^bpower^n*4 turns."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); - addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -5305,20 +5321,51 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); + addot(OT_S_SOULLINK, "soul link", "Creates a psychic bond between two creatures. As long as the bond holds, damage dealt to either creature will be felt by the other.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power I, you can only link creatures to yourself."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power III, you can form a link between two other creatures."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 3, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_STASIS, "stasis", "Freezes time inside your body, preventing many effects such as poison, hunger, and even breathing.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Note that health regeneration is also prevented while in stasis."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - // l5 addot(OT_S_CHARM, "charm", "Causes another lifeform to temporary become friendly.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability and duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); - addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); 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_DONTNEED, NA, NULL); + addot(OT_S_MINDSHIELD, "mind shield", "Erects an impenetrable mental shield around the caster's mind, rendering them immune to all psionic abilities (but also unable to use them).", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); + // l5 + addot(OT_S_DELAYDEATH, "delay death", "This power will allow its user to defy death itself, remaining alive even after their HP drops to zero.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "WARNING: losing concentration before damage is healed will result in instant death."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); + // l6 + addot(OT_S_REMOTEKO, "remote ko", "Disable the conscious part of a creature's mind, instantly knocking it unconscious.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, 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_RANGE, 3, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); /////////////////// // summoning /////////////////// @@ -7483,7 +7530,7 @@ void initobjects(void) { addflag(lastot->flags, F_SHRINKSTO, OT_STICK, VT_OB, NA, NULL); addot(OT_BARRICADE, "barricade", "A short barricade constructed of metal. Looks like you might able to climb over it.", MT_METAL, 200, OC_DFEATURE, SZ_HUMAN); - addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_RARE, NULL); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); addflag(lastot->flags, F_RARITY, H_CAVE, 100, RR_RARE, NULL); addflag(lastot->flags, F_GLYPH, C_METAL, '\\', NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); @@ -7866,6 +7913,7 @@ void initobjects(void) { addflag(lastot->flags, F_GOESON, BP_LEGS, NA, NA, NULL); addflag(lastot->flags, F_GOESON, BP_HANDS, NA, NA, NULL); addflag(lastot->flags, F_ARMOURRATING, 3, NA, NA, NULL); + addflag(lastot->flags, F_UNDERCLOTHING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_EQUIPCONFER, F_ARMOURPENALTY, 0, 5, NULL); addflag(lastot->flags, F_EQUIPCONFER, F_DTIMMUNE, DT_COLD, NA, NULL); addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL); @@ -7892,6 +7940,7 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_FOREST, 100, RR_UNCOMMON, NULL); addflag(lastot->flags, F_MULTISIZE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL); + addflag(lastot->flags, F_UNDERCLOTHING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL); addflag(lastot->flags, F_ATTREQ, A_AGI, 15, NA, NULL); @@ -7905,6 +7954,7 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_COMMON, NULL); addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL); addflag(lastot->flags, F_ARMOURRATING, 5, NA, NA, NULL); + addflag(lastot->flags, F_UNDERCLOTHING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_EQUIPCONFER, F_DTIMMUNE, DT_FIRE, NA, NULL); addflag(lastot->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 40, 40, NA, NULL); @@ -8003,6 +8053,7 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_FOREST, 100, RR_UNCOMMON, NULL); addflag(lastot->flags, F_MULTISIZE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL); + addflag(lastot->flags, F_UNDERCLOTHING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL); addflag(lastot->flags, F_ATTREQ, A_AGI, 15, NA, NULL); @@ -12065,7 +12116,7 @@ void initrace(void) { addflag(lastrace->flags, F_AVIAN, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_MUTABLE, B_TRUE, NA, "100"); - addrace(R_CENTAUR, "centaur", 500, 'u', C_BROWN, MT_FLESH, RC_ANIMAL, "Centaurs look like horses with their neck upwards replaced by a human torso and arms."); + addrace(R_CENTAUR, "centaur", 500, 'u', C_FLESH, MT_FLESH, RC_ANIMAL, "Centaurs look like horses with their neck upwards replaced by a human torso and arms."); setbodytype(lastrace, BT_QUADRAPED); addbodypart(lastrace, BP_TAIL, NULL); addflag(lastrace->flags, F_ALIGNMENT, AL_NEUTRAL, NA, NA, NULL); @@ -18797,6 +18848,34 @@ void initrace(void) { addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming barbed whip"); addflag(lastrace->flags, F_RESISTMAG, 10, NA, NA, NULL); + addrace(R_DEECH, "deech", 20, ':', C_MAGENTA, MT_FLESH, RC_DEMON, "The name deech is short for 'Demon Chameleon'. These minor demonic reptiles can blend into their surroundings, becoming all but invisible to their prey."); + setbodytype(lastrace, BT_QUADRAPED); + addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_MASTERVAULTS, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 2, NA, NA, NULL); + 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_HASATTACK, OT_CLAWS, 3, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_LTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_RANDOM, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_RANDOM, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_RANDOM, NA, NULL); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "hisses^a hiss"); + addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTHIDDENPCT, 100, NA, NA, NULL); + addflag(lastrace->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_STEALTH, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_NOFLEE, B_TRUE, NA, NA, NULL); addrace(R_DRETCH, "dretch", 30, '&', C_BROWN, MT_FLESH, RC_DEMON, "An ape-like creature with extended forearms ending in clawed hands. They stand about 4 feet tall and weigh 60 pounds."); setbodytype(lastrace, BT_HUMANOID); diff --git a/data/hiscores.db b/data/hiscores.db index 913609c..cc7a084 100644 Binary files a/data/hiscores.db and b/data/hiscores.db differ diff --git a/data/vaults/antnest.vlt b/data/vaults/antnest.vlt index dba024c..f7825de 100644 --- a/data/vaults/antnest.vlt +++ b/data/vaults/antnest.vlt @@ -15,6 +15,7 @@ d:cell:dirt wall .:cell:dirt O:exit R:reusable +R:cell:dirt wall @end @flags diff --git a/data/vaults/babayagahut_2.vlt b/data/vaults/babayagahut_2.vlt index b09dc2e..80a6a32 100644 --- a/data/vaults/babayagahut_2.vlt +++ b/data/vaults/babayagahut_2.vlt @@ -28,6 +28,7 @@ C:ob:ornate chest X:ob:hut's doorway w:ob:great random weapon r:reusable +r:cell:brick wall @end @flags diff --git a/data/vaults/cave_bear.vlt b/data/vaults/cave_bear.vlt index 37bfca8..9fb1693 100644 --- a/data/vaults/cave_bear.vlt +++ b/data/vaults/cave_bear.vlt @@ -15,6 +15,7 @@ O:ob:boulder O:exit B:mon:grizzly bear r:reusable +r:cell:dirt wall @end @flags diff --git a/data/vaults/cave_troll.vlt b/data/vaults/cave_troll.vlt index 6405a4c..b433c80 100644 --- a/data/vaults/cave_troll.vlt +++ b/data/vaults/cave_troll.vlt @@ -14,6 +14,7 @@ rrr######rr O:ob:boulder O:exit r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/caveboss1.vlt b/data/vaults/caveboss1.vlt index 2236758..7355b4b 100644 --- a/data/vaults/caveboss1.vlt +++ b/data/vaults/caveboss1.vlt @@ -27,6 +27,7 @@ _:ob:pentagram _:ob:ancient stone key C:ob:ornate chest r:reusable +r:cell:metal wall @end @flags diff --git a/data/vaults/caveboss2.vlt b/data/vaults/caveboss2.vlt index 461305b..4115791 100644 --- a/data/vaults/caveboss2.vlt +++ b/data/vaults/caveboss2.vlt @@ -29,6 +29,7 @@ _:ob:cursed excellent weapon _:ob:ancient stone key C:ob:ornate chest r:reusable +r:cell:metal wall @end @flags diff --git a/data/vaults/circle.vlt b/data/vaults/circle.vlt index 4718ad9..3750ec5 100644 --- a/data/vaults/circle.vlt +++ b/data/vaults/circle.vlt @@ -16,6 +16,7 @@ rr###+###rr +:ob:wooden door +:exit r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/diagcross.vlt b/data/vaults/diagcross.vlt index 3272d13..8d4457d 100644 --- a/data/vaults/diagcross.vlt +++ b/data/vaults/diagcross.vlt @@ -16,6 +16,7 @@ r##..#..##r O:ob:large fire:50 X:exit r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/firepit.vlt b/data/vaults/firepit.vlt index 734a391..917bd50 100644 --- a/data/vaults/firepit.vlt +++ b/data/vaults/firepit.vlt @@ -19,6 +19,7 @@ f:ob:immutable large fire c:ob:untrapped ornate chest d:ob:secret door:15:cell:SOLID r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/fishbowl.vlt b/data/vaults/fishbowl.vlt index b01036c..0cb1663 100644 --- a/data/vaults/fishbowl.vlt +++ b/data/vaults/fishbowl.vlt @@ -15,6 +15,7 @@ r#ggggg#r g:cell:glass wall w:ob:very deep water:100 r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/oval.vlt b/data/vaults/oval.vlt index a7de8f4..c8ad4b7 100644 --- a/data/vaults/oval.vlt +++ b/data/vaults/oval.vlt @@ -14,6 +14,7 @@ rr##+##rr +:ob:wooden door +:exit r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/pitbridge_guarded.vlt b/data/vaults/pitbridge_guarded.vlt index 09619c9..69d0e69 100644 --- a/data/vaults/pitbridge_guarded.vlt +++ b/data/vaults/pitbridge_guarded.vlt @@ -14,6 +14,7 @@ _:ob:barricade m:mon:humanoid with firearm X:exit r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/playerstart5.vlt b/data/vaults/playerstart5.vlt index ef50dfb..81d32ee 100644 --- a/data/vaults/playerstart5.vlt +++ b/data/vaults/playerstart5.vlt @@ -13,6 +13,7 @@ r#####r p:ob:playerstart x:exit r:reusable +r:cell:SOLID @end @flags goesin:dungeon diff --git a/data/vaults/playerstart6.vlt b/data/vaults/playerstart6.vlt index 16690d5..55f5254 100644 --- a/data/vaults/playerstart6.vlt +++ b/data/vaults/playerstart6.vlt @@ -15,6 +15,7 @@ rrr### p:ob:playerstart x:exit r:reusable +r:cell:SOLID @end @flags goesin:dungeon diff --git a/data/vaults/roundabout.vlt b/data/vaults/roundabout.vlt index 6389646..ef539b5 100644 --- a/data/vaults/roundabout.vlt +++ b/data/vaults/roundabout.vlt @@ -13,6 +13,7 @@ r##x##r #:cell:SOLID x:exit r:reusable +r:cell:SOLID @end @flags diff --git a/data/vaults/sauna.vlt b/data/vaults/sauna.vlt index d785497..4e2b93e 100644 --- a/data/vaults/sauna.vlt +++ b/data/vaults/sauna.vlt @@ -10,6 +10,7 @@ r#+#r @legend #:cell:wyrmwood wall r:reusable +r:cell:wyrmwood wall +:exit +:ob:wooden door h:cell:tiled floor diff --git a/data/vaults/uturn.vlt b/data/vaults/uturn.vlt index 0c991fd..34d1c42 100644 --- a/data/vaults/uturn.vlt +++ b/data/vaults/uturn.vlt @@ -10,6 +10,7 @@ r#########r #:cell:SOLID X:exit r:reusable +r:cell:SOLID @end @flags diff --git a/defs.h b/defs.h index 90c917b..3e8b387 100644 --- a/defs.h +++ b/defs.h @@ -109,6 +109,9 @@ #define B_MAYBE (-2) #define B_ONEOF (-3) +#define B_FROMCRIT (-1) +#define B_NOCRIT (0) + #define B_BLINDABLE (-1) #define B_ALLOWEXTRA (-1) @@ -259,6 +262,7 @@ // must be >= max # of spells/abilities AND // >= max # of cells on the map +// >= MAXFLAGS #define MAXCANDIDATES 2048 #define MAXCHOICES 400 @@ -1493,6 +1497,7 @@ enum RACE { R_STIRGE, // demons R_BALROG, + R_DEECH, R_DRETCH, R_GRIDDLER, R_LURKINGHORROR, @@ -1996,6 +2001,7 @@ enum OBTYPE { OT_S_GRAVBOOST, OT_S_HASTE, OT_S_LEVITATION, + OT_S_SILENCE, OT_S_SLOW, OT_S_TRUESTRIKE, OT_S_WHATGOESUP, @@ -2019,6 +2025,7 @@ enum OBTYPE { OT_S_CHARM, OT_S_CHIBOLT, OT_S_CHISTRIKE, + OT_S_DELAYDEATH, OT_S_DISORIENT, OT_S_HUNGER, OT_S_LETHARGY, @@ -2026,6 +2033,7 @@ enum OBTYPE { OT_S_KNOWWEAKNESS, OT_S_MFEEDBACK, OT_S_MINDSCAN, + OT_S_MINDSHIELD, OT_S_MINDWHIP, OT_S_MIRRORIMAGE, OT_S_PACIFY, @@ -2033,8 +2041,11 @@ enum OBTYPE { OT_S_PSIBLAST, OT_S_PSYARMOUR, OT_S_PSYSHOVE, + OT_S_REMOTEKO, OT_S_SLEEP, OT_S_SLEEPMASS, + OT_S_SLOWMISSILES, + OT_S_SOULLINK, OT_S_STASIS, OT_S_STUN, OT_S_TELEKINESIS, @@ -2910,6 +2921,8 @@ enum FLAG { F_GOESON, // val0 = where it can be equipped. F_GOESONMULTI, // ob is equipped on _ALL_ F_GOESON flags, rather than // equipped on _ONE OF_ the. + F_UNDERCLOTHING, // yuo can wear other armour on top of this one + // (on the same body part) F_BONUS, // val0=bonus/penalty to damage+accuracy/armour. ie. +1 sword F_THROWMISSILE, // weapon would make a good thrown missle - used by AI F_THROWNBY, // this object was thrown by lifeform id v0. @@ -4041,8 +4054,12 @@ enum FLAG { F_AWARENESS, // you can see 360 degrees around yourself F_BEINGSTONED,// turn to stone when v0 drops to zero. (drops 1/turn) F_BLIND, // cannot see anything + F_SOULLINK, // lf is soul-linked to lfid v0. if they take damage, so do + // we. + // text = damstring, ie. "x's soullink spell" F_CONFUSED, // move randomly about F_DEAF, // cannot hear + F_SILENCED, // cannot make nosies F_NEEDOBFORSPELLS, // if v0 != NA, lf can only cast spells if // it has object v0 // if v1 != NA, lf can only cast spells if they @@ -4492,6 +4509,7 @@ enum ERROR { E_NOUNARMEDATTACK, E_NOTEQUIPPED, E_EQUIPPED, + E_UNDERNEATH, E_NOPICKUP, E_STUCK, E_MONSTERNEARBY, @@ -4516,6 +4534,7 @@ enum ERROR { E_NOTARGET, E_NOAMMO, E_GRAVBOOSTED, + E_NOABIL, E_NOMP, E_NOSTAM, E_NOSPELLS, @@ -4542,6 +4561,7 @@ enum ERROR { E_OFFMAP, E_RAGE, E_STUNNED, + E_SILENCED, // charm failure reasons // LOWIQ E_UNDEAD, diff --git a/flag.c b/flag.c index 67670f6..12ab424 100644 --- a/flag.c +++ b/flag.c @@ -763,6 +763,7 @@ int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid) { case F_PRODUCESLIGHT: case F_PRONE: case F_RAGE: + case F_SILENCED: case F_SPRINTING: case F_SLOWMOVE: case F_STUNNED: diff --git a/io.c b/io.c index 5e1614d..2485741 100644 --- a/io.c +++ b/io.c @@ -2092,6 +2092,14 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_SILENCED: + if (isplayer(lf)) { + msg("^%cYou are surrounded in a field of silence!",getlfcol(lf, CC_BAD)); + } else { + msg("^%c%s is surrounded in a field of silence!", getlfcol(lf, CC_BAD), lfname); + } + donesomething = B_TRUE; + break; case F_SILENTMOVE: if (isplayer(lf)) { // don't know if monsters get it msg("You now move silently."); @@ -2116,6 +2124,23 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { msg("^%c%s %s",getlfcol(lf, CC_VBAD), lfname, isplayer(lf) ? "feel slow and sluggish." : "looks slow and sluggish."); donesomething = B_TRUE; break; + case F_SOULLINK: + if (isplayer(lf)) { // don't know if monsters get it + lf2 = findlf(lf->cell->map, f->val[0]); + if (lf2) { + if (cansee(player, lf2)) { + real_getlfname(lf2, buf, NULL, B_NOSHOWALL, B_REALRACE); + } else { + real_getlfnamea(lf2, buf, NULL, B_NOSHOWALL, B_REALRACE); + } + msg("^wA psychic bond forms between you and %s!", buf); + } else { + // shouldn't happen. + msg("^wA psychic bond forms between you and something unknown."); + } + donesomething = B_TRUE; + } + break; case F_SPIDERCLIMB: if (isplayer(lf)) { // don't know if monsters get it msg("Your skin becomes adhesive!"); @@ -2881,6 +2906,14 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_SILENCED: + if (isplayer(lf)) { + msg("You can now make noises again."); + } else { + msg("The field of silence around %s vanishes.", lfname); + } + donesomething = B_TRUE; + break; case F_SILENTMOVE: if (isplayer(lf)) { // don't know if monsters lose it msg("You no longer move silently."); @@ -2905,6 +2938,12 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; } break; + case F_SOULLINK: + if (isplayer(lf)) { // don't know if monsters get it + msg("^wYour psychic soul-link vanishes."); + donesomething = B_TRUE; + } + break; case F_SPIDERCLIMB: if (isplayer(lf)) { msg("Your skin is no longer adhesive."); @@ -5030,6 +5069,12 @@ void docomms(lifeform_t *lf) { char ch; lifeform_t *lfarg; cell_t *cellarg; + + if (lfhasflag(player, F_SILENCED)) { + msg("You are unable to make a sound!"); + return; + } + //int moneyowing = 0; if (!lf) { where = askcoords("Talk to who?", "Talk->", TT_MONSTER, player, UNLIMITED, LOF_DONTNEED, B_FALSE); @@ -6054,7 +6099,7 @@ char *makedesc_ob(object_t *o, char *retbuf) { for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_GOESON) { - compareob = getequippedob(player->pack, f->val[0]); + compareob = getouterequippedob(player, f->val[0]); if (compareob) { break; } @@ -7352,6 +7397,10 @@ char *makedesc_ob(object_t *o, char *retbuf) { sprintf(buf2, "%s prevents distant creatures from seeing you.\n", buf); strncat(retbuf, buf2, HUGEBUFLEN); break; + case F_SILENCED: + sprintf(buf2, "%s prevents you from making sounds.\n", buf); + strncat(retbuf, buf2, HUGEBUFLEN); + break; case F_SILENTMOVE: sprintf(buf2, "%s allows you to move silently.\n", buf); strncat(retbuf, buf2, HUGEBUFLEN); @@ -11332,12 +11381,25 @@ void drawstatus(void) { unsetcol(statwin, C_LIGHTBLUE); } + if (hasactivespell(player, OT_S_DELAYDEATH)) { + setcol(statwin, C_LIGHTBLUE); + wprintw(statwin, " DelayDeath"); + unsetcol(statwin, C_LIGHTBLUE); + } + if (lfhasflag(player, F_RAGE)) { setcol(statwin, C_RED); wprintw(statwin, " Rage"); unsetcol(statwin, C_RED); } + if (lfhasflag(player, F_SILENCED)) { + setcol(statwin, C_RED); + wprintw(statwin, " Mute"); + unsetcol(statwin, C_RED); + } + + if (isswimming(player)) { setcol(statwin, C_LIGHTBLUE); wprintw(statwin, " Swim"); @@ -11847,12 +11909,13 @@ int showhiscoreline(void *hilitescore, int ncols, char **argv, char **colname) { void showlfarmour(lifeform_t *lf) { enum BODYPART bp; object_t *o; - object_t *arm[MAXBODYPARTS]; + object_t *arm[MAXBODYPARTS*2]; + int narm = 0; int y,i; char buf[BUFLEN],ch; int keepgoing = B_TRUE; - for (i = 0; i < MAXBODYPARTS; i++) { + for (i = 0; i < MAXBODYPARTS*2; i++) { arm[i] = NULL; } @@ -11870,17 +11933,27 @@ void showlfarmour(lifeform_t *lf) { char rhs[BUFLEN]; // default strcpy(rhs, ""); - if (hasbp(lf, bp)) { object_t *outerob; + object_t *oo; + int nhere = 0; snprintf(buf, BUFLEN, "%13s:%1s",getbodypartname(lf, bp), " "); - o = getequippedob(lf->pack, bp); - arm[bp] = o; // remember for later + // get outermost armour here so that we can show 'covered' if needed outerob = getouterequippedob(lf, bp); - if (o) { + + for (oo = lf->pack->first ;oo ; oo = oo->next) { flag_t *f; int thisar = 0,showar = B_FALSE; char obname[BUFLEN]; + o = NULL; + // is this object equipped in the right place? + if (hasflagval(oo->flags, F_EQUIPPED, bp, NA, NA, NULL)) { + o = oo; + nhere++; + } + if (!o) continue; + + arm[narm++] = o; // remember for later // two handed weapons. if ((bp == BP_SECWEAPON ) && (o == arm[BP_WEAPON])) { @@ -11923,16 +11996,22 @@ void showlfarmour(lifeform_t *lf) { strcat(rhs, numbuf); } } - } else { - strcpy(rhs, "-"); - } - mvwprintw(mainwin, y, 0, "%s", buf); - if (o) setobcolour(mainwin, o, B_TRUE); - //wprintw(mainwin, "%s", rhs); - textwithcol(mainwin, rhs); - if (o) setobcolour(mainwin, o, B_FALSE); - y++; + mvwprintw(mainwin, y, 0, "%s", buf); + setobcolour(mainwin, o, B_TRUE); + //wprintw(mainwin, "%s", rhs); + textwithcol(mainwin, rhs); + setobcolour(mainwin, o, B_FALSE); + y++; + } + // no armour in this body part? + if (!nhere) { + strcpy(rhs, "-"); + mvwprintw(mainwin, y, 0, "%s", buf); + //wprintw(mainwin, "%s", rhs); + textwithcol(mainwin, rhs); + y++; + } } } @@ -11945,9 +12024,9 @@ void showlfarmour(lifeform_t *lf) { keepgoing = B_FALSE; } else { // does it match an object? - for (bp = BP_WEAPON; bp < MAXBODYPARTS; bp++) { - if (arm[bp] && (arm[bp]->letter == ch)) { - describeob(arm[bp]); + for (o = 0; i < MAXBODYPARTS*2; i++) { + if (arm[i] && (arm[i]->letter == ch)) { + describeob(arm[i]); } } keepgoing = B_TRUE; @@ -13615,7 +13694,7 @@ void showlfstats(lifeform_t *lf, int showall) { f = hasflag_real(lf->flags, F_VEGETARIAN, B_TRUE, NULL, FROMRACE); if (f) { getflagsourcetext(f,source); - mvwprintw(mainwin, y, 0, "%s %s a vegetarian (will not eat meat).%s", you(lf), is(lf)); + mvwprintw(mainwin, y, 0, "%s %s a vegetarian (will not eat meat).%s", you(lf), is(lf),source); y++; } f = hasflag_real(lf->flags, F_PARTVEGETARIAN, B_TRUE, NULL, FROMRACE); @@ -14380,6 +14459,14 @@ void showlfstats(lifeform_t *lf, int showall) { y++; } + f = hasflag_real(lf->flags, F_SILENCED, B_TRUE, NULL, FROMRACE); + if (f) { + getflagsourcetext(f,source); + mvwprintw(mainwin, y, 0, "%s %s been magically silenced.%s", you(lf), + isplayer(lf) ? "have" : "has", source); + y++; + } + f = lfhasflag(lf, F_STRIKETOKO); if (f && (f->known)) { getflagsourcetext(f,source); diff --git a/lf.c b/lf.c index c4532be..842a025 100644 --- a/lf.c +++ b/lf.c @@ -108,6 +108,7 @@ void autoweild(lifeform_t *lf) { object_t *o,*firearm; int donesecondary = B_FALSE; int pretimespent; + int pass; pretimespent = lf->timespent; @@ -128,15 +129,22 @@ void autoweild(lifeform_t *lf) { // weild armour/2nd weapons if required, // and mark other weapons as secondary - for (o = lf->pack->first ; o ; o = o->next) { - 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 { - if (canwear(lf, o, BP_NONE)) { - wear(lf, o); + for (pass = 0; pass < 2; pass++) { + for (o = lf->pack->first ; o ; o = o->next) { + 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 { + if (canwear(lf, o, BP_NONE)) { + if ((pass == 0) && !hasflag(o->flags, F_UNDERCLOTHING)) { + // only equip underclothing on the first pass. + } else { + wear(lf, o); + } + } } } } @@ -701,8 +709,10 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { flag_t *f; objecttype_t *ot; int stamcost = 0; - // TODO: check for mute? - + if (lfhasflag(lf, F_SILENCED)) { + reason = E_SILENCED; + return B_FALSE; + } if (lfhasflag(lf, F_STUNNED)) { reason = E_STUNNED; return B_FALSE; @@ -879,6 +889,9 @@ int canclimb(lifeform_t *lf, enum ERROR *reason) { } else if (isburdened(lf) || lfhasflag(lf, F_GRAVBOOSTED)) { if (reason) *reason = E_TOOHEAVY; return B_FALSE; + } else if ((getraceclass(lf) != RC_HUMANOID) && !getskill(lf, SK_CLIMBING)) { + if (reason) *reason = E_NOABIL; + return B_FALSE; } return B_TRUE; } @@ -1625,7 +1638,7 @@ int canwear(lifeform_t *lf, object_t *o, enum BODYPART where) { return B_FALSE; } // is the GIVEN part free? - if (!isfreebp(lf, where)) { + if (!isfreebp(lf, where, o)) { reason = E_WEARINGSOMETHINGELSE; return B_FALSE; } @@ -1633,7 +1646,7 @@ int canwear(lifeform_t *lf, object_t *o, enum BODYPART where) { if (hasflag(o->flags, F_GOESONMULTI)) { // are ALL of these body parts free? for (i = 0; i < nparts; i++) { - if (!isfreebp(lf, possbp[i])) { + if (!isfreebp(lf, possbp[i], o)) { reason = E_WEARINGSOMETHINGELSE; return B_FALSE; } @@ -1643,7 +1656,7 @@ int canwear(lifeform_t *lf, object_t *o, enum BODYPART where) { int ok = B_FALSE; // are ANY of these body parts free? for (i = 0; i < nparts; i++) { - if (isfreebp(lf, possbp[i])) { + if (isfreebp(lf, possbp[i], o)) { ok = B_TRUE; break; } @@ -1769,8 +1782,12 @@ int canweild(lifeform_t *lf, object_t *o) { return B_TRUE; } -int cantakeoff(lifeform_t *lf, object_t *o) { +int cantakeoff(lifeform_t *lf, object_t *o, object_t **errob) { flag_t *f; + object_t *oo; + enum BODYPART bp = BP_NONE; + + if (errob) *errob = NULL; reason = E_OK; f = hasflag(o->flags, F_EQUIPPED); @@ -1778,6 +1795,14 @@ int cantakeoff(lifeform_t *lf, object_t *o) { reason = E_NOTEQUIPPED; return B_FALSE; } + bp = f->val[0]; + // something else equipped here? + oo = getouterequippedob(lf, bp); + if (oo && (oo != o)) { + if (errob) *errob = oo; + reason = E_UNDERNEATH; + return B_FALSE; + } // cursed? if (o->blessed == B_CURSED) { @@ -1798,6 +1823,9 @@ int cantakeoff(lifeform_t *lf, object_t *o) { } int cantalk(lifeform_t *lf) { + if (lfhasflag(lf, F_SILENCED)) { + return B_FALSE; + } // phantasms dont talk if (lfhasflag(lf, F_PHANTASM)) { return B_FALSE; @@ -1884,6 +1912,9 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar case E_STUNNED: msg("You can't cast spells while stunned."); break; + case E_SILENCED: + msg("You are unable to utter the words to your spell!"); + break; default: msg("For some reason, you can't cast that."); break; @@ -5231,18 +5262,20 @@ int eat(lifeform_t *lf, object_t *o) { } } } - if ((fid != F_NONE) && f->id == F_EATMUTATE) { - // lose half your max hp! - losehp_real(lf, (lf->maxhp/2), DT_DIRECT, NULL, - "the shock of mutation", - B_NODAMADJUST, o, B_NORETALIATE, NULL, B_DAMEFFECTS, - BP_NONE); - if (isplayer(lf)) { - msg("^%cYou convulse in agony as your body mutates!", - getlfcol(lf, CC_BAD)); - } else if (cansee(player, lf)) { - msg("^%c%s convulses in agony as its body mutates!", - getlfcol(lf, CC_BAD), lfname); + if (fid != F_NONE) { + if (f->id == F_EATMUTATE) { + // lose half your max hp! + losehp_real(lf, (lf->maxhp/2), DT_DIRECT, NULL, + "the shock of mutation", + B_NODAMADJUST, o, B_NORETALIATE, NULL, B_DAMEFFECTS, + BP_NONE, B_NOCRIT); + if (isplayer(lf)) { + msg("^%cYou convulse in agony as your body mutates!", + getlfcol(lf, CC_BAD)); + } else if (cansee(player, lf)) { + msg("^%c%s convulses in agony as its body mutates!", + getlfcol(lf, CC_BAD), lfname); + } } // still alive? you gain the ability! if (!isdead(lf)) { @@ -8331,12 +8364,28 @@ char *getbodypartequipname(enum BODYPART bp) { } object_t *getequippedob(obpile_t *op, enum BODYPART bp) { - object_t *o; + object_t *o,*poss[MAXCANDIDATES]; + int nposs = 0,i; + + // first get a list of all objects equipped there. + // normally there will only be one, unless we have + // underclothing (ie. shirt + body armour). for (o = op->first; o ; o = o->next) { if (hasflagval(o->flags, F_EQUIPPED, bp, NA, NA, NULL)) { - return o; + poss[nposs++] = o; } } + if (!nposs) { + return NULL; + } else if (nposs == 1) { + return poss[0]; + } + // we have more than one item equipped. get the inner one with F_UNDERCLOTHING. + for(i = 0; i < nposs; i++) { + if (hasflag(poss[i]->flags, F_UNDERCLOTHING)) return poss[i]; + } + // should never get here... + assert("bug in getequippedob()." == 0); return NULL; } @@ -9364,23 +9413,45 @@ char *getlfconditionname(enum LFCONDITION cond) { } object_t *getouterequippedob(lifeform_t *lf, enum BODYPART bp) { - object_t *o; + object_t *o,*poss[MAXCANDIDATES]; + enum BODYPART where = bp; + int i,nposs = 0; + switch (bp) { case BP_RIGHTFINGER: case BP_LEFTFINGER: - o = getequippedob(lf->pack, BP_HANDS); - if (o) return o; - else return getequippedob(lf->pack, bp); + if (getequippedob(lf->pack, BP_HANDS)) { + where = BP_HANDS; + } break; case BP_BODY: - o = getequippedob(lf->pack, BP_SHOULDERS); - if (o) return o; - else return getequippedob(lf->pack, bp); + if (getequippedob(lf->pack, BP_SHOULDERS)) { + where = BP_SHOULDERS; + } break; default: break; } - return getequippedob(lf->pack, bp); + // get all obs equipped here. + for (o = lf->pack->first ; o ; o = o->next) { + if (hasflagval(o->flags, F_EQUIPPED, where, NA, NA, NULL)) { + poss[nposs++] = o; + } + } + if (!nposs) { + return NULL; + } else if (nposs == 1) { + return poss[0]; + } + // return the OUTER one. + for (i = 0;i < nposs; i++) { + if (!hasflag(poss[i]->flags, F_UNDERCLOTHING)) { + return poss[i]; + } + } + // should never get here. + assert("bug in getouterequippedob()." == 0); + return NULL; } /* @@ -10106,7 +10177,7 @@ char *real_getlfname(lifeform_t *lf, char *buf, lifeform_t *usevis, int showall, racesize = SZ_HUMAN; // default } size = getlfsize(lf); - if (size != racesize) { + if (!useorigrace && (size != racesize)) { strcat(descstring, getsizetext(size)); strcat(descstring, " "); } @@ -13939,6 +14010,17 @@ void loseobflags(lifeform_t *lf, object_t *o, int kind) { } } +int handlearmour(lifeform_t *lf, object_t *fromob, int *dam, enum DAMTYPE dt) { + int damreducedbyarmour = 0; + if (!armourcanstopdam(dt)) return 0; + // modify for defender's armour + // first figure out how much to reduce the damage by. + damreducedbyarmour = getarmourdamreduction(lf, fromob, *dam, dt); + // now actually reduce the damage amount + applyarmourdamreduction(lf, fromob, damreducedbyarmour, dam, dt); + return damreducedbyarmour; +} + int hasbp(lifeform_t *lf, enum BODYPART bp) { int i; if (lfhasflagval(lf, F_NOBODYPART, bp, NA, NA, NULL)) { @@ -14408,7 +14490,12 @@ int isclimbing(lifeform_t *lf) { int isdead(lifeform_t *lf) { if (!lf->alive) return B_TRUE; - if (lf->hp <= 0) return B_TRUE; + if (lf->hp <= 0) { + if (hasactivespell(lf, OT_S_DELAYDEATH) && (lf->hp > -15)) { + } else { + return B_TRUE; + } + } return B_FALSE; } @@ -14550,8 +14637,17 @@ int isflyingwithwings(lifeform_t *lf) { return B_FALSE; } -int isfreebp(lifeform_t *lf, enum BODYPART bp) { - if (hasobwithflagval(lf->pack, F_EQUIPPED, bp, NA, NA, NULL)) return B_FALSE; +int isfreebp(lifeform_t *lf, enum BODYPART bp, object_t *whatfor) { + object_t *o; + o = hasobwithflagval(lf->pack, F_EQUIPPED, bp, NA, NA, NULL); + if (o) { + if (whatfor && !hasflag(whatfor->flags, F_UNDERCLOTHING) && + hasflag(o->flags, F_UNDERCLOTHING)) { + // ok. + } else { + return B_FALSE; + } + } if (!hasbp(lf, bp)) return B_FALSE; @@ -16839,15 +16935,15 @@ void loseconsciousness(lifeform_t *lf, int howlong, lifeform_t *fromlf) { // lose hp, and adjust damage based on resistances int losehp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc) { - return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_DAMEFFECTS, BP_NONE); + return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_DAMEFFECTS, BP_NONE, B_NOCRIT); } int losehp_bp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int bodypart) { - return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_DAMEFFECTS, bodypart); + return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_DAMEFFECTS, bodypart, B_NOCRIT); } // returns the amt of damage taken -int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam, object_t *fromob, int retaliate, int *waskod, int doeffects, int bodypart) { +int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam, object_t *fromob, int retaliate, int *waskod, int doeffects, int bodypart, int fromcrit) { char buf[BUFLEN]; char buf2[BUFLEN]; char lfname[BUFLEN]; @@ -16855,6 +16951,8 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml int murder = B_FALSE; flag_t *f; int predead = B_FALSE; + int damreducedbyarm = 0; + int hpleftafterdam; if (gamemode < GM_GAMESTARTED) return 0; @@ -16876,6 +16974,11 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml murder = B_TRUE; } + if (doeffects) { + // handle armour + damreducedbyarm = handlearmour(lf, fromob, &amt, damtype); + } + // adjust for source object's material if (fromob) { if (lfhasflagval(lf, F_MATIMMUNE, fromob->material->id, NA, NA, NULL)) { @@ -16910,13 +17013,16 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml killflagsofid(lf->flags, F_HIDING); // methods of knocking unconscious - if (lfcanbekod(lf)) { + // would this damage reduce the lf to < 0 hp ??? + hpleftafterdam = lf->hp - amt; + if (lfcanbekod(lf) && (lf->hp > 1) && (hpleftafterdam <= 0)) { + int threshold = 0,kochance = 0; // merciful weapons - these will ALWAYS ko, even if // they are already unconscious. if (!ko && fromob) { f = hasflag(fromob->flags, F_MERCIFUL); if (f && (amt >= lf->hp)) { - ko = B_TRUE; + kochance = 100; if (fromob->pile->owner && cansee(player, fromob->pile->owner)) { f->known = B_TRUE; } @@ -16926,7 +17032,6 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml // bashing damage sometimes ko's // damage when eating corpses also does this. if (!ko && !isunconscious(lf)) { - int threshold,kochance = 0; int damtypeok = B_FALSE; int playerinvolved = B_FALSE; @@ -16946,28 +17051,34 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml if (playerinvolved && godprayedto(R_GODMERCY)) { threshold = -10; if (isplayer(lf)) { - kochance = 75; + // player being hit? + kochance += 75; } else { - kochance = 50; + // player hitting something else? + kochance += 30; } } else { threshold = -5; - kochance = 30; + kochance += 15; // base chance to KO with bashing. + } + + // TRYING to ko rather than kill? + if (fromlf && lfhasflag(fromlf, F_STRIKETOKO)) { + if (cansee(fromlf, lf)) { + kochance += 30; + threshold -= 5; + } } } - if (kochance) { - int hpleftafterdam; - // if this damage would reduce the lf to between -threshold and 0 hp - hpleftafterdam = lf->hp - amt; - if ((lf->hp > 1) && (hpleftafterdam >= threshold) && (hpleftafterdam <= 0)) { - if (pctchance(kochance)) { - ko = B_TRUE; - } + if (kochance && (hpleftafterdam >= threshold)) { + if (pctchance(kochance)) { + ko = B_TRUE; } } } + /* if (!ko && !isunconscious(lf)) { if (fromlf && lfhasflag(fromlf, F_STRIKETOKO)) { if (cansee(fromlf, lf)) { @@ -16975,11 +17086,13 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml } } } + */ - // just knock them out, don't kill. - if (ko) { - amt = lf->hp - 1; // ie end up at 1hp - } + } + + // just knock them out, don't kill. + if (ko) { + amt = lf->hp - 1; // ie end up at 1hp } @@ -17051,7 +17164,7 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml } if (doeffects || ko) { - losehpeffects(lf, amt, damtype, fromlf, fromob, retaliate, ko, waskod, prelowhp, bodypart); + losehpeffects(lf, amt, damtype, fromlf, fromob, retaliate, ko, waskod, prelowhp, bodypart, damreducedbyarm, fromcrit); } if ((lf->hp > 0) && !ko && fromlf && (getcelldist(lf->cell, fromlf->cell) > 1)) { @@ -17141,15 +17254,70 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml // update screen drawstatus(); updatestatus(); + + f = lfhasflag(lf, F_SOULLINK); + if (f && (damtype != DT_DIRECT)) { + lifeform_t *otherlf; + otherlf = findlf(lf->cell->map, f->val[0]); + if (otherlf) { + losehp_real(otherlf, amt, DT_DIRECT, lf, f->text, B_FALSE, NULL, B_FALSE, NULL, B_NODAMEFFECTS, BP_NONE, B_NOCRIT); + } + } + return amt; } -void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fromlf, object_t *fromob, int retaliate, int ko, int *waskod, int prelowhp, int bodypart) { +void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fromlf, object_t *fromob, int retaliate, int ko, int *waskod, int prelowhp, int bodypart, int damreducedbyarm, int crit) { int postlowhp = B_FALSE; - flag_t *retflag[MAXCANDIDATES],*f; + flag_t *retflag[MAXCANDIDATES],*f,*magicarm = NULL; int nretflags; char buf[BUFLEN],lfname[BUFLEN]; getlfname(lf, lfname); + + // armour damage + if ((dam > 0) && isphysicaldam(damtype)) { + getflags(lf->flags, retflag, &nretflags, F_HEAVENARM, F_MAGICARMOUR, F_NONE); + if (nretflags) { + magicarm = retflag[0]; + } + } + // now magic armour will pulse and maybe vanish. + if (magicarm) { + int damprevented; + // prevent all damage if possible + damprevented = dam; + magicarm->val[0] -= damprevented; + if (magicarm->val[0] <= 0) { + // did magic armour come from a spell? + if (magicarm->lifetime == FROMSPELL) { + flag_t *spellflag; + spellflag = hasactivespell(lf, magicarm->obfrom); + if (spellflag) { + killflag(spellflag); + } + } + // magic armour vanishes now. + killflag(magicarm); + } else { + if (cansee(player, lf)) { + msg("^%d%s%s %s pulses!^n", + CC_GOOD, + lfname, getpossessive(lfname), + magicarm->text); + } + } + } else { + // victim's armour loses hp. note that it loses the full + // amount of damage dealt, not just what it reduced. + if (damreducedbyarm && !crit) { + applyarmourdamage(lf, fromob, dam + damreducedbyarm, damtype, fromlf); + // train armour + practice(lf, SK_ARMOUR, 1); + } + } + + + if (lf->hp > 0) { // effects based on damage type, if lf is still alive if (damtype == DT_COLD) { @@ -17227,7 +17395,7 @@ void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fr for (i = 0 ; i < nretcells; i++) { if (retcell[i]->lf && (retcell[i]->lf != lf)) { if (!isairborne(retcell[i]->lf, NULL)) { - losehp_real(retcell[i]->lf, dam, DT_ELECTRIC, fromlf, "an electric shock", B_TRUE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE); + losehp_real(retcell[i]->lf, dam, DT_ELECTRIC, fromlf, "an electric shock", B_TRUE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE, B_NOCRIT); } } } @@ -17744,7 +17912,7 @@ int makelearnable(lifeform_t *lf, enum SKILL skid) { // returns TRUE on failure. int makenauseated(lifeform_t *lf, int amt, int howlong, enum ERROR *why) { - flag_t *f; + flag_t *f,*nflag = NULL; if (why) *why = E_OK; switch (lf->race->raceclass->id) { @@ -17777,27 +17945,28 @@ int makenauseated(lifeform_t *lf, int amt, int howlong, enum ERROR *why) { //if (!lfhasflag(lf, F_HUMANOID)) return B_TRUE; + nflag = lfhasflag(lf, F_NAUSEATED); + // skillcheck to avoid this. if (skillcheck(lf, SC_CON, 80 + (amt*10), gettr(lf)*2)) { // passed skillcheck if (why) *why = E_RESISTED; // announce if (isplayer(lf)) { - msg("You feel momentarily unwell."); + msg("You feel momentarily %sunwell.", nflag ? "more " : ""); } else if (cansee(player, lf)) { char buf[BUFLEN]; getlfname(lf, buf); - msg("%s looks momentarily unwell.", buf); + msg("%s looks momentarily %sunwell.", buf, nflag ? "more " : ""); } return B_TRUE; } // already nauseated? - f = lfhasflag(lf, F_NAUSEATED); - if (f) { - if ((f->lifetime >= 0) && (f->lifetime < howlong)) { - f->lifetime = howlong; - f->val[0] = MAXOF(f->val[0], amt); + if (nflag) { + if ((nflag->lifetime >= 0) && (nflag->lifetime < howlong)) { + nflag->lifetime = howlong; + nflag->val[0] = MAXOF(f->val[0], amt); } } else { addtempflag(lf->flags, F_NAUSEATED, amt, NA, NA, NULL, howlong); @@ -18615,57 +18784,9 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, } } // end if isplayer and not asleep - // wake up a little f = lfhasflag(l, F_ASLEEP); if (f && (f->val[1] != ST_KO)) { - if (f->lifetime > 0) { // ie. temporary - timeeffectsflag(f, myvol + rnd(1,3)); - } else if (f->lifetime == PERMENANT) { - if (f->val[2] == NA) { - // ie asleep rather than 'resting' - // wake up! - if (isplayer(l)) { - msg("^wA nearby noise %s you!", (f->val[1] == ST_MEDITATING) ? "" : "awakens" ); - rv = B_TRUE; - } - killflag(f); - } else { - // ie resting on purpose via 'R' - // only wake up if the sound if very close - if (myvol >= getcelldist(c, l->cell)) { - // wake up! - if (isplayer(l)) { - char wakenoise[BUFLEN]; - char *punc; - strcpy(wakenoise, text); - // omit punctuation - punc = &(wakenoise[strlen(wakenoise)-1]); - switch (*punc) { - case '"': break; - default: - *punc = '\0'; - break; - } - //msg("A nearby noise awakens you!"); - msg("^wThe sound of %s awakens you!", wakenoise); - rv = B_TRUE; - } - killflag(f); - } - } - // make it temporary - //f->lifetime = rnd(1,10); - } - - // still asleep? - f = lfhasflag(l, F_ASLEEP); - if (f && (f->val[1] == ST_ASLEEP) && cansee(player, l)) { - char lfname[BUFLEN]; - getlfname(l, lfname); - msg("%s stir%s in %s slumber...", lfname, - isplayer(l) ? "" : "s", - isplayer(l) ? "your" : "its"); - } + stir(l,myvol,getcelldist(c, l->cell), text); } else { // not asleep, but can hear it. // monsters will go to investigate the sound, as long as they're // not otherwise occupied @@ -20173,6 +20294,10 @@ int say(lifeform_t *lf, char *text, int volume) { char *localtext; int rv; + if (lf && lfhasflag(lf, F_SILENCED)) { + return B_FALSE; + } + localtext = strdup(text); // adjust text and volume for gods @@ -20245,6 +20370,9 @@ int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *t char buf3[BUFLEN]; char *p,*p2; race_t *r; + if (lf && lfhasflag(lf, F_SILENCED)) { + return B_FALSE; + } switch (what) { case SP_ALLY_ATTACK: switch (rnd(1,3)) { @@ -22373,6 +22501,7 @@ void startlfturn(lifeform_t *lf) { if (db) dblog("startlfturn for lf id %d %s", lf->id, lf->race->name); +// if (lf->hp < 0) lf->hp = 0; // debugging lf->redraws = 0; @@ -23463,7 +23592,7 @@ void startlfturn(lifeform_t *lf) { msg("^%c%s%s spine snaps!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname)); } - losehp_real(lf, lf->maxhp, DT_DIRECT, NULL, "tetanus", B_FALSE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE); + losehp_real(lf, lf->maxhp, DT_DIRECT, NULL, "tetanus", B_FALSE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE, B_FROMCRIT); } break; case BP_TAIL: @@ -23816,7 +23945,7 @@ void startlfturn(lifeform_t *lf) { // effects for/on your own flags 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_FULLSHIELD,F_HPDRAIN, F_INCUBATING, F_INJURY, - F_NOFLEEFROM, F_PETOF, F_SIZETIMER, F_SPOTTED, F_STRIKETOKO, F_TARGETCELL, F_TARGETLF, F_NONE); + F_NOFLEEFROM, F_PETOF, F_SIZETIMER, F_SOULLINK, F_SPOTTED, F_STRIKETOKO, F_TARGETCELL, F_TARGETLF, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; // remove impossible/expired flags @@ -23909,7 +24038,7 @@ void startlfturn(lifeform_t *lf) { if (f->obfrom == B_NEWINJURY) f->obfrom = B_FALSE; - arm = getequippedob(lf->pack, f->val[1]); + arm = getouterequippedob(lf, f->val[1]); if (arm && !hasobmod(arm, findobmod(OM_BLOODSTAINED)) && pctchance(5)) applyobmod(arm, findobmod(OM_BLOODSTAINED)); } @@ -23934,6 +24063,14 @@ void startlfturn(lifeform_t *lf) { } } */ + if (f->id == F_SOULLINK) { + lifeform_t *lf2; + lf2 = findlf(lf->cell->map, f->val[0]); + if (!lf2) { + killflag(f); + continue; + } + } if (f->id == F_SPOTTED) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); @@ -24173,6 +24310,68 @@ int steal(lifeform_t *lf, obpile_t *op, enum FLAG wantflag) { return B_FALSE; } +// returns TRUE if l woke up. +int stir(lifeform_t *l, int vol, int dist, char *noisetext) { + flag_t *f; + int rv = B_FALSE; + + // wake up a little + f = lfhasflag(l, F_ASLEEP); + if (f && (f->val[1] != ST_KO)) { + if (f->lifetime > 0) { // ie. temporary + timeeffectsflag(f, vol + rnd(1,3)); + } else if (f->lifetime == PERMENANT) { + if (f->val[2] == NA) { + // ie asleep rather than 'resting' + // wake up! + if (isplayer(l)) { + msg("^wA nearby noise %s you!", (f->val[1] == ST_MEDITATING) ? "startles" : "awakens" ); + rv = B_TRUE; + } + killflag(f); + } else { + // ie resting on purpose via 'R' + // only wake up if the sound if very close + if (vol >= dist) { + // wake up! + if (isplayer(l)) { + if (noisetext) { + char wakenoise[BUFLEN]; + char *punc; + strcpy(wakenoise, noisetext); + // omit punctuation + punc = &(wakenoise[strlen(wakenoise)-1]); + switch (*punc) { + case '"': break; + default: + *punc = '\0'; + break; + } + msg("^wThe sound of %s awakens you!", wakenoise); + } else { + msg("Something awakens you!"); + } + rv = B_TRUE; + } + killflag(f); + } + } + // make it temporary + //f->lifetime = rnd(1,10); + } + + // still asleep? + f = lfhasflag(l, F_ASLEEP); + if (f && (f->val[1] == ST_ASLEEP) && cansee(player, l)) { + char lfname[BUFLEN]; + getlfname(l, lfname); + msg("%s stir%s in %s slumber...", lfname, + isplayer(l) ? "" : "s", + isplayer(l) ? "your" : "its"); + } + } + return rv; +} int stone(lifeform_t *lf) { char lfname[BUFLEN]; @@ -24364,6 +24563,7 @@ lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, char *ra int takeoff(lifeform_t *lf, object_t *o) { char obname[BUFLEN]; char buf[BUFLEN]; + object_t *errob = NULL; if (!isarmour(o)) { return unweild(lf, o); @@ -24376,7 +24576,7 @@ int takeoff(lifeform_t *lf, object_t *o) { getobname(o, obname, 1); - if (!cantakeoff(lf, o)) { + if (!cantakeoff(lf, o, &errob)) { switch (reason) { case E_CURSED: if (isplayer(lf)) { @@ -24396,6 +24596,16 @@ int takeoff(lifeform_t *lf, object_t *o) { msg("Your injury prevents you from removing your %s.", noprefix(obname)); } break; + case E_UNDERNEATH: + if (isplayer(lf)) { + if (errob) { + getobname(errob, buf, 1); + msg("Your %s is in the way!", noprefix(buf)); + } else { + msg("You will need to remove your outer armour first."); + } + } + break; default: if (isplayer(lf)) { msg("For some reason, you cannot remove your %s!", noprefix(obname)); @@ -24819,7 +25029,7 @@ int real_touch(lifeform_t *lf, object_t *o, int onpurpose) { f = hasflag(lf->flags, F_FREEZINGTOUCH); if (f) { // not wearing gloves? - if (!getequippedob(lf->pack, BP_HANDS)) { + if (!getouterequippedob(lf, BP_HANDS)) { // default power of 4 dospelleffects(lf, OT_S_FREEZEOB, 4, NULL, o, NULL, B_UNCURSED, NULL, B_FALSE, NULL); @@ -24838,7 +25048,7 @@ int real_touch(lifeform_t *lf, object_t *o, int onpurpose) { } } - gloves = getequippedob(lf->pack, BP_HANDS); + gloves = getouterequippedob(lf, BP_HANDS); // undead and blessed objects? if (isundead(lf) && isblessed(o)) { @@ -25138,6 +25348,7 @@ void unsummon(lifeform_t *lf, int vanishobs) { int unweild(lifeform_t *lf, object_t *o) { char obname[BUFLEN]; char buf[BUFLEN]; + object_t *errob; getobname(o, obname, 1); @@ -25146,7 +25357,7 @@ int unweild(lifeform_t *lf, object_t *o) { return B_TRUE; } - if (!cantakeoff(lf, o)) { + if (!cantakeoff(lf, o, &errob)) { switch (reason) { case E_CURSED: if (isplayer(lf)) { @@ -25161,9 +25372,19 @@ int unweild(lifeform_t *lf, object_t *o) { msg("You are not weilding that!"); } break; + case E_UNDERNEATH: + if (isplayer(lf)) { + if (errob) { + getobname(errob, buf, 1); + msg("Your %s is in the way!", noprefix(buf)); + } else { + msg("You will need to remove your outer armour first."); + } + } + break; default: if (isplayer(lf)) { - msg("For some reason, you cannot stop weilding your %s!", noprefix(obname)); + msg("For some reason, you cannot take off your %s!", noprefix(obname)); } break; } @@ -26457,7 +26678,7 @@ int wear(lifeform_t *lf, object_t *o) { if (o->type->obclass->id == OC_RING) { bp = BP_NONE; for (i = 0; i < nparts; i++) { - if (isfreebp(lf, possbp[i])) { + if (isfreebp(lf, possbp[i], o)) { bp = possbp[i]; break; } @@ -26478,11 +26699,15 @@ int wear(lifeform_t *lf, object_t *o) { initprompt(&prompt, buf); for (i = 0; i < nparts; i++) { object_t *inway; - inway = getequippedob(lf->pack, possbp[i]); + inway = getouterequippedob(lf, possbp[i]); if (inway) { char inwayname[BUFLEN]; getobname(inway, inwayname, inway->amt); - snprintf(buf, BUFLEN, "%s (replace %s)", getbodypartname(lf, possbp[i]), inwayname); + if (hasflag(inway->flags, F_UNDERCLOTHING)) { + snprintf(buf, BUFLEN, "%s (over %s)", getbodypartname(lf, possbp[i]), inwayname); + } else { + snprintf(buf, BUFLEN, "%s (replace %s)", getbodypartname(lf, possbp[i]), inwayname); + } } else { snprintf(buf, BUFLEN, "%s", getbodypartname(lf, possbp[i])); } @@ -27227,8 +27452,8 @@ int willbackstab(lifeform_t *lf, lifeform_t *victim, object_t *wep) { int willbleedfrom(lifeform_t *lf, enum BODYPART bp) { object_t *o; - o = getequippedob(lf->pack, bp); - if (o && (o->type->id == OT_BANDAGE)) { + o = hasequippedobidon(lf->pack, OT_BANDAGE, bp); + if (o) { // don't bleed. return B_FALSE; } diff --git a/lf.h b/lf.h index d58de10..2781af8 100644 --- a/lf.h +++ b/lf.h @@ -72,7 +72,7 @@ int canthrow(lifeform_t *lf, object_t *o, enum ERROR *why); int canuseweapons(lifeform_t *lf); int canwear(lifeform_t *lf, object_t *o, enum BODYPART where); int canweild(lifeform_t *lf, object_t *o); -int cantakeoff(lifeform_t *lf, object_t *o); +int cantakeoff(lifeform_t *lf, object_t *o, object_t **errob); 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); @@ -347,6 +347,7 @@ int lfproduceslight(lifeform_t *lf, object_t **fromwhat); int lighthurtseyes(lifeform_t *lf); int lockpick(lifeform_t *lf, cell_t *targcell, object_t *target, object_t *device); void loseobflags(lifeform_t *lf, object_t *o, int kind); +int handlearmour(lifeform_t *lf, object_t *fromob, int *dam, enum DAMTYPE dt); int hasbp(lifeform_t *lf, enum BODYPART bp); flag_t *hasactivespell(lifeform_t *lf, enum OBTYPE sid); int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest); @@ -371,7 +372,7 @@ object_t *isdualweilding(lifeform_t *lf); flag_t *isfleeing(lifeform_t *lf); flag_t *isfleeingfrom(lifeform_t *lf, lifeform_t *runfrom); int isflyingwithwings(lifeform_t *lf); -int isfreebp(lifeform_t *lf, enum BODYPART bp); +int isfreebp(lifeform_t *lf, enum BODYPART bp, object_t *whatfor); int isfriendly(lifeform_t *lf); int isfullyhealed(lifeform_t *lf); int isexhausted(lifeform_t *lf); @@ -425,8 +426,8 @@ void loseconcentration(lifeform_t *lf); void loseconsciousness(lifeform_t *lf, int howlong, lifeform_t *fromlf); int losehp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc); int losehp_bp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int bodypart); -int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam, object_t *fromob, int retaliate, int *waskod, int doeffects, int bodypart); -void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fromlf, object_t *fromob, int retaliate, int ko, int *waskod, int prelowhp, int bodypart); +int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam, object_t *fromob, int retaliate, int *waskod, int doeffects, int bodypart, int fromcrit); +void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fromlf, object_t *fromob, int retaliate, int ko, int *waskod, int prelowhp, int bodypart, int damreducebyarm, int crit); void losemp(lifeform_t *lf, int amt); void loselevel(lifeform_t *lf, int amt, lifeform_t *fromlf); void loseskill(lifeform_t *lf, enum SKILL skid); @@ -519,6 +520,7 @@ int slipon(lifeform_t *lf, object_t *o); void sortlf(map_t *map, lifeform_t *lf); void startlfturn(lifeform_t *lf); int steal(lifeform_t *lf, obpile_t *op, enum FLAG wantflag); +int stir(lifeform_t *lf, int vol, int dist, char *noisetext); int stone(lifeform_t *lf); int stopclimbing(lifeform_t *lf, int onpurpose); void stopeating(lifeform_t *lf); diff --git a/map.c b/map.c index 6aa5f77..26f4016 100644 --- a/map.c +++ b/map.c @@ -985,24 +985,37 @@ void adjustcellglyph(cell_t *c, glyph_t *g, enum CELLADJUSTTYPE how) { if ((how == CA_CH) || (how == CA_BOTH)) { // for certain cell types, select glyph based on surrounding cells of same type if (g->ch == UNI_DYNAMIC) { - int adj = 0,i; + int adj = 0,i,ndirslinked = 0,n; cell_t *c2; - for (i = D_N; i <= D_W; i++) { - int this; - c2 = getcellindir(c,i); - if (c2 && c2->known && ((c2->type->id == c->type->id) || hasdoor(c2)) ) { - //if (c2 && c2->known && (issolid(c2) || hasdoor(c2)) ) { - this = 1; - // we want: - // N E S W - // 8 4 2 1 - if (i == D_N) this <<= 3; - else if (i == D_E) this <<= 2; - else if (i == D_S) this <<= 1; - } else { - this = 0; + for (n = 0; ((ndirslinked == 0) && (n < 2)); n++) { + for (i = D_N; i <= D_W; i++) { + int this = 0; + int typematches = B_FALSE; + c2 = getcellindir(c,i); + if (c2) { + if (n == 0) { // first pass + if (c2->type->id == c->type->id) { + typematches = B_TRUE; + } + } else { // second pass + if (issolid(c2)) { + typematches = B_TRUE; + } + } + + if (c2->known && (typematches || hasdoor(c2)) ) { + this = 1; + ndirslinked++; + // we want: + // N E S W + // 8 4 2 1 + if (i == D_N) this <<= 3; + else if (i == D_E) this <<= 2; + else if (i == D_S) this <<= 1; + } + } + adj |= this; } - adj |= this; } switch (adj) { case 1: // left diff --git a/move.c b/move.c index 56d87de..bdc5b5a 100644 --- a/move.c +++ b/move.c @@ -132,17 +132,18 @@ int canswapwith(lifeform_t *lf, lifeform_t *lf2) { return B_FALSE; } - // cannot swap with sleeping lfs - if (lfhasflag(lf2, F_ASLEEP)) { - return B_FALSE; - } // allies can always swap if (areallies(lf, lf2)) { return B_TRUE; } - if (isplayer(lf) && !areenemies(lf, lf2)) { - if (isknownpeaceful(lf2)) { + + if (isplayer(lf)) { + if (lfhasflag(lf2, F_ASLEEP)) { + if (getlfsize(lf2) <= getlfsize(lf)) { + return B_TRUE; + } + } else if (!areenemies(lf, lf2) && isknownpeaceful(lf2)) { // player can swap with peaceful lfs // if they are a lot smaller //if (getlfsize(lf) - getlfsize(lf2) >= 2) { @@ -1459,7 +1460,7 @@ int movelf(lifeform_t *lf, cell_t *newcell, int onpurpose) { if (f && hasbp(lf, BP_FEET) && !lfhasflag(lf, F_CAREFULMOVE) && !isairborne(lf, NULL)) { object_t *boots; // has boots on? - boots = getequippedob(lf->pack, BP_FEET); + boots = getouterequippedob(lf, BP_FEET); if (!boots) { // take damage getobname(o, obname, 1); @@ -2862,6 +2863,11 @@ void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int changedir1, int changedir2 } setlosdirty(lf1); setlosdirty(lf2); + + // ie. asleep, meditating or unconscious + if (lfhasflag(lf2, F_ASLEEP)) { + stir(lf2, SV_TALK, 1, NULL); + } } // teleport somewhere, along with puffs of smoke etc @@ -3061,7 +3067,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { */ // warn before moving onto dangerous cells - if (onpurpose && isplayer(lf) && !lfhasflag(lf, F_CAREFULMOVE) && !rndmove) { + if (onpurpose && isplayer(lf) && !lfhasflag(lf, F_CAREFULMOVE) && !rndmove && !lfhasflag(lf, F_RAGE)) { char ques[BUFLEN]; char ch; if (cell && celldangerous(lf, cell, B_TRUE, &errcode)) { diff --git a/nexus.c b/nexus.c index 689f025..798482c 100644 --- a/nexus.c +++ b/nexus.c @@ -810,9 +810,12 @@ void checkdeath(void) { removedeadobs(lf->pack); // check for death if (lf->hp <= 0) { - // die! - die(lf); - continue; + if (hasactivespell(lf, OT_S_DELAYDEATH) && (lf->hp > -15)) { + } else { + // die! + die(lf); + continue; + } } } diff --git a/objects.c b/objects.c index 04aed08..79915cb 100644 --- a/objects.c +++ b/objects.c @@ -7430,6 +7430,13 @@ object_t *hasequippedobid(obpile_t *op, enum OBTYPE oid) { } return NULL; } +object_t *hasequippedobidon(obpile_t *op, enum OBTYPE oid, enum BODYPART bp) { + object_t *o; + for (o = op->first ; o ; o = o->next) { + if ((o->type->id == oid) && isequippedon(o, bp)) return o; + } + return NULL; +} object_t *hasknownob(obpile_t *op, enum OBTYPE oid) { object_t *o; @@ -12817,6 +12824,15 @@ int readsomething(lifeform_t *lf, object_t *o) { } return B_TRUE; } + + if (lfhasflag(lf, F_SILENCED)) { + if (o->type->obclass->id != OC_BOOK) { + if (isplayer(lf)) { + msg("You are unable to make a sound!"); + } + return B_TRUE; + } + } strcpy(triedonbuf, ""); @@ -14179,7 +14195,7 @@ int real_takedamage(object_t *o, int howmuch, int damtype, int wantannounce, lif // now use the REAL name real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_SHOWALL); - losehp_real(owner, howmuch , damtype, NULL, obname, B_DAMADJUST, o, B_NORETALIATE, NULL, B_DAMEFFECTS, f ? f->val[0] : BP_NONE); + losehp_real(owner, howmuch , damtype, NULL, obname, B_DAMADJUST, o, B_NORETALIATE, NULL, B_DAMEFFECTS, f ? f->val[0] : BP_NONE, B_NOCRIT); if (isdead(owner)) { return howmuch; } @@ -15009,6 +15025,16 @@ int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int sp youhit = B_FALSE; myroll = rnd(1,100); + // target has 'nudge missiles' spell? penalty, and the missile + // gets slightly slowed. + if (target && hasactivespell(target, OT_S_SLOWMISSILES)) { + myroll += 10; + if (speed > 1) speed--; + } + + // easier to hit with faster projectiles. + myroll -= (speed*2); + // blessed projectile vs undead? 20% bonus. if (isblessed(o) && isundead(target)) { myroll -= 20; @@ -15261,7 +15287,7 @@ int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int sp whogetsxp = thrower; } losehp_real(target, dam, DT_PROJECTILE, whogetsxp, damstring, B_DAMADJUST, o, - B_RETALIATE, NULL, B_DAMEFFECTS, BP_NONE); + B_RETALIATE, NULL, B_DAMEFFECTS, BP_NONE, B_NOCRIT); } if (reduceamt && (speed >= 3)) { diff --git a/objects.h b/objects.h index 2bc303f..2b1d9d7 100644 --- a/objects.h +++ b/objects.h @@ -167,6 +167,7 @@ char *gettopobname(cell_t *c, char *retbuf); enum BODYPART getweildloc(object_t *o, lifeform_t *lf, enum BODYPART *otherloc, int *twohanded); int hasedibleob(obpile_t *op); object_t *hasequippedobid(obpile_t *op, enum OBTYPE oid); +object_t *hasequippedobidon(obpile_t *op, enum OBTYPE oid, enum BODYPART bp); object_t *hasknownob(obpile_t *op, enum OBTYPE oid); object_t *hasob(obpile_t *op, enum OBTYPE oid); object_t *hasobletter(obpile_t *op, char letter); diff --git a/spell.c b/spell.c index e94c6f2..da4ad6a 100644 --- a/spell.c +++ b/spell.c @@ -826,6 +826,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef case E_LFINWAY: msg("Something is in the way!"); break; case E_SWIMMING: msg("You can't climb while swimming!"); break; case E_TOOHEAVY: msg("Your load is too heavy to climb with!"); break; + case E_NOABIL: msg("You cannot climb!"); break; default: msg("For some reason, you can't climb."); break; } } @@ -2206,6 +2207,10 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef if (f) killflag(f); } else if (abilid == OT_A_SONICBOLT) { int volume,nwalls; + if (lfhasflag(user, F_SILENCED)) { + if (isplayer(user)) msg("You are unable to make a sound!"); + return B_TRUE; + } if (!validatespellcell(user, &targcell,TT_MONSTER, abilid, power, B_FALSE)) return B_TRUE; target = targcell->lf; @@ -3668,6 +3673,10 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef practice(user, SK_THIEVERY, 1); } } else if (abilid == OT_A_WARCRY) { + if (lfhasflag(user, F_SILENCED)) { + if (isplayer(user)) msg("You are unable to make a sound!"); + return B_TRUE; + } // announce if (isplayer(user)) { msg("You shout a blood-curdling war cry!"); @@ -3965,6 +3974,43 @@ void addbuildchoice(prompt_t *p, lifeform_t *lf, enum OBTYPE oid, char *ch) { } +// returns true if you can't charm them. +int checkcharm(lifeform_t *caster, lifeform_t *target) { + char targetname[BUFLEN]; + getlfname(target, targetname); + // if the target is of a different raceclass, you usually + // can't charm them. + if (getraceclass(target) != getraceclass(caster)) { + 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_TRUE; + } + } + + if (!ischarmable(target)) { + if (isplayer(caster)) { + err_nocharm(reason, targetname); + } + return B_TRUE; + } + // ok + return B_FALSE; +} + // returns TRUE on error int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_t *target, object_t *targob, cell_t *targcell, int blessed, int *seenbyplayer, int frompot, object_t *fromob) { char buf[BUFLEN]; @@ -5473,60 +5519,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } getlfname(target, targetname); - // if the target is of a different raceclass, you usually - // can't charm them. - if (getraceclass(target) != getraceclass(caster)) { - 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; - } - } + if (checkcharm(caster, target)) return B_FALSE; - if (!ischarmable(target)) { - if (isplayer(caster)) { - switch (reason) { - case E_DRUNK: - msg("%s%s mind is too alcohol-impaired for you to charm.",targetname,getpossessive(targetname)); - break; - case E_LOWIQ: - msg("%s%s intellect is too simple for you to charm.",targetname,getpossessive(targetname)); - break; - case E_UNDEAD: - msg("The undead are immune to charming."); - break; - case E_ROBOT: - msg("Robots 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; - } - } - return B_FALSE; - } - - if (getallegiance(caster) == AL_PEACEFUL) { - fizzle(caster); - return B_TRUE; - } - if (ispetof(target, caster)) { + if ((getallegiance(target) == AL_PEACEFUL) || ispetof(target, caster)) { if (isplayer(caster)) { msg("%s is already allied with you!",targetname); } @@ -5575,31 +5570,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } - if (!ischarmable(target)) { - if (isplayer(caster)) { - switch (reason) { - case E_DRUNK: - msg("%s%s mind is too alcohol-impaired for you to charm.",targetname,getpossessive(targetname)); - break; - case E_LOWIQ: - msg("%s%s intellect is too simple for you to charm.",targetname,getpossessive(targetname)); - break; - case E_UNDEAD: - msg("The undead are immune to charming."); - break; - case E_ROBOT: - msg("Robots 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; - } - } - return B_FALSE; - } + if (checkcharm(caster, target)) return B_FALSE; if (getallegiance(caster) == AL_PEACEFUL) { fizzle(caster); @@ -6257,6 +6228,15 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } + } else if (spellid == OT_S_DELAYDEATH) { + if (!target) { + target = caster; + } + if (isplayer(target)) { + msg("^gYou bend your psionic will towards defying death!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + statdirty = B_TRUE; + } } else if (spellid == OT_S_DETECTAURA) { if (isplayer(caster)) { object_t *o; @@ -8704,6 +8684,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); return B_TRUE; } + } else if (spellid == OT_S_MINDSHIELD) { + flag_t *f; + f = addtempflag(caster->flags, F_MINDSHIELD, B_TRUE, NA, NA, NULL, FROMSPELL); + f->obfrom = spellid; } else if (spellid == OT_S_MINDWHIP) { target = targcell->lf; if (!target) { @@ -8816,6 +8800,14 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f = addtempflag(caster->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; + } else if (spellid == OT_S_SLOWMISSILES) { + if (!target) { + target = caster; + } + if (isplayer(target)) { + msg("^gYou attune your mind to deflect incoming projectiles."); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } } else if (spellid == OT_S_NULLIFY) { flag_t *retflag[MAXCANDIDATES],*poss[MAXCANDIDATES],*f; int nretflags,i,ndone = 0,nposs; @@ -11295,6 +11287,28 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("^%c%s%s body seems to shimmer and bend!", getlfcol(caster, CC_GOOD), castername, getpossessive(castername)); } + } else if (spellid == OT_S_REMOTEKO) { + char targetname[BUFLEN]; + + target = targcell->lf; + + if (!target) { + fizzle(caster); + return B_TRUE; + } + getlfname(target, targetname); + + if (checkcharm(caster, target)) return B_FALSE; + + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { + // they get angry! + if (!isplayer(target) && cansee(target, caster)) { + fightback(target, caster); + } + } else { + // ko ! + loseconsciousness(target, rnd(50,100), caster); + } } else if (spellid == OT_S_REPELINSECTS) { // just announce if (isplayer(caster)) { @@ -11702,6 +11716,35 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); return B_TRUE; } + } else if (spellid == OT_S_SILENCE) { + int howlong = 30; + target = targcell->lf; + + if (!target || hasflag(target->flags, F_SILENCED)) { + fizzle(caster); + return B_TRUE; + } + + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { + if (!isdeaf(player)) { + if (isplayer(target)) { + msg("Noise around you sound softer for a moment."); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (haslos(player, target->cell)) { + getlfname(target, buf); + msg("Noises around %s sound softer for a moment.", buf); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + } + return B_FALSE; + } + + howlong = rnd(20,30) + power*2; + + addtempflag(target->flags, F_SILENCED, NA, NA, NA, NULL, howlong); + if (isplayer(target) || haslos(player, target->cell)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; + } } else if (spellid == OT_S_SIXTHSENSE) { flag_t *f; if (!target) target = caster; @@ -11859,6 +11902,11 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int howlong = 15; target = targcell->lf; + if (!target) { + fizzle(caster); + return B_TRUE; + } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily slower."); @@ -12063,6 +12111,64 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); return B_TRUE; } + } else if (spellid == OT_S_SOULLINK) { + lifeform_t *targ2 = NULL; + char buf[BUFLEN],tname[BUFLEN],t2name[BUFLEN]; + if (caster && !isplayer(caster)) { + fizzle(caster); + return B_TRUE; + } + if (!target) { + target = targcell->lf; + } + if (!target || (target == caster)) { + fizzle(caster); + return B_TRUE; + } + + real_getlfnamea(target, tname, NULL, B_SHOWALL, B_REALRACE); + + if (power < 3) { + // link caster to target + targ2 = caster; + } else { + cell_t *where; + char smallprompt[BUFLEN]; + // link target to target2 + // ask for a target cell + snprintf(buf, BUFLEN, "Who will you soul-link %s to?", tname); + snprintf(smallprompt, BUFLEN, "soul link:%s->", tname); + where = askcoords(buf, smallprompt, TT_MONSTER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); + if (where && where->lf && cansee(caster, where->lf)) { + targ2 = where->lf; + } else { + fizzle(caster); + return B_TRUE; + } + } + + if (targ2 == target) { + fizzle(caster); + return B_TRUE; + } + + real_getlfnamea(targ2, t2name, NULL, B_SHOWALL, B_REALRACE); + + if (hasflag(target->flags, F_SOULLINK) || hasflag(targ2->flags, F_SOULLINK)) { + msg("^bAn existing soul link prevents your spell from working!"); + return B_TRUE; + } + + // link caster to target + sprintf(buf, "%s%s soul link spell", castername, getpossessive(castername)); + addflag(target->flags, F_SOULLINK, targ2->id, NA, NA, buf); + addflag(targ2->flags, F_SOULLINK, target->id, NA, NA, buf); + + // soullink flag is only announce for the player, so oif we linked to someone else, + // provide some feedback. + if (targ2 != player) { + msg("^gYou establish a soul link between %s and %s!",tname,t2name); + } } else if (spellid == OT_S_SPARK) { object_t *o,*nexto; int donesomething = B_FALSE; @@ -15206,8 +15312,12 @@ int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power bonus += 8; } - if (spellisfromschool(spellid, SS_MENTAL) && lfhasflag(target, F_MINDSHIELD)) { - resisted = B_TRUE; + if (spellisfromschool(spellid, SS_MENTAL)) { + if (lfhasflag(target, F_MINDSHIELD)) { + resisted = B_TRUE; + } else { + resisted = skillcheck(target, SC_IQ, getmrdiff(spellid,power), bonus); + } } else { resisted = skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), bonus); } diff --git a/spell.h b/spell.h index 39e6515..2057bc1 100644 --- a/spell.h +++ b/spell.h @@ -4,6 +4,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifeform_t *target, flag_t *cwflag); void addbuildchoice(prompt_t *p, lifeform_t *lf, enum OBTYPE oid, char *ch); +int checkcharm(lifeform_t *caster, lifeform_t *target); int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_t *target, object_t *targob, cell_t *targcell, int blessed, int *seenbyplayer, int frompot, object_t *fromob); objecttype_t *findspelln(char *buf); enum SPELLSCHOOL findspellschoolbyname(char *buf); diff --git a/text.c b/text.c index 71e6b02..1caf5ae 100644 --- a/text.c +++ b/text.c @@ -7,6 +7,7 @@ #include "attack.h" #include "defs.h" #include "flag.h" +#include "io.h" #include "lf.h" #include "map.h" #include "move.h" @@ -485,6 +486,29 @@ char *dicetotext(int ndice, int nsides, int bonus, int *min, int *max, char *dic return dicebuf; } +void err_nocharm(enum ERROR reason, char *targetname) { + switch (reason) { + case E_DRUNK: + msg("%s%s mind is too alcohol-impaired for you to charm.",targetname,getpossessive(targetname)); + break; + case E_LOWIQ: + msg("%s%s intellect is too simple for you to charm.",targetname,getpossessive(targetname)); + break; + case E_UNDEAD: + msg("The undead are immune to charming."); + break; + case E_ROBOT: + msg("Robots 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; + } +} + int flip(int ch) { switch (ch) { case 'a': return 0x0250; @@ -1278,14 +1302,14 @@ char *getflagsourcetext(flag_t *f, char *buf) { case FROMOBEQUIP: case FROMOBHOLD: case FROMOBACTIVATE: - if (f->lifetime == FROMOBEQUIP) strcpy(action, "equipping"); - else if (f->lifetime == FROMOBEQUIP) strcpy(action, "holding"); - else strcpy(action, "activating"); + if (f->lifetime == FROMOBEQUIP) strcpy(action, "equipped"); + else if (f->lifetime == FROMOBEQUIP) strcpy(action, "held"); + else strcpy(action, "activated"); if (f->pile->ob) { getobname(f->pile->ob, obname, f->pile->ob->amt); } else { - strcpy(obname, "an object"); + strcpy(obname, "object"); } sprintf(buf," (from %s %s)", action, obname); break; diff --git a/text.h b/text.h index 561ee07..f06d0a9 100644 --- a/text.h +++ b/text.h @@ -8,6 +8,7 @@ char *capitaliseall(char *text); enum COLOUR chartocol(char ch); char *construct_hit_string(lifeform_t *lf, lifeform_t *victim, char *attackername, char *victimname, char *victimbpname, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp, int damidx, int critical, int backstab, int fatal, int isunarmed, char *retbuf); char *dicetotext(int ndice, int nsides, int bonus, int *min, int *max, char *dicebuf, char *minmaxbuf); +void err_nocharm(enum ERROR reason, char *targetname); int flip(int ch); char *getaccuracyname(int accpct); int getaccuracymodnum(int accmodpct);