diff --git a/ai.c b/ai.c index 9fcdd5b..5b5bce4 100644 --- a/ai.c +++ b/ai.c @@ -2475,6 +2475,9 @@ 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_DEATHKEEN) { + if (!isnighttime()) specificcheckok = B_FALSE; + } if ((ot->id == OT_S_DANCINGFLAME) || (ot->id == OT_S_CLEANSINGFIRE)) { int i; int found = B_FALSE; diff --git a/attack.c b/attack.c index bafb33a..07fe4ba 100644 --- a/attack.c +++ b/attack.c @@ -824,7 +824,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) if (lfhasflag(victim, F_NONCORPOREAL) && !lfhasflag(lf, F_NONCORPOREAL) ) { // using a magical or blessed weapon? if so you're ok. - if (wep && (ismagical(wep) || isblessed(wep)) ) { + if (wep && (ismagical(wep) || isblessed(wep) || (wep->material->id == MT_SILVER)) ) { } else { weppassthrough = B_TRUE; hit = B_FALSE; diff --git a/data.c b/data.c index 244f2c8..3cd543e 100644 --- a/data.c +++ b/data.c @@ -10,6 +10,7 @@ #include "objects.h" #include "spell.h" +extern behaviour_t *firstbehaviour,*lastbehaviour; extern command_t *firstcommand,*lastcommand; extern option_t *firstoption,*lastoption; extern map_t *firstmap; @@ -81,6 +82,34 @@ option_t *addoption(enum OPTION id, char *text, int def) { return a; } +behaviour_t *addbehaviour(enum BEHAVIOUR id, char *name) { + behaviour_t *a; + + assert(!findbehaviour(id)); + + // add to the end of the list + if (firstbehaviour == NULL) { + firstbehaviour = malloc(sizeof(behaviour_t)); + a = firstbehaviour; + a->prev = NULL; + } else { + // go to end of list + a = lastbehaviour; + a->next = malloc(sizeof(behaviour_t)); + a->next->prev = a; + a = a->next; + } + lastbehaviour = a; + a->next = NULL; + + // set props + a->id = id; + a->name = strdup(name); + a->flags = addflagpile(NULL, NULL); + + return a; +} + command_t *addcommand(enum COMMAND id, char ch, char *desc) { command_t *a; @@ -2969,6 +2998,13 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 4, NA, NULL); // l5 + addot(OT_S_DEATHKEEN, "midnight dirge", "Emits a dreadful wailing keen which instantly slays any who hear it.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell will only function at night."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 5, NA, NULL); addot(OT_S_HECTASSERVANT, "hecta's hand", "Summons an enormous skeletal hand to drag foes to their doom. BEWARE: the hand will attack anything living, including the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); @@ -7689,6 +7725,7 @@ void initobjects(void) { addflag(lastot->flags, F_ATTREQ, A_STR, 50, 60, "10"); addflag(lastot->flags, F_CRITCHANCE, 5, NA, NA, NULL); addflag(lastot->flags, F_OBATTACKDELAY, 110, NA, NA, NULL); + addflag(lastot->flags, F_CANBEDIFFMAT, MT_BONE, 33, NA, NULL); addot(OT_GREATCLUB, "great club", "An enormous, very heavy, blunt instrument to hit things with.", MT_STONE, 15, OC_WEAPON, SZ_MEDIUM); addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); addflag(lastot->flags, F_RARITY, H_CAVE, 50, NA, NULL); @@ -7813,7 +7850,7 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_SWAMP, 100, RR_COMMON, NULL); addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_FIRESPEED, 4, NA, NA, NULL); + addflag(lastot->flags, F_FIRESPEED, 2, NA, NA, NULL); addflag(lastot->flags, F_ACCURACY, 70, NA, NA, NULL); addflag(lastot->flags, F_RANGE, 5, NA, NA, NULL); addflag(lastot->flags, F_AMMOOB, OT_DART, NA, NA, NULL); @@ -7828,14 +7865,28 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_CAVE, 100, RR_COMMON, NULL); addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_FIRESPEED, 4, NA, NA, NULL); + addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 4, NA, NA, NULL); + addflag(lastot->flags, F_AMMOOB, OT_ARROW, NA, NA, NULL); + addflag(lastot->flags, F_AMMOCAPACITY, 1, NA, NA, NULL); + addflag(lastot->flags, F_RELOADTURNS, 1, NA, NA, NULL); + addflag(lastot->flags, F_ATTREQ, A_STR, 45, NA, NULL); + addflag(lastot->flags, F_RODSHAPED, B_TRUE, NA, NA, NULL); + + addot(OT_COMPOSITEBOW, "composite bow", "A short bow, smaller than a longbow but more powerful than a shortbow.", MT_WOOD, 7, OC_WEAPON, SZ_MEDIUM); + addflag(lastot->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL); + addflag(lastot->flags, F_RARITY, H_FOREST, NA, RR_UNCOMMON, NULL); + addflag(lastot->flags, F_RARITY, H_CAVE, NA, RR_UNCOMMON, NULL); + addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_FIRESPEED, 6, NA, NA, NULL); addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL); addflag(lastot->flags, F_RANGE, 5, NA, NA, NULL); addflag(lastot->flags, F_AMMOOB, OT_ARROW, NA, NA, NULL); addflag(lastot->flags, F_AMMOCAPACITY, 1, NA, NA, NULL); addflag(lastot->flags, F_RELOADTURNS, 1, NA, NA, NULL); - addflag(lastot->flags, F_ATTREQ, A_STR, 45, NA, NULL); - addflag(lastot->flags, F_RODSHAPED, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_ATTREQ, A_STR, 65, NA, NULL); addot(OT_CROSSBOW, "crossbow", "A standard crossbow. Very powerful, but slow to reload and needs high strength to use.", MT_WOOD, 8, OC_WEAPON, SZ_MEDIUM); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); @@ -7844,7 +7895,7 @@ void initobjects(void) { addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_FIRESPEED, 10, NA, NA, NULL); addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL); - addflag(lastot->flags, F_RANGE, 10, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 6, NA, NA, NULL); addflag(lastot->flags, F_AMMOOB, OT_BOLT, NA, NA, NULL); addflag(lastot->flags, F_AMMOCAPACITY, 1, NA, NA, NULL); addflag(lastot->flags, F_RELOADTURNS, 2, NA, NA, NULL); @@ -7857,7 +7908,7 @@ void initobjects(void) { addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_FIRESPEED, 5, NA, NA, NULL); addflag(lastot->flags, F_ACCURACY, 75, NA, NA, NULL); - addflag(lastot->flags, F_RANGE, 7, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 5, NA, NA, NULL); addflag(lastot->flags, F_AMMOOB, OT_BOLT, NA, NA, NULL); addflag(lastot->flags, F_AMMOCAPACITY, 3, NA, NA, NULL); addflag(lastot->flags, F_RELOADTURNS, 1, NA, NA, NULL); @@ -7869,7 +7920,7 @@ void initobjects(void) { addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_FIRESPEED, 8, NA, NA, NULL); addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL); - addflag(lastot->flags, F_RANGE, 10, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 8, NA, NA, NULL); addflag(lastot->flags, F_AMMOOB, OT_ARROW, NA, NA, NULL); addflag(lastot->flags, F_AMMOCAPACITY, 1, NA, NA, NULL); addflag(lastot->flags, F_RELOADTURNS, 1, NA, NA, NULL); @@ -7898,7 +7949,7 @@ void initobjects(void) { addflag(lastot->flags, F_TWOHANDED, SZ_HUMAN, NA, NA, NULL); addflag(lastot->flags, F_FIRESPEED, 20, NA, NA, NULL); addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL); - addflag(lastot->flags, F_RANGE, 3, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 2, NA, NA, NULL); addflag(lastot->flags, F_AMMOOB, OT_BULLET, NA, NA, NULL); addflag(lastot->flags, F_AMMOCAPACITY, 2, NA, NA, NULL); addflag(lastot->flags, F_RELOADTURNS, 3, NA, NA, NULL); @@ -7910,7 +7961,7 @@ void initobjects(void) { addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_FIRESPEED, 4, NA, NA, NULL); addflag(lastot->flags, F_ACCURACY, 70, NA, NA, NULL); - addflag(lastot->flags, F_RANGE, 5, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 4, NA, NA, NULL); addflag(lastot->flags, F_AMMOOB, OT_STONE, NA, NA, NULL); addflag(lastot->flags, F_AMMOCAPACITY, 1, NA, NA, NULL); addflag(lastot->flags, F_RELOADTURNS, 1, NA, NA, NULL); @@ -8069,6 +8120,28 @@ void initrace(void) { skill_t *sk; int i; + // behaviours + addbehaviour(BH_INSANE, "insane"); + addflag(lastbehaviour->flags, F_TERRITORIAL, 2, NA , NA, NULL); + addbehaviour(BH_HUNGRY, "hungry"); + addflag(lastbehaviour->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS , NA, NULL); + addbehaviour(BH_TIMID, "timid"); + // givebehaviour() will also halve morale + addflag(lastbehaviour->flags, F_FLEEONDAM, B_TRUE, NA , NA, NULL); + addbehaviour(BH_DRUGGED, "drugged"); + // givebehaviour() will also remove fleeonxxx flags + addflag(lastbehaviour->flags, F_NOFLEE, B_TRUE, NA , NA, NULL); + addbehaviour(BH_DRUNK, "drunk"); + // givebehaviour() will add f_drunk rnd(2,5) + addbehaviour(BH_DETERMINED, "determined"); + addflag(lastbehaviour->flags, F_FOLLOWTIME, (DEF_AIFOLLOWTIME*2), NA, NA, NULL); + addbehaviour(BH_LAZY, "lazy"); + addflag(lastbehaviour->flags, F_FOLLOWTIME, (DEF_AIFOLLOWTIME/4), NA, NA, NULL); + addbehaviour(BH_MUSCLED, "muscled"); + // givebehaviour() will modify maxhp + addbehaviour(BH_SCRAWNY, "scrawny"); + // givebehaviour() will modify maxhp + // unique monsters addrace(R_JAILER, "jailer", 110, '@', C_MAGENTA, MT_FLESH, RC_HUMANOID, "Jailers are generally known for their surplus of brawn and utter lack of brains. This one is no different."); setbodytype(lastrace, BT_HUMANOID); @@ -9237,7 +9310,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANCAST, OT_S_PARALYZE, NA, NA, "pw:2;"); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 8, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, B_APPENDYOU, "gazes"); - addflag(lastrace->flags, F_CASTTYPE, CT_GAZE, NA, NA, NULL); + addflag(lastrace->flags, F_CASTTYPE, OT_NONE, CT_GAZE, NA, NULL); addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_EXPERT, NA, NULL); @@ -9476,9 +9549,10 @@ void initrace(void) { addflag(lastrace->flags, F_CANCAST, OT_S_PLANTWALK, NA, NA, NULL); addflag(lastrace->flags, F_CANCAST, OT_S_CHARM, NA, NA, "pw:1;"); addflag(lastrace->flags, F_CASTCHANCE, 70, NA, NA, NULL); + addflag(lastrace->flags, F_CASTTYPE, OT_S_CHARM, CT_GAZE, NA, NULL); //addflag(lastrace->flags, F_CANCAST, OT_S_SLEEP, 10, 10, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_PLANTWALK, NA, NA, NULL); - addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_CHARM, NA, B_APPENDYOU, "beckons"); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_CHARM, NA, B_APPENDYOU, "smiles seductively and beckons"); addflag(lastrace->flags, F_WANTSOBFLAG, F_GEM, B_COVETS, NA, NULL); addflag(lastrace->flags, F_WANTS, OT_GOLD, B_COVETS, NA, NULL); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "shouts^a shout"); @@ -9591,7 +9665,7 @@ void initrace(void) { addflag(lastrace->flags, F_CASTCHANCE, 50, NA, NA, NULL); addflag(lastrace->flags, F_CANCAST, OT_S_DISPERSAL, NA, NA, NULL); addflag(lastrace->flags, F_CANCAST, OT_S_GRAVBOOST, NA, NA, NULL); - addflag(lastrace->flags, F_CASTTYPE, CT_GAZE, NA, NA, NULL); + addflag(lastrace->flags, F_CASTTYPE, OT_NONE, CT_GAZE, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, B_APPENDYOU, "gazes"); addflag(lastrace->flags, F_TREMORSENSE, 5, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); @@ -9628,7 +9702,7 @@ void initrace(void) { addflag(lastrace->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 4, NA, "bellows^a bellow"); addflag(lastrace->flags, F_CANCAST, OT_S_STUN, 5, 5, "pw:1;"); - addflag(lastrace->flags, F_CASTTYPE, CT_GAZE, NA, NA, NULL); + addflag(lastrace->flags, F_CASTTYPE, OT_NONE, CT_GAZE, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_STUN, NA, B_APPENDYOU, "gazes"); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); @@ -10244,6 +10318,7 @@ void initrace(void) { 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); + addbodypart(lastrace, BP_WINGS, 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); @@ -10286,6 +10361,7 @@ void initrace(void) { 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); + addbodypart(lastrace, BP_WINGS, 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, ""); @@ -10611,6 +10687,7 @@ void initrace(void) { addrace(R_MANTICORE, "manticore", 80, 'm', C_RED, MT_FLESH, RC_MAGIC, "Horrific beasts with the body of a lion, bat-like winds and a human head. The tip of their tail contains a mass of iron spikes."); setbodytype(lastrace, BT_QUADRAPED); addbodypart(lastrace, BP_TAIL, NULL); + addbodypart(lastrace, BP_WINGS, 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); @@ -13139,6 +13216,42 @@ void initrace(void) { addflag(lastrace->flags, F_CASTCHANCE, 100, NA, NA, NULL); addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "hop"); + addrace(R_HARPY, "harpy", 60, 'A', C_ORANGE, MT_FLESH, RC_HUMANOID, "Hideous humanoid females, with the lower body and wings of a vulture."); // 'A' for Avian + setbodytype(lastrace, BT_HUMANOID); + addbodypart(lastrace, BP_WINGS, NULL); + setbodypartname(lastrace, BP_HANDS, "talons"); + setbodypartname(lastrace, BP_RIGHTFINGER, "right claw"); + setbodypartname(lastrace, BP_LEFTFINGER, "left claw"); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_LOW, 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_LOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, 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, 7, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 7, NA, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 2, 2, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 3, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 3, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 6, NA, NULL); + addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 3, NA, "screeches in pain^screeches of pain"); + addflag(lastrace->flags, F_AVIAN, 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_STARTOB, 50, NA, NA, "bone club"); + addflag(lastrace->flags, F_CANCAST, OT_S_CHARM, 15, 15, "pw:5;"); + addflag(lastrace->flags, F_CASTTYPE, OT_S_CHARM, CT_SOUNDBASED, NA, NULL); + addflag(lastrace->flags, F_CASTCHANCE, 60, NA, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_CHARM, NA, B_APPENDYOU, "sings a seductive song"); + addrace(R_HAWKYOUNG, "young hawk", 1, 'A', C_GREY, MT_FLESH, RC_ANIMAL, "A young baby hawk."); // 'A' for Avian setbodytype(lastrace, BT_BIRD); lastrace->baseid = R_HAWK; @@ -13796,7 +13909,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, B_APPENDYOU, "spits"); - addflag(lastrace->flags, F_CASTTYPE, CT_EYESPIT, NA, NA, NULL); + addflag(lastrace->flags, F_CASTTYPE, OT_NONE, CT_EYESPIT, NA, NULL); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTIMMUNE, DT_POISON, NA, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_S_BLINDNESS, 4, 4, "pw:3;range:2;"); @@ -14213,7 +14326,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANINE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); - addrace(R_WORMGLUT, "glutwyrm", 25, 'W', C_MAGENTA, MT_FLESH, RC_DRAGON, "Gigantic wyrms who have become so obese over the centuries that they have evolved without wings. They swallow their prey whole, slowly digesting their still living bodies."); + addrace(R_WORMGLUT, "glutwyrm", 25, 'W', C_MAGENTA, MT_FLESH, RC_DRAGON, "Gigantic wyrms who have become so obese over the centuries that they have evolved to be wingless. They swallow their prey whole, slowly digesting their still living bodies."); addbodypart(lastrace, BP_HEAD, NULL); addbodypart(lastrace, BP_TAIL, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); @@ -15529,6 +15642,37 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_COLD, NA, "15"); + addrace(R_BANSHEE, "banshee", 50, 'p', C_BLUE, MT_FLESH, RC_UNDEAD, "A floating phantom, with wild unbrushed hair."); + 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_IQ, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_VLOW, NA, NULL); + addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL); + addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_VERYRARE, NULL); + addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_VERYRARE, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 7, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 7, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_HASATTACK, OT_TOUCHNECROTIC, 8, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_S_DEATHKEEN, 100, 100, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_DEATHKEEN, NA, NA, NULL); + addflag(lastrace->flags, F_DTIMMUNE, DT_ELECTRIC, NA, NA, NULL); + addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_INDUCEFEAR, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SEEINDARK, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); + addflag(lastrace->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL); + addrace(R_GHAST, "ghast", 50, 'Z', C_BOLDGREEN, MT_FLESH, RC_UNDEAD, "A more slender and ghost-like form of ghoul. Ghasts are cunning and deadly, and possess a paralyzing touch."); setbodytype(lastrace, BT_HUMANOID); setbodypartname(lastrace, BP_HANDS, "claws"); @@ -15559,7 +15703,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_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 + addrace(R_GHOST, "ghost", 50, 'p', C_GREY, MT_FLESH, 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_STR, AT_LOW, NA, NULL); @@ -15691,7 +15835,7 @@ void initrace(void) { addflag(lastrace->flags, F_HITCONFERVALS, P_ROT, 3, NA, NULL); // strong! addflag(lastrace->flags, F_CANCAST, OT_S_FEAR, 50, 50, "pw:3;"); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_FEAR, NA, B_APPENDYOU, "gazes"); - addflag(lastrace->flags, F_CASTTYPE, CT_GAZE, NA, NA, NULL); + addflag(lastrace->flags, F_CASTTYPE, OT_NONE, CT_GAZE, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_FIRE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, 6, NA, NA, NULL); @@ -16306,7 +16450,7 @@ void initskills(void) { addskilldesc(SK_FIRSTAID, PR_INEPT, "- Determines how long poison effects will last.", B_FALSE); addskilldesc(SK_FIRSTAID, PR_NOVICE, "+2 hit points per level.", B_FALSE); addskilldesc(SK_FIRSTAID, PR_BEGINNER, "+4 hit points per level.", B_FALSE); - addskilldesc(SK_FIRSTAID, PR_BEGINNER, "You now notice the onset of poison.", B_TRUE); + addskilldesc(SK_FIRSTAID, PR_BEGINNER, "You now recognise the onset of poison or sickness.", B_TRUE); addskilldesc(SK_FIRSTAID, PR_ADEPT, "+6 hit points per level.", B_FALSE); addskilldesc(SK_FIRSTAID, PR_ADEPT, "^gYou can now recognise when poison is potentially fatal.^n", B_TRUE); addskilldesc(SK_FIRSTAID, PR_SKILLED, "+8 hit points per level.", B_FALSE); @@ -16619,6 +16763,32 @@ void killoption(option_t *w) { } } +void killbehaviour(behaviour_t *b) { + behaviour_t *nextone, *lastone; + + // free mem + if (b->name) free(b->name); + killflagpile(b->flags); + + // remove from list + nextone = b->next; + if (nextone != NULL) { + nextone->prev = b->prev; + } else { /* last */ + lastbehaviour = b->prev; + } + + if (b->prev == NULL) { + /* first */ + nextone = b->next; + free(firstbehaviour); + firstbehaviour = nextone; + } else { + lastone = b->prev; + free (lastone->next ); + lastone->next = nextone; + } +} void killcommand(command_t *cmd) { command_t *nextone, *lastone; diff --git a/data.h b/data.h index 3e2ea06..310f6aa 100644 --- a/data.h +++ b/data.h @@ -2,6 +2,7 @@ void addbonustext(flagpile_t *fp, enum FLAG fid, char *text); option_t *addoption(enum OPTION id, char *text, int def); +behaviour_t *addbehaviour(enum BEHAVIOUR id, char *name); command_t *addcommand(enum COMMAND id, char c, char *desc); void initcommands(void); void initjobs(void); @@ -10,6 +11,7 @@ void initoptions(void); void initrace(void); void initraceclasses(void); void initskills(void); +void killbehaviour(behaviour_t *b); void killcommand(command_t *cmd); void killoption(option_t *o); void make_basic_shop(flagpile_t *fp); diff --git a/defs.h b/defs.h index c5b0895..9e2dd9c 100644 --- a/defs.h +++ b/defs.h @@ -322,6 +322,19 @@ #define MAXDIR_MAP 15 +enum BEHAVIOUR { + BH_NONE = 0, + BH_INSANE, + BH_HUNGRY, + BH_TIMID, + BH_DRUGGED, + BH_DRUNK, + BH_DETERMINED, + BH_LAZY, + BH_MUSCLED, + BH_SCRAWNY, +}; + // relative directions enum RELATIVEDIR { RD_FORWARDS, @@ -489,7 +502,8 @@ enum COLOUR { enum CASTTYPE { CT_NORMAL = 0, CT_GAZE, - CT_EYESPIT + CT_EYESPIT, + CT_SOUNDBASED, }; enum DRAINTYPE { @@ -1123,6 +1137,7 @@ enum RACE { R_DOGWAR, R_ELEPHANT, R_GYRFALCON, + R_HARPY, R_HAWK, R_HAWKYOUNG, R_HAWKBLOOD, @@ -1191,6 +1206,7 @@ enum RACE { R_LINGTRAPPER, R_QUASIT, // undead + R_BANSHEE, R_GHAST, R_GHOST, R_GHOUL, @@ -1514,6 +1530,7 @@ enum OBTYPE { OT_S_BLIGHT, OT_S_COMMANDUNDEAD, OT_S_CURSE, + OT_S_DEATHKEEN, OT_S_DRAINLIFE, OT_S_FEAR, OT_S_FLAYFLESH, @@ -2162,6 +2179,7 @@ enum OBTYPE { // projectile weapons OT_BLOWGUN, OT_BOW, + OT_COMPOSITEBOW, OT_CROSSBOW, OT_CROSSBOWHAND, OT_LONGBOW, @@ -2243,6 +2261,7 @@ enum DEPTH { }; enum NOISETYPE { + N_DEATHKEEN, N_GETANGRY, N_WALK, N_FLY, @@ -2835,8 +2854,7 @@ enum FLAG { // if you won by becoming a god, v1 = godid // if you won by becoming a demigod, v1 = godid // text = copied F_GODFLAG text from god. - F_BEHAVIOUR, // textual field describing special behaviour for this - // lf + F_BEHAVIOUR, // v0 = enum behaviour. F_RNDSPELLCOUNT, // this monster starts with v0 random spells. // needs to also have either f_rndspellschool OR // f_rndspellposs. @@ -3300,8 +3318,8 @@ enum FLAG { F_CONFUSED, // move randomly about F_DEAF, // cannot hear F_NEEDOBFORSPELLS, // lf can only cast spells if it has object v0 - F_CASTTYPE, // lf uses enum CASTTYPE v0 for spells - // optional v1 is colour for casttype-based animations + F_CASTTYPE, // lf uses enum CASTTYPE v1 for spellid v0. (ot_none=all) + // optional v2 is colour for casttype-based animations // (ie. spit spells) F_CAFFEINATED, // can't sleep. F_CANEATRAW, // lf can eat raw food with no issues @@ -3822,6 +3840,13 @@ enum COMMAND { }; +typedef struct behaviour_s { + enum BEHAVIOUR id; + char *name; + struct flagpile_s *flags; + struct behaviour_s *next, *prev; +} behaviour_t; + typedef struct npcname_s { char *name; int valid; diff --git a/io.c b/io.c index 48c32e1..c2d984e 100644 --- a/io.c +++ b/io.c @@ -1613,11 +1613,12 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { case F_POISONED: pt = findpoisontype(f->val[0]); if (isplayer(lf)) { - if (streq(pt->desc, "Sick")) { + if (streq(pt->desc, "Sick") || (pt->id == P_FOOD) || (pt->id == P_FOODBAD)) { msg("^%cYou have contracted %s.", getlfcol(lf, CC_VBAD), pt->name); } else { msg("^%cYou are sick with %s.", getlfcol(lf, CC_VBAD), pt->name); } + more(); } else { msg("^%c%s looks very sick.", getlfcol(lf, CC_VBAD), lfname); } @@ -2266,15 +2267,6 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { case F_FRIENDLY: msg("%s no longer looks quite so friendly!", lfname); break; - case F_INCUBATING: - if (isplayer(lf)) { - poisontype_t *pt; - pt = findpoisontype(f->val[0]); - if (pt) { - msg("^%cYour body has fought off %s.", getlfcol(lf, CC_GOOD), pt->name); - } - } - break; case F_POISONED: msg("^%c%s %s less sick now.^n", getlfcol(lf, CC_VGOOD), lfname, isplayer(lf) ? "feel" : "looks"); donesomething = B_TRUE; @@ -7174,6 +7166,7 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel case F_LEVITATING: if (lorelev >= PR_NOVICE) sprintf(buf, "Can levitate at will"); break; case F_MEDITATES: if (lorelev >= PR_ADEPT) sprintf(buf, "Meditates to retain awareness while sleeping."); break; case F_MPMOD: if (f->val[0] > 0) sprintf(buf, "+%d Mana", f->val[0]); break; + case F_NONCORPOREAL: if (lorelev >= PR_NOVICE) sprintf(buf, "Only affected by blessed, magic or silver weapons"); break; case F_NOSLEEP: if (lorelev >= PR_BEGINNER) sprintf(buf, "Does not sleep"); break; case F_PHALANX: if (lorelev >= PR_ADEPT) sprintf(buf, "Gains extra defence when in a pack."); break; case F_PHOTOMEM: sprintf(buf, "Photographic memory"); break; diff --git a/lf.c b/lf.c index 44203de..5009872 100644 --- a/lf.c +++ b/lf.c @@ -32,6 +32,7 @@ extern int enteringmap; extern map_t *firstmap; extern race_t *firstrace, *lastrace; extern raceclass_t *firstraceclass, *lastraceclass; +extern behaviour_t *firstbehaviour, *lastbehaviour; extern job_t *firstjob, *lastjob; extern skill_t *firstskill, *lastskill; extern poisontype_t *firstpoisontype,*lastpoisontype; @@ -835,6 +836,7 @@ int caneat(lifeform_t *lf, object_t *o) { int canhaverandombehaviour(lifeform_t *lf) { + if (lfhasflag(lf, F_BEHAVIOUR)) return B_FALSE; if (isundead(lf)) return B_FALSE; if (isgod(lf)) return B_FALSE; if (getraceclass(lf) == RC_HUMANOID) { @@ -1609,11 +1611,14 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar int power; int spellblessed = B_UNCURSED; objecttype_t *sp; + enum CASTTYPE casttype = CT_NORMAL; + if (fromob) { spellblessed = fromob->blessed; power = getobspellpower(fromob, lf); } else { + flag_t *ctf; spellblessed = B_UNCURSED; power = getspellpower(lf, sid); // check whether we _can_ cast it. @@ -1682,6 +1687,14 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar return B_TRUE; } } + ctf = lfhasflagval(lf, F_CASTTYPE, sid, NA, NA, NULL); + if (!ctf) { + ctf = lfhasflagval(lf, F_CASTTYPE, OT_NONE, NA, NA, NULL); + } + if (ctf) { + casttype = ctf->val[1]; + } + } sp = findot(sid); @@ -1808,7 +1821,14 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar // announce if (!isplayer(lf) && !fromob) { + int doannounce = B_FALSE; + if (cansee(player, lf)) { + doannounce = B_TRUE; + } else if ((casttype == CT_SOUNDBASED) && canhear(player, lf->cell, SV_TALK)) { + doannounce = B_TRUE; + } + if (doannounce) { char lfname[BUFLEN]; char whattosay[BUFLEN]; getlfname(lf, lfname); @@ -1851,23 +1871,28 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar // eye protection will stop some spells! if (targcell && targcell->lf) { - flag_t *casttype; lifeform_t *victim; victim = targcell->lf; - casttype = lfhasflag(lf, F_CASTTYPE); - if (casttype) { - object_t *protob = NULL; - switch (casttype->val[0]) { - case CT_EYESPIT: - protob = getarmour(victim, BP_EYES); - break; - case CT_GAZE: - protob = eyesshaded(victim); - break; - default: - protob = NULL; - break; - } + int protected = B_FALSE; + object_t *protob = NULL; + switch (casttype) { + case CT_EYESPIT: + protob = getarmour(victim, BP_EYES); + if (protob) protected = B_TRUE; + break; + case CT_GAZE: + protob = eyesshaded(victim); + if (protob) protected = B_TRUE; + break; + case CT_SOUNDBASED: + if (isdeaf(victim)) protected = B_TRUE; + break; + default: + protob = NULL; + protected = B_FALSE; + break; + } + if (protected) { if (protob) { if (isplayer(victim)) { char gbuf[BUFLEN]; @@ -1879,8 +1904,16 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar getlfname(victim, lfname); msg("%s%s %s protects it.", lfname, getpossessive(lfname), noprefix(gbuf) ); } - return B_FALSE; + } else { + if (isplayer(victim)) { + msg("You are unaffected."); + } else if (cansee(player, victim)) { + char lfname[BUFLEN]; + getlfname(victim, lfname); + msg("%s is unaffected.", lfname); + } } + return B_FALSE; } } @@ -2549,6 +2582,24 @@ int countinnateattacks(lifeform_t *lf) { } +int countlegs(lifeform_t *lf) { + enum BODYPART bp; + int legs = 0; + for (bp = 0; bp <= MAXBODYPARTS; bp++) { + if (hasbp(lf, bp)) { + switch (bp) { + case BP_LEGS: + case BP_FRONTLEGS: + case BP_BACKLEGS: + legs += 2; + break; + default: break; + } + } + } + return legs; +} + int countnearbyallies(lifeform_t *lf) { lifeform_t *l; int count = 0; @@ -5341,6 +5392,14 @@ void fightback(lifeform_t *lf, lifeform_t *attacker) { } } +behaviour_t *findbehaviour(enum BEHAVIOUR bid) { + behaviour_t *b; + for (b = firstbehaviour ; b ; b = b->next) { + if (b->id == bid) return b; + } + return NULL; +} + job_t *findjob(enum JOB jobid) { job_t *j; for (j = firstjob ; j ; j = j->next) { @@ -5613,7 +5672,8 @@ int flee(lifeform_t *lf) { if (isplayer(thisone)) { pleasegodmaybe(R_GODMERCY, 5); if ((lf->lastdamlf == player->id) || cansee(player, lf)) { - // ie. only if the player + // ie. only if the player saw them run away, or has already + // attacked them. angergodmaybe(R_GODDEATH, 10, GA_MERCY); } } @@ -5684,6 +5744,15 @@ int flee(lifeform_t *lf) { if (stairs && !lfhasflag(lf, F_NOSTAIRS)) { if (db) dblog("%s - trying to flee via %s", lfname, stairs->type->name); if (!usestairs(lf, stairs, B_TRUE, B_TRUE)) { + // success + if (isplayer(fleefrom)) { + pleasegodmaybe(R_GODMERCY, 5); + if ((lf->lastdamlf == player->id) || cansee(player, lf)) { + // ie. only if the player saw them run away, or has already + // attacked them. + angergodmaybe(R_GODDEATH, 10, GA_MERCY); + } + } return B_TRUE; } if (db) dblog("%s - failed to flee via %s", lfname, stairs->type->name); @@ -7833,6 +7902,11 @@ int getnoisedetails(lifeform_t *lf, enum NOISETYPE nid, flag_t *noiseflag, } return B_FALSE; } + } else if (nid == N_DEATHKEEN) { + if (volume) *volume = SV_TALK; + if (heartext) strcpy(heartext, "the dread wailing of death!"); + if (seetext) strcpy(seetext, "wails with the power of death!"); + return B_FALSE; } else if (nid == N_SONICBOLT) { if (volume) *volume = 5; if (heartext) strcpy(heartext, "a ear-splitting burst of sound!"); @@ -8543,8 +8617,12 @@ char *real_getlfname(lifeform_t *lf, char *buf, lifeform_t *usevis, int showall, if (dobehaviour) { f = lfhasflag(lf, F_BEHAVIOUR); if (f) { - strcat(descstring, f->text); - strcat(descstring, " "); + behaviour_t *b; + b = findbehaviour(f->val[0]); + if (b) { + strcat(descstring, b->name); + strcat(descstring, " "); + } } } @@ -8961,6 +9039,29 @@ object_t *getrandomarmour(lifeform_t *lf, lifeform_t *attacker) { return o; } +enum BEHAVIOUR getrandombehaviour(void) { + behaviour_t *b; + int numb = 0,sel; + enum BEHAVIOUR selb = BH_NONE; + for (b = firstbehaviour ; b ; b = b->next) { + numb++; + } + sel = rnd(0,numb-1); + numb = 0; + for (b = firstbehaviour ; b ; b = b->next) { + if (numb == sel) { + selb = b->id; + break; + } + numb++; + } + if (selb == BH_NONE) { + // should never happen! + assert("getrandombehaviour() failed!" == 0); + } + return selb; +} + // pick a random major body part // ie. head, body, arms/hands, legs, wings, tail enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker) { @@ -9758,6 +9859,39 @@ long getxpforlev(int level) { return needxp; } +void givebehaviour(lifeform_t *lf, enum BEHAVIOUR bid) { + behaviour_t *b; + flag_t *f; + b = findbehaviour(bid); + if (!b) return; + addflag(lf->flags, F_BEHAVIOUR, bid, NA, NA, NULL); + copyflags(lf->flags, b->flags, FROMRACE); + switch (bid) { + case BH_TIMID: + f = lfhasflag(lf, F_MORALE); + if (f) { + f->val[0] /= 2; + } + break; + case BH_DRUGGED: + killflagsofid(lf->flags, F_FLEEONDAM); + killflagsofid(lf->flags, F_FLEEONHPPCT); + break; + case BH_DRUNK: + addflag(lf->flags, F_DRUNK, rnd(2,5), NA, NA, NULL); + case BH_MUSCLED: + lf->maxhp = pctof(rnd(125,200), lf->maxhp); // 25-100% more hp + lf->hp = lf->maxhp; + break; + case BH_SCRAWNY: + lf->maxhp = pctof(rnd(50,75), lf->maxhp); // 25-50% less hp + limit(&(lf->maxhp), 1, NA); + lf->hp = lf->maxhp; + break; + default: break; + } +} + void givejob(lifeform_t *lf, enum JOB jobid) { job_t *j; flag_t *f; @@ -11601,11 +11735,13 @@ flag_t *lfhasknownflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int } // returns radius of light produces -int lfproduceslight(lifeform_t *lf) { +int lfproduceslight(lifeform_t *lf, object_t **fromwhat) { int temp = 0; int radius = 0; object_t *o; + if (fromwhat) *fromwhat = NULL; + // lf producing light itself? sumflags(lf->flags, F_PRODUCESLIGHT, &temp, NULL, NULL); if (temp) radius = temp; @@ -11616,6 +11752,7 @@ int lfproduceslight(lifeform_t *lf) { temp = obproduceslight(o); if (temp > radius) { radius = temp; + if (fromwhat) *fromwhat = o; } } } @@ -15585,7 +15722,8 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, if (isdead(noisemaker)) { msg("The dying %s %s.", noprefix(lfname), realseetext); } else { - msg("%s %s.", lfname, realseetext); + msg("%s %s%s", lfname, realseetext, + (realseetext[strlen(realseetext)-1] == '!') ? "" : "."); } rv = B_TRUE; } @@ -15785,6 +15923,7 @@ enum NOISECLASS noisetypetoclass(enum NOISETYPE nt) { case N_FLY: return NC_MOVEMENT; case N_SONICBOLT: + case N_DEATHKEEN: case N_WARCRY: return NC_SPELLEFFECT; default: @@ -16061,16 +16200,18 @@ void poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char if (ii->val[2] < 1) ii->val[2] = 1; if (getskill(lf, SK_FIRSTAID) >= PR_BEGINNER) { ii->known = B_TRUE; - msg("You feel %s coming on more quickly.", pt->name); + msg("^BYou feel %s coming on more quickly.", pt->name); } } else { char ftext[BUFLEN]; sprintf(ftext, "%d^%s", power, fromwhat); - ii = addflag(lf->flags, F_INCUBATING, ptype, power, pt->incubationtime, ftext); + ii = addflag(lf->flags, F_INCUBATING, ptype, pt->incubationtime, howlong, ftext); ii->obfrom = srcrace ? srcrace->id : NA; if (getskill(lf, SK_FIRSTAID) >= PR_BEGINNER) { ii->known = B_TRUE; - msg("You feel %s coming on.", pt->name); + msg("^BYou feel %s coming on.", pt->name); + } else { + ii->known = B_FALSE; } } } else { @@ -19485,8 +19626,9 @@ void startlfturn(lifeform_t *lf) { } else if (f->val[0] == P_MIGRAINE) { // sleeping will avoid all migraine effects if (!asleep && !ko) { + object_t *lamp = NULL; int amt; - amt = lfproduceslight(lf); + amt = lfproduceslight(lf, &lamp); if (amt) { int dam; // note: amt will be doubled due to light vulnerability, @@ -19495,7 +19637,13 @@ void startlfturn(lifeform_t *lf) { limit(&dam, 1, NA); losehp(lf, amt, DT_LIGHT, NULL, "a migraine"); if (isplayer(lf)) { - msg("Your head explodes in pain at your light!"); + char obname[BUFLEN]; + if (lamp) { + getobname(lamp, obname, lamp->amt); + } else { + strcpy(obname, "a body"); // noprefix will strip the 'a ' + } + msg("Your head explodes in pain at the light from your %s!", noprefix(obname)); } } } @@ -21439,6 +21587,11 @@ int validateraces(void) { printf("ERROR in race '%s' - duplicate f_startatt flags detected.\n", r->name); goterror = B_TRUE; } + + if (strstr(r->desc, "wings") && !hasbp(lf, BP_WINGS)) { + printf("ERROR in race '%s' - description refers to wings but race has no bp_wings.\n", r->name); + goterror = B_TRUE; + } if (!hasflag(r->flags, F_SIZE)) { diff --git a/lf.h b/lf.h index 426110f..67ea876 100644 --- a/lf.h +++ b/lf.h @@ -79,6 +79,7 @@ void copycorpseflags(flagpile_t *dst, flagpile_t *src); int continuedigging(lifeform_t *lf); int continuerepairing(lifeform_t *lf, flag_t *repairflag); int countinnateattacks(lifeform_t *lf); +int countlegs(lifeform_t *lf); int countnearbyallies(lifeform_t *lf); int countnearbyhurtallies(lifeform_t *lf); int countplantsinsight(lifeform_t *lf); @@ -104,6 +105,7 @@ int fall(lifeform_t *lf, lifeform_t *fromlf, int announce); int fallasleep(lifeform_t *lf, enum SLEEPTYPE how, int howlong); int fall_from_air(lifeform_t *lf); void fightback(lifeform_t *lf, lifeform_t *attacker); +behaviour_t *findbehaviour(enum BEHAVIOUR bid); job_t *findjob(enum JOB jobid); job_t *findjobbyname(char *name); lifeform_t *findlf(map_t *m, int lfid); @@ -241,6 +243,7 @@ enum POISONSEVERITY getpoisonseverity(enum POISONTYPE ptype); int getraceclass(lifeform_t *lf); int getracerarity(map_t *map, enum RACE rid, enum RARITY *rr); object_t *getrandomarmour(lifeform_t *lf, lifeform_t *attacker); +enum BEHAVIOUR getrandombehaviour(void); enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker); race_t *getrandomcorpserace(cell_t *c); job_t *getrandomjob(int onlyplayerjobs); @@ -269,6 +272,7 @@ object_t *getweapon(lifeform_t *lf); int getweapons(lifeform_t *lf, int meleeonly, object_t **wep, flag_t **damflag, int *lastweaponidx, obpile_t **op, int *nweps); enum SKILLLEVEL getweaponskill(lifeform_t *lf, object_t *o); long getxpforlev(int level); +void givebehaviour(lifeform_t *lf, enum BEHAVIOUR bid); void givejob(lifeform_t *lf, enum JOB jobid); void givesubjob(lifeform_t *lf, enum SUBJOB sj); int givemoney(lifeform_t *from, lifeform_t *to, int amt); @@ -293,7 +297,7 @@ flag_t *lfhasflag(lifeform_t *lf, enum FLAG fid); flag_t *lfhasflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, char *text); flag_t *lfhasknownflag(lifeform_t *lf, enum FLAG fid); flag_t *lfhasknownflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, /*@null@*/ char *text); -int lfproduceslight(lifeform_t *lf); +int lfproduceslight(lifeform_t *lf, object_t **fromwhat); int lockpick(lifeform_t *lf, cell_t *targcell, object_t *target, object_t *device); void loseobflags(lifeform_t *lf, object_t *o, int kind); int hasbp(lifeform_t *lf, enum BODYPART bp); diff --git a/map.c b/map.c index fae2bfa..2fc5770 100644 --- a/map.c +++ b/map.c @@ -23,6 +23,7 @@ int enteringmap = B_FALSE; extern habitat_t *firsthabitat,*lasthabitat; extern job_t *firstjob; extern map_t *firstmap,*lastmap; +extern behaviour_t *firstbehaviour,*lastbehaviour; extern region_t *firstregion,*lastregion; extern regionoutline_t *firstregionoutline,*lastregionoutline; extern regiontype_t *firstregiontype,*lastregiontype; @@ -223,6 +224,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok flagpile_t *wantflags = NULL; enum JOB wantjob = J_NONE; enum SUBJOB wantsubjob = SJ_NONE; + enum BEHAVIOUR wantbehaviour = BH_NONE; if (nadded) *nadded = 0; @@ -249,7 +251,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok } else { //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - rid = parserace(racename, wantflags, &wantjob, &wantsubjob); + rid = parserace(racename, wantflags, &wantjob, &wantsubjob, &wantbehaviour); //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging if (rid == R_RANDOM) { @@ -394,7 +396,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok } } - finalisemonster(lf, NULL, wantflags, 0); + finalisemonster(lf, NULL, wantflags, wantbehaviour, 0); // NOTE: because the initial maps (world, heaven, dungeon lev1) are created BEFORE the player, // monsters on these maps will not have their hostility adjusted! @@ -465,7 +467,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok newlf->born = B_FALSE; - finalisemonster(newlf, lf, wantflags, idx); + finalisemonster(newlf, lf, wantflags, idx, BH_NONE); newlf->born = B_TRUE; @@ -506,7 +508,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, NULL); if (!adjcell) break; - newrid = parserace(f->text, NULL, NULL, NULL); + newrid = parserace(f->text, NULL, NULL, NULL, NULL); newr = findrace(newrid); if (!newr) break; @@ -515,7 +517,8 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok if (nadded) (*nadded)++; - finalisemonster(newlf, lf, wantflags, 0); + // wantbehaviour only applies to initial monster + finalisemonster(newlf, lf, wantflags, 0, BH_NONE); newlf->born = B_TRUE; } @@ -2039,7 +2042,7 @@ void calclight(map_t *map) { // has lightproducing lf? (ie.hasflag f_produceslight) if (c->lf) { - radius = lfproduceslight(c->lf); + radius = lfproduceslight(c->lf, NULL); if (radius) { makelitradius(c, radius, L_TEMP, -1); } @@ -5463,7 +5466,7 @@ void finalisemap(map_t *map, object_t *entryob) { } -void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, int idx) { +void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, enum BEHAVIOUR wantbehaviour, int idx) { flag_t *f; enum FLAG noflag[MAXCANDIDATES]; int nnoflags = 0,i; @@ -5506,56 +5509,10 @@ void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, } // random monster behaviours - if (canhaverandombehaviour(lf) && onein(6)) { - switch (rnd(0,8)) { - case 0: // insane - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "insane"); - addflag(lf->flags, F_TERRITORIAL, 2, NA, NA, NULL); // attack anything within 1 cells - break; - case 1: // hungry - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "hungry"); - addflag(lf->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL); - break; - case 2: // timid - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "timid"); - f = lfhasflag(lf, F_MORALE); - if (f) { - f->val[0] /= 2; - } else { - addflag(lf->flags, F_FLEEONDAM, B_TRUE, NA, NA, NULL); - } - break; - case 3: // drugged - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "drugged"); - killflagsofid(lf->flags, F_FLEEONDAM); - killflagsofid(lf->flags, F_FLEEONHPPCT); - addflag(lf->flags, F_NOFLEE, B_TRUE, NA, NA, NULL); - break; - case 4: // drunk - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "drunk"); - addflag(lf->flags, F_DRUNK, rnd(2,5), NA, NA, NULL); - break; - case 5: // determined - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "determined"); - addflag(lf->flags, F_FOLLOWTIME, (DEF_AIFOLLOWTIME*2), NA, NA, NULL); - break; - case 6: // lazy - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "lazy"); - addflag(lf->flags, F_FOLLOWTIME, (DEF_AIFOLLOWTIME/4), NA, NA, NULL); - break; - case 7: // extra hp - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "muscled"); - lf->maxhp = pctof(rnd(125,200), lf->maxhp); // 25-100% more hp - lf->hp = lf->maxhp; - break; - case 8: // less hp - addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "scrawny"); - lf->maxhp = pctof(rnd(50,75), lf->maxhp); // 25-50% less hp - limit(&(lf->maxhp), 1, NA); - lf->hp = lf->maxhp; - break; - } + if ((wantbehaviour == BH_NONE) && canhaverandombehaviour(lf) && onein(6)) { + wantbehaviour = getrandombehaviour(); } + givebehaviour(lf, wantbehaviour); if (wantflags) { copyflags(lf->flags, wantflags, NA); @@ -7392,17 +7349,20 @@ int orthdir(int compassdir) { return D_NONE; } -enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum SUBJOB *wantsubjob) { +enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum SUBJOB *wantsubjob, enum BEHAVIOUR *wantbehaviour) { int donesomething; char *p,*suff; job_t *j; char named[BUFLEN]; char *localname; char *namestart; + behaviour_t *b; // get params donesomething = B_TRUE; + if (wantbehaviour) *wantbehaviour = BH_NONE; + // take a local copy so we can strip suffixes off it if (strstarts(name, "the ")) { @@ -7425,8 +7385,23 @@ enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum S if (wantflags) addflag(wantflags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL); donesomething = B_TRUE; } + if (wantbehaviour && (*wantbehaviour != BH_NONE)) { + } else { + // try removing prefixes for behaviours + for (b = firstbehaviour ; b; b = b->next) { + char lookfor[BUFLEN]; + sprintf(lookfor, "%s ", b->name); + if (strstarts(p, lookfor)) { + p += strlen(lookfor); + if (wantbehaviour) *wantbehaviour = b->id; + break; + } + } + } } + + // try removing suffixes for "named xxx" strcpy(named, ""); suff = strends(localname, " named "); diff --git a/map.h b/map.h index 4752891..786716c 100644 --- a/map.h +++ b/map.h @@ -87,7 +87,7 @@ void expand_cave(map_t *map, int numpasses); void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *centre); void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int dirtype, int wantannounce); void finalisemap(map_t *map, object_t *entryob); -void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, int idx); +void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, enum BEHAVIOUR wantbehaviour, int idx); celltype_t *findcelltype(enum CELLTYPE cid); celltype_t *findcelltypebyname(char *name); habitat_t *findhabitat(enum HABITAT id); @@ -167,7 +167,7 @@ void markroomwalls(map_t *m, room_t *r); void mapentereffects(map_t *m); void moveobtoclearcell(object_t *o); int orthdir(int compassdir); -enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum SUBJOB *wantsubjob); +enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum SUBJOB *wantsubjob, enum BEHAVIOUR *wantbehaviour); int remove_deadends(map_t *m, int howmuch); void set_scanned_glyph(int targettype, void *what, char *descappend, char *desc, glyph_t *glyph); void setcellknown(cell_t *cell, int forcelev); diff --git a/move.c b/move.c index d89aa22..208e448 100644 --- a/move.c +++ b/move.c @@ -1280,7 +1280,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) { if (gamemode == GM_GAMESTARTED) { // update light - if ((isplayer(lf) && changedlev) || lfproduceslight(lf)) { + if ((isplayer(lf) && changedlev) || lfproduceslight(lf, NULL)) { calclight(lf->cell->map); setlosdirty(lf); } diff --git a/nexus.c b/nexus.c index 207bbcc..ad34919 100644 --- a/nexus.c +++ b/nexus.c @@ -28,6 +28,7 @@ objecttype_t *objecttype = NULL,*lastobjecttype = NULL; brand_t *firstbrand = NULL,*lastbrand = NULL; obmod_t *firstobmod = NULL,*lastobmod = NULL; celltype_t *firstcelltype = NULL,*lastcelltype = NULL; +behaviour_t *firstbehaviour = NULL,*lastbehaviour = NULL; command_t *firstcommand = NULL,*lastcommand = NULL; race_t *firstrace = NULL,*lastrace = NULL; raceclass_t *firstraceclass = NULL,*lastraceclass = NULL; @@ -661,6 +662,8 @@ void cleanup(void) { free(npcname); // free hidden names while (firsthiddenname) killhiddenname(firsthiddenname); + // behaviours + while (firstbehaviour) killbehaviour(firstbehaviour); //WriteMemLeak(); } diff --git a/objects.c b/objects.c index fb0606d..8568a1b 100644 --- a/objects.c +++ b/objects.c @@ -2375,6 +2375,7 @@ void adjustdammaterial(int *dam, enum DAMTYPE damtype, enum MATERIAL mat) { if (mat == MT_MAGIC) { switch (damtype) { case DT_DIRECT: + case DT_MAGIC: case DT_NONE: break; default: diff --git a/spell.c b/spell.c index 69380c7..280a4d1 100644 --- a/spell.c +++ b/spell.c @@ -1808,7 +1808,6 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } losehp(target, roll(damstr), DT_SONIC, user, "a bolt of sound"); } - } else if (abilid == OT_A_SPRINT) { flag_t *f; f = lfhasflag(user, F_SPRINTING); @@ -2331,6 +2330,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } else if (abilid == OT_A_TRIPLF) { object_t *wep; int skillmod = 0; + int legs; // ask for direction if (!targcell) { char dirch; @@ -2365,6 +2365,13 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef return B_TRUE; } + legs = countlegs(target); + + if (legs == 0) { + if (isplayer(user)) msg("You can't trip something which has no legs!"); + return B_TRUE; + } + getlfname(target, targetname); @@ -2380,7 +2387,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef skillmod = getskill(user, SK_UNARMED); } - if (skillcheckvs(user, SC_DEX, skillmod, target, SC_SLIP, 0)) { + if (skillcheckvs(user, SC_DEX, skillmod, target, SC_SLIP, (legs > 2) ? 5 : 0)) { if (cansee(player, user)) { msg("^w%s trip%s %s.",username, isplayer(user) ? "" : "s", targetname); } @@ -3555,12 +3562,12 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // special: spit attacks need an animation casttype = lfhasflag(caster, F_CASTTYPE); - if (casttype && (casttype->val[0] == CT_EYESPIT)) { + if (casttype && (casttype->val[1] == CT_EYESPIT)) { enum COLOUR col; - if (casttype->val[1] == NA) { + if (casttype->val[2] == NA) { col = C_GREEN; } else { - col = casttype->val[1]; + col = casttype->val[2]; } anim(caster->cell, targcell, '}', col); } @@ -5417,6 +5424,28 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ needredraw = B_TRUE; drawscreen(); } + } else if (spellid == OT_S_DEATHKEEN) { + if (!isnighttime()) { + fizzle(caster); + return B_TRUE; + } + if (isplayer(caster)) { + msg("You wail with the power of death!"); + } + noise(caster->cell, NULL, NC_OTHER, SV_TALK, "the dread wail of a banshee", NULL); + makenoise(caster, N_DEATHKEEN); + + // all in range must pass a magic resistance check or die + for (target = caster->cell->map->lf ; target ; target = target->next) { + if (target != caster) { + if (canhear(target, caster->cell, SV_TALK)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { + } else { + losehp(target, target->hp, DT_SONIC, caster, "a banshee's wail"); + } + } + } + } } else if (spellid == OT_S_DETECTAURA) { if (isplayer(caster)) { object_t *o; @@ -5668,6 +5697,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (spellid == OT_S_DETECTPOISON) { if (isplayer(caster)) { int npoisoned = 0; + int selfpoisoned = B_FALSE; int n,i; flag_t *retflag[MAXCANDIDATES]; int nretflags; @@ -5679,6 +5709,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ pt = findpoisontype(f->val[0]); msg("You detect %s in your body.", pt->name); f->known = B_TRUE; + selfpoisoned = B_TRUE; } for (n = 0; n < caster->nlos; n++) { @@ -5739,7 +5770,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } if (!npoisoned) { - msg("You can't detect any poison nearby."); + msg("You can't detect any %spoison nearby.", selfpoisoned ? "other " : ""); } } // end if isplayer } else if (spellid == OT_S_DETONATE) { diff --git a/vault.c b/vault.c index e3bdc51..39fe72e 100644 --- a/vault.c +++ b/vault.c @@ -1676,7 +1676,7 @@ int vaultthingok(enum VAULTTHING vt, char *what) { if (r) { return B_TRUE; } else { - if (parserace(what, NULL, NULL, NULL)) { + if (parserace(what, NULL, NULL, NULL, NULL)) { return B_TRUE; } }