diff --git a/ai.c b/ai.c index 66874ed..366fd1b 100644 --- a/ai.c +++ b/ai.c @@ -1950,7 +1950,8 @@ int aipickup(lifeform_t *lf, object_t *o) { if ((o->type->id == OT_COFFIN) && (lf->race->id == R_GASCLOUD) && lfhasflagval(lf, F_ORIGRACE, R_VAMPIRE, NA, NA, NULL)) { return rest(lf, B_TRUE); } - if (isedible(o)) { +// if (isedible(o)) { + if (caneat(lf, o)) { return eat(lf, o); } else { return pickup(lf, o, o->amt, B_TRUE, B_TRUE); @@ -2474,6 +2475,11 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG if ((ot->id == OT_S_BLINDNESS) && isblind(victim)) { specificcheckok = B_FALSE; } + if (ot->id == OT_S_BLOODBOIL) { + if (lfhasflag(victim, F_BLOODBOIL)) { + specificcheckok = B_FALSE; + } + } if (ot->id == OT_S_DEATHKEEN) { if (!isnighttime()) specificcheckok = B_FALSE; } @@ -2503,6 +2509,28 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG if ((ot->id == OT_S_DRAINLIFE) && isimmuneto(victim->flags, DT_NECROTIC, B_FALSE)) { specificcheckok = B_FALSE; } + if (ot->id == OT_S_GATHERFLAME) { + int i,found=B_FALSE; + // don't cast if we're made of fire! + if ((lf->material->id == MT_FIRE) && (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE)) { + specificcheckok = B_FALSE; + } else { + // must be fire nearby + for (i = 0; i < lf->nlos; i++) { + if (lf->los[i]->lf && (lf->los[i]->lf->material->id == MT_FIRE)) { + found = B_TRUE; + break; + } + if (hasobofmaterial(lf->los[i]->obpile, MT_FIRE)) { + found = B_TRUE; + break; + } + } + if (!found) { + specificcheckok = B_FALSE; + } + } + } if (ot->id == OT_A_FLIP) { if (getlfsize(victim) > getlfsize(lf)) { specificcheckok = B_FALSE; @@ -2713,6 +2741,12 @@ int aiwants_real(lifeform_t *lf, object_t *o, int *covets, int *noids, enum OBTY } } for (i = 0; i < *nwantflags; i++) { + // special case to cope with eating objects that aren't normally edible. + // eg. goats eating wood due to F_CANEATMATERIAL + if ((wantflag[i] == F_EDIBLE) && caneat(lf, o)) { + if (covets) *covets = wantflagcovet[i]; + return B_TRUE; + } if (hasflag(o->flags, wantflag[i])) { if ((wantflag[i] == F_EDIBLE) && !caneat(lf, o)) { // special case } else { diff --git a/attack.c b/attack.c index d15118f..8d95356 100644 --- a/attack.c +++ b/attack.c @@ -21,6 +21,7 @@ extern lifeform_t *player; extern lifeform_t *godlf[]; extern int needredraw; +extern int statdirty; extern enum ERROR reason; @@ -795,7 +796,22 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) if (critical && !lfhasflag(lf, F_PHANTASM)) { object_t *armour; char noun[BUFLEN]; - critpos = getrandomcorebp(victim, lf); + + if (lfhasflag(victim, F_CANSEVER) && wep && (getdamtype(wep) == DT_SLASH)) { + flag_t *retflag[MAXCANDIDATES],*poss[MAXCANDIDATES],*f; + int nretflags = 0,nposs = 0; + // select a random sever-able body part + getflags(victim->flags, retflag, &nretflags, F_CANSEVER, F_NONE); + for (i = 0; i < nretflags; i++) { + if (hasbp(victim, retflag[i]->val[0])) { + poss[nposs++] = retflag[i]; + } + } + f = poss[rnd(0,nposs-1)]; + critpos = f->val[0]; + } else { + critpos = getrandomcorebp(victim, lf); + } if (critpos == BP_NONE) { strcpy(victimbpname, victimname); } else { @@ -1829,6 +1845,65 @@ void criticalhit(lifeform_t *lf, lifeform_t *victim, enum BODYPART hitpos, int d if (damtype == DT_PIERCE) damtype = DT_SLASH; if (damtype == DT_CHOP) damtype = DT_SLASH; + // special case - beheading multiheaded monsters + if (lf && victim && (damtype == DT_SLASH)) { + flag_t *selflag; + selflag = lfhasflagval(victim, F_CANSEVER, hitpos, NA, NA, NULL); + if (selflag) { + int i,nretflags,num; + char dambuf[BUFLEN]; + char bpname[BUFLEN]; + flag_t *retflag[MAXCANDIDATES]; + strcpy(bpname, getbodypartname(victim, hitpos)); + // special case + if (!victim->race->id == R_HYDRA) { + // remove it + addflag(victim->flags, F_NOBODYPART, hitpos, NA, NA, NULL); + // remove hasattack flags + getflags(victim->flags, retflag, &nretflags, F_CANCAST, F_HASATTACK, F_NONE); + for (i = 0; i < nretflags; i++) { + if (retflag[i]->id == F_CANCAST) { + if (retflag[i]->val[0] == selflag->val[2]) { + killflag(retflag[i]); + continue; + } + } else if (retflag[i]->id == F_HASATTACK) { + if (retflag[i]->val[2] == selflag->val[1]) { + killflag(retflag[i]); + continue; + } + } + } + } + if (cansee(player, victim)) { + char lfname[BUFLEN],vname[BUFLEN]; + getlfname(victim, vname); + if (lf && cansee(player, lf)) { // can see who did it + getlfname(lf, lfname); + } else { + strcpy(lfname, "Something"); + } + msg("^%c%s slice%s off %s%s %s!", getlfcol(victim, CC_VBAD), + lfname, isplayer(lf) ? "" : "s", vname, getpossessive(vname), bpname); + } + // take extra damage based on number of severable limbs + num = countflagsofid(victim->race->flags, F_CANSEVER); + + if (victim->race->id == R_HYDRA) { + growhydrahead(victim, B_TRUE); + } else { + sprintf(dambuf, "a severed %s", bpname); + losehp(victim, pctof(100/num, victim->maxhp), DT_DIRECT, lf, dambuf); + } + + // drop the head + if (strlen(selflag->text)) { + addob(victim->cell->obpile, selflag->text); + } + return; + } + } + if (damtype == DT_BASH) { switch (hitpos) { default: @@ -2572,8 +2647,6 @@ void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam) { } } - - getflags(fp, retflag, &nretflags, F_FLAMESTRIKE, F_HEAVYBLOW, F_HITCONFER, F_RACESLAY, F_REVENGE, F_RUSTED, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; diff --git a/data.c b/data.c index dd26faf..3a6bafd 100644 --- a/data.c +++ b/data.c @@ -3420,6 +3420,14 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 1, NA, NULL); + addot(OT_S_GATHERFLAME, "harvest flame", "Draws all nearby fire into the caster, boosting the power of their next spell.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Your next spell's power will be boosted by the amount of fire consumed."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the maximum amount of fire consumed."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_S_SPARK, "flambe", "Creates very hot but short lived burst of flame around the target, dealing 2d3 fire damage to creatures and objects.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); @@ -3443,6 +3451,13 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 2, NA, NULL); // l2 + addot(OT_S_BLOODBOIL, "bloodboil", "Energised the molecules of the target's blood, causing them to explode into flames upon death.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_CLEANSINGFIRE, "cleansing fire", "Draws power from nearby fires to heal the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell will affect up to ^bpower^n fires, healing 30% hit points from each."); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); @@ -4021,7 +4036,7 @@ void initobjects(void) { // mental/psionic /////////////////// // l1 - addot(OT_S_BOOSTCONFIDENCE, "boost confidence", "Instils the target with infinite courage, allowing them to (perhaps foolishly) continue fighting even when all seems lost.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_BOOSTCONFIDENCE, "ego boost", "Instils the target with boundless courage, allowing them to (perhaps foolishly) continue fighting even when all seems lost.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); @@ -9683,6 +9698,54 @@ void initrace(void) { addflag(lastrace->flags, F_MINIONS, 20, 1, 3, "goblin warrior"); addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); + addrace(R_CHIMERA, "chimera", 300, 'm', C_MAGENTA, MT_FLESH, RC_MAGIC, "A monstrous hybrid where a goat's rear has been magically attached to a lion's front. It has three heads - a goat's, a lion's and a wyrm's - and a pair of bat-like wings."); + setbodytype(lastrace, BT_QUADRAPED); + setbodypartname(lastrace, BP_HEAD, "lion's head"); + addbodypart(lastrace, BP_HEAD2, "dragon's head"); + addbodypart(lastrace, BP_HEAD3, "goat's head"); + addbodypart(lastrace, BP_TAIL, NULL); + addbodypart(lastrace, BP_WINGS, 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_SIZE, SZ_LARGE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_ALL, NA, RR_VERYRARE, NULL); + addflag(lastrace->flags, F_HITDICE, 9, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 9, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_VLOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_VLOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_VLOW, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL); + 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_HASATTACK, OT_TEETH, 4, BP_HEAD, NULL); // lion head + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 3, BP_HEAD2, NULL); // dragon head + addflag(lastrace->flags, F_HASATTACK, OT_HORN, 5, BP_HEAD3, NULL); // goat horns + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 3, NA, NULL); // front lion claws + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 3, NA, NULL); // front lion claws + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, SV_CAR, NA, "roars^a roar"); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, SV_SHOUT, NA, "bleats^an angry bleating"); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 3, NA, "screeches in pain^screeches of pain"); + addflag(lastrace->flags, F_CANSEVER, BP_HEAD, BP_HEAD, NA, "lion head"); + addflag(lastrace->flags, F_CANSEVER, BP_HEAD2, BP_HEAD2, OT_S_BURNINGWAVE, "red wyrm head"); + addflag(lastrace->flags, F_CANSEVER, BP_HEAD3, BP_HEAD3, NA, "goat head"); + addflag(lastrace->flags, F_CANWILL, OT_A_SWOOP, NA, NA, "range:2;"); + addflag(lastrace->flags, F_CANCAST, OT_S_BURNINGWAVE, NA, NA, "pw:6;"); + addflag(lastrace->flags, F_CASTCHANCE, 40, NA, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, "unleashes its fiery breath"); + addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 14, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); + addflag(lastrace->flags, F_FELINE, B_TRUE, NA, NA, NULL); + 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_GREY, 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); @@ -10778,6 +10841,43 @@ void initrace(void) { addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 7, NA, NA, NULL); + addrace(R_HYDRA, "hydra", 300, 'W', C_BLUE, MT_FLESH, RC_DRAGON, "A four legged serpentine reptile, resembling a wyrm except for its many extra heads."); + 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_SIZE, SZ_HUGE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_ALL, NA, RR_VERYRARE, NULL); + addflag(lastrace->flags, F_HITDICE, 5, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 11, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_VLOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_VLOW, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 4, NA, NULL); + // note: addlf() will add extra teeth attacks based on heads. + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, SV_CAR, NA, "hisses^a loud hissing"); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 3, NA, "roars in pain^roars of pain"); + addflag(lastrace->flags, F_CANSEVER, BP_HEAD, BP_HEAD, NA, "hydra head"); + //addflag(lastrace->flags, F_CANCAST, OT_S_BURNINGWAVE, NA, NA, "pw:6;"); + //addflag(lastrace->flags, F_CASTCHANCE, 40, NA, NA, NULL); + //addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, "unleashes its fiery breath"); + addflag(lastrace->flags, F_SEEINDARK, 4, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); + addflag(lastrace->flags, F_DTIMMUNE, DT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); + + addrace(R_KOBOLD, "kobold", 18, 'k', C_BROWN, MT_FLESH, RC_HUMANOID, "An evil humanoid race with doglike features, kobolds are known for their cowardace and prefer to attack from a distance if at all possible."); setbodytype(lastrace, BT_HUMANOID); setbodypartname(lastrace, BP_HANDS, "paws"); @@ -13806,6 +13906,42 @@ void initrace(void) { addflag(lastrace->flags, F_DTVULN, DT_SONIC, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL); + addrace(R_GOAT, "goat", 37, 'q', C_YELLOW, MT_FLESH, RC_ANIMAL, "Small, horned livestocks animals known for their appetite."); + setbodytype(lastrace, BT_QUADRAPED); + addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_ALIGNMENT, AL_NEUTRAL, NA, NA, NULL); + addflag(lastrace->flags, F_TERRITORIAL, 3, NA , NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, ""); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_COMMON, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_VHIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_LTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_HITDICE, 1, 2, NA, NULL); + addflag(lastrace->flags, F_TR, 1, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_HORN, 3, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 3, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 1, 1, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_CHARGE, NA, NA, "range:3;"); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 4, NA, "bleats in pain^bleating"); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, SV_SHOUT, NA, "bleats^an angry bleating"); + addflag(lastrace->flags, F_FLEEONHPPCT, 80, NA, NA, ""); + addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_WOOD, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_CLOTH, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_LEATHER, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_PAPER, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_PLASTIC, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_WETPAPER, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_RUBBER, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_SILK, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATMATERIAL, MT_PLANT, NA, NA, NULL); addrace(R_GYRFALCON, "gyrfalcon", 1, 'A', C_WHITE, MT_FLESH, RC_ANIMAL, "An enormous falcon, commonly found in arctic climates."); // 'A' for Avian setbodytype(lastrace, BT_BIRD); @@ -14817,7 +14953,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); - addrace(R_DRAGONBLUE, "blue wyrm", 400, 'W', C_BLUE, MT_FLESH, RC_DRAGON, "Blue wyrms are massive reptilian creatures who can (and will) consume almost any living creature."); + addrace(R_DRAGONBLUE, "blue wyrm", 400, 'W', C_CYAN, MT_FLESH, RC_DRAGON, "Blue wyrms are massive reptilian creatures who can (and will) consume almost any living creature."); setbodytype(lastrace, BT_HUMANOID); addbodypart(lastrace, BP_WINGS, NULL); addbodypart(lastrace, BP_TAIL, NULL); @@ -14868,7 +15004,7 @@ void initrace(void) { addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_ELECTRIC, NA, "100"); addflag(lastrace->flags, F_EATCONFER, F_ATTRMOD, A_STR, 5, "50"); - addrace(R_DRAGONBLUEY, "blue wyrmling", 150, 'w', C_BLUE, MT_FLESH, RC_DRAGON, "Blue wyrms are massive reptilian creatures who can (and will) consume almost any living creature."); + addrace(R_DRAGONBLUEY, "blue wyrmling", 150, 'w', C_CYAN, MT_FLESH, RC_DRAGON, "Blue wyrms are massive reptilian creatures who can (and will) consume almost any living creature."); setbodytype(lastrace, BT_HUMANOID); addbodypart(lastrace, BP_WINGS, NULL); addbodypart(lastrace, BP_TAIL, NULL); @@ -14919,7 +15055,7 @@ void initrace(void) { addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_ELECTRIC, NA, "50"); addflag(lastrace->flags, F_EATCONFER, F_ATTRMOD, A_STR, 5, "25"); - addrace(R_DRAGONBLUEA, "ancient blue wyrm", 600, 'W', C_BLUE, MT_FLESH, RC_DRAGON, "Blue wyrms are massive reptilian creatures who can (and will) consume almost any living creature."); + addrace(R_DRAGONBLUEA, "ancient blue wyrm", 600, 'W', C_CYAN, MT_FLESH, RC_DRAGON, "Blue wyrms are massive reptilian creatures who can (and will) consume almost any living creature."); setbodytype(lastrace, BT_HUMANOID); addbodypart(lastrace, BP_WINGS, NULL); addbodypart(lastrace, BP_TAIL, NULL); @@ -16657,6 +16793,11 @@ void initrace(void) { addflag(r->flags, F_NOFLEE, B_TRUE, NA, NA, NULL); addflag(r->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(r->flags, F_SEEINVIS, B_TRUE, NA, NA, NULL); + } else if (r->raceclass->id == RC_DRAGON) { + // wyrms hate hydras + if (r->id != R_HYDRA) { + addflag(r->flags, F_HATESRACE, R_HYDRA, NA, NA, NULL); + } } else if (r->raceclass->id == RC_GOD) { addflag(r->flags, F_PIETY, 100, NA, NA, NULL); addflag(r->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); diff --git a/defs.h b/defs.h index 9bfcd36..e833105 100644 --- a/defs.h +++ b/defs.h @@ -1021,6 +1021,7 @@ enum RACE { R_BOGGART, R_BUGBEAR, R_CENTAUR, + R_CHIMERA, R_COCKATRICE, R_CREEPINGCLAW, R_CRYMIDIA, @@ -1045,6 +1046,7 @@ enum RACE { R_HIPPOGRIFF, R_HOBGOBLIN, R_HOBGOBLINWAR, + R_HYDRA, R_KOBOLD, R_LEPRECHAUN, R_LESHY, @@ -1142,6 +1144,7 @@ enum RACE { R_DOGDEATH, R_DOGWAR, R_ELEPHANT, + R_GOAT, R_GYRFALCON, R_HARPY, R_HAWK, @@ -1589,8 +1592,9 @@ enum OBTYPE { OT_S_TORNADO, OT_S_WHIRLWIND, OT_S_WINDSHIELD, - // -- elemental - fire + // -- elemental - fire magic OT_S_BLADEBURN, + OT_S_BLOODBOIL, OT_S_BURNINGFEET, OT_S_BURNINGWAVE, OT_S_CLEANSINGFIRE, @@ -1599,6 +1603,7 @@ enum OBTYPE { OT_S_FIREBALL, OT_S_FLAMEPILLAR, OT_S_FLAMEBURST, + OT_S_GATHERFLAME, OT_S_IMMOLATE, OT_S_METEOR, OT_S_PYROMANIA, @@ -2249,8 +2254,10 @@ enum BODYPART { BP_FRONTLEGS = 15, BP_WINGS = 16, // <- core bodypart BP_TAIL = 17, // <- core bodypart + BP_HEAD2 = 18, + BP_HEAD3 = 19, }; -#define MAXBODYPARTS (18) +#define MAXBODYPARTS (20) // depth on a human @@ -2723,6 +2730,7 @@ enum FLAG { F_OBATTACKDELAY, // how long weapon takes to attack F_USESSKILL, // weapon needs skill sk_v0 F_MAGICBOOST, // boost power of all spells by v0 + F_TEMPMAGICBOOST, // boost power of next spell cast (only) by v0 F_WHIP, // this weapon is a whip - use different damtext. F_CANHAVEOBMOD, // weapon can have obmod om_v0 applied // optional: v1 is chance of randomly having it @@ -2874,6 +2882,13 @@ enum FLAG { // f_armourrating is used for innate armour. // f_arboost is used by objects "of protection" which // enhance your armour rating. + F_BLOODBOIL, // lf will explode into flames upon death + F_CANSEVER, // critical slash attacks might sever bodypart v0 + // v1: (optional) this will also remove HASATTACK + // flags with v2=this. + // v2: (optional) will also remove flags of CANCAST + // with v0 = this. + // text: object name of severed limb to drop F_DONEPICKUP, // lf has used their free pickup/drop for this turn. F_DONEKNOWLEDGETRADE, // you've already traded knowledge with this // person. @@ -3164,6 +3179,7 @@ enum FLAG { F_WANTS, // lf will try to pick up object type val0. // if val1 = B_COVETS, will even abandon attacks // for it! + F_WANTSOBMAT, // lf will look for obs of material v0. val1=covets F_WANTSOBFLAG, // lf will look for obs with this flag. val1=covets F_WANTSBETTERWEP, // lf will look for better weapons, val1=covets F_WANTSBETTERARM, // lf will look for better armour, val1=covets @@ -3295,6 +3311,8 @@ enum FLAG { // try to change its facing. F_CANTALK, // this lf can talk, even if its raceclass normally // wouldn't be able to. + F_CANEATMATERIAL, // this lf can eat objects with material v0, + // even if it's not normally edible. F_AQUATIC, // this race can attack normally in water and suffers no // movement penalties. they can also swim at master // level. diff --git a/io.c b/io.c index ac1061a..9d42651 100644 --- a/io.c +++ b/io.c @@ -1450,6 +1450,13 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_BLOODBOIL: + if (isplayer(lf)) { + msg("A malignant red nimbus surrounds you."); + } else if (cansee(player, lf)) { + msg("A malignant red nimbus surrounds %s.", lfname); + } + break; case F_BREATHWATER: if (isplayer(lf)) { msg("You can now breath normally underwater."); @@ -1947,6 +1954,13 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { msg("%s %s stunned!",lfname, is(lf)); donesomething = B_TRUE; break; + case F_TEMPMAGICBOOST: + // don't know if monsters get it + // also we only announce GAINING this flag, not losing it + if (isplayer(lf)) { + msg("Your magical power feels temporarily boosted!"); + } + break; case F_TREMORSENSE: if (isplayer(lf)) { // don't know if monsters get it msg("You can now 'see' by sensing vibrations around you."); @@ -2168,6 +2182,13 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { msg("%s can see again.",lfname); donesomething = B_TRUE; break; + case F_BLOODBOIL: + if (isplayer(lf)) { + msg("A calming blue nimbus surrounds you."); + } else if (cansee(player, lf)) { + msg("A calming blue nimbus surrounds %s.", lfname); + } + break; case F_BREATHWATER: if (isplayer(lf)) { msg("%s can no longer breath underwater.",lfname); @@ -11552,46 +11573,68 @@ void showlfstats(lifeform_t *lf, int showall) { // unarmed attacks op = addobpile(NULL, NULL, NULL); - for (f = lf->flags->first ; f ; f = f->next) { - if (f->id == F_HASATTACK) { - object_t *o; - objecttype_t *ot; - ot = findot(f->val[0]); - o = addobfast(op, ot->id); - if (o) { - char dambuf[BUFLEN]; - strcpy(dambuf, ""); - snprintf(buf, BUFLEN, "%s", o->type->name); - // damage - if (showall || (lorelev >= PR_BEGINNER)) { - int mindam,maxdam; + getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE); + for (i = 0; i < nretflags; i++) { + object_t *o; + objecttype_t *ot; + int instances = 1; + int n; - getdamrange(o, f, &mindam, &maxdam); + f = retflag[i]; + for (n = i+1 ; n < nretflags; n++) { + if ((retflag[n]->val[0] == f->val[0]) && + (retflag[n]->val[1] == f->val[1]) && + (retflag[n]->val[2] == f->val[2]) && + streq(retflag[n]->text, f->text)) { + instances++; + } + } - // DONT apply damage mod for strength - /* - if (!hasflag(o->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) { - mindam = (int)((float)mindam * dammod); - maxdam = (int)((float)maxdam * dammod); - } - if (mindam < 0) mindam = 0; - if (maxdam < 0) maxdam = 0; - */ - snprintf(dambuf, BUFLEN, " (%d-%d basedmg)",(int)mindam,(int)maxdam); + ot = findot(f->val[0]); + o = addobfast(op, ot->id); + if (o) { + char dambuf[BUFLEN]; + strcpy(dambuf, ""); + snprintf(buf, BUFLEN, "%s", o->type->name); + // damage + if (showall || (lorelev >= PR_BEGINNER)) { + int mindam,maxdam; + + getdamrange(o, f, &mindam, &maxdam); + + // DONT apply damage mod for strength + /* + if (!hasflag(o->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) { + mindam = (int)((float)mindam * dammod); + maxdam = (int)((float)maxdam * dammod); } + if (mindam < 0) mindam = 0; + if (maxdam < 0) maxdam = 0; + */ + snprintf(dambuf, BUFLEN, " (%d-%d basedmg)",(int)mindam,(int)maxdam); + } - doheadingsmall(mainwin, y2, x2, ftext, "Innate Attack"); + doheadingsmall(mainwin, y2, x2, ftext, "Innate Attack"); + // add '2 x whatever' for multiples. + if (instances == 1) { wprintw(mainwin, "%s", buf); - if (strlen(dambuf)) { - if (lorelev >= PR_BEGINNER) setcol(mainwin, lorecol); - wprintw(mainwin, "%s", dambuf); - if (lorelev >= PR_BEGINNER) unsetcol(mainwin, lorecol); - } - y2++; - } // end if o - } // end if fid == hasattack + } else { + wprintw(mainwin, "%d x %s", instances, buf); + } + if (strlen(dambuf)) { + if (lorelev >= PR_BEGINNER) setcol(mainwin, lorecol); + wprintw(mainwin, "%s", dambuf); + if (lorelev >= PR_BEGINNER) unsetcol(mainwin, lorecol); + } + y2++; + } // end if o + // now skip over multiple instances of the same flag + if (instances >= 2) { + i += (instances-1); + } } // end for each flag + // no attacks at all? if ((nweps == 0) && !op->first) { snprintf(buf, BUFLEN, "N/A"); @@ -12849,6 +12892,14 @@ void showlfstats(lifeform_t *lf, int showall) { isplayer(lf) ? "your" : "its", boost); y++; } + f = lfhasknownflag(lf, F_TEMPMAGICBOOST); + if (f && (f->known)) { + int boost; + sumflags(lf->flags, F_TEMPMAGICBOOST, &boost, NULL, NULL); + mvwprintw(mainwin, y, 0, "The power of %s next spell will be boosted by %d.", + isplayer(lf) ? "your" : "its", boost); + y++; + } f = lfhasknownflag(lf, F_MINDSHIELD); if (f && (f->known)) { mvwprintw(mainwin, y, 0, "%s are protected from psionic attacks.", you(lf)); diff --git a/lf.c b/lf.c index 2a3ca64..ed9fc15 100644 --- a/lf.c +++ b/lf.c @@ -768,6 +768,11 @@ int caneat(lifeform_t *lf, object_t *o) { return B_FALSE; } + // note: must do this check _before_ call to isedible()! + if (lfhasflagval(lf, F_CANEATMATERIAL, o->material->id, NA, NA, NULL)) { + return B_TRUE; + } + if (!isedible(o)) { reason = E_WRONGOBTYPE; return B_FALSE; @@ -1145,6 +1150,8 @@ int canreachbp(lifeform_t *lf, lifeform_t *victim, enum BODYPART bp) { // upper case BP_EYES: case BP_HEAD: + case BP_HEAD2: + case BP_HEAD3: case BP_EARS: case BP_NECK: case BP_SHOULDERS: @@ -1768,12 +1775,27 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar } } + if (isplayer(lf)) { + if ((sid == OT_S_GATHERFLAME) && (lf->material->id == MT_FIRE)) { + if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { + char ch; + ch = askchar("Casting this while made of flame will harm you, continue?","yn", "n", B_TRUE, B_FALSE); + if (ch != 'y') { + msg("Cancelled."); + return B_TRUE; + } + } + } + } + // stop hiding killflagsofid(lf->flags, F_HIDING); // take time taketime(lf, getspellspeed(lf)); if (!fromob) { + flag_t *retflag[MAXCANDIDATES]; + int nretflags,tempboost = 0,i; // lose mp losemp(lf, cost); @@ -1806,6 +1828,20 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar power += countplantsinsight(lf); limit(&power, NA, 10); } + + tempboost = 0; + getflags(lf->flags, retflag, &nretflags, F_TEMPMAGICBOOST, F_NONE); + for (i = 0; i < nretflags; i++) { + tempboost += retflag[i]->val[0]; + killflag(retflag[i]); + } + if (tempboost) { + power += tempboost; + if (isplayer(lf)) { + msg("^gYour spell's power is boosted!^n"); + } + } + if (!willflag && real_getmr(lf, B_ONLYEXTERNAL) && skillcheck(lf, SC_RESISTMAG, 20 + power, 0) && !isgod(lf)) { if (power > 1) { // half strength @@ -2581,19 +2617,12 @@ int continuerepairing(lifeform_t *lf, flag_t *repairflag) { } int countinnateattacks(lifeform_t *lf) { - int count = 0,i; - flag_t *f; - flag_t *retflag[MAXCANDIDATES]; + flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE); - for (i = 0; i < nretflags; i++) { - f = retflag[i]; - if (f->id == F_HASATTACK) count++; - } - return count; + return nretflags; } - int countlegs(lifeform_t *lf) { enum BODYPART bp; int legs = 0; @@ -3257,8 +3286,18 @@ void die(lifeform_t *lf) { amt = pctof( rnd(1,soulflag->val[0]), lf->maxhp); limit(&amt, 1, NA); gainhp(souleater, amt); + // copy some flags + copyflag(souleater->flags, lf->flags, F_BLOODBOIL); // drop bones addob(corpsecell->obpile, "pile of ash"); + } else if (lfhasflag(lf, F_BLOODBOIL)) { + if (haslos(player, corpsecell)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s explodes into flames!", lfname); + } + // explode into flames + addobsinradius(corpsecell, 1, DT_COMPASS, "medium fire", B_TRUE, NULL); } else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) { // shattered fragments(corpsecell, "chunk of ice", 2, UNLIMITED); @@ -4237,15 +4276,19 @@ int eat(lifeform_t *lf, object_t *o) { // get total nutrition nutrition = getnutrition(o); - if (nutrition == 0) { - // this might happen if you purposely try to eat a potion. - // technically a potion will pass above checks because you can - // drink it. - if (isplayer(lf)) { - msg("That doesn't seem very nutritious..."); + // this might happen if you purposely try to eat something non-edible but drinkable + // (eg. a potion). also when you have f_caneatmaterial and you something which + // isn't normally edible (ie. a goat eating wood). + if (lfhasflagval(lf, F_CANEATMATERIAL, o->material->id, NA, NA, NULL)) { + // nutrition based on object weight + nutrition = getobweight(o)*5; + } else { + if (isplayer(lf)) { + msg("That doesn't seem very nutritious..."); + } + return B_TRUE; } - return B_TRUE; } @@ -4342,7 +4385,7 @@ int eat(lifeform_t *lf, object_t *o) { sprintf(taste, "The raw meat tastes disgusting!"); } else if (hasflagval(o->flags, F_CORPSEOF, R_CHICKEN, NA, NA, NULL)) { sprintf(taste, "Tastes like chicken!"); - } else if (f->val[1] >= 20) { + } else if (f && (f->val[1] >= 20)) { sprintf(taste, "Yum!"); } else { strcpy(taste, ""); @@ -7183,6 +7226,8 @@ int getbodyparthitchance(enum BODYPART bp) { case BP_SECWEAPON: return 0; case BP_EYES: return 1; case BP_HEAD: return 2; + case BP_HEAD2: return 2; + case BP_HEAD3: return 2; case BP_WAIST: return 3; case BP_HANDS: return 3; case BP_FEET: return 3; @@ -7234,6 +7279,10 @@ char *getbodypartname(lifeform_t *lf, enum BODYPART bp) { return "eyes"; case BP_HEAD: return "head"; + case BP_HEAD2: + return "second head"; + case BP_HEAD3: + return "third head"; case BP_NECK: return "neck"; case BP_BODY: @@ -7268,6 +7317,8 @@ char *getbodypartequipname(enum BODYPART bp) { case BP_LEFTFINGER: case BP_HANDS: case BP_HEAD: + case BP_HEAD2: + case BP_HEAD3: case BP_BODY: case BP_FRONTLEGS: case BP_BACKLEGS: @@ -8302,6 +8353,7 @@ int getattacks(lifeform_t *lf, int *min, int *max) { if (f) { minattacks = f->val[0]; maxattacks = f->val[1]; + } else { minattacks = countinnateattacks(lf); maxattacks = countinnateattacks(lf); @@ -8786,6 +8838,15 @@ char *real_getlfname(lifeform_t *lf, char *buf, lifeform_t *usevis, int showall, strcat(descstring, " "); } + if (lf->race->id == R_HYDRA) { + int nheads; + char numbuf[BUFLEN]; + nheads = countflagsofid(lf->flags, F_HASATTACK); + numtotext(nheads, numbuf); + strcat(descstring, numbuf); + strcat(descstring, "-headed "); + } + // need a certain amount of race knowledge to recognise ai traits if (lorelev < PR_SKILLED) { dobehaviour = B_FALSE; @@ -11436,6 +11497,40 @@ int gotosleep(lifeform_t *lf, int onpurpose) { return B_FALSE; } +void growhydrahead(lifeform_t *lf, int announce) { + flag_t *f; + char vname[BUFLEN]; + int dr = 0; + f = lfhasflagval(lf, F_HASATTACK, OT_TEETH, NA, NA, NULL); + dr = f->val[1]; // remember dr + if (announce) { + // remove one hasattack flag so that the name string + // is correct. + killflag(f); + getlfname(lf, vname); + // regrow + if (cansee(player, lf)) { + msg("^%c%s grow%s two more heads!", getlfcol(lf, CC_GOOD), vname, isplayer(lf) ? "" : "s"); + } + } + + if (announce) { + // add two more attack flags, since we removed one before. + addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE); + addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE); + } else { + // just add one + addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE); + } + // also add extra hp + lf->maxhp += HITDIESIDES; + gainhp(lf, HITDIESIDES); + if (isplayer(lf)) statdirty = B_TRUE; + // adjust TR + f = hasflag(lf->flags, F_TR); + f->val[0]++; +} + flag_t *hasbleedinginjury(lifeform_t *lf, enum BODYPART bp) { flag_t *f, *retflag[MAXCANDIDATES]; int nretflags,i; @@ -11960,6 +12055,16 @@ int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype, enum INJUR break; } + if (where == BP_TAIL) { + if (lf->race->id == R_MANTICORE) { + flag_t *f; + // can't use spike volley + f = hasflagval(lf->flags, F_CANWILL, OT_S_SPIKEVOLLEY, NA, NA, NULL); + if (f) killflag(f); + } + } + + return B_FALSE; } diff --git a/lf.h b/lf.h index 2a2e9b3..79c113e 100644 --- a/lf.h +++ b/lf.h @@ -286,6 +286,7 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp); void givestartskills(lifeform_t *lf, flagpile_t *fp); map_t *gotolev(lifeform_t *lf, int depth, object_t *fromstairs); int gotosleep(lifeform_t *lf, int onpurpose); +void growhydrahead(lifeform_t *lf, int announce); flag_t *hasbleedinginjury(lifeform_t *lf, enum BODYPART bp); int hasfreeaction(lifeform_t *lf); int real_hasfreeaction(lifeform_t *lf, enum FLAG exception); diff --git a/map.c b/map.c index b964aa5..e89c382 100644 --- a/map.c +++ b/map.c @@ -6148,6 +6148,16 @@ void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, copyflags(lf->flags, v->flags, F_STAYINROOM); } + if (lf->race->id == R_HYDRA) { + int nextra,i; + // they always have at least 5 heads. add more... + // grow extra heads + nextra = rnd(0,7); + for (i = 0; i < nextra; i++) { + growhydrahead(lf, B_FALSE); + } + } + /* getflags(lf->flags, retflag, &nretflags, F_LIFEOB, F_NONE); for (i = 0; i < nretflags; i++) { diff --git a/nexus.c b/nexus.c index 2ec1d91..ee6d2c5 100644 --- a/nexus.c +++ b/nexus.c @@ -131,6 +131,8 @@ int main(int argc, char **argv) { enum SKILLLEVEL slev; flag_t *retflag[MAXCANDIDATES]; int nretflags; + map_t fakemap; + cell_t fakecell; atexit(cleanup); @@ -256,6 +258,15 @@ int main(int argc, char **argv) { } } + // add player in fake cell + createfakes(&fakemap, &fakecell); + real_addlf(&fakecell, startrace->id, 1, C_PLAYER); // this will assign 'player' + // give them basic abilities + addflag(player->flags, F_CANWILL, OT_A_CHECKSTAIRS, NA, NA, NULL); + addflag(player->flags, F_CANWILL, OT_A_PRAY, NA, NA, NULL); + addflag(player->flags, F_CANWILL, OT_A_TRAIN, NA, NA, NULL); + addflag(player->flags, F_CANWILL, OT_A_DEBUG, NA, NA, NULL); ///////// + // make the initial level newworld = B_TRUE; @@ -288,6 +299,7 @@ int main(int argc, char **argv) { } // add player in the starting position + //where = real_getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL); where = findobinmap(dmap, OT_PLAYERSTART); if (!where) { @@ -296,19 +308,16 @@ int main(int argc, char **argv) { more(); exit(1); } + movelf(player, where); + // new remove fakes + killfakes(&fakemap, &fakecell); + - // this is the hole which you fell down to get here. + // this is the hole which you fell down to get here. addobfast(where->obpile, OT_HOLEINROOF); // kill any objects which were already there, or which fell down the hole killallobsexcept(where->obpile, OT_HOLEINROOF, OT_NONE); - // now add the player - real_addlf(where, startrace->id, 1, C_PLAYER); // this will assign 'player' - // give them basic abilities - addflag(player->flags, F_CANWILL, OT_A_CHECKSTAIRS, NA, NA, NULL); - addflag(player->flags, F_CANWILL, OT_A_PRAY, NA, NA, NULL); - addflag(player->flags, F_CANWILL, OT_A_TRAIN, NA, NA, NULL); - addflag(player->flags, F_CANWILL, OT_A_DEBUG, NA, NA, NULL); ///////// /*o = hasob(where->obpile, OT_PLAYERSTART); killob(o);*/ diff --git a/objects.c b/objects.c index 70da048..b1d1c54 100644 --- a/objects.c +++ b/objects.c @@ -14736,6 +14736,13 @@ int getcritchance(lifeform_t *lf, object_t *o, lifeform_t *victim) { chance *= 2; } + // extra crit chance if you can sever victim's limbs + if (victim && lfhasflag(victim, F_CANSEVER)) { + if (o && (getdamtype(o) == DT_SLASH)) { + chance += 30; + } + } + return chance; } diff --git a/spell.c b/spell.c index 035ec84..0ae68a6 100644 --- a/spell.c +++ b/spell.c @@ -4280,6 +4280,15 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ teleportto(caster, targcell, B_TRUE); setfacing(caster, target->facing); + } else if (spellid == OT_S_BLOODBOIL) { + if (!target) { + target = targcell->lf; + } + if (!target || lfhasflag(target, F_BLOODBOIL)) { + fizzle(caster); + return B_TRUE; + } + addflag(target->flags, F_BLOODBOIL, B_TRUE, NA, NA, NULL); } else if (spellid == OT_S_LOWERMETAB) { flag_t *f; // ie. 2 - 4 @@ -6919,6 +6928,54 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } polymorphto(target, R_GASCLOUD, howlong); } + } else if (spellid == OT_S_GATHERFLAME) { + cell_t *c; + int amt = 0,i; + if (!target) target = caster; + // override castername + getlfname(target, castername); + // all flame in sight + for (i = 0; i < target->nlos; i++) { + c = target->los[i]; + if (c->lf && (c->lf->material->id == MT_FIRE)) { + if (gettr(c->lf) <= power) { + char buf[BUFLEN]; + // instadeath + sprintf(buf, "being sucked into %s", castername); + losehp(c->lf, c->lf->maxhp, DT_DIRECT, target, buf); + if (isplayer(c->lf)) { + msg("Your essence is sucked into %s!", castername); + } else if (cansee(player, c->lf)) { + char lfname[BUFLEN]; + getlfname(c->lf, lfname); + msg("%s%s essence is sucked into %s!", lfname, getpossessive(lfname), castername); + } + amt++; + } + } + } + if (isdead(target)) { // consumed yourself? + return B_FALSE; + } + // now gather flame from cells in los + for (i = 0; (i < target->nlos) && (amt < power); i++) { + object_t *o, *nexto; + for (o = target->los[i]->obpile->first ; o ; o = nexto) { + nexto = o->next; + if (o->material->id == MT_FIRE) { + removeob(o, ALL); + amt++; + } + break; + } + } + if (amt) { + // boost spells... + addflag(target->flags, F_TEMPMAGICBOOST, amt, NA, NA, NULL); + } else { + fizzle(caster); + return B_TRUE; + } } else if (spellid == OT_S_GLACIATE) { object_t *o,*nexto; int donesomething = B_FALSE; diff --git a/text.c b/text.c index 76c3797..1e769a5 100644 --- a/text.c +++ b/text.c @@ -1142,6 +1142,11 @@ char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int d canbehead = B_FALSE; } } + // can't behead multiheaded things at the moment... + if (victim && hasbp(victim, BP_HEAD) && hasbp(victim, BP_HEAD2)) { + canbehead = B_FALSE; + } + if (canbehead) { if (victim && (victim->race->id == R_EARTHWYRM)) { return "bisect"; @@ -2006,6 +2011,36 @@ char *numtotext(int num, char *buf) { case 10: snprintf(buf, BUFLEN, "ten"); break; + case 11: + snprintf(buf, BUFLEN, "eleven"); + break; + case 12: + snprintf(buf, BUFLEN, "twelve"); + break; + case 13: + snprintf(buf, BUFLEN, "thirteen"); + break; + case 14: + snprintf(buf, BUFLEN, "fourteen"); + break; + case 15: + snprintf(buf, BUFLEN, "fifteen"); + break; + case 16: + snprintf(buf, BUFLEN, "sixteen"); + break; + case 17: + snprintf(buf, BUFLEN, "seventeen"); + break; + case 18: + snprintf(buf, BUFLEN, "eighteen"); + break; + case 19: + snprintf(buf, BUFLEN, "nineteen"); + break; + case 20: + snprintf(buf, BUFLEN, "twenty"); + break; default: snprintf(buf, BUFLEN, "%d",num); break;