diff --git a/ai.c b/ai.c index 1dd9e8a..d4841d6 100644 --- a/ai.c +++ b/ai.c @@ -895,7 +895,7 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { } else { getflags(lf->flags, retflag, &nretflags, F_HATESRACEWITHFLAG, F_NONE); for (i = 0; i < nretflags; i++) { - if (lfhasflag(who, retflag[i]->id) && + if (lfhasflagval(who, retflag[i]->val[0], retflag[i]->val[1], retflag[i]->val[2], NA, NULL) && !areallies(lf, who)) { if (db) dblog(".oO { found a target with hated flags - lfid %d (%s) ! }",who->id, who->race->name); @@ -2096,6 +2096,9 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG int needlos = B_TRUE; enum LOFTYPE needlof = LOF_NEED; + int castchance = 0; + enum SPELLTARGET targettype = TT_NONE; + if (lfhasflag(lf, F_DEBUG)) { db = B_TRUE; } @@ -2177,11 +2180,31 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG } } - f = hasflag(ot->flags, purpose); + f = lfhasflagval(lf, F_AISPELLTARGETOVERRIDE, ot->id, purpose, NA, NULL); + if (f) { + if (strlen(f->text)) { + castchance = atoi(f->text); + } else { + castchance = 100; + } + targettype = f->val[2]; + } else { + f = hasflag(ot->flags, purpose); + if (f) { + if (f->val[1] == NA) { + castchance = 100; + } else { + castchance = f->val[1]; + } + targettype = f->val[0]; + } + } + + if (f) { - if ((f->val[1] == NA) || pctchance(f->val[1])) { + if (pctchance(castchance)) { int range; - switch (f->val[0]) { + switch (targettype) { case ST_VICTIM: range = getspellrange(lf, spellid, getspellpower(lf, spellid)); if ((range == UNLIMITED) || (getcelldist(lf->cell, victim->cell) <= range)) { @@ -2229,10 +2252,13 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG case ST_SPECIAL: specialcase = B_TRUE; break; + case ST_NONE: + ok = B_FALSE; + break; } // now check for line of sight / fire - switch (f->val[0]) { + switch (targettype) { case ST_VICTIM: case ST_ADJVICTIM: if (needlos && (!victim || !cansee(lf, victim)) ) { @@ -2533,6 +2559,8 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG specificcheckok = B_FALSE; } else if ((ot->id == OT_A_SWOOP) && !lfhasflag(lf, F_FLYING)) { specificcheckok = B_FALSE; + } else if ((ot->id == OT_A_SWOOP) && !lfhasflagval(lf, F_HASATTACK, OT_CLAWS, NA, NA, NULL)) { + specificcheckok = B_FALSE; } else if (getcelldist(lf->cell, victim->cell) > srange) { specificcheckok = B_FALSE; } else if (getcelldist(lf->cell, victim->cell) == 1) { // ie already adjacent diff --git a/data.c b/data.c index deef6e7..4c4a679 100644 --- a/data.c +++ b/data.c @@ -3984,7 +3984,6 @@ void initobjects(void) { addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how powerful a creature you can become."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); -oooooooo addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, 10, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); // l5 @@ -7816,7 +7815,7 @@ void initrace(void) { // races / monsters // playable races - addrace(R_HUMAN, "human", 75, '@', C_GREY, MT_FLESH, RC_HUMANOID, "Your average example of the Homo Sapiens species."); + addrace(R_HUMAN, "human", 75, '@', C_BROWN, MT_FLESH, RC_HUMANOID, "Your average example of the Homo Sapiens species."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_PLAYABLE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_STR, AT_AVERAGE, NA, NULL); @@ -8917,6 +8916,42 @@ void initrace(void) { addflag(lastrace->flags, F_MINIONS, 20, 1, 3, "goblin warrior"); addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); + 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); + addflag(lastrace->flags, F_ALIGNMENT, AL_NEUTRAL, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, 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_IQ, AT_RANDOM, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, 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_SIZE, SZ_LARGE, 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, 4, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 5, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_HOOF, 6, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_HOOF, 6, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 2, 2, NA, NULL); + // 50% chance of a shortbow. + // if not, then 50% chance of a longbow + f = addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "short bow"); + addcondition(f, FC_NOCONDITION, 50); + addaltval(f, F_STARTOB, 50, NA, NA, "longbow"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "10-30 arrows"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "shield"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "lance"); + addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "1-20 gold coins"); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 4, NA, "screams in pain^screams of pain"); + addflag(lastrace->flags, F_MORALE, 14, NA, NA, NULL); + addflag(lastrace->flags, F_EQUINE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_RANGED, PR_ADEPT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_SHIELDS, PR_ADEPT, NA, NULL); + addrace(R_COCKATRICE, "cockatrice", 5, 'c', C_YELLOW, MT_FLESH, RC_DRAGON, "A small two-legged wyrm with a rooster's head. Its touch is said to petrify the flesh of living creatures."); setbodytype(lastrace, BT_QUADRAPED); addbodypart(lastrace, BP_WINGS, NULL); @@ -9324,7 +9359,7 @@ void initrace(void) { // TODO: storm giant // TODO: storm titan - addrace(R_GNOLL, "gnoll", 130, 'h', C_BROWN, MT_FLESH, RC_HUMANOID, "Gnolls are doglike warriors - the gladiators of the kobold race. They are highly organised and often travel in packs."); + addrace(R_GNOLL, "gnoll", 130, 'k', C_YELLOW, MT_FLESH, RC_HUMANOID, "Gnolls are doglike warriors - the gladiators of the kobold race. They are highly organised and often travel in packs."); setbodytype(lastrace, BT_HUMANOID); setbodypartname(lastrace, BP_HANDS, "claws"); setbodypartname(lastrace, BP_RIGHTFINGER, "right foreclaw"); @@ -9650,6 +9685,74 @@ void initrace(void) { addflag(lastrace->flags, F_CANCAST, OT_S_GASEOUSFORM, NA, NA, "pw:1;"); // gremlins also cause tech to fail around them. + addrace(R_GRIFFON, "griffon", 220, 'f', C_YELLOW, MT_FLESH, RC_ANIMAL, "Griffons have a lion's body and the head, torso and forelegs of an eagle."); + setbodytype(lastrace, BT_QUADRAPED); + addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_ALIGNMENT, AL_NEUTRAL, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_HITDICE, 7, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 7, NA, NA, NULL); + addflag(lastrace->flags, F_EVASION, 20, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_GTAVERAGE, 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_MAXATTACKS, 2, 2, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_BEAK, 14, NA, NULL); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "screeches^a screeches"); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 3, NA, "screeches in pain^screeches of pain"); + addflag(lastrace->flags, F_SEEINDARK, 6, NA, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, 8, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_SWOOP, NA, NA, NULL); + addflag(lastrace->flags, F_SWOOPRANGE, 4, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 12, 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_STARTSKILL, SK_PERCEPTION, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_WANTSOBFLAG, F_EQUINE, B_COVETS, 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_HATESRACEWITHFLAG, F_EQUINE, NA, NA, NULL); + + addrace(R_HIPPOGRIFF, "hippogriff", 500, 'u', C_YELLOW, MT_FLESH, RC_ANIMAL, "Hippogriffs are fierce hybrids of a horse and an eagle. Their head, wings and claws take the form of the latter."); + setbodytype(lastrace, BT_QUADRAPED); + addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_ALIGNMENT, AL_NEUTRAL, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_RARE, ""); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_VHIGH, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_LARGE, 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_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_HITDICE, 3, 3, NA, NULL); + addflag(lastrace->flags, F_TR, 5, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 6, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 6, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_BEAK, 10, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 2, 2, NA, NULL); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 4, NA, "screams in pain^screams of pain"); + addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, ""); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 8, NA, NA, NULL); + addflag(lastrace->flags, F_EQUINE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); + addrace(R_HOBGOBLIN, "hobgoblin", 90, 'g', C_YELLOW, MT_FLESH, RC_HUMANOID, "A larger, stronger, smarter and more menacing form of a goblin."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -9934,7 +10037,7 @@ void initrace(void) { addflag(lastrace->flags, F_RARITY, H_FOREST, 66, RR_UNCOMMON, NULL); addflag(lastrace->flags, F_RARITY, H_SWAMP, 66, RR_UNCOMMON, NULL); addflag(lastrace->flags, F_HITDICE, 4, NA, NA, NULL); - addflag(lastrace->flags, F_TR, 4, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 5, NA, NA, NULL); addflag(lastrace->flags, F_EVASION, -5, NA, NA, NULL); addflag(lastrace->flags, F_ARMOURRATING, 11, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); @@ -10281,9 +10384,10 @@ void initrace(void) { addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_COLD, NA, "80"); - addrace(R_PEGASUS, "pegasus", 130, 'Q', C_WHITE, MT_FLESH, RC_MAGIC, "A legendary white, winged horse."); + addrace(R_PEGASUS, "pegasus", 500, 'u', C_CYAN, MT_FLESH, RC_ANIMAL, "A legendary white, winged horse."); setbodytype(lastrace, BT_QUADRAPED); addflag(lastrace->flags, F_ALIGNMENT, AL_GOOD, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CANTALK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 57, NA, ""); addflag(lastrace->flags, F_RARITY, H_FOREST, 57, NA, NULL); @@ -10304,13 +10408,15 @@ void initrace(void) { addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 3, NA, NULL); addflag(lastrace->flags, F_MAXATTACKS, 3, 3, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_A_SWOOP, NA, NA, NULL); - addflag(lastrace->flags, F_CANCAST, OT_S_SMITEEVIL, NA, NA, "pw:8;"); addflag(lastrace->flags, F_SWOOPRANGE, 4, NA, NA, NULL); + addflag(lastrace->flags, F_CANCAST, OT_S_SMITEEVIL, NA, NA, "pw:8;"); addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 4, NA, "screams in pain^screams of pain"); addflag(lastrace->flags, F_RESISTMAG, 5, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 25, NA, NA, NULL); addflag(lastrace->flags, F_ENHANCESMELL, 3, NA, NA, NULL); + addflag(lastrace->flags, F_VEGETARIAN, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_EQUINE, B_TRUE, NA, NA, NULL); addrace(R_PIXIE, "pixie", 5, 'n', C_GREEN, MT_FLESH, RC_MAGIC, "A small magical woodland creature, flying around on moth-like wings."); setbodytype(lastrace, BT_HUMANOID); @@ -11762,7 +11868,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_S_WATERJET, NA, NA, "pw:4;"); addflag(lastrace->flags, F_STARTSKILL, SK_SWIMMING, PR_MASTER, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, NULL); - addflag(lastrace->flags, F_AISPELLTARGETOVERRIDE, OT_S_FLOOD, ST_SELF, NA, NULL); + addflag(lastrace->flags, F_AISPELLTARGETOVERRIDE, OT_S_FLOOD, F_AICASTTOATTACK, ST_SELF, "100"); addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "hop"); addflag(lastrace->flags, F_EATCONFER, F_BREATHWATER, B_TRUE, NA, "50"); @@ -11906,7 +12012,30 @@ void initrace(void) { addflag(lastrace->flags, F_TAMABLE, 40, NA, NA, NULL); addflag(lastrace->flags, F_CANINE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_FATALFOOD, OT_CHOCOLATE, NA, NA, NULL); + + + addrace(R_HORSE, "horse", 500, 'u', C_BROWN, MT_FLESH, RC_ANIMAL, "Large quadrapeds with many purposes: transport, moving goods, pulling ploughs to name but a few."); + setbodytype(lastrace, BT_QUADRAPED); + addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_ALIGNMENT, AL_NEUTRAL, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_COMMON, ""); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_COMMON, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_VHIGH, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_LARGE, 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, 2, 2, NA, NULL); + addflag(lastrace->flags, F_TR, 3, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_HOOF, 6, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_HOOF, 6, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 3, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 2, 2, NA, NULL); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 4, NA, "screams in pain^screams of pain"); + addflag(lastrace->flags, F_FLEEONHPPCT, 80, NA, NA, ""); + addflag(lastrace->flags, F_MORALE, 1, NA, NA, NULL); + addflag(lastrace->flags, F_EQUINE, B_TRUE, NA, NA, NULL); addrace(R_FROG, "impaler frog", 10, ';', C_BOLDGREEN, MT_FLESH, RC_ANIMAL, "As their name implies, impaler frogs are dangerous frogs whose tongues end in a very sharp point. They use this to spear their enemies from afar, often while hiding underwater."); setbodytype(lastrace, BT_QUADRAPED); @@ -12212,7 +12341,8 @@ void initrace(void) { addflag(lastrace->flags, F_RETALIATE, 1, 4, DT_PIERCE, "sharp spines"); addflag(lastrace->flags, F_CORPSEFLAG, F_SHARP, 1, 4, NULL); addflag(lastrace->flags, F_MORALE, 8, NA, NA, NULL); - addrace(R_RAT, "dire rat", 3, 'r', C_BROWN, MT_FLESH, RC_ANIMAL, "A rodent of unusual size."); + + addrace(R_RAT, "giant rat", 3, 'r', C_BROWN, MT_FLESH, RC_ANIMAL, "An aggressive rodent, approximately the size of a cat."); setbodytype(lastrace, BT_QUADRAPED); addbodypart(lastrace, BP_TAIL, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); @@ -12237,6 +12367,58 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addrace(R_RATDIRE, "dire rat", 3, 'r', C_MAGENTA, MT_FLESH, RC_ANIMAL, "Dire rats are massive rats, larger than most dogs. Unlike dogs, dire rats are equipped with razor sharp shark-like teeth and their bite is very much worse than their bark."); + setbodytype(lastrace, BT_QUADRAPED); + addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, 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_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_FAST, NA, NA, ""); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_SEWER, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_HITDICE, 3, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 3, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 8, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 1, 1, NA, NULL); + addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL); + addflag(lastrace->flags, F_SEEINDARK, 4, NA, NA, NULL); + addflag(lastrace->flags, F_DTVULN, DT_POISONGAS, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 7, NA, NA, NULL); + + addrace(R_RATPLAGUE, "plague rat", 3, 'r', C_GREEN, MT_FLESH, RC_ANIMAL, "Plague rats are named both for their infectious bite as well as the great speed at which they run."); + setbodytype(lastrace, BT_QUADRAPED); + addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_VERYFAST, NA, NA, ""); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_SIZE, SZ_TINY, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_COMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_COMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_SEWER, NA, RR_FREQUENT, NULL); + addflag(lastrace->flags, F_HITDICE, 0, 1, NA, NULL); + addflag(lastrace->flags, F_TR, 1, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 1, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 1, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 1, 1, NA, NULL); + addflag(lastrace->flags, F_HITCONFER, F_POISONED, SC_POISON, 10, "2-4"); + addflag(lastrace->flags, F_HITCONFERVALS, P_VENOM, 1, NA, NULL); + addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL); + addflag(lastrace->flags, F_SEEINDARK, 4, NA, NA, NULL); + addflag(lastrace->flags, F_DTVULN, DT_POISONGAS, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 1, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addrace(R_ROC, "roc", 1, 'A', C_MAGENTA, MT_FLESH, RC_ANIMAL, "Rocs are unbelievably gargantuan birds or prey, large enough to carry a fully-grown elephant. They are generally peaceful, but deadly once provoked."); // 'A' for Avian setbodytype(lastrace, BT_BIRD); addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_VERYRARE, NULL); @@ -12687,6 +12869,7 @@ void initrace(void) { addrace(R_WOLF, "wolf", 25, 'd', C_GREY, MT_FLESH, RC_ANIMAL, "Highly intelligent members of the canine family."); setbodytype(lastrace, BT_QUADRAPED); addbodypart(lastrace, BP_TAIL, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_CON, AT_VHIGH, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); @@ -12721,6 +12904,7 @@ void initrace(void) { addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, AT_AVERAGE, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_FAST, NA, NA, ""); addflag(lastrace->flags, F_ENHANCESMELL, 6, NA, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL); @@ -13963,7 +14147,7 @@ void initrace(void) { addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); - addrace(R_GHOST, "ghost", 50, 'p', C_BLUE, MT_MAGIC, RC_UNDEAD, "Wispy spirits formed when a soul refuses to depart the earthly realm after death, ghosts exist part way between dimensions. The sight of a ghost can cause fear in all who behold it, and their ethereal nature makes them immune to most attacks."); // p for sPirit + addrace(R_GHOST, "ghost", 50, 'p', C_GREY, MT_MAGIC, RC_UNDEAD, "Wispy spirits formed when a soul refuses to depart the earthly realm after death, ghosts exist part way between dimensions. The sight of a ghost can cause fear in all who behold it, and their ethereal nature makes them immune to most attacks."); // p for sPirit setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, AT_GTAVERAGE, NA, NULL); @@ -14181,6 +14365,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_A_CHARGE, NA, NA, "range:3;"); addflag(lastrace->flags, F_CANCAST, OT_S_STUN, 5, 5, "pw:1;"); addflag(lastrace->flags, F_CANCAST, OT_S_SHAPESHIFT, 3, 3, "pw:1;race:vampire bat;"); + addflag(lastrace->flags, F_AISPELLTARGETOVERRIDE, OT_S_SHAPESHIFT, F_AICASTTOATTACK, ST_SELF, "100"); addflag(lastrace->flags, F_CASTCHANCE, 40, NA, NA, NULL); addflag(lastrace->flags, F_DETECTOBS, 10, OT_COFFIN, NA, NULL); addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "pile of ash"); @@ -14191,6 +14376,86 @@ void initrace(void) { addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); + addrace(R_WEREBEAR, "werebear", 90, '@', C_BROWN, MT_FLESH, RC_HUMANOID, "Stout, well-muscled humans with large, thick beards."); + setbodytype(lastrace, BT_HUMANOID); + addflag(lastrace->flags, F_ALIGNMENT, AL_GOOD, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_LTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_LTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_MATVULN, MT_SILVER, 200, NA, NULL); + addflag(lastrace->flags, F_SEEINDARK, 3, NA, 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_SPELLSPEED, SP_SLOW, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 6, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 6, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_FISTS, 6, NA, NULL); + addflag(lastrace->flags, F_REGENERATES, 1, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_GRAB, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_CRUSH, NA, NA, "dam:2d8;"); + addflag(lastrace->flags, F_CANCAST, OT_S_SHAPESHIFT, 3, 3, "pw:1;race:grizzly bear;"); + addflag(lastrace->flags, F_CANCAST, OT_S_SUMMONANIMALSLG, 10, 10, "pw:5;race:black bear;count:2;"); + addflag(lastrace->flags, F_CASTCHANCE, 20, NA, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, "bares its teeth"); + addflag(lastrace->flags, F_AISPELLTARGETOVERRIDE, OT_S_SHAPESHIFT, F_AICASTTOATTACK, ST_SELF, "100"); + addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_FIRSTAID, PR_ADEPT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_UNARMED, PR_ADEPT, NA, NULL); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "hatchet"); + addflag(lastrace->flags, F_ENHANCESMELL, 5, NA, NA, NULL); + addflag(lastrace->flags, F_HATESRACE, R_WERERAT, NA, NA, NULL); + addflag(lastrace->flags, F_HATESRACE, R_WEREWOLF, NA, NA, NULL); + addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_LYCANTHROPE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 14, NA, NA, NULL); + addflag(lastjob->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); + + addrace(R_WERERAT, "wererat", 50, '@', C_BROWN, MT_FLESH, RC_HUMANOID, "Weedy humans with shifty eyes and whiskers, wererats are known for their extreme cunning."); + setbodytype(lastrace, BT_HUMANOID); + addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_SEWER, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); + addflag(lastrace->flags, F_MATVULN, MT_SILVER, 200, NA, NULL); + addflag(lastrace->flags, F_SEEINDARK, 7, NA, 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_SPELLSPEED, SP_ULTRAFAST, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 3, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 3, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 3, NA, NULL); + addflag(lastrace->flags, F_REGENERATES, 1, NA, NA, NULL); + addflag(lastrace->flags, F_CANCAST, OT_S_SHAPESHIFT, 3, 3, "pw:1;race:plague rat;"); + addflag(lastrace->flags, F_CANCAST, OT_S_SUMMONANIMALSSM, 10, 10, "pw:5;race:giant rat;count:5;"); + addflag(lastrace->flags, F_STARTOBWEPSK, 100, SK_SHORTBLADES, NA, NULL); + addflag(lastrace->flags, F_STARTOB, 10, NA, NA, "potion of rum"); + addflag(lastrace->flags, F_AISPELLTARGETOVERRIDE, OT_S_SHAPESHIFT, F_AICASTTOFLEE, ST_SELF, "100"); + addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_THIEVERY, PR_BEGINNER, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, 3, NA, NA, NULL); + addflag(lastrace->flags, F_CASTCHANCE, 40, NA, NA, NULL); + addflag(lastrace->flags, F_WANTS, OT_POT_RUM, NA, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, "twitches its nose"); + addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_LYCANTHROPE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); + addrace(R_WEREWOLF, "werewolf", 100, '@', C_BROWN, MT_FLESH, RC_HUMANOID, "Shaggy humans with the uncanny ability to shapeshift into a ferocious wolf."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -14200,10 +14465,10 @@ void initrace(void) { addflag(lastrace->flags, F_STARTATT, A_CON, AT_GTAVERAGE, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_CHA, AT_LOW, NA, NULL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_UNCOMMON, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); - addflag(lastrace->flags, F_DTVULN, DT_LIGHT, NA, NA, "3d6"); addflag(lastrace->flags, F_MATVULN, MT_SILVER, 200, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); @@ -14214,11 +14479,17 @@ void initrace(void) { addflag(lastrace->flags, F_REGENERATES, 1, NA, NA, NULL); addflag(lastrace->flags, F_CANCAST, OT_S_SHAPESHIFT, 3, 3, "pw:1;race:dire wolf;"); addflag(lastrace->flags, F_CANCAST, OT_S_SUMMONANIMALSMD, 10, 10, "pw:5;race:young wolf;"); + addflag(lastrace->flags, F_AISPELLTARGETOVERRIDE, OT_S_SHAPESHIFT, F_AICASTTOATTACK, ST_SELF, "100"); addflag(lastrace->flags, F_STARTSKILL, SK_FIRSTAID, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_ENHANCESMELL, 3, NA, NA, NULL); addflag(lastrace->flags, F_CASTCHANCE, 40, NA, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, "howls"); + addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_LYCANTHROPE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CANINE, B_TRUE, NA, NA, NULL); // ie. cats will know! + addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); // special monsters diff --git a/defs.h b/defs.h index 3566bb6..0d94e8b 100644 --- a/defs.h +++ b/defs.h @@ -62,6 +62,7 @@ //#define UNI_SOLID '#' // getrandomemptycell() params +#define WE_NONE 0 #define WE_WALKABLE 1 #define WE_EMPTY 2 #define WE_PORTAL 3 @@ -982,6 +983,7 @@ enum RACE { // monsters R_BEHOLDER, R_BUGBEAR, + R_CENTAUR, R_COCKATRICE, R_CREEPINGCLAW, R_CRYMIDIA, @@ -1003,6 +1005,8 @@ enum RACE { R_GOBLINWAR, R_GOBLINSHOOTER, R_GREMLIN, + R_GRIFFON, + R_HIPPOGRIFF, R_HOBGOBLIN, R_HOBGOBLINWAR, R_KOBOLD, @@ -1046,6 +1050,8 @@ enum RACE { R_TROLLSNOW, R_TROLLSWAMP, R_VAMPIRE, + R_WEREBEAR, + R_WERERAT, R_WEREWOLF, R_XAT, // fish @@ -1094,12 +1100,15 @@ enum RACE { R_HAWKYOUNG, R_HAWKBLOOD, R_HAWKFROST, + R_HORSE, R_FROG, R_LEECH, R_MAMMOTH, R_NEWT, R_PORCUPINE, R_RAT, + R_RATDIRE, + R_RATPLAGUE, R_ROC, R_SLUG, R_SNAIL, @@ -2243,6 +2252,14 @@ enum SLEEPTYPE { ST_KO, }; + +enum ANIMALTYPE { + AT_AVIAN, + AT_CANINE, + AT_EQUINE, + AT_FELINE, +}; + enum FLAG { F_NONE = 0, // dummy flag // map flags @@ -2326,8 +2343,10 @@ enum FLAG { F_THROWMISSILE, // weapon would make a good thrown missle - used by AI F_CANHOME, // this object can have the 'homing' flag F_UNIQUE, // only one may appear - F_GLYPH, // override the glyph with the first char of text. + F_GLYPH, // override the glyph with f->val[1] // v0 is either NA (white) or colourid (C_xxx). + // OPTIONAL v2: if you lorelev for this race is less + // then v2, F_NOGLYPH, // this object doesn't appear normally F_COSMETIC, // this object is mostly cosmetic, don't say 'you see xx' // also don't stop the player running past it. @@ -3003,6 +3022,8 @@ enum FLAG { // sight F_HATESRACECLASS, // lf will attack lfs with raceclass->id=v0 F_HATESRACEWITHFLAG, // lf will attack lfs with flag v0 on sight + // will also check flag v0=this v1 and + // flag v1 = this v2 F_TERRITORIAL, // lf will attack ALL other visible lfs within range v0 F_HARMLESS, // it is safe to rest around this lf F_RNDHOSTILE, // v0% chance of being hostile. @@ -3010,8 +3031,8 @@ enum FLAG { F_FRIENDLY, // lf will attack all non-players if in sight F_FATALFOOD, // if lf eats food with id = v0, they die. F_NATURALFLIGHT, // lf can fly natural using wings or similar - F_WANTS, // lf will try to pick up object type val0. if - // val1 = B_COVETS, will even abandon attacks + F_WANTS, // lf will try to pick up object type val0. + // if val1 = B_COVETS, will even abandon attacks // for it! F_WANTSOBFLAG, // lf will look for obs with this flag. val1=covets F_WANTSBETTERWEP, // lf will look for better weapons, val1=covets @@ -3062,9 +3083,13 @@ enum FLAG { // for spellid v0 F_SPELLCASTTIME, // override F_CASTINGTIME to v0 when this lf // casts spellid v1. - F_AISPELLTARGETOVERRIDE, // when casting spellid v0, this lf will - // use v1 (ST_xxx) instead of what the AICASTTOxxx - // flag specifies. + F_AISPELLTARGETOVERRIDE, // when casting spellid v0, + // use AICASTTOxxx=v1 instead of the one + // from the spell. + // use targettype = v2, intsead of the one + // from the spell. + // OPTIONAL: text = pctchance to cast for + // thispurpose. if not given, chance is 100. F_NODEATHANNOUNCE, // don't say 'the xx dies' if this lf dies F_NODEATHSPEECH, // lf doesn't talk when dying F_BEHEADED, // use special corpse drop code @@ -3142,12 +3167,16 @@ enum FLAG { F_AQUATIC, // this race can attack normally in water and suffers no // movement penalties. they can also swim at master // level. + F_AVIAN, // this race is an avian F_CANINE, // this race is a canine + F_EQUINE, // this race is an equine F_FELINE, // this race is a feline + F_HUMANOID, // this race is a humanoid // (can wear armour / use weapons) F_INSECT, // this race is classed as an insect + F_LYCANTHROPE, // this race is a lycanthrope F_UNDEAD, // this race is classed as undead F_COLDBLOOD, // this race is coldblooded F_NOBODYPART, // this race doesn't have bodypart val0 @@ -3349,6 +3378,7 @@ enum FLAG { F_HASATTACK, // v0 = obid to use when attacking unarmed // if v1 is set, it overrides DR(damagerating) // if text is set, it overrides the damage + F_FORCEATTACK, // this lf may only attack using F_HASATTACK v0 F_EVASION, // % chance of evading an attack // healing/resting/training diff --git a/doc/glyphs.txt b/doc/glyphs.txt index 99bc9c2..320bbef 100644 --- a/doc/glyphs.txt +++ b/doc/glyphs.txt @@ -11,7 +11,7 @@ A = avian / bird b = small roBot B = bat c = cockatrice / chicken / small bird -C = celestial / divine ? +C = ? celestial/divine? d = canine/dog D = ? e = eye or floating thing @@ -25,9 +25,11 @@ H = large humanoid i = insect I = large insect j = jelly/ooze/leech -k = kobold +J = ? +k = dog-like humanoid (kobold, gnoll) +K = ? L = lich, or other powerful undead ? -m = mutant / magic creature +m = mutant / hybrid creature M = mummy n = small humanoid / nymph / sprite N = ling / alieN creature @@ -35,7 +37,7 @@ o = orc O = monstrous humanoid (ie. ogre) P = gastropod p = sPirit -q = quadraped +q = medium quadraped Q = large quadraped r = rodent R = robot @@ -43,7 +45,7 @@ s = snake S = spider t = troll T = walkingtree-like monster (dryad, treant) -u = ? make this be horse? if so, change pegasus. +u = horse. U = unearthly/horrific creature v = ? V = vampire diff --git a/io.c b/io.c index 9c392ad..605d394 100644 --- a/io.c +++ b/io.c @@ -11771,14 +11771,16 @@ void showlfstats(lifeform_t *lf, int showall) { } // non-intrinsic effecst like polymorph, eye shading - f = lfhasknownflag(lf, F_POLYMORPHED); - if (f && (f->known)) { - snprintf(buf, BUFLEN, "%s have been polymorphed into a %s.",you(lf), lf->race->name); - //if (lfhasflag(lf, F_OMNIPOTENT) || lfhasflag(lf, F_EXTRAINFO)) { - snprintf(buf2, BUFLEN, " [%d left]", f->lifetime); - strcat(buf, buf2); - // } - wrapprint(mainwin, &y, &x, 0, "%s ", buf); + if (isplayer(lf)) { + f = lfhasknownflag(lf, F_POLYMORPHED); + if (f && (f->known)) { + snprintf(buf, BUFLEN, "%s have been polymorphed into a %s.",you(lf), lf->race->name); + //if (lfhasflag(lf, F_OMNIPOTENT) || lfhasflag(lf, F_EXTRAINFO)) { + snprintf(buf2, BUFLEN, " [%d left]", f->lifetime); + strcat(buf, buf2); + // } + wrapprint(mainwin, &y, &x, 0, "%s ", buf); + } } if (!canuseweapons(lf)) { diff --git a/lf.c b/lf.c index 17ee08c..85e7d55 100644 --- a/lf.c +++ b/lf.c @@ -2856,7 +2856,7 @@ void die(lifeform_t *lf) { if (premaxhp < lf->maxhp) { float ratio; ratio = (float)premaxhp / (float) lf->maxhp; - lf->maxhp = ratio * lf->maxhp; + lf->hp = ratio * lf->maxhp; limit(&(lf->hp), 1, lf->maxhp); } if (thisisplayer) statdirty = B_TRUE; @@ -3135,7 +3135,12 @@ void die(lifeform_t *lf) { copyflag(corpse->flags, lf->flags, F_CANCAST); copyflag(corpse->flags, lf->flags, F_CANWILL); copyflag(corpse->flags, lf->flags, F_JOB); - + // race subspecies flags, so that we can have mosnters which eat + // only certain kinds of corpses + copyflag(corpse->flags, lf->flags, F_AVIAN); + copyflag(corpse->flags, lf->flags, F_CANINE); + copyflag(corpse->flags, lf->flags, F_EQUINE); + copyflag(corpse->flags, lf->flags, F_FELINE); f = hasflag(corpse->flags, F_CORPSEOF); if (f) { @@ -8356,10 +8361,28 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { char jobstring[BUFLEN]; char the[6]; char lname[BUFLEN]; + race_t *lfrace; flag_t *f; enum LFSIZE size,racesize; int dobehaviour = B_TRUE; + enum SKILLLEVEL lorelev; + if (gamemode == GM_GAMESTARTED) { + lorelev = getlorelevel(player, lf->race->raceclass->id); + } else { + lorelev = PR_MASTER; + } + + if (lfhasflag(lf, F_LYCANTHROPE)) { + // lycanthropes appear as human unless you know better + if (lorelev >= PR_ADEPT) { + lfrace = lf->race; + } else { + lfrace = findrace(R_HUMAN); + } + } else { + lfrace = lf->race; + } // 'the' or 'your' ? f = lfhasflag(lf, F_NAME); @@ -8368,7 +8391,7 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { strcpy(lname, f->text); } else if (lfhasflag(lf, F_UNIQUE)) { strcpy(the, ""); - strcpy(lname, lf->race->name); + strcpy(lname, lfrace->name); } else { if (ispetof(lf, player)) { strcpy(the, "your "); @@ -8377,33 +8400,35 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { } f = lfhasflag(lf, F_NAMED); if (f) { // ie. "the xat named blah" - strcpy(lname, lf->race->name); + strcpy(lname, lfrace->name); strcat(lname, " named "); strcat(lname, f->text); } else { // ie. "the xat" - strcpy(lname, lf->race->name); + strcpy(lname, lfrace->name); } } // construct description string strcpy(descstring, ""); - // has their size changed? - f = hasflag(lf->race->flags, F_SIZE); - if (f) { - racesize = f->val[0]; - } else { - racesize = SZ_HUMAN; // default - } - size = getlfsize(lf); - if (size != racesize) { - strcat(descstring, getsizetext(size)); - strcat(descstring, " "); + // if you lorelev is high enough, you can tell when the race is larger or smaller than + // they should be. + if (lorelev >= PR_BEGINNER) { + f = hasflag(lfrace->flags, F_SIZE); + if (f) { + racesize = f->val[0]; + } else { + racesize = SZ_HUMAN; // default + } + size = getlfsize(lf); + if (size != racesize) { + strcat(descstring, getsizetext(size)); + strcat(descstring, " "); + } } - // need a certain amount of race knowledge to recognise ai traits - if (getlorelevel(player, lf->race->raceclass->id) < PR_SKILLED) { + if (lorelev < PR_SKILLED) { dobehaviour = B_FALSE; } // frozen/headless trump behavioural descriptions like "insane" @@ -8426,7 +8451,7 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { // construct job string strcpy(jobstring, ""); - if (showall || isplayer(lf) || (getlorelevel(player, lf->race->raceclass->id) >= PR_BEGINNER)) { + if (showall || isplayer(lf) || (lorelev >= PR_BEGINNER)) { if (!lfhasflag(lf, F_NOJOBTEXT) && !lfhasflag(lf, F_NAME) && !lfhasflag(lf, F_UNIQUE)) { if (getjob(lf)) { snprintf(jobstring, BUFLEN, " %s", getjobname(lf)); @@ -8483,6 +8508,29 @@ char *getlfnamea(lifeform_t *lf, char *buf) { } char *real_getlfnamea(lifeform_t *lf, char *buf, int usevis, int showall) { + race_t *lfrace; + enum SKILLLEVEL lorelev; + + if (gamemode == GM_GAMESTARTED) { + lorelev = getlorelevel(player, lf->race->raceclass->id); + } else { + lorelev = PR_MASTER; + } + + switch (lf->race->id) { + case R_WERERAT: + case R_WEREWOLF: + if (lorelev >= PR_ADEPT) { + lfrace = lf->race; + } else { + lfrace = findrace(R_HUMAN); + } + break; + default: + lfrace = lf->race; + break; + } + if (isplayer(lf)) { snprintf(buf, BUFLEN, "you"); } else { @@ -8497,7 +8545,7 @@ char *real_getlfnamea(lifeform_t *lf, char *buf, int usevis, int showall) { if (ispetof(lf, player)) { strcpy(the, "your "); } else { - if (isvowel(lf->race->name[0])) { + if (isvowel(lfrace->name[0])) { strcpy(the, "an "); } else { strcpy(the, "a "); @@ -9513,39 +9561,44 @@ object_t *getweapon(lifeform_t *lf) { // this function MIGHT allocat op! int getweapons(lifeform_t *lf, object_t **wep, flag_t **damflag, int *lastweaponidx, obpile_t **op, int *nweps) { int gotweapon = B_FALSE; - flag_t *retflag[MAXCANDIDATES],*f; + flag_t *retflag[MAXCANDIDATES],*f,*forcewep; int nretflags,i; // first use our weapon... - *nweps = 0; - wep[*nweps] = getweapon(lf); - if (wep[*nweps]) { - if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM); - if (lastweaponidx) *lastweaponidx = 0; - (*nweps)++; - gotweapon = B_TRUE; - } - // if we are skilled at twoweaponing, we can attack with our second weapon - // as well, with a possible accuracy penalty depending on our skill level. - if (getskill(lf, SK_TWOWEAPON)) { - wep[*nweps] = getsecmeleeweapon(lf); + *nweps = 0; + + forcewep = lfhasflag(lf, F_FORCEATTACK); + if (!forcewep) { + wep[*nweps] = getweapon(lf); if (wep[*nweps]) { - if ((*nweps >= 1) && (wep[*nweps] == wep[(*nweps)-1])) { - // can't be the same as first one - } else { - if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM); - if (lastweaponidx) *lastweaponidx = *nweps; - (*nweps)++; - gotweapon = B_TRUE; + if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM); + if (lastweaponidx) *lastweaponidx = 0; + (*nweps)++; + gotweapon = B_TRUE; + } + + // if we are skilled at twoweaponing, we can attack with our second weapon + // as well, with a possible accuracy penalty depending on our skill level. + if (getskill(lf, SK_TWOWEAPON)) { + wep[*nweps] = getsecmeleeweapon(lf); + if (wep[*nweps]) { + if ((*nweps >= 1) && (wep[*nweps] == wep[(*nweps)-1])) { + // can't be the same as first one + } else { + if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM); + if (lastweaponidx) *lastweaponidx = *nweps; + (*nweps)++; + gotweapon = B_TRUE; + } } } - } + } // end if !forcewep // then use all our innate attacks.. getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; - if (f->id == F_HASATTACK) { + if (!forcewep || (f->val[0] == forcewep->val[0])) { objecttype_t *ot; if (!(*op)) { @@ -16235,11 +16288,20 @@ int readytotrain(lifeform_t *lf) { } int recruit(lifeform_t *lf) { + enum ALIGNMENT pa,lfa; + // alignments must match, otherwire VERY hard to make them join! + pa = getalignment(player); + lfa = getalignment(lf); int rv = B_FALSE; + if (lfhasflag(lf, F_NOHIRE)) { // refusing to join at all. sayphrase(lf, SP_RECRUIT_DECLINE, SV_TALK, NA, NULL); rv = B_TRUE; + } else if ( ((pa == AL_GOOD) && (lfa == AL_EVIL)) || + ((pa == AL_EVIL) && (lfa == AL_GOOD)) ) { + sayphrase(lf, SP_RECRUIT_DECLINE, SV_TALK, NA, NULL); + rv = B_TRUE; } else { int dohire = B_FALSE; int askingprice = -1; @@ -16259,6 +16321,7 @@ int recruit(lifeform_t *lf) { // since you have to be at least speech=4(skilled) to ask someone to // join, add +8 to difficulty (pr_skilled * 2) difficulty = 25 + 8 + ((gettr(player) - gettr(lf))*2); + if (real_skillcheck(player, SC_SPEECH, difficulty, 0, &result)) { minmult = 10; maxmult = 20; @@ -16970,11 +17033,30 @@ int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *t rv = say(lf, buf, volume); break; case SP_RECRUIT_ASKPRICE: - snprintf(buf, BUFLEN, "My services will cost you $%d.",val0); + switch (rnd(1,8)) { + case 1: snprintf(buf, BUFLEN, "My services will cost you $%d.", val0); break; + case 2: snprintf(buf, BUFLEN, "$%d and you have yourself a deal.", val0); break; + case 3: snprintf(buf, BUFLEN, "Okay. How does $%d sound?", val0); break; + case 4: snprintf(buf, BUFLEN, "I'll do it for $%d.", val0); break; + case 5: snprintf(buf, BUFLEN, "Let's see... $%d should do it.", val0); break; + case 6: snprintf(buf, BUFLEN, "My fee is $%d. Still interested?", val0); break; + case 7: snprintf(buf, BUFLEN, "I don't work for free. $%d.", val0); break; + case 8: snprintf(buf, BUFLEN, "$%d and your cause is mine.", val0); break; + } rv = say(lf, buf, volume); break; case SP_RECRUIT_DECLINE: - rv = say(lf, "No, I regretfully decline your offer.", volume); + switch (rnd(1,8)) { + case 1: snprintf(buf, BUFLEN, "No, I regretfully decline your offer."); break; + case 2: snprintf(buf, BUFLEN, "You dare ask me for help?"); break; + case 3: snprintf(buf, BUFLEN, "I will never help you."); break; + case 4: snprintf(buf, BUFLEN, "No."); break; + case 5: snprintf(buf, BUFLEN, "Me? Help you?."); break; + case 6: snprintf(buf, BUFLEN, "Ahem. I think not."); break; + case 7: snprintf(buf, BUFLEN, "I don't think that would be a good idea."); break; + case 8: snprintf(buf, BUFLEN, "Sorry, but no."); break; + } + rv = say(lf, buf, volume); break; case SP_RECRUIT_DECLINE_CANTPAY: rv = say(lf, "...which I see you cannot afford.", volume); @@ -17417,8 +17499,15 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { killflag(f); nkilled++; } else if ((f->lifetime > 0) && (f->id != F_POLYMORPHED)) { - killflag(f); - nkilled++; + // kill most temporary flags, with exceptions + switch (f->id) { + case F_FLEEFROM: + break; + default: + killflag(f); + nkilled++; + break; + } } } diff --git a/map.c b/map.c index ddb57b6..a2a7233 100644 --- a/map.c +++ b/map.c @@ -142,12 +142,18 @@ void addhomeobs(lifeform_t *lf, int dolevelobs) { amt = rnd(f->val[0],f->val[1]); for (i = 0; i < amt; i++) { cell_t *c; - // pick new EMPTY random spot - c = getrandomcell(lf->cell->map); - while (!cellwalkable(NULL, c, NULL)) { + // pick new EMPTY random spot. try rooms first. + c = getrandomroomcell(lf->cell->map, ANYROOM, WE_WALKABLE); + if (!c) { + // if no rooms, get any walkable cell. c = getrandomcell(lf->cell->map); + while (!cellwalkable(NULL, c, NULL)) { + c = getrandomcell(lf->cell->map); + } + } + if (c) { + o = addob(c->obpile, f->text); } - o = addob(c->obpile, f->text); } } @@ -1236,6 +1242,39 @@ int adjcellokforreachability(cell_t *c, int srcroomid, int dir, int wantfilled) return B_FALSE; } +int cellmatchescondition(cell_t *c, int wecond) { + int ok = B_FALSE; + if (wecond == WE_EMPTY) { + // make sure it's empty + if (isempty(c)) { + ok = B_TRUE; + } + } else if (wecond == WE_WALKABLE) { + if (cellwalkable(NULL, c, NULL)) { + ok = B_TRUE; + } + } else if (wecond == WE_PORTAL) { + if (cellwalkable(NULL, c, NULL) && !hasenterableobject(c) ) { + if (!hasobwithflag(c->obpile, F_DOOR)) { + ok = B_TRUE; + } + } + } else if (wecond == WE_NOTWALL) { + if ((!c->type->solid) && !hasobwithflag(c->obpile, F_IMPASSABLE)) { + //if (!c->type->solid) { + ok = B_TRUE; + } + } else if (wecond == WE_NOLF) { + if (!c->lf) ok = B_TRUE; + } else if (wecond == WE_SOLID) { + if (c->type->solid) ok = B_TRUE; + } else { + // always ok + ok = B_TRUE; + } + return ok; +} + // returns B_TRUE, B_FALSE or B_MAYBE int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom) { int db = B_FALSE; @@ -2873,9 +2912,9 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ if (db) dblog("--> Will add %d pillars",numpillars); for (n = 0; n < numpillars;n++ ) { //dblog("----> Adding pillar %d/%d",n+1,numpillars); - c = getrandomroomcell(map, i); + c = getrandomroomcell(map, i, WE_EMPTY); - if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { + if (c && !countobs(c->obpile, B_TRUE)) { setcelltype(c, solidcell); } } @@ -2897,10 +2936,9 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ int nmonsters = 0; done = B_FALSE; while (!done) { - c = getrandomroomcell(map, i); + c = getrandomroomcell(map, i, WE_WALKABLE); // if nothing there - if (c && cellwalkable(NULL, c, NULL)) { - + if (c) { int obchance; int nadded = 0; @@ -3418,7 +3456,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex break; case RT_OBJECT: if (db) dblog(" adding forced regionthing object: %s", thing[i]->what); - c = getrandomroomcell(map, ANYROOM); + c = getrandomroomcell(map, ANYROOM, WE_WALKABLE); if (!c) c = getrandomcell(map); c = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); addob(c->obpile, thing[i]->what); @@ -3967,7 +4005,7 @@ void createsewer(map_t *map, int depth, map_t *parentmap, int exitdir, object_t // also ensure it's not over water. c = NULL; while (!c || (countadjwalls(c) == 0) || getcellwaterdepth(c, NULL)) { - c = getrandomroomcell(map, ANYROOM); + c = getrandomroomcell(map, ANYROOM, WE_NONE); assert(c); } o = addobfast(c->obpile, OT_GRATINGROOF); @@ -4659,13 +4697,13 @@ int linkexits(map_t *m, int roomid) { assert(roomidx != -1); // does this roomid actually exist?? - c = getrandomroomcell(m, roomid); + c = getrandomroomcell(m, roomid, WE_NONE); if (!c) return B_FALSE; if (db) { char buf[BUFLEN]; vault_t *v; - c = getrandomroomcell(m, roomid); + c = getrandomroomcell(m, roomid, WE_NONE); v = getcellvault(c); snprintf(buf, BUFLEN, "*** linkexits for roomid %d (%s) [%d,%d-%d,%d]", roomid, v ? v->id : "novault", @@ -5195,8 +5233,8 @@ void finalisemap(map_t *map, object_t *entryob) { flag_t *f; // first dungeon level. just one exit stairs c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomroomcell(map, ANYROOM); + while (!c || countobs(c->obpile, B_TRUE)) { + c = getrandomroomcell(map, ANYROOM, WE_EMPTY); } o = addobfast(c->obpile, upstairtype); if (entryob) { @@ -5223,8 +5261,8 @@ void finalisemap(map_t *map, object_t *entryob) { // up stairs on all other levels for (i = 0; i < nupstairsneeded; i++) { c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomroomcell(map, ANYROOM); + while (!c || countobs(c->obpile, B_TRUE)) { + c = getrandomroomcell(map, ANYROOM, WE_EMPTY); if (!c) { // ANY cell at all, doesn't have to be a room. c = getrandomcell(map); @@ -5248,8 +5286,8 @@ void finalisemap(map_t *map, object_t *entryob) { if ((downstairtype != OT_NONE) && (map->depth < map->region->rtype->maxdepth)) { for (i = 0; i < ndownstairsneeded; i++) { c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomroomcell(map, ANYROOM); + while (!c || countobs(c->obpile, B_TRUE)) { + c = getrandomroomcell(map, ANYROOM, WE_EMPTY); if (!c) { // ANY cell at all, doesn't have to be a room. c = getrandomcell(map); @@ -5329,7 +5367,7 @@ void finalisemap(map_t *map, object_t *entryob) { if (roomlightchance) { for (i = 0; i < map->nrooms; i++) { if (pctchance(roomlightchance)) { - c = getrandomroomcell(map, i); + c = getrandomroomcell(map, i, WE_WALKABLE); addob(c->obpile, roomlightob); } } @@ -5474,6 +5512,13 @@ void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, addhomeobs(lf, B_TRUE); + // if monster can shapeshift, it will sometimes start in its alternate form. + if (cancast(lf, OT_S_SHAPESHIFT, NULL)) { + if (onein(4)) { + castspell(lf, OT_S_SHAPESHIFT, lf, NULL, lf->cell, NULL, NULL); + } + } + /* getflags(lf->flags, retflag, &nretflags, F_LIFEOB, F_NONE); for (i = 0; i < nretflags; i++) { @@ -6018,34 +6063,8 @@ cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LO enum OBTYPE *badoid; int ok = B_FALSE; numwithlof++; - if (wantempty == WE_EMPTY) { - // make sure it's empty - if (isempty(new)) { - ok = B_TRUE; - } - } else if (wantempty == WE_WALKABLE) { - if (cellwalkable(NULL, new, NULL)) { - ok = B_TRUE; - } - } else if (wantempty == WE_PORTAL) { - if (cellwalkable(NULL, new, NULL) && !hasenterableobject(new) ) { - if (!hasobwithflag(new->obpile, F_DOOR)) { - ok = B_TRUE; - } - } - } else if (wantempty == WE_NOTWALL) { - if ((!new->type->solid) && !hasobwithflag(new->obpile, F_IMPASSABLE)) { - //if (!new->type->solid) { - ok = B_TRUE; - } - } else if (wantempty == WE_NOLF) { - if (!new->lf) ok = B_TRUE; - } else if (wantempty == WE_SOLID) { - if (new->type->solid) ok = B_TRUE; - } else { - // always ok - ok = B_TRUE; - } + + ok = cellmatchescondition(new, wantempty); // obs we dont want? if (dontwantob) { @@ -6149,7 +6168,7 @@ int getrandomdirexcept(int dirtype, int exception) { return dir; } -cell_t *getrandomroomcell(map_t *map, int roomid) { +cell_t *getrandomroomcell(map_t *map, int roomid, int wantempty) { int npossible = 0; int selidx; int x,y; @@ -6175,7 +6194,7 @@ cell_t *getrandomroomcell(map_t *map, int roomid) { } } - if (ok) { + if (ok && cellmatchescondition(c, wantempty)) { poss[npossible] = c; npossible++; } diff --git a/map.h b/map.h index ae7a2dc..c25f1a5 100644 --- a/map.h +++ b/map.h @@ -16,6 +16,7 @@ int autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, in void breakwall(cell_t *c); int cellhaslos(cell_t *c1, cell_t *dest); int cellisfixedvaultwall(cell_t *c); +int cellmatchescondition(cell_t *c, int wecond); int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom); void clearcell(cell_t *c); void clearcell_exceptflags(cell_t *c, ...); @@ -120,7 +121,7 @@ cell_t *getrandomcell(map_t *map); cell_t *getrandomcelloftype(map_t *map, enum CELLTYPE id); int getrandomdir(int dirtype); int getrandomdirexcept(int dirtype, int exception); -cell_t *getrandomroomcell(map_t *map, int roomid); +cell_t *getrandomroomcell(map_t *map, int roomid, int wantempty); void getroomcells(map_t *m, int roomid, cell_t **retcell, int *ncells); int getslipperyness(cell_t *c, object_t **slipob); cell_t *getstairdestination(object_t *o, int *madenewmap); diff --git a/objects.c b/objects.c index eda2984..2d6a6a2 100644 --- a/objects.c +++ b/objects.c @@ -942,7 +942,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum targetmap = addmap(); createmap(targetmap, dlev, c->map->region, NULL, D_NONE, NULL); } - targetcell = getrandomroomcell(targetmap, ANYROOM); + targetcell = getrandomroomcell(targetmap, ANYROOM, WE_WALKABLE); if (!targetcell) targetcell = getrandomcell(targetmap); while (!cellwalkable(NULL, targetcell, NULL)) { targetcell = getrandomadjcell(targetcell, WE_WALKABLE, B_ALLOWEXPAND); @@ -1634,6 +1634,12 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum cf->val[1] = sizetonutrition(rf->val[0]); } } + + // fill in animal subspecies + copyflag(o->flags, corpserace->flags, F_AVIAN); + copyflag(o->flags, corpserace->flags, F_CANINE); + copyflag(o->flags, corpserace->flags, F_EQUINE); + copyflag(o->flags, corpserace->flags, F_FELINE); } else if (o->type->id == OT_MAP) { region_t *srcregion; regiontype_t *dstrt = NULL; diff --git a/spell.c b/spell.c index 8c6162f..1b164d8 100644 --- a/spell.c +++ b/spell.c @@ -2067,9 +2067,9 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } // find a random empty cell here - entry = getrandomroomcell(newmap, ANYROOM); + entry = getrandomroomcell(newmap, ANYROOM, WE_WALKABLE); while (!cellwalkable(target, entry, NULL)) { - entry = getrandomroomcell(newmap, ANYROOM); + entry = getrandomroomcell(newmap, ANYROOM, WE_WALKABLE); } // link the map to this lf @@ -2121,6 +2121,9 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } else if (!lfhasflag(user, F_FLYING)) { if (isplayer(user)) msg("You are not flying, therefore cannot swoop."); return B_TRUE; + } else if (!lfhasflagval(user, F_HASATTACK, OT_CLAWS, NA, NA, NULL)) { + if (isplayer(user)) msg("You need claws to perform a swoop attack."); + return B_TRUE; } if (!targcell) { @@ -2189,7 +2192,10 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } // attack + addflag(user->flags, F_FORCEATTACK, OT_CLAWS, NA, NA, "fromuseability"); attackcell(user, targcell, B_TRUE); + f = lfhasflagval(user, F_FORCEATTACK, OT_CLAWS, NA, NA, "fromuseability"); + if (f) killflag(f); // teleport back to initial pos movelf(user, origcell); @@ -4891,7 +4897,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // determine type of mosnter - if (getforcedspellrace(caster, spellid, buf)) { + if (getforcedspellrace(caster, spellid, buf, NULL)) { } else if ((power >= 7) && isplayer(caster)) { // ask what kind of monster askstring("Create what kind of monster", '?', buf, BUFLEN, NULL); @@ -4952,13 +4958,13 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ ch = askchar("Teleport to the new vault", "yn","y", B_TRUE, B_FALSE); if (ch == 'y') { int ntries = 0; - c = getrandomroomcell(caster->cell->map, caster->cell->map->nrooms-1); + c = getrandomroomcell(caster->cell->map, caster->cell->map->nrooms-1, WE_WALKABLE); if (!c || !cellwalkable(caster, c, NULL)) { if (++ntries >= 10) { msg("Oops, couldn't find a free space."); return B_FALSE; } - c = getrandomroomcell(caster->cell->map, caster->cell->map->nrooms-1); + c = getrandomroomcell(caster->cell->map, caster->cell->map->nrooms-1, WE_WALKABLE); } teleportto(caster, c, B_TRUE); return B_FALSE; @@ -8980,7 +8986,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if ((spellid == OT_S_POLYMORPH) || (spellid == OT_S_SHAPESHIFT)) { race_t *r = NULL; - if (caster && (frompot || (spellid == OT_S_SHAPESHIFT))) { target = caster; } else { @@ -9007,7 +9012,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } //if ((caster == target) && getforcedspellrace(caster, spellid, buf)) { - if (getforcedspellrace(target, spellid, buf)) { + if (getforcedspellrace(target, spellid, buf, NULL)) { r = findracebyname(buf); } else { if (spellid == OT_S_POLYMORPH) { @@ -10812,6 +10817,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ (spellid == OT_S_HECTASSERVANT) || (spellid == OT_S_SUMMONDEMON)) { int lifetime, nwant,ngot,successrate; + int tempcount = 0; enum LFSIZE wantsize; enum RACECLASS wantrc; enum RACE wantrace = R_NONE; @@ -10887,7 +10893,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // override with forced race? - if (caster && getforcedspellrace(caster, spellid, racename)) { + if (caster && getforcedspellrace(caster, spellid, racename, &tempcount)) { race_t *r; r = findracebyname(racename); if (r) { @@ -10896,6 +10902,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ wantsize = SZ_ANY; } } + // override count + if (tempcount > 0) { + nwant = tempcount; + } if (!pctchance(successrate)) { fizzle(caster); @@ -11842,7 +11852,7 @@ enum OBTYPE getfirstwizspell(enum SPELLSCHOOL school) { return firstspell; } -char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr) { +char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr, int *count) { flag_t *f; // forced? f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL); @@ -11850,7 +11860,7 @@ char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr) { f = lfhasflagval(lf, F_CANCAST, spellid, NA, NA, NULL); } if (f) { - texttospellopts(f->text, "race:", racestr, NULL); + texttospellopts(f->text, "race:", racestr, "count:", count, NULL); if (strlen(racestr)) { return racestr; } diff --git a/spell.h b/spell.h index 552b618..8943618 100644 --- a/spell.h +++ b/spell.h @@ -9,7 +9,7 @@ enum SPELLSCHOOL findspellschoolbyname(char *buf); void fizzle(lifeform_t *caster); //int getiqreq(enum OBTYPE oid); enum OBTYPE getfirstwizspell(enum SPELLSCHOOL school); -char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr); +char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr, int *count); int getmpcost(lifeform_t *lf, enum OBTYPE oid); int getmrdiff(enum OBTYPE spellid, int power); enum OBTYPE getrandomspell(int maxlev); diff --git a/text.c b/text.c index e6c2d1e..a1fd051 100644 --- a/text.c +++ b/text.c @@ -2304,6 +2304,7 @@ int texttospellopts(char *text, ... ) { int nfilled = 0; char *validname[] = { "pw:", + "count:", "dam:", "needgrab:", "range:", @@ -2313,6 +2314,7 @@ int texttospellopts(char *text, ... ) { }; int argdefault[] = { 0, + 1, -1, // string B_FALSE, 0, @@ -2321,6 +2323,7 @@ int texttospellopts(char *text, ... ) { -99, // last }; char argtype[] = { + 'i', 'i', 's', 'b', @@ -2336,50 +2339,51 @@ int texttospellopts(char *text, ... ) { wantname = va_arg(args, char *); if (wantname) writeto = va_arg(args, void *); while (wantname) { // process this one - int foundidx = -1,i; - - // validate 'wantname' - must match one of 'validname[]' - for (i = 0; validname[i]; i++) { - if (streq(validname[i], wantname)) { - foundidx = i; - break; + if (writeto) { + int foundidx = -1,i; + + // validate 'wantname' - must match one of 'validname[]' + for (i = 0; validname[i]; i++) { + if (streq(validname[i], wantname)) { + foundidx = i; + break; + } } - } - assert(foundidx != -1); + assert(foundidx != -1); - // blank our dest buffer - if (argtype[foundidx] == 'i') { - *((int *)writeto) = argdefault[foundidx]; - } else if (argtype[foundidx] == 'b') { - *((int *)writeto) = argdefault[foundidx]; - } else if (argtype[foundidx] == 's') { - strcpy((char *)writeto, ""); - } + // blank our dest buffer + if (argtype[foundidx] == 'i') { + *((int *)writeto) = argdefault[foundidx]; + } else if (argtype[foundidx] == 'b') { + *((int *)writeto) = argdefault[foundidx]; + } else if (argtype[foundidx] == 's') { + strcpy((char *)writeto, ""); + } - // look for 'wantname' within 'text' - for (p = text ; *p ; p++) { - if (!strncmp(p, wantname, strlen(wantname)) ) { // found it! - char localval[BUFLEN]; - char *valfull; + // look for 'wantname' within 'text' + for (p = text ; *p ; p++) { + if (!strncmp(p, wantname, strlen(wantname)) ) { // found it! + char localval[BUFLEN]; + char *valfull; - // extract value from text - // p will point to "pw:xxx;" - strcpy(localval, p + strlen(wantname)); // localval is "xxx;" - valfull = strtok(localval, ";"); // valfull is "xxx" - if (valfull) { - // if it's there, write the value into 'writeto' - if (argtype[foundidx] == 'i') { - *((int *)writeto) = atoi(valfull); - } else if (argtype[foundidx] == 'b') { - *((int *)writeto) = atoi(valfull) ? B_TRUE : B_FALSE; - } else if (argtype[foundidx] == 's') { - strcpy((char *)writeto, valfull); + // extract value from text + // p will point to "pw:xxx;" + strcpy(localval, p + strlen(wantname)); // localval is "xxx;" + valfull = strtok(localval, ";"); // valfull is "xxx" + if (valfull) { + // if it's there, write the value into 'writeto' + if (argtype[foundidx] == 'i') { + *((int *)writeto) = atoi(valfull); + } else if (argtype[foundidx] == 'b') { + *((int *)writeto) = atoi(valfull) ? B_TRUE : B_FALSE; + } else if (argtype[foundidx] == 's') { + strcpy((char *)writeto, valfull); + } + nfilled++; } - nfilled++; } } } - // get next one wantname = va_arg(args, char *); if (wantname) writeto = va_arg(args, void *);