From 060470ac3af7aeb15ca2cf5240ca97c8d44f1e2c Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Fri, 6 Jan 2012 00:20:57 +0000 Subject: [PATCH] - [+] allies should always give out info without payment - [+] ....but only about their home level! - [+] f_startmapid - [+] cave entrances should make noise - [+] drip - [+] echoing - [+] cope with multiple f_makesnoise flags on objects (pick one randomly) - [+] showlfstats skill display bug - "MORE" keystroke doesn't fall through. - [+] You impale the chicken! The chicken turns to face you. - [+] shouldn't turn to face if your'e dead! - [+] nulllify spell not populating seenbyplayer - [+] crash in createfakes() - [+] animals hsould still walk onto SHARP objects. - [+] secret doors showing up as empty remembered cells when you look away from them (and have lowish cartography) - [+] don't call remove_deadends on vaults. - [+] when walking down stairs to level 3: - [+] ERROR - stairs link to existing map 3('dungeon L2 (id #3)', depth 2), but it has no free stairs - [+] ie. Level 3 has too many up staircases ? no. 3 on all of them. - [+] FIXED. countstairs() was including too much. now using countmapobs(map, stairtype) instead. - [+] The goblin rogue a half-sized leather armour (null). - [+] fixed crash when you cast rage on someone who is eating. - [+] crash when catching a glowbug in a flask - [+] use canreachbp code when selecting armour to damage as well.... ie newt can't hit your helmet! - [+] BUG: "tunnel doing up" went down! - [+] for monsters:auto raise lf stats to match starting weapons - [+] crash in aigetspelltarget() for CLIMB - [+] should deactiveate all spells on polymorph - [+] allow usage of FEIGNDEATH while prone. - [+] make coprses non-stackable - [+] CRASH in animatedead - [+] shouldn't say 'you attack x from behind' if x has awareness --- ai.c | 49 +++++++++++++++++----- ai.h | 2 +- attack.c | 23 +++++++---- attack.h | 2 +- data.c | 70 +++++++++++++++++++++++++++---- data/hiscores.db | Bin 13312 -> 13312 bytes defs.h | 11 ++++- doc/vaults.txt | 2 + io.c | 25 ++++++++++-- lf.c | 95 +++++++++++++++++++++++++++++++++++-------- lf.h | 5 ++- map.c | 57 ++++++++++++++++++++------ move.c | 20 +++++---- nexus.c | 6 +++ objects.c | 83 ++++++++++++++++++++++++++----------- objects.h | 1 + spell.c | 12 ++++-- text.c | 4 +- vault.c | 23 +++++++++++ vault.h | 1 + vaults/caveboss1.vlt | 39 ++++++++++++++++++ 21 files changed, 428 insertions(+), 102 deletions(-) create mode 100644 vaults/caveboss1.vlt diff --git a/ai.c b/ai.c index 6030525..4e05e1b 100644 --- a/ai.c +++ b/ai.c @@ -371,7 +371,7 @@ object_t *aigetrangedattack(lifeform_t *lf, lifeform_t *target, enum RANGEATTACK return NULL; } -void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose) { +int aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose) { int specialcase = B_FALSE; flag_t *f; @@ -429,6 +429,13 @@ void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victi poss[nposs++] = lf->los[i]; } } + if (!nposs) { + if (spellcell) spellcell = NULL; + if (spelllf) *spelllf = NULL; + if (spellob) *spellob = NULL; + return B_TRUE; + } + if (spellcell) { *spellcell = poss[rnd(0,nposs-1)]; } @@ -541,6 +548,7 @@ void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victi } } } + return B_FALSE; } object_t *aigetwand(lifeform_t *lf, enum FLAG purpose) { @@ -1411,7 +1419,9 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { spellcell = target->cell; } else { // pick targets based on spell flags - aigetspelltarget(lf, st, target, &spelllf, &spellcell, &spellob, F_AICASTTOATTACK); + if (aigetspelltarget(lf, st, target, &spelllf, &spellcell, &spellob, F_AICASTTOATTACK)) { + spellfailed = B_TRUE; + } } if (spellfailed) { @@ -1549,6 +1559,7 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { if (db) dblog(".oO { throw failed! }"); } } else if (rangedattack == RA_WAND) { + int wandfailed = B_FALSE; objecttype_t *st; cell_t *zapcell = NULL; st = getlinkspell(rangedob); @@ -1559,19 +1570,22 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { } else { purpose = F_AICASTTOATTACK; } - aigetspelltarget(lf, st, target, NULL, &zapcell, NULL, purpose); + if (aigetspelltarget(lf, st, target, NULL, &zapcell, NULL, purpose)) { + wandfailed = B_TRUE; + } } else { // no linkspell - just zap it. zapcell = NULL; } // zap it - - if (!operate(lf, rangedob, zapcell)) { - // succesful - return B_FALSE; - } else { - if (db) dblog(".oO { zap failed! }"); + if (!wandfailed) { + if (!operate(lf, rangedob, zapcell)) { + // succesful + return B_FALSE; + } else { + if (db) dblog(".oO { zap failed! }"); + } } } } // end if rangedattackok @@ -2133,11 +2147,24 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG specificcheckok = B_FALSE; } } + if (ot->id == OT_S_ANIMATEDEAD) { + int found = B_FALSE,i; + // must be a corpse in sight + for (i = 0; i < lf->nlos; i++) { + if (hasob(lf->los[0]->obpile, OT_CORPSE)) { + found = B_TRUE; + break; + } + } + if (!found) { + specificcheckok = B_FALSE; + } + } if (ot->id == OT_S_ANIMATEMETAL) { object_t *wep; wep = getweapon(lf); if (!wep || !ismetal(wep->material->id)) { - specificcheckok = B_TRUE; + specificcheckok = B_FALSE; } } if ((ot->id == OT_S_BLINDNESS) && isblind(victim)) { @@ -2544,7 +2571,7 @@ int lookforobs(lifeform_t *lf) { } } else if ((celldist == 1) && c->lf && (isunconscious(c->lf) || isasleep(c->lf)) && - (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) > IQ_ANIMAL) && !isundead(lf)) { + (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) > IQ_ANIMAL) && !isundead(lf) && !willeatlf(lf, c->lf)) { // intelligent enemies will loot unconscious lfs to make sure they are not a threat. // // in this case they'll loot more than normal. even if they wouldn't normally pick up diff --git a/ai.h b/ai.h index 1ff329a..7e12a26 100644 --- a/ai.h +++ b/ai.h @@ -6,7 +6,7 @@ enum OBTYPE aigetattackspell(lifeform_t *lf, lifeform_t *victim); enum OBTYPE aigetfleespell(lifeform_t *lf); cell_t *aigetlastknownpos(lifeform_t *lf, lifeform_t *target, int *lastx, int *lasty, int *lastdir); object_t *aigetrangedattack(lifeform_t *lf, lifeform_t *target, enum RANGEATTACK *ra, int *range); -void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose); +int aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose); object_t *aigetwand(lifeform_t *lf, enum FLAG purpose); flag_t *aigoto(lifeform_t *lf, cell_t *c, enum MOVEREASON why, void *data, int timelimit); int ai_attack_existing_target(lifeform_t *lf); diff --git a/attack.c b/attack.c index 3f4a5f4..7d44bae 100644 --- a/attack.c +++ b/attack.c @@ -20,7 +20,7 @@ extern lifeform_t *player; extern enum ERROR reason; -int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype) { +int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype, lifeform_t *attacker) { object_t *armour = NULL; int damtaken = 0; @@ -44,7 +44,7 @@ int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damty // figure out what bit of armour was hit if (!armour) { // pick a random piece of armour - armour = getrandomarmour(lf); + armour = getrandomarmour(lf, attacker); } if (armour) { @@ -112,7 +112,7 @@ void applyarmourdamreduction(lifeform_t *lf, object_t *wep, int reduceamt, int * if (dam) { int ar; newdam = *dam; - ar = getarmourrating(lf, NULL, NULL, NULL); + ar = getarmourrating(lf, NULL, NULL, NULL, NULL); // if you did at least one damage... if ((*dam >= 1) && (reduceamt >= 0)) { @@ -159,6 +159,7 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { AT_OB = 2, } attacktype = AT_NONE; void *attacktarget; + int attacklfid = -1; int nweps = 0; int innateattacks = 0; int i; @@ -240,6 +241,7 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { attacktype = AT_LF; attacktarget = c->lf; + attacklfid = c->lf->id; // remember for later if (areallies(lf, attacktarget)) attackedfriend = B_TRUE; if (!cansee(attacktarget, lf) || isfleeing(attacktarget)) attackedhelpless = B_TRUE; @@ -468,10 +470,15 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { killobpile(op); } + if (attacktype == AT_LF) { + // in case the lf disappered.... + attacktarget = findlf(lf->cell->map, attacklfid); + } + // now stop hiding killflagsofid(lf->flags, F_HIDING); - if (saysorry) { + if (saysorry && attacktarget) { sayphrase(lf, SP_SORRY, -1, NA, NULL); } @@ -483,7 +490,7 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { } // god effects... - if ((attacktype == AT_LF) && isplayer(lf)) { + if ((attacktype == AT_LF) && isplayer(lf) && attacktarget) { if (attackedfriend) { angergodmaybe(R_GODMERCY, 100, GA_ATTACKALLY); angergodmaybe(R_GODPURITY, 100, GA_ATTACKALLY); @@ -882,7 +889,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) sprintf(attackname, "%s%s attack", attackername, getpossessive(attackername)); //difficulty = 20 + ((gethitdice(lf) - gethitdice(victim)) ); //difficulty = 20 + gethitdice(lf); - difficulty = 20 + (gethitdice(lf)*2) - gethitdice(victim); + difficulty = 24 + gethitdice(victim) - gethitdice(lf); if (check_for_block(lf, victim, dam[i], damtype[i], difficulty, attackname)) { blocked = B_TRUE; break; // stop processing damage now. @@ -1008,7 +1015,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) // victim's armour loses hp if (reduceamt && !critical) { - applyarmourdamage(victim, wep, dam[i], damtype[i]); + applyarmourdamage(victim, wep, dam[i], damtype[i], lf); // train armour practice(victim, SK_ARMOUR, 1); } @@ -1646,7 +1653,7 @@ int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE d reduceamt = 0; } - ar = getarmourrating(lf, NULL, NULL, NULL); + ar = getarmourrating(lf, NULL, NULL, NULL, NULL); // between 25% and 75% of AR. // ie. with AR of 20, all damage is reduced by 5-15. diff --git a/attack.h b/attack.h index f151ec2..371465f 100644 --- a/attack.h +++ b/attack.h @@ -1,6 +1,6 @@ #include "defs.h" -int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype); +int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype, lifeform_t *attacker); void applyarmourdamreduction(lifeform_t *lf, object_t *wep, int reduceamt, int *dam, enum DAMTYPE damtype); int attackcell(lifeform_t *lf, cell_t *c, int force); int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag); diff --git a/data.c b/data.c index 48de6be..8cb60ba 100644 --- a/data.c +++ b/data.c @@ -213,14 +213,14 @@ void initjobs(void) { } addflag(lastjob->flags, F_NOSCORE, B_TRUE, NA, NA, NULL); - addjob(J_ADVENTURER, "Adventurer", "Adventurers are a basic jack-of-all-trades type job. They can learn all skills, and already have basic Cartography and Lore skills. They start the game with three healing potions. Recommended for beginners."); + addjob(J_ADVENTURER, "Adventurer", "Adventurers are a versatile jack-of-all-trades type job. They can learn all skills, and already have basic Cartography and Lore skills. They also start the game with three healing potions. Recommended for beginners."); // stat mods // initial objects addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "gladius"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather armour"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 gold coins"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "3 potions of healing"); - addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "2 bananas"); + addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "lockpick"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "rope"); // initial skills addflag(lastjob->flags, F_STARTSKILL, SK_ATHLETICS, PR_NOVICE, NA, NULL); @@ -539,7 +539,7 @@ void initjobs(void) { // abilities addflag(lastjob->flags, F_OBESE, B_TRUE, NA, NA, NULL); addflag(lastjob->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); - addjob(J_NINJA, "Ninja", "A dark warrior dedicated to the art of ninjutsu. Ninjas are skilled with long blades, and gain special martial arts abilities at higher levels."); + addjob(J_NINJA, "Ninja", "A dark warrior dedicated to the art of ninjutsu. Ninjas are skilled with exotic weapons, and gain special martial arts abilities at higher levels."); // stats addflag(lastjob->flags, F_JOBATTRMOD, A_STR, 1, NA, NULL); addflag(lastjob->flags, F_JOBATTRMOD, A_AGI, 1, NA, NULL); @@ -1242,14 +1242,13 @@ void initobjects(void) { addflag(lastobjectclass->flags, F_SMELLY, B_TRUE, NA, NA, NULL); addoc(OC_GODSTONE, "Godstones", "Ancient artifacts, created by the elder gods.", '*', C_BOLDMAGENTA, RR_NEVER); addflag(lastobjectclass->flags, F_UNIQUE, NA, NA, NA, NULL); - addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, "pulsating purple stone"); + addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, "pulsating stone"); addflag(lastobjectclass->flags, F_UNIQUE, NA, NA, NA, NULL); addflag(lastobjectclass->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addoc(OC_CORPSE, "Corpses", "Dead flesh which was once living.", '%', C_GREY, RR_NEVER); - addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, ""); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_OBHP, 50, 50, NA, NULL); @@ -1414,9 +1413,11 @@ void initobjects(void) { addflag(lastot->flags, F_OPPOSITESTAIRS, OT_TUNNELUP, NA, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "a strange echoing."); + addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "an echoing drip."); addot(OT_TUNNELUP, "tunnel leading up", "A wide tunnel leading upwards.", MT_STONE, 3000, OC_DFEATURE, SZ_HUGE); addflag(lastot->flags, F_GLYPH, C_BROWN, '<', NA, NULL); - addflag(lastot->flags, F_CLIMBABLE, D_DOWN, NA, NA, NULL); + addflag(lastot->flags, F_CLIMBABLE, D_UP, NA, NA, NULL); addflag(lastot->flags, F_OPPOSITESTAIRS, OT_TUNNELDOWN, NA, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); @@ -1930,11 +1931,19 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, RR_VERYRARE, NULL); // godstones addot(OT_GODSTONEJ, "Godstone of Justice", "An ancient artifact representing the power of justice.", MT_STONE, 3, OC_GODSTONE, SZ_SMALL); + addflag(lastot->flags, F_GLYPH, C_MAGENTA, '*', NA, NULL); addflag(lastot->flags, F_VALUE, 1000, NA, NA, NULL); addflag(lastot->flags, F_CHARGES, 100, 100, NA, NULL); addflag(lastot->flags, F_RECHARGE, 1, NA, NA, NULL); addflag(lastot->flags, F_AIFLEEITEM, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_REPLENISHABLE, B_TRUE, NA, NA, NULL); + addot(OT_GODSTONER, "Godstone of Rage", "An ancient artifact representing the power of anger.", MT_STONE, 3, OC_GODSTONE, SZ_SMALL); + addflag(lastot->flags, F_GLYPH, C_RED, '*', NA, NULL); + addflag(lastot->flags, F_VALUE, 1000, NA, NA, NULL); + addflag(lastot->flags, F_CHARGES, 100, 100, NA, NULL); + addflag(lastot->flags, F_RECHARGE, 1, NA, NA, NULL); + addflag(lastot->flags, F_AIBOOSTITEM, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_REPLENISHABLE, B_TRUE, NA, NA, NULL); // flora addot(OT_FLOWER, "flower", "A colourful woodland flower.", MT_PLANT, 0.01, OC_FLORA, SZ_TINY); addflag(lastot->flags, F_RARITY, H_FOREST, 100, RR_FREQUENT, ""); @@ -2104,6 +2113,10 @@ void initobjects(void) { addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, ""); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); addflag(lastot->flags, F_RARITY, H_FOREST, 100, RR_UNCOMMON, NULL); + addot(OT_POISONSAC, "venom sac", "A small sac of flesh, filled with potent venom.", MT_FLESH, 0.2, OC_FOOD, SZ_TINY); // weight normally comes from corpse type + addflag(lastot->flags, F_GLYPH, C_BLUE, '%', NA, NULL); + addflag(lastot->flags, F_EDIBLE, B_TRUE, 1, NA, ""); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); addot(OT_ROASTMEAT, "chunk of roast meat", "A chunk of flame-roasted flesh.", MT_FLESH, 1, OC_FOOD, SZ_TINY); // weight normally comes from corpse type addflag(lastot->flags, F_GLYPH, C_BROWN, '%', NA, NULL); addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, ""); @@ -3016,7 +3029,7 @@ void initobjects(void) { addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); - addot(OT_S_SOFTENEARTH, "soften earth", "Converts earth into mud, slowing down.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_SOFTENEARTH, "soften earth", "Converts earth into mud, slowing down all who pass.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how much mud will be created."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -4843,7 +4856,7 @@ void initobjects(void) { addot(OT_BOOKSHELF, "bookshelf", "A set of wooden shelves, sized for book storage.", MT_WOOD, 150, OC_FURNITURE, SZ_HUMAN); addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_RARE, NULL); - addflag(lastot->flags, F_GLYPH, C_GREY, '\\', NA, NULL); + addflag(lastot->flags, F_GLYPH, C_BROWN, '\\', NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); @@ -6860,6 +6873,10 @@ void initobjects(void) { OT_MUSHROOMSHI, 1, B_TRUE, OT_BREADSTALE, 1, B_TRUE, OT_NONE); + addrecipe(OT_POT_POISON, + OT_POISONSAC, 1, B_TRUE, + OT_POT_WATER, 1, B_TRUE, + OT_NONE); addrecipe(OT_POT_SOUPCHICKEN, OT_ROASTMEAT, 1, B_TRUE, // special case in getingredients() enforces chicken meat OT_POT_WATER, 1, B_TRUE, @@ -7305,7 +7322,7 @@ void initrace(void) { addflag(lastrace->flags, F_DEMANDSBRIBE, NA, NA, NA, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); - addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "2d4"); + addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "3d4"); addflag(lastrace->flags, F_NUMAPPEAR, 1, 3, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL); @@ -8150,6 +8167,39 @@ void initrace(void) { addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 0, NA, NA, NULL); + addrace(R_GOBLINKING, "goblin king", 40, 'g', C_MAGENTA, MT_FLESH, RC_HUMANOID, "On rare occasion a goblin becomes powerful enough to command respect from almost all their peers. Far from standard cowardly goblins, these self-crowned 'kings' are a force to be reckoned with."); + setbodytype(lastrace, BT_HUMANOID); + lastrace->baseid = R_GOBLIN; + addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); + addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "goblin corpse"); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, RR_VERYRARE, NULL); + addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "4d4+10"); + addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 6, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_HIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTOBWEPSK, 100, SK_AXES, NA, "great"); + addflag(lastrace->flags, F_STARTOBWEPSK, 100, SK_AXES, NA, "great"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "golden crown"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "great suit of ring mail"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "great random armour"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "great random armour"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "100-300 gold coins"); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "shouts^a shout"); + addflag(lastrace->flags, F_SEEINDARK, 4, NA, NA, NULL); + addflag(lastrace->flags, F_DODGES, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_PACKATTACK, 2, DT_SLASH, 3, NULL); + addflag(lastrace->flags, F_MINIONS, 90, 4, 6, "goblin"); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL); + addrace(R_GREMLIN, "gremlin", 20, 'g', C_GREEN, MT_FLESH, RC_HUMANOID, "Small mischievous imps known for their love of sabotage."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -9705,6 +9755,8 @@ void initrace(void) { addflag(lastrace->flags, F_TREMORSENSE, 10, NA, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_A_CHARGE, NA, NA, "range:4;"); addflag(lastrace->flags, F_CANWILL, OT_A_SUCKBLOOD, NA, NA, "dam:0d1+4;"); + addflag(lastrace->flags, F_WANTS, OT_BLOODSPLASH, B_COVETS, NA, NULL); + addflag(lastrace->flags, F_WANTS, OT_BLOODPOOL, B_COVETS, NA, NULL); addflag(lastrace->flags, F_CASTCHANCE, 60, NA, NA, NULL); addflag(lastrace->flags, F_ENHANCESMELL, 5, NA, NA, NULL); addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); diff --git a/data/hiscores.db b/data/hiscores.db index b30f5058760ceaf7396a571e6cfc1775f05d91a2..8e021e1766f8c434c5e27424d5be879a32d8ca04 100644 GIT binary patch delta 1119 zcmYjOe`p(J82;Yxy*58CDXIOH+88P?l3fkdDr_M2^s06BRtt&(Q$4$MvRPeixm-l;~ z_j&H_`3@uxBwwp@{>=WjKJkZ)-kwF{kCB3FD1x0BeNbh z>x@}fY1-BR>-3)OV`rKPCbq~Au@cJJo* z9qRCHW`|P@PQePV=TGui_yWI$t+*2}YxS=EXqeqkvO7wbD}r_lY-g|?UV=}^RavPu zRbB!+X4p3jDttXLTK6>2XuvM=VHRS%!WU()>R5|5TzwK8PTdJ`k>Mg9*BtJ{=+c3Z zu56F8d@jCiZ;1NZtHmC5v{cu2^_4tMx9{@M`##rvJqVlN5hk(%V+C--AvnVy9h$&rtytmZ0k%ztMDRcQ*}y@k0?U)qGT_7om|sBMibp zSm0j%1^*j2qk?bYGA?V9_abnK^kdVsOp8iaYMIEuCu8#G;lPrc>vJQ(t1QlAV~$EBUSRgp!rwFG#X9 zo|F^0sa^6!U`S1wvN6aQRu9Tjd_)-^R}%WnWImPF72b(tJ~y?YjLX?XYQh%?sLva^ zNC)gRUl)-ErC1)Na)I|EKar-tA!(VA5FvZS2PX%0AG?gyQ5rQ%C?0y9Ru7w=>u9rdZK|1yLe5DlfSD%HM4h zB~JBqCtl%4+`axcZr4v15vrss%Yc{otlM<9{G)qKJ>4%nFfL#mE=k3@RJ;{O@f^;P zMiX{}=L&F6iQ4*Ie?XnYaKi-w7hnMc_&C0X)A&0z(sp{?4wN4v?)P`_w_c-CgN*_< z!YlA0uEMMMH?`ZomGj`j&B7GmZEDZ(QmCG5eJ*#x@~Hy7O=)pN<{Is+Zwfq;=*}p! zJ^o|FrZ>zlC3IJ^t5|KcU?q>Fg8XxZc0K^gYFWKP6oe>%AJ)N`>}3Ic*n@}gJT6j* za&(fe=SM!~Lhu=-(@Lw{8#=ATtp`;8H55}!E2G-A_w<62v>sLMUE$M8K!*ka4bTUN zVHzXYk4Nxx{Fjmv>y#agd;ur~FE#B^3@fVgKs4cMwc@Jmt|jv_TQy<+&#&7v(KcwL zywJrhA#TaPX*awD-(w}N#e>qkMGDgsbXCp>ZRJR8h(D=QxHF{P(U|3l*j{(a>Tx{k z6t6nINh54Ez69m6?N-TXWDf2d-mzhSGh5~w{wvWQ)A3Wuc|*tx-G+JmP4YZNCukPF zgELY?wLF@nK{`$|qATB3X(s%>sgKWc_oZ|nu{Fi?aoO`a`?G_m78~_ z9^#7BW}Y|i<9Ezf-fO1a8>}r3&a+~S!3e9*@7ldTw~vQHb!-?ri+bY{D;|H*!7_PI hw7Oy-cwpd#glY8`zr43Yyv>_J4Sd;nn6D+z{tvOF^r`>= diff --git a/defs.h b/defs.h index edcd002..918e96a 100644 --- a/defs.h +++ b/defs.h @@ -886,9 +886,10 @@ enum RACE { R_GNOLL, R_GNOLLHM, R_GOBLIN, + R_GOBLINHEXER, + R_GOBLINKING, R_GOBLINWAR, R_GOBLINSHOOTER, - R_GOBLINHEXER, R_GREMLIN, R_HOBGOBLIN, R_HOBGOBLINWAR, @@ -1151,6 +1152,7 @@ enum OBTYPE { OT_GOLD, // godstones OT_GODSTONEJ, + OT_GODSTONER, // flora OT_FLOWER, OT_LEAF, @@ -1180,6 +1182,7 @@ enum OBTYPE { OT_MUSHROOMSTUFFED, OT_NUT, OT_PASSIONFRUIT, + OT_POISONSAC, OT_ROASTMEAT, OT_RUMBALL, OT_SALT, @@ -2410,6 +2413,7 @@ enum FLAG { // text = obid of hotel F_ALIGNMENT, // v0 = al_good, al_neutral, al_evil. default neutral. F_PIETY, // for god lifeforms - tracks player's piety with them + F_HOMEMAP, // which map did this lf get created on? F_TOOKACTION, // lf purposely took action in their last turn. F_MOVED, // lf purposely walked/flew/swum/moved in prev turn F_HASBEENMOVED, // an object moved this lf since their last turn. @@ -2485,6 +2489,8 @@ enum FLAG { // v1 = wepskill of object // optional val2 = addition to map depth for rarity // calculation + // optional text = prefix for ob name. + // eg "good" "branded" "cursed" etc F_CONTAINER, // this object is a container - you can use 'o' // to take stuff out or put it in. F_HOLDING, // this container is a xxx of holding and makes objects @@ -2969,6 +2975,8 @@ enum FLAG { // text=x1,y1,x2,y2,mincount-maxcount,thingname // if maxcount is PCT, mincount is a percentage // of the total space. + F_VAULTTAG, // vault has tag 'text'. for use with + // vt_rndvaultwithtag. F_VAULTMAYROTATE, // may rotate this vault in 90degree increments. F_VAULTMAYFLIPX, // may flip this vault horizontally F_VAULTMAYFLIPY, // may flip this vault vertically @@ -3291,6 +3299,7 @@ enum REGIONTHING { // what is stair object type RT_VAULT, // what is vaultname RT_RNDVAULTWITHFLAG, // val is wantedflag + RT_RNDVAULTWITHTAG, // what is wanted tag }; typedef struct regionthing_s { diff --git a/doc/vaults.txt b/doc/vaults.txt index 46d93ee..1fa313c 100644 --- a/doc/vaults.txt +++ b/doc/vaults.txt @@ -92,6 +92,8 @@ Flags can be: shop // } this vault is a shop/shrine/etc stomach // } + tag:xxxx // add tag "xxx" to vault(for use with rndvaultwithtag) + NOTES: when adding lfs/objects, "random" creates a random one. diff --git a/io.c b/io.c index f960839..a37b737 100644 --- a/io.c +++ b/io.c @@ -4128,6 +4128,15 @@ void docomms_areainfo(char *who, flagpile_t *fp, lifeform_t *lf) { msg("\"I don't know anything about that!\""); return; } + f = hasflag(fp, F_HOMEMAP); + if (f) { + // (make the assumption that the player is on the + // same map as the person they are talking to!) + if (f->val[0] != player->cell->map->id) { + msg("\"I'm not familiar with this area.\""); + return; + } + } // shops if (strchr(knowflag->text, 's')) { @@ -4198,6 +4207,15 @@ void docomms_areadangers(char *who, flagpile_t *fp, lifeform_t *lf) { msg("\"I don't know anything about that!\""); return; } + f = hasflag(fp, F_HOMEMAP); + if (f) { + // (make the assumption that the player is on the + // same map as the person they are talking to!) + if (f->val[0] != player->cell->map->id) { + msg("\"I'm not familiar with this area.\""); + return; + } + } // traps or trapped objects if (strchr(knowflag->text, 't')) { @@ -7553,7 +7571,7 @@ int downline(int *y, int h, char *heading, char *subheading, char *bottomstring, if (bottomstring) { centre(mainwin, C_WHITE, h-1, bottomstring); } - ch = getch(); + ch = getch(); if (ch == 27) { // ESC if (retchar) *retchar = ch; return B_TRUE; @@ -9321,7 +9339,7 @@ void drawstatus(void) { */ wattron(statwin, A_BOLD); wprintw(statwin, "AR:"); wattroff(statwin, A_BOLD); - snprintf(buf, BUFLEN, "%d ", getarmourrating(player, NULL, NULL, NULL)); + snprintf(buf, BUFLEN, "%d ", getarmourrating(player, NULL, NULL, NULL, NULL)); wprintw(statwin, buf); wattron(statwin, A_BOLD); wprintw(statwin, "EV:"); wattroff(statwin, A_BOLD); @@ -10351,7 +10369,7 @@ void showlfstats(lifeform_t *lf, int showall) { if (showall || (lorelev >= PR_NOVICE)) { int min,max; //int min,max; - arating = getarmourrating(lf, NULL, NULL, NULL); + arating = getarmourrating(lf, NULL, NULL, NULL, NULL); //min = pctof(25, arating); //max = pctof(75, arating); @@ -10953,6 +10971,7 @@ void showlfstats(lifeform_t *lf, int showall) { } } } + if (finished) break; } } else if (mode == 'm') { char subheading[BUFLEN]; diff --git a/lf.c b/lf.c index a38f517..52eb9d2 100644 --- a/lf.c +++ b/lf.c @@ -538,7 +538,7 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { reason = E_STUNNED; return B_FALSE; } - if (isprone(lf)) { + if (isprone(lf) && (oid != OT_A_FEIGNDEATH)) { reason = E_PRONE; return B_FALSE; } @@ -705,6 +705,11 @@ int caneat(lifeform_t *lf, object_t *o) { return B_FALSE; } + if (!isplayer(lf) && lfhasflag(lf, F_RAGE)) { + reason = E_WONT; + return B_FALSE; + } + // ring of hunger overrides most eating checks if (!hasequippedobid(lf->pack, OT_RING_HUNGER)) { // ai won't eat bad food @@ -2571,6 +2576,7 @@ void die(lifeform_t *lf) { // inherit lifeform knowledge in case we raise it copyflag(corpse->flags, lf->flags, F_KNOWSABOUT); + copyflag(corpse->flags, lf->flags, F_HOMEMAP); // corpse of a player pet? if (ispetof(lf, player)) { @@ -3337,7 +3343,7 @@ int eat(lifeform_t *lf, object_t *o) { } } - // special case for bananas + // special cases for object types if (o->type->id == OT_BANANA) { object_t *skin; skin = addobfast(lf->pack, OT_BANANASKIN); @@ -3359,6 +3365,9 @@ int eat(lifeform_t *lf, object_t *o) { } else if (o->type->id == OT_CARROT) { killtransitoryflags(lf->flags, F_BLIND); addtempflag(lf->flags, F_SEEINDARK, 3, NA, NA, NULL, rnd(20,40)); + } else if (o->type->id == OT_POISONSAC) { + // very bad! + poison(lf, rnd(10,20), P_VENOM, 4, "eating a venom sac"); } if (isplayer(lf)) makeknown(o->type->id); @@ -4068,7 +4077,7 @@ void fightback(lifeform_t *lf, lifeform_t *attacker) { } // turn to face our attacker - if (!lfhasflag(lf, F_STUNNED)) { + if (!lfhasflag(lf, F_STUNNED) && !isdead(lf)) { if (isadjacent(lf->cell, attacker->cell)) { turntoface(lf, attacker->cell); } @@ -5047,7 +5056,7 @@ int getarmournoise(lifeform_t *lf) { // hitob, hitchnace and narms are optional -int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, int *narms) { +int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, enum BODYPART *hitbp, int *narms) { object_t *o; flag_t *f; int ar = 0, i; @@ -5066,6 +5075,7 @@ int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, int *narms if (hitob) { hitob[*narms] = NULL; hitchance[*narms] = getbodyparthitchance(BP_BODY); + if (hitbp) hitbp[*narms] = BP_BODY; (*narms)++; } } @@ -5074,6 +5084,7 @@ int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, int *narms if (hitob) { hitob[*narms] = NULL; hitchance[*narms] = getbodyparthitchance(BP_BODY); + if (hitbp) hitbp[*narms] = BP_BODY; (*narms)++; } } @@ -5095,6 +5106,7 @@ int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, int *narms if (hitob) { hitob[*narms] = NULL; hitchance[*narms] = getbodyparthitchance(BP_BODY); + if (hitbp) hitbp[*narms] = BP_BODY; (*narms)++; } } @@ -5151,6 +5163,7 @@ int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, int *narms if (hitob) { hitob[*narms] = o; hitchance[*narms] = getbodyparthitchance(isshield ? BP_BODY : eqflag->val[0]); + if (hitbp) hitbp[*narms] = isshield ? BP_BODY : eqflag->val[0]; (*narms)++; } } @@ -5863,7 +5876,7 @@ int gethitstokill(lifeform_t *lf, lifeform_t *victim, int useevasion, int usearm // modify by victim's armour? if (usearmour) { int ar,aravg,amin,amax; - ar = getarmourrating(victim, NULL, NULL, NULL); + ar = getarmourrating(victim, NULL, NULL, NULL, NULL); getarrange(ar, &amin, &amax); aravg = (int)(((float)amin + (float)amax) / 2.0); maxdam -= aravg; @@ -7359,31 +7372,41 @@ int getracerarity(map_t *map, enum RACE rid, enum RARITY *rr) { return rarity; } -object_t *getrandomarmour(lifeform_t *lf) { +// if optional 'attacker' is provided, only select form armours which they +// can reach. +object_t *getrandomarmour(lifeform_t *lf, lifeform_t *attacker) { object_t *o; object_t *poss[MAXBODYPARTS]; object_t **hitposition; int hitchance[MAXBODYPARTS]; + enum BODYPART hitbp[MAXBODYPARTS]; int nposs = 0; int maxroll = 0; int i,n,idx; int sel; // make a list of all valid armour - getarmourrating(lf, poss, hitchance, &nposs); + getarmourrating(lf, poss, hitchance, hitbp, &nposs); if (!nposs) return NULL; maxroll = 0; for (i = 0; i < nposs; i++) { - maxroll += hitchance[i]; + if (!attacker || canreachbp(attacker, lf, hitbp[i])) { + maxroll += hitchance[i]; + } + } + if (maxroll == 0) { + return NULL; } // now figure out chances of each one getting hit hitposition = malloc(maxroll * sizeof(object_t *)); idx = 0; for (i = 0; i < nposs; i++) { - for (n = 0; n < hitchance[i]; n++) { - hitposition[idx] = poss[i]; - idx++; + if (!attacker || canreachbp(attacker, lf, hitbp[i])) { + for (n = 0; n < hitchance[i]; n++) { + hitposition[idx] = poss[i]; + idx++; + } } } @@ -9073,8 +9096,10 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { default: break; } if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, val[1], B_TRUE, OC_NONE, DT_NONE)) { + char buf3[BUFLEN]; if (db) snprintf(buf2, BUFLEN, "finished startobwepsk successfuly."); - o = addob(op, buf); + sprintf(buf3, "%s%s%s",text,strlen(text) ? " " : "",buf); + o = addob(op, buf3); } else { if (db) snprintf(buf2, BUFLEN, "finished startobwepsk, failed."); } @@ -10249,8 +10274,10 @@ flag_t *isasleep(lifeform_t *lf) { } // is lf behind otherlf? +// you can never be "behind" something with f_awareness int isbehind(lifeform_t *lf, lifeform_t *otherlf) { int dir; + if (lfhasflag(otherlf, F_AWARENESS)) return B_FALSE; dir = getdirtowards(otherlf->cell, lf->cell, NULL, B_FALSE, DT_ORTH); if (getrelativedir(otherlf, dir) == RD_BACKWARDS) { return B_TRUE; @@ -10969,6 +10996,7 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { } // a->losdirty = B_TRUE; + addflag(a->flags, F_HOMEMAP, cell->map->id, NA, NA, NULL); return a; } @@ -11368,6 +11396,9 @@ lifeform_t *makezombie(object_t *o) { int areenemies(lifeform_t *lf1, lifeform_t *lf2) { reason = E_OK; + if (!isplayer(lf1) && lfhasflagval(lf1, F_TARGETLF, lf2->id, NA, NA, NULL)) return B_TRUE; + if (!isplayer(lf2) && lfhasflagval(lf2, F_TARGETLF, lf1->id, NA, NA, NULL)) return B_TRUE; + if (hasjob(lf1, J_DRUID) && (getraceclass(lf2) == RC_PLANT)) { return B_FALSE; } else if (hasjob(lf2, J_DRUID) && (getraceclass(lf1) == RC_PLANT)) { @@ -11672,6 +11703,8 @@ void autoskill(lifeform_t *lf) { object_t *o; enum SKILLLEVEL slev; int nweps = 0; + flag_t *retflag[MAXCANDIDATES],*f; + int nretflags,i; if (hasjob(lf, J_COMMANDO)) { return; @@ -11689,6 +11722,18 @@ void autoskill(lifeform_t *lf) { if (sk && !getskill(lf, sk->id)) { giveskilllev(lf, sk->id, slev); } + + // monsters:increase stats to match attribn requirements for starting + // weapon. + if (!isplayer(lf)) { + getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if (getattr(lf, f->val[0]) < f->val[1]) { + setattr(lf, f->val[0], f->val[1]); + } + } + } nweps++; } if (isfirearm(o) && canweild(lf, o)) { @@ -12361,7 +12406,7 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml setlastdam(lf, buf); - if (fromlf && lfhasflag(fromlf, F_CARNIVORE)) { + if (fromlf && willeatlf(fromlf, lf)) { setkillverb(lf, "Eaten"); } else { switch (damtype) { @@ -13231,7 +13276,7 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, if (willrespond) { // turn to face the sound - if (isplayer(noisemaker) && cansee(l, player) && !lfhasflag(l, F_AWARENESS)) { + if (isplayer(noisemaker) && cansee(l, player) && !lfhasflag(l, F_AWARENESS) && !isdead(l)) { // peaceful things only turn sometimes if (!ispeaceful(l) || onein(6)) { char lfname[BUFLEN]; @@ -13271,6 +13316,7 @@ void outfitlf(lifeform_t *lf) { //int db = B_FALSE; givestartskills(lf, lf->flags); givestartobs(lf, NULL, lf->flags); + autoskill(lf); // weild/wear stuff autoweild(lf); @@ -14786,6 +14832,8 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { retainhp = B_TRUE; } + loseconcentration(lf); + //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (frompolymorph && (gamemode == GM_GAMESTARTED) && lf->race) { race_t *origrace = NULL; @@ -17246,6 +17294,7 @@ int touch(lifeform_t *lf, object_t *o) { } void turntoface(lifeform_t *lf, cell_t *dstcell) { + if (isdead(lf)) return; // not providing srclf, since this will make getdirtowards() not include // directions in which the next cell is unwalkable. in this case we're // not actually walking there, so we don't care. @@ -18438,7 +18487,7 @@ int wear(lifeform_t *lf, object_t *o) { case 1: strcpy(verb, "puts on"); break; case 2: strcpy(verb, "dons"); break; } - msg("%s %s %s.", buf, obname); + msg("%s %s %s.", buf, verb, obname); } @@ -18835,4 +18884,18 @@ int willburden(lifeform_t *lf, object_t *o, int howmany) { } - +int willeatlf(lifeform_t *eater, lifeform_t *eatee) { + if (isplayer(eater)) return B_FALSE; + // doesn't want to eat + if (!lfhasflagval(eater, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL)) { + return B_FALSE; + } + // does eater eat eatee's material? + if ((eatee->material->id == MT_FLESH) && lfhasflag(eater, F_CARNIVORE)) { + return B_TRUE; + } + if ((eatee->material->id == MT_PLANT) && lfhasflag(eater, F_VEGETARIAN)) { + return B_TRUE; + } + return B_FALSE; +} diff --git a/lf.h b/lf.h index 271eb9f..2a07647 100644 --- a/lf.h +++ b/lf.h @@ -124,7 +124,7 @@ enum ALLEGIENCE getallegiance(lifeform_t *lf); int getallouterarmour(lifeform_t *lf, object_t **ob, int *nobs); object_t *getarmour(lifeform_t *lf, enum BODYPART bp); int getarmournoise(lifeform_t *lf); -int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, int *narms); +int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, enum BODYPART *hitbp, int *narms); int getattackspeed(lifeform_t *lf); float getattackstamloss(lifeform_t *lf); float getattackstamloss(lifeform_t *lf); @@ -218,7 +218,7 @@ char *getpoisonname(enum POISONTYPE ptype); 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); +object_t *getrandomarmour(lifeform_t *lf, lifeform_t *attacker); enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker); race_t *getrandomcorpserace(cell_t *c); job_t *getrandomjob(int onlyplayerjobs); @@ -427,4 +427,5 @@ int wear(lifeform_t *lf, object_t *o); int weild(lifeform_t *lf, object_t *o); int willbleedfrom(lifeform_t *lf, enum BODYPART bp); int willburden(lifeform_t *lf, object_t *o, int howmany); +int willeatlf(lifeform_t *eater, lifeform_t *eatee); //int youhear(cell_t *c, char *text); diff --git a/map.c b/map.c index d4a1371..91f7ffc 100644 --- a/map.c +++ b/map.c @@ -1695,7 +1695,7 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int int includethiscell = B_FALSE; cell = getcellat(map, rx,ry); - // NEVER create a vault whcih will: + // NEVER create a room whcih will: // - be on top of the player (normally this can't happen, // but debugging via 'create vault' could do it) if (cell->lf && isplayer(cell->lf)) { @@ -1764,12 +1764,13 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int if (db) dblog("cell %d,%d - a %dx%d room would not fit here",x,y,score,w,h); } coordscore[i] = score; - if (db) dblog("cell %d,%d - score %d",x,y,score); + if (db) dblog("topleft at %d,%d => score %d",x,y,score); } if (foundvalid) { // now go through and make a list of all BEST positions nposs = 0; + assert(bestscore != 9888); for (i = 0; i < ncoords; i++) { if (coordscore[i] == bestscore) { poss[nposs++] = coord[i]; @@ -2434,10 +2435,10 @@ void createfakes(map_t *map, cell_t *cell) { map->lastlf = NULL; map->name = strdup("fake map"); map->habitat = findhabitat(H_DUNGEON); - setcelltype(cell, CT_FAKE); - cell->lf = NULL; cell->map = map; + cell->lf = NULL; cell->obpile = addobpile(NULL, NULL, NULL); + setcelltype(cell, CT_FAKE); } void createforest(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob, int nclearings) { @@ -2814,6 +2815,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex switch (thing[nthings]->whatkind) { case RT_VAULT: case RT_RNDVAULTWITHFLAG: + case RT_RNDVAULTWITHTAG: // this will reduce the amount of random rooms which can // be created on this map. map->nfixedrooms++; @@ -2888,6 +2890,15 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex failed = B_TRUE; } break; + case RT_RNDVAULTWITHTAG: + if (db) dblog(" adding rndvaultwithtag"); + v = findvaultwithtag(thing[i]->what); + assert(v); + if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { + dblog("ERROR - couldn't create rndvaultwithtag %s on map %s", v->id, map->name); + failed = B_TRUE; + } + break; } } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging @@ -4632,7 +4643,7 @@ void finalisemap(map_t *map, object_t *entryob) { } else { // up stairs on all other levels int nneeded; - nneeded = map->region->rtype->stairsperlev - countstairs(map, D_UP); + nneeded = map->region->rtype->stairsperlev - countmapobs(map, upstairtype); for (i = 0; i < nneeded; i++) { c = NULL; while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { @@ -4659,7 +4670,7 @@ void finalisemap(map_t *map, object_t *entryob) { // DOWN STAIRS if ((downstairtype != OT_NONE) && (map->depth < map->region->rtype->maxdepth)) { int nneeded; - nneeded = map->region->rtype->stairsperlev - countstairs(map, D_DOWN); + nneeded = map->region->rtype->stairsperlev - countmapobs(map, downstairtype); for (i = 0; i < nneeded; i++) { c = NULL; while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { @@ -5599,7 +5610,7 @@ void initmap(void) { // region types addregiontype(RG_WORLDMAP, "The Surface", B_FALSE, H_FOREST, 10, 0, D_NONE, B_TRUE, 0); addregiontype(RG_MAINDUNGEON, "The Main Dungeon", B_FALSE, H_DUNGEON, 25, 3, D_DOWN, B_TRUE, 0); - addregiontype(RG_CAVE, "The Goblin Caves", B_TRUE, H_CAVE, 6, 1, D_DOWN, B_TRUE, 5); + addregiontype(RG_CAVE, "The Goblin Caves", B_TRUE, H_CAVE, 5, 1, D_DOWN, B_TRUE, 5); addregiontype(RG_HEAVEN, "The Realm of Gods", B_FALSE, H_HEAVEN, 1, 0, D_NONE, B_FALSE, 0); addregiontype(RG_PIT, "A Pit", B_FALSE, H_PIT, 1, 1, D_DOWN, B_FALSE, 0); addregiontype(RG_SEWER, "A Sewer", B_FALSE, H_SEWER, 1, 0, D_NONE, B_FALSE, 2); @@ -5665,7 +5676,9 @@ void initmap(void) { addregionthing(lastregionoutline, rnd(17,19), NA, NA, RT_OBJECT, NA, "random building"); addregionthing(lastregionoutline, rnd(20,22), NA, NA, RT_OBJECT, NA, "random building"); addregionthing(lastregionoutline, rnd(23,25), NA, NA, RT_OBJECT, NA, "random building"); + addregionoutline(RG_CAVE); + addregionthing(lastregionoutline, 5, NA, NA, RT_RNDVAULTWITHTAG, NA, "caveboss"); // add initial regions addregion(RG_WORLDMAP, NULL, -1, 0); @@ -6106,11 +6119,15 @@ int linkstairs(object_t *o, object_t *o2) { } if (othermap) { int found = B_FALSE; - // find an empty staircase in other map + object_t *poss[MAXCANDIDATES]; + int nposs = 0; + // find an empty staircase of the correct type in the other map for (n = 0; n < othermap->w*othermap->h; n++) { c2 = othermap->cell[n]; oo = hasob(c2->obpile, otherstairtype->id); if (oo) { + // remember all stairs of correct type, for debugging. + poss[nposs++] = oo; // does it go nowhere? if (!hasflag(oo->flags, F_MAPLINK)) { o2 = oo; @@ -6120,9 +6137,9 @@ int linkstairs(object_t *o, object_t *o2) { } } if (!found) { - dblog("ERROR - stairs link to existing map %d('%s', depth %d), but it has no free stairs.",othermap->id, + dblog("ERROR - stairs should link to existing map ('%s', depth %d), but it has no free stairs.", othermap->name, othermap->depth); - msg("ERROR - stairs link to existing map %d('%s', depth %d), but it has no free stairs.",othermap->id, + msg("ERROR - stairs should link to existing map ('%s', depth %d), but it has no free stairs.", othermap->name, othermap->depth); more(); raise(SIGINT); // debug @@ -6378,7 +6395,7 @@ int remove_deadends(map_t *m, int howmuch) { for (n = 0; n < m->w * m->h; n++) { cell_t *c; c = m->cell[n]; - if (countcellexits(c, DT_ORTH) == 1) { + if (!c->room && (countcellexits(c, DT_ORTH) == 1)) { // erase this cell clearcell(c); setcelltype(c, solidcell); @@ -6422,8 +6439,16 @@ void setcellknown(cell_t *cell, int forcelev) { } cell->known = B_TRUE; - // default to remembering the cell's glyph - cell->knownglyph = cell->type->glyph; + // default to remembering the cell's glyph, or a wall if there's a secret door there + o = hassecretdoor(cell->obpile); + if (o) { + celltype_t *ct; + ct = findcelltype(cell->habitat->solidcelltype); + cell->knownglyph = ct->glyph; + } else { + cell->knownglyph = cell->type->glyph; + } + // high cartography skill lets us remember certain objects... if (slev >= PR_EXPERT) { o = gettopobject(cell, B_TRUE); @@ -6715,6 +6740,12 @@ int validateregionthing(regionthing_t *thing) { goterrors = B_TRUE; } break; + case RT_RNDVAULTWITHTAG: + if (!findvaultwithtag(thing->what)) { + dblog("Invalid rt_rndvaultwithtag specified in regionthing."); + goterrors = B_TRUE; + } + break; } killfakes(&fakemap, &fakecell); return goterrors; diff --git a/move.c b/move.c index 7506fbd..4fe975b 100644 --- a/move.c +++ b/move.c @@ -171,6 +171,8 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err return B_FALSE; } + wis = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL); + // obvious things that you can see if (!onlyifknown || (haslos(lf, cell) && !lfhasflag(lf, F_UNDEAD))) { // water needing creature out of water? @@ -269,7 +271,6 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err if (!onlyifknown) { include_nonobvious = B_TRUE; } else { - wis = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL); if ((wis >= AT_AVERAGE) && haslos(lf, cell)) { if (!lfhasflag(lf, F_UNDEAD)) { include_nonobvious = B_TRUE; @@ -279,13 +280,15 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err if (include_nonobvious) { for (o = cell->obpile->first ; o ; o = o->next) { // don't walk on sharp objects without boots - if (hasflag(o->flags, F_SHARP)) { - if (!getequippedob(lf->pack, BP_FEET)) { - if (error) { - *error = E_AVOIDOB; - rdata = o; - } - return B_TRUE; + if (!onlyifknown || (wis >= AT_AVERAGE)) { + if (hasflag(o->flags, F_SHARP)) { + if (!getequippedob(lf->pack, BP_FEET)) { + if (error) { + *error = E_AVOIDOB; + rdata = o; + } + return B_TRUE; + } } } } @@ -3225,6 +3228,7 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { // don't attack other monsters // or non-enemies + // (unless we have targetted them) if (cell->lf) { // if someone is in the way object_t *defenderwep = NULL; if (lf->race->raceclass->id == RC_INSECT) { diff --git a/nexus.c b/nexus.c index aac1a15..db6d663 100644 --- a/nexus.c +++ b/nexus.c @@ -934,13 +934,19 @@ void donextturn(map_t *map) { //dbtimeend(buf); } } + if (!isplayer(who) && (who->timespent == 0) && !donormalmove) { + // our auto action failed! + taketime(who, getactspeed(who)); + } } } + // us or the player moved into a new map? stop turn. if ((who->cell->map != map) || (player->cell->map != map)) { break; } + } if (hasflag(player->flags, F_ASLEEP)) { diff --git a/objects.c b/objects.c index 1eda5c8..be49d2d 100644 --- a/objects.c +++ b/objects.c @@ -4,6 +4,7 @@ #include #include #include +#include "ai.h" #include "attack.h" #include "defs.h" #include "flag.h" @@ -2785,7 +2786,7 @@ int countobsoftype(obpile_t *op, enum OBTYPE oid) { int count = 0; for (o = op->first ; o ; o = o->next) { - if (o->id == oid) { + if (o->type->id == oid) { count++; } } @@ -6218,6 +6219,15 @@ object_t *hasobid(obpile_t *op, long id) { return NULL; } +object_t *hassecretdoor(obpile_t *op) { + object_t *o; + for (o = op->first ; o ; o = o->next) { + if (isdoor(o, NULL) && hasflag(o->flags, F_SECRET)) { + return o; + } + } + return NULL; +} // fully identify a single object. void identify(object_t *o) { @@ -8541,7 +8551,7 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { if (!seen) { noise(where, NULL, NC_OTHER, SV_WHISPER, "something burning.", NULL); } - } else if (o->type->id == OT_GODSTONEJ) { + } else if (o->type->obclass->id == OC_GODSTONE) { f = hasflag(o->flags, F_CHARGES); if (f && (f->val[0] == f->val[1])) { int x,y; @@ -8554,22 +8564,46 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { msg("%s%s %s unleashes a blast of power!", lfname, getpossessive(lfname), noprefix(obname)); } noise(lf->cell, NULL, NC_OTHER, 10, "an ear-splitting crack", NULL); - // everyone in lof drops to same hp as user - for (y = 0; y < lf->cell->map->h; y++) { - for (x = 0; x < lf->cell->map->w; x++) { - cell_t *c; - c = getcellat(lf->cell->map, x, y); - if (c && c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_NEED, NULL)) { - c->lf->hp = lf->hp; - if (isplayer(c->lf)) { - msg("You are blasted with the power of justice!"); - } else if (cansee(player, c->lf)) { - char lfname[BUFLEN]; - getlfname(c->lf, lfname); - msg("%s is blasted with the power of justice!", lfname); + switch (o->type->id) { + case OT_GODSTONEJ: // justice + // everyone in lof drops to same hp as user + for (y = 0; y < lf->cell->map->h; y++) { + for (x = 0; x < lf->cell->map->w; x++) { + cell_t *c; + c = getcellat(lf->cell->map, x, y); + if (c && c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_NEED, NULL)) { + c->lf->hp = lf->hp; + if (isplayer(c->lf)) { + msg("You are blasted with the power of justice!"); + } else if (cansee(player, c->lf)) { + char lfname[BUFLEN]; + getlfname(c->lf, lfname); + msg("%s is blasted with the power of justice!", lfname); + } + } } } - } + break; + case OT_GODSTONER: // rage + // everyone in lof gets f_rage, and hates everything + for (y = 0; y < lf->cell->map->h; y++) { + for (x = 0; x < lf->cell->map->w; x++) { + cell_t *c; + c = getcellat(lf->cell->map, x, y); + if (c && c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_NEED, NULL)) { + int howlong = 50; + addtempflag(c->lf->flags, F_RAGE, B_TRUE, NA, NA, NULL, howlong); + if (!isplayer(c->lf)) { + addtempflag(c->lf->flags, F_HATESALL, B_TRUE, NA, NA, NULL, howlong); + loseaitargets(c->lf); + } + + } + } + } + break; + default: + break; } f->val[0] = 0; // use up all charges } else { @@ -11918,7 +11952,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, } if (reduceamt && (speed >= 3)) { - applyarmourdamage(target, o, reduceamt, DT_PROJECTILE); + applyarmourdamage(target, o, reduceamt, DT_PROJECTILE, NULL); } wepeffects(o->flags, target->cell, hasflag(o->flags, F_DAM), dam); @@ -12202,12 +12236,15 @@ void timeeffectsob(object_t *o) { if (location) { // object makes noise? - f = hasflag(o->flags, F_MAKESNOISE); - if (f && pctchance(f->val[0])) { - // these are generally just to notify the player that something - // is nearby, so don't make noises the the player is already there. - if (location != player->cell) { - noise(location, NULL, NC_OTHER, f->val[1], f->text, NULL); + getflags(o->flags, retflag, &nretflags, F_MAKESNOISE, F_NONE); + if (nretflags) { + f = retflag[rnd(0,nretflags-1)]; + if (pctchance(f->val[0])) { + // these are generally just to notify the player that something + // is nearby, so don't make noises the the player is already there. + if (location != player->cell) { + noise(location, NULL, NC_OTHER, f->val[1], f->text, NULL); + } } } // does object's material change cell type? diff --git a/objects.h b/objects.h index c185008..5369b58 100644 --- a/objects.h +++ b/objects.h @@ -159,6 +159,7 @@ object_t *hasobmulti(obpile_t *op, enum OBTYPE *oid, int noids); object_t *hasobwithflag(obpile_t *op, enum FLAG flagid); object_t *hasobwithflagval(obpile_t *op, enum FLAG flagid, int val0, int val1, int val2, char *text); object_t *hasobid(obpile_t *op, long id); +object_t *hassecretdoor(obpile_t *op); void identify(object_t *o); void ignite(object_t *o); flag_t *isarmour(object_t *o); diff --git a/spell.c b/spell.c index 19f31e3..fdcf13a 100644 --- a/spell.c +++ b/spell.c @@ -983,7 +983,9 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef msg("%s dies.", username); } } - addflag(user->flags, F_PRONE, B_TRUE, NA, NA, NULL); + if (!isprone(user)) { + addflag(user->flags, F_PRONE, B_TRUE, NA, NA, NULL); + } addflag(user->flags, F_FEIGNINGDEATH, B_TRUE, NA, NA, NULL); // anyone attacking you stops for (lf = user->cell->map->lf ; lf ; lf = lf->next) { @@ -2966,7 +2968,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } } - f = addflag(user->flags, F_ACCURACYMOD, 100, NA, NA, NULL); + f = addflag(user->flags, F_ACCURACYMOD, 200, NA, NA, NULL); attackcell(user, targcell, B_FALSE); taketime(user, getattackspeed(user)*2); killflag(f); @@ -3642,7 +3644,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int i; object_t *o,*nexto; int donesomething = B_FALSE; - if (isplayer(caster)) { + if (!target) { target = caster; } // animate corpses within lof of caster @@ -6930,10 +6932,12 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (isplayer(target)) { msg("^wYou are engulfed in an anti-magic field!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^w%s is engulfed in an anti-magic field!", lfname); + if (seenbyplayer) *seenbyplayer = B_TRUE; } while (ndone < power) { // get a list of flags which could be destroyed @@ -7494,7 +7498,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } for (i = 0; i < howmany; i++) { // pick armour - o = getrandomarmour(target); + o = getrandomarmour(target, NULL); if (o) { char obname[BUFLEN]; // move it diff --git a/text.c b/text.c index f22ac9d..fc4683c 100644 --- a/text.c +++ b/text.c @@ -431,11 +431,11 @@ char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam } } else if (damtype == DT_CHOP) { if (pct <= 5) { - return "hit"; + return "chop"; } else if (pct <= 15) { return "hack"; } else { - return "chop"; + return "cleave"; } } else if (damtype == DT_COLD) { if (pct <= 10) { diff --git a/vault.c b/vault.c index e3ff838..727d0b4 100644 --- a/vault.c +++ b/vault.c @@ -521,6 +521,24 @@ vault_t *findvaultwithflag(enum FLAG fid) { return v; } +// return a random vault with the given tag (ie. f_vaulttag "xxx"). +// don't care about rarity. +vault_t *findvaultwithtag(char *tag) { + vault_t *v; + vault_t *poss[MAXCANDIDATES]; + int nposs = 0; + for (v = firstvault ; v ; v = v->next) { + if (!v->valid) continue; + if (hasflagval(v->flags, F_VAULTTAG, NA, NA, NA, tag)) poss[nposs++] = v; + } + if (nposs) { + v = poss[rnd(0,nposs-1)]; + } else { + v = NULL; + } + return v; +} + // generate vault map 1 as x-flipped map0. // remember offsets into map[0] void generatevaultflipsx(vault_t *v) { @@ -1405,6 +1423,11 @@ int handleline(vault_t *v, char *line) { } else if (streq(line, "shop")) { addflag(v->flags, F_VAULTISSHOP, B_TRUE, NA, NA, NULL); ok = B_TRUE; + } else if (strstarts(line, "tag:")) { + char *p; + p = line + 4; + addflag(v->flags, F_VAULTTAG, B_TRUE, NA, NA, p); + ok = B_TRUE; } else if (streq(line, "shrine")) { // a godstone shrine addflag(v->flags, F_VAULTISSHRINE, B_TRUE, NA, NA, NULL); ok = B_TRUE; diff --git a/vault.h b/vault.h index 6941101..ce72dfa 100644 --- a/vault.h +++ b/vault.h @@ -9,6 +9,7 @@ void dumpvault(char *name, int rotation); vault_t *findvault(char *id); vault_t *findvaultbyid(int id); vault_t *findvaultwithflag(enum FLAG fid); +vault_t *findvaultwithtag(char *tag); void generatevaultflipsx(vault_t *v); void generatevaultflipsy(vault_t *v); void generatevaultrotations(vault_t *v); diff --git a/vaults/caveboss1.vlt b/vaults/caveboss1.vlt new file mode 100644 index 0000000..d34bb05 --- /dev/null +++ b/vaults/caveboss1.vlt @@ -0,0 +1,39 @@ +! Boss room for goblin caves +@id:caveboss_1 + +@map +################## +#c......a.c.+AA### +#.w#.#.#.#..#AA+C# ++..........f####_# +#.w#.#.#.#..#WW+C# +#c......a.c.+WW### +################## +@end + +@legend +#:cell:rock wall +f:ob:wooden footstool +f:mon:goblin king +a:mon:goblin archer +w:mon:goblin warrior +c:ob:lit candelabrum ++:ob:iron gate ++:exit +A:ob:good armour +W:ob:good weapon +_:ob:pentagram +_:ob:Godstone of Rage +C:ob:chest +@end + +@flags +goesin:cave +norandom +atoneof(10,1)(10,3)(10,5) ob:portal to lv1 +scatter(1,1,-2,-2) ob:wooden footstool:0-3 +scatter(1,1,-2,-2) ob:random food:0-2 +mayflipx +tag:caveboss +@end +