- [+] 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
This commit is contained in:
Rob Pearce 2012-01-06 00:20:57 +00:00
parent 040b9bf052
commit 060470ac3a
21 changed files with 428 additions and 102 deletions

39
ai.c
View File

@ -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,14 +1570,16 @@ 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 (!wandfailed) {
if (!operate(lf, rangedob, zapcell)) {
// succesful
return B_FALSE;
@ -1574,6 +1587,7 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) {
if (db) dblog(".oO { zap failed! }");
}
}
}
} // end if rangedattackok
} // end if attackok
@ -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

2
ai.h
View File

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

View File

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

View File

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

70
data.c
View File

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

Binary file not shown.

11
defs.h
View File

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

View File

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

23
io.c
View File

@ -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')) {
@ -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];

87
lf.c
View File

@ -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,33 +7372,43 @@ 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++) {
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++) {
if (!attacker || canreachbp(attacker, lf, hitbp[i])) {
for (n = 0; n < hitchance[i]; n++) {
hitposition[idx] = poss[i];
idx++;
}
}
}
sel = rnd(0, maxroll-1);
o = hitposition[sel];
@ -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;
}

5
lf.h
View File

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

55
map.c
View File

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

6
move.c
View File

@ -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,6 +280,7 @@ 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 (!onlyifknown || (wis >= AT_AVERAGE)) {
if (hasflag(o->flags, F_SHARP)) {
if (!getequippedob(lf->pack, BP_FEET)) {
if (error) {
@ -290,6 +292,7 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err
}
}
}
}
return B_FALSE;
}
@ -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) {

View File

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

View File

@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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,6 +8564,8 @@ 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);
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++) {
@ -8571,6 +8583,28 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) {
}
}
}
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 {
if (isplayer(lf)) {
@ -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,14 +12236,17 @@ void timeeffectsob(object_t *o) {
if (location) {
// object makes noise?
f = hasflag(o->flags, F_MAKESNOISE);
if (f && pctchance(f->val[0])) {
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?
if (o->material->id == MT_FIRE) {
if (hasflag(location->type->material->flags, F_FLAMMABLE)) {

View File

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

10
spell.c
View File

@ -983,7 +983,9 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef
msg("%s dies.", username);
}
}
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

4
text.c
View File

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

23
vault.c
View File

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

View File

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

39
vaults/caveboss1.vlt Normal file
View File

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