- [+] bones file error:" the lazy minotaur"

- [+] parserace needs to cope with lazy etc.
        - [+] make enum BEHAVIOUR
        - [+] make behaviour_t
            - [+] id
            - [+] name
            - [+] flags
        - [+] killbehaviours()
        - [+] addbehaviour() calls to define them
        - [+] cleanup callsk illbehaviours
        - [+] givebehaviour(lf, enum behaviour)
        - [+] getrandombehaviour()
        - [+] instead of checking f_behaviour->text, use
              findbehaviour(f->behaviour->val[0])->name
        - [+] parserace should handle these and populate wantflags
              appropriately.
- [+] add disease incubation times (instead of getting them right away)
- [+] OT_A_TRIP should be a lot harder on anything with more than 2 legs
- [+] monster fleeing up/down stairs should anger hecta
- [+] reduce short bow damage.
- [+] add composite bow (between short & long)
- [+] need magic/silver weapons to hurt MT_MAGIC things
    - [+] spirits should no lnoger be made of "MAGIC" - make them
          something else. flesh will do.
    - [+] in attack.c, noncorporeal check should also check for silver
          / magic weapons
    - [+] describe noncorporeal in io.c if knowledge is high enough.
- [+] new spell casttype: ct_sounbased. deafness protects.
- [+] Banshee (7hd, spirit, death keen at night) - blue / red 'p'
    - [+] death keen = sonic damage"midnight dirge"
    - [+] but only at night. 
- [+] harpy: 7hd - orange 'A'
    - [+] charm (via sound, so sonic prevents)
    - [+] 50% chance of bone club
    - [+] bite, claw
This commit is contained in:
Rob Pearce 2012-03-30 03:34:24 +00:00
parent 984999d912
commit 53ada31364
15 changed files with 487 additions and 127 deletions

3
ai.c
View File

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

View File

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

204
data.c
View File

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

2
data.h
View File

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

35
defs.h
View File

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

13
io.c
View File

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

181
lf.c
View File

@ -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) {
int protected = B_FALSE;
object_t *protob = NULL;
switch (casttype->val[0]) {
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,9 +1904,17 @@ 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;
}
}
if (!fromob) {
@ -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,10 +8617,14 @@ 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);
behaviour_t *b;
b = findbehaviour(f->val[0]);
if (b) {
strcat(descstring, b->name);
strcat(descstring, " ");
}
}
}
// construct job string
strcpy(jobstring, "");
@ -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));
}
}
}
@ -21440,6 +21588,11 @@ int validateraces(void) {
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)) {
printf("ERROR in race '%s' - missing F_SIZE.\n", r->name);

6
lf.h
View File

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

89
map.c
View File

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

4
map.h
View File

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

2
move.c
View File

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

View File

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

View File

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

43
spell.c
View File

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

View File

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