diff --git a/ai.c b/ai.c index 1b6a6d6..a22f76c 100644 --- a/ai.c +++ b/ai.c @@ -1336,7 +1336,7 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { // need to train skills? if (!enraged) { - if (readytotrain(lf) && safetorest(lf)) { + if (readytotrain(lf) && safetorest(lf, NULL)) { // special case - monsters don't need to actually rest to gain // skills, although they DO still need to wait until the player // is out of sight. @@ -1375,11 +1375,44 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { /////////////////////////////////////////////// if (!enraged) { + // hide? + if (!ispetof(lf, player) && cancast(lf, OT_A_HIDE, NULL) && aispellok(lf, OT_A_HIDE, lf, F_AICASTTOFLEE)) { + if (db) dblog(".oO { trying to hide. }"); + if (!useability(lf, OT_A_HIDE, lf, lf->cell)) { + if (db) dblog(".oO { success! }"); + return B_TRUE; + } + } // need to heal? - if (needstorest(lf, NULL) && safetorest(lf) && !hasflag(lf->flags, F_RAGE)) { - if (db) dblog(".oO { resting to heal }"); - rest(lf, B_TRUE); - return B_TRUE; + if (needstorest(lf, NULL) && !hasflag(lf->flags, F_RAGE)) { + enum ERROR why; + if (safetorest(lf, &why) || (why == E_TOOSOON)) { + if (db) dblog(".oO { resting to heal }"); + rest(lf, B_TRUE); + return B_TRUE; + } + } + // pretending to be an object? revert. + f = lfhasflag(lf, F_PRETENDSTOBE); + if (f) { + if (safetorest(lf, NULL)) { + object_t *oo; + oo = addobfast(lf->cell->obpile, f->val[0]); + if (oo) { + killflagsofid(oo->flags, F_SIZE); + addflag(oo->flags, F_SIZE, getlfsize(lf), NA, NA, NULL); + // replace contents object + if (oo->contents->first && strlen(f->text)) { + killob(oo->contents->first); + addob(oo->contents, f->text); + } + // kill original monster + addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); + addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL); + lf->hp = 0; + } + } } } @@ -1507,7 +1540,7 @@ int ai_healing(lifeform_t *lf) { } // feigning death with enemies in sight, and hurt? - if (lfhasflag(lf, F_FEIGNINGDEATH) && !safetorest(lf)) { + if (lfhasflag(lf, F_FEIGNINGDEATH) && !safetorest(lf, NULL)) { if (islowhp(lf)) { if (db) dblog(".oO { i am feigning death and bleeding (hp=%d/%d), skipping turn. }",lf->hp,lf->maxhp); // just wait... @@ -1532,7 +1565,7 @@ int ai_healing(lifeform_t *lf) { } else if (cansleep(lf)) { // don't have or can't use our healing items // no enemies in sight? - if (safetorest(lf)) { + if (safetorest(lf, NULL)) { // gods will only sleep/meditate if they are in the realm of gods if (isgod(lf) && (lf->cell->habitat->id != H_HEAVEN)) { } else { @@ -1898,15 +1931,41 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { if (cansee(lf, target)) { int dist,wantdistmin,wantdistmax; int attackok; + flag_t *f; enum OBTYPE spell; - object_t *rangedob = NULL; enum RANGEATTACK rangedattack = RA_NONE; int shootrange = 0; int movefailed = B_FALSE; int closethrowok = B_FALSE; + object_t *rangedob = NULL; + int spellchance = 0; if (db) dblog(".oO { can see my target }"); + // see if we have a ranged attack. if so, adjust wantdist + // to maintain distance. + rangedob = aigetrangedattack(lf, target, &rangedattack, &shootrange); + + // try spells first. + // can we attack with spells (ie. ones which target the victim)? + // if target is adjacent, we will normally just attack rather than try a spell. + + // random chance of casting a spell + f = lfhasflag(lf, F_CASTCHANCE); + if (f) spellchance = f->val[0]; + else spellchance = 30; + + // some attacks can always happen + if (cancast(lf, OT_A_THRUST, NULL) && (dist == 2) && haslofknown(lf->cell, target->cell, LOF_NEED, NULL)) { + spellchance = 100; + } + + if (pctchance(spellchance)) { + spell = aigetattackspell(lf, target); + } else { + spell = OT_NONE; + } + // pet movement if (ismaster) { if (isresting(target)) { @@ -1943,7 +2002,7 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { if (wantattack) { if (dist == 1) { attackok = B_TRUE; - } else if (!lfhasflag(lf, F_HIDING)) { + } else if (!lfhasflag(lf, F_HIDING) || rangedob || (spell != OT_NONE)) { attackok = B_TRUE; } else if (!lfhasflag(lf, F_FEIGNINGDEATH)) { attackok = B_TRUE; @@ -1952,33 +2011,12 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { if (attackok) { objecttype_t *st; - flag_t *f; - int spellchance = 0; // drink boost potions if (!useitemwithflag(lf, F_AIBOOSTITEM)) { return B_FALSE; } - - // try spells first. - // can we attack with spells (ie. ones which target the victim)? - // if target is adjacent, we will normally just attack rather than try a spell. - - // random chance of casting a spell - f = lfhasflag(lf, F_CASTCHANCE); - if (f) spellchance = f->val[0]; - else spellchance = 30; - - // some attacks can always happen - if (cancast(lf, OT_A_THRUST, NULL) && (dist == 2) && haslofknown(lf->cell, target->cell, LOF_NEED, NULL)) { - spellchance = 100; - } - - if (pctchance(spellchance)) { - spell = aigetattackspell(lf, target); - } else { - spell = OT_NONE; - } st = findot(spell); + if ( (spell != OT_NONE) && // found a valid spell/ability to use // AND one of these: ((dist != 1) || // there is distance between us and target @@ -2065,10 +2103,6 @@ int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack) { } } - // see if we have a ranged attack. if so, adjust wantdist - // to maintain distance. - rangedob = aigetrangedattack(lf, target, &rangedattack, &shootrange); - // for firearms/projectiles, chance to fire/throw depends on accuracy. if ((rangedattack == RA_GUN) || (rangedattack == RA_THROW)) { int chance; @@ -3057,11 +3091,12 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG } } if (ot->id == OT_A_HIDE) { + enum ERROR why; if (lfhasflag(lf, F_HIDING)) { specificcheckok = B_FALSE; } else if (lfhasflag(lf, F_FEIGNINGDEATH)) { specificcheckok = B_FALSE; - } else if (!safetorest(lf)) { + } else if (!safetorest(lf, &why) && (why != E_TOOSOON)) { specificcheckok = B_FALSE; } else if (lfproduceslight(lf, NULL)) { specificcheckok = B_FALSE; diff --git a/attack.c b/attack.c index 8614989..28705d8 100644 --- a/attack.c +++ b/attack.c @@ -1621,11 +1621,14 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) makewet(wep, 1); } + // special weapon effects, as long as you're not doing a heavy blow + if (!lfhasflag(lf, F_HEAVYBLOW) && dam[0]) { + // confer flags from weapon + wepeffects(wep->flags, victim->cell, damflag, dam[0], isunarmed); + // confer flags from attacker themselves + wepeffects(lf->flags, victim->cell, damflag, dam[0], isunarmed); + } if (!isdead(victim) && !blocked && !dodged) { - // special weapon effects, as long as you're not doing a heavy blow - if (!lfhasflag(lf, F_HEAVYBLOW) && dam[0]) { - wepeffects(wep->flags, victim->cell, damflag, dam[0], isunarmed); - } f = lfhasflag(lf, F_FREEZINGTOUCH); if (f) { int diff; @@ -1646,10 +1649,6 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) criticalhit(lf, victim, critpos, wep, dam[0], damtype[0]); } - // confer flags from attacker? - if (dam[0]) { - wepeffects(lf->flags, victim->cell, damflag, dam[0], isunarmed); - } // special lifeform-based effects if ((lf->race->id == R_COCKATRICE) && dam[0]) { @@ -3306,18 +3305,25 @@ void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam, int isu } } - getflags(fp, retflag, &nretflags, F_AUTOTANGLE, F_FLAMESTRIKE, F_HEAVYBLOW, F_HITCONFER, F_RACESLAY, F_REVENGE, F_RUSTED, F_NONE); + getflags(fp, retflag, &nretflags, F_AUTOTANGLE, F_DRAINONHIT, F_FLAMESTRIKE, F_HEAVYBLOW, F_HITCONFER, + F_RACESLAY, F_REVENGE, F_RUSTED, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; - if (f->id == F_AUTOTANGLE) { + if ((f->id == F_AUTOTANGLE) && victim && !isdead(victim)) { if (pctchance(f->val[0]) && !hasob(where->obpile, OT_VINE)) { dospelleffects(owner, OT_S_ENTANGLE, f->val[1], victim, NULL, where, B_UNCURSED, NULL, B_FALSE, NULL); f->known = B_KNOWN; } + } else if ((f->id == F_DRAINONHIT) && victim && !isdead(victim)) { + if (!leveldrain(victim, f->val[0], f->val[1], f->val[2], owner)) { + if (strlen(f->text)) { + gainhp(owner, roll(f->text)); + } + } } else if (f->id == F_FLAMESTRIKE) { if (!hasob(where->obpile, OT_FIRESMALL)) { - // ignite! + // ignite addobfast(where->obpile, OT_FIRESMALL); // announce if (haslos(player, where)) { @@ -3412,6 +3418,18 @@ void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam, int isu continue; } + // sometimes we can only hitconfer against certain races + if (hasflag(fp, F_HITCONFERRC)) { + if (!hasflagval(fp, F_HITCONFERRC, getraceclass(victim), NA, NA, NULL)) { + continue; + } + } + if (hasflag(fp, F_HITCONFERDEADONLY)) { + if (!isdead(victim)) { + continue; + } + } + // the f_poisoned flag stacks, others don't. if (!lfhasflag(victim, fid) || (fid == F_POISONED)) { int passedcheck = B_FALSE; @@ -3482,7 +3500,7 @@ void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam, int isu addflag(owner->flags, F_USEDPOISON, B_TRUE, NA, NA, NULL); } } - } else if ((f->id == F_RUSTED) && victim ) { + } else if ((f->id == F_RUSTED) && victim && !isdead(victim) ) { int pct; pct = f->val[0] * 10; if (pctchance(pct)) { diff --git a/data.c b/data.c index a08f233..b73a989 100644 --- a/data.c +++ b/data.c @@ -177,8 +177,9 @@ void initcommands(void) { addcommand(CMD_AGAIN, 'g', "Repeat last action."); addcommand(CMD_REST, '.', "Rest once."); addcommand(CMD_PICKUP, ',', "Pick up something from the ground."); - addcommand(CMD_CLOSE, 'c', "Close a door."); - addcommand(CMD_COMMS, 'C', "Chat/Communicate with someone."); + addcommand(CMD_COMMS, 'c', "Chat/Communicate with someone."); + addcommand(CMD_COMMSALL, 'C', "Command all allies."); + //addcommand(CMD_COMMS, 'C', "Chat/Communicate with someone."); //addcommand(CMD_DROP, 'd', "Drop an item."); addcommand(CMD_DROPMULTI, 'd', "Drop one or more items."); addcommand(CMD_EAT, 'e', "Eat something."); @@ -190,7 +191,7 @@ void initcommands(void) { addcommand(CMD_QUAFF, 'q', "Quaff (drink) a potion."); addcommand(CMD_READ, 'r', "Read a scroll/book."); addcommand(CMD_RESTFULL, 'R', "Rest until healed, or train your skills."); - addcommand(CMD_SLOWWALK, 's', "Step carefully."); + addcommand(CMD_CLOSE, 's', "Shut a door."); addcommand(CMD_THROW, 't', "Throw an object."); addcommand(CMD_TAKEOFF, 'T', "Take off an item of clothing/jewelery."); addcommand(CMD_WEILD, 'w', "Weild a weapon."); @@ -2138,7 +2139,7 @@ void initobjects(void) { addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addoc(OC_FLORA, "Plants", "Some kind of plant/foliage.", ',', C_GREEN, RR_FREQUENT); addocnoun(lastobjectclass, "plant"); - addoc(OC_ROCK, "Rocks/Gems", "Boring (or not so boring) rocks or plants.", '*', C_GREY, RR_FREQUENT); + addoc(OC_ROCK, "Rocks/Gems", "Boring (or not so boring) rocks or plants.", '*', C_STONE, RR_FREQUENT); addoc(OC_FOOD, "Food", "Yum!", '%', C_GREY, RR_FREQUENT); addocnoun(lastobjectclass, "food"); addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, ""); @@ -2822,6 +2823,15 @@ void initobjects(void) { addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL); + // traps - hiding monsters + addot(OT_GARGOYLE, "gargoyle", "A sneaky hidden gargoyle!", MT_STONE, 80, OC_TRAP, SZ_HUMAN); + addflag(lastot->flags, F_GLYPH, C_STONE, '\'', NA, NULL); + addflag(lastot->flags, F_RARITY, H_DUNGEON, NA, RR_VERYRARE, NULL); + addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_MAX, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_ISMONSTER, R_GARGOYLE, OT_STATUE, 140, "3"); // reveal if within 3 cells + addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); // traps - object only addot(OT_TRAPNEEDLEP, "poison needle trap", "A springed needle coated with poison.", MT_NOTHING, 0, OC_TRAP, SZ_SMALL); @@ -2991,7 +3001,7 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_FOREST, NA, RR_RARE, ""); addflag(lastot->flags, F_RARITY, H_CAVE, NA, RR_COMMON, NULL); addflag(lastot->flags, F_RARITY, H_ICECAVE, NA, RR_COMMON, NULL); - addflag(lastot->flags, F_GLYPH, NA, '\'', NA, NULL); + addflag(lastot->flags, F_GLYPH, C_STONE, '\'', NA, NULL); addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_LARGE, NA, NULL); // will be overridden addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSLOF, B_TRUE, NA, NA, NULL); @@ -5903,6 +5913,8 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_RANGE, 2, NA, NA, NULL); + addot(OT_A_TIPTOE, "tiptoe", "Walk carefully to avoid obstacles or slippery floors.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_TRIPLF, "trip", "Attempt to trip your opponent over.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_STAMCOST, 2, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); @@ -6857,7 +6869,6 @@ void initobjects(void) { // tech - l6 ??? // misc - addot(OT_TUSK, "ivory tusk", "A large ivory tusk from a slain elephant. Could be used as weapon by very large creatures.", MT_BONE, 61, OC_MISC, SZ_HUMAN); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_VALUE, 450, NA, NA, NULL); @@ -12514,6 +12525,47 @@ void initrace(void) { addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 20, NA, NA, NULL); + addrace(R_GARGOYLE, "gargoyle", 220, '\'', C_STONE, MT_STONE, RC_MAGIC, "Gargoyles are winged statues which have been imbued with magical life. These evil creatures delight in tricking unsuspecting adventurers by pretending to be ordinary statues."); + setbodytype(lastrace, BT_HUMANOID); + addbodypart(lastrace, BP_TAIL, NULL); + addbodypart(lastrace, BP_WINGS, NULL); + addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_GETKILLEDVERB, NA, NA, NA, "defeat"); + addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_RARITY, H_MASTERVAULTS, NA, RR_UNCOMMON, NULL); + addflag(lastrace->flags, F_HITDICE, 5, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 7, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_VHIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_VLOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_EXHIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_FAST, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_NATURALFLIGHT, B_TRUE, NA, NA, ""); + addflag(lastrace->flags, F_CANWILL, OT_A_FLY, NA, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, OT_A_FLY, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 4, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 6, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 3, 3, NA, NULL); + addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_NOBREATH, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_NOSTAM, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_NOTAKECRITS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_NONAUSEA, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SEEINDARK, 6, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_SWOOP, NA, NA, NULL); + addflag(lastrace->flags, F_SWOOPRANGE, 3, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_FLIGHT, PR_ADEPT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_PRETENDSTOBE, OT_GARGOYLE, NA, NA, NULL); + + addrace(R_GIANTHILL, "mountain giant", 160, 'H', C_GREY, MT_FLESH, RC_HUMANOID, "Enormous humanoids who dwell in the mountains, using their grat strength to leap between valleys and pelt their prey with enormous boulders."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -12897,6 +12949,7 @@ void initrace(void) { addflag(lastrace->flags, F_STARTHIDDENPCT, 75, NA, NA, NULL); 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_STARTSKILL, SK_STEALTH, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 2, NA, NA, NULL); addflag(lastrace->flags, F_ATTACKRANGE, 2, 5, NA, NULL); // maintain distance @@ -19038,6 +19091,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "screechs^an other-wordly screech"); // undead + addrace(R_ZOMBIE, "zombie", 50, 'Z', C_BLUE, MT_FLESH, RC_UNDEAD, "The re-animated corpse of a once living entity, zombies seek to consume the brains of living creatures in an attempt to regain their soul."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -19074,7 +19128,6 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "groans^a gutteral groan"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "gurgles^a gurgle"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "growls^a growl"); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL); @@ -19107,6 +19160,9 @@ void initrace(void) { addflag(lastrace->flags, F_MAXATTACKS, 1, 1, NA, NULL); addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 3, NA, NULL); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, 3, NA, NULL); + addflag(lastrace->flags, F_HITCONFER, F_REVIVETIMER, NA, 0, NULL); + addflag(lastrace->flags, F_HITCONFERVALS, 0, 1, R_ZOMBIECON, "rises up as a zombie"); + addflag(lastrace->flags, F_HITCONFERDEADONLY, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_A_GRAB, NA, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_RANDOMTALKPCT, 20, NA, NA, NULL); @@ -19116,10 +19172,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "groans^a gutteral groan"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "gurgles^a gurgle"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "growls^a growl"); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); - addflag(lastrace->flags, F_HITCONFER, F_REVIVETIMER, SC_POISON, 165, NULL); - addflag(lastrace->flags, F_HITCONFERVALS, 0, 1, R_ZOMBIECON, "rises up as a zombie"); addrace(R_SKELETON, "skeleton", 20, 'Z', C_BONE, MT_BONE, RC_UNDEAD, "A walking set of bones, animated through the use of necromancy. Due to their lack of soft flesh, they have little to fear from edged weapons."); setbodytype(lastrace, BT_HUMANOID); @@ -19161,7 +19214,6 @@ void initrace(void) { addflag(lastrace->flags, F_DTVULN, DT_FALL, NA, NA, "1d3+3"); addflag(lastrace->flags, F_DTRESIST, DT_PIERCE, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_FOLLOWTIME, 0, NA, NA, NULL); @@ -19202,7 +19254,6 @@ void initrace(void) { addflag(lastrace->flags, F_DTVULN, DT_FALL, NA, NA, "1d3+3"); addflag(lastrace->flags, F_DTRESIST, DT_PIERCE, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addrace(R_SKELLION, "skellion", 10, 'r', C_RED, MT_BONE, RC_UNDEAD, "A floating skull, immersed in flames. Skellions are often created when attempting to animate a beheaded corpse."); @@ -19236,6 +19287,36 @@ void initrace(void) { addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_FOLLOWTIME, 10, NA, NA, NULL); + addrace(R_WIGHT, "wight", 50, 'Z', C_LIGHTBLUE, MT_FLESH, RC_UNDEAD, "A twisted and misshappen creature of evil, vague recognisable as humanoid."); + setbodytype(lastrace, BT_HUMANOID); + addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_AGI, AT_LTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_LOW, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_ALL, NA, RR_RARE, NULL); + addflag(lastrace->flags, F_NOSTAIRS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_BLOODOB, 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, 5, NA, NA, NULL); + addflag(lastrace->flags, F_TR, 5, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_MAXATTACKS, 1, 1, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_TOUCHNECROTIC, 4, NA, NULL); + addflag(lastrace->flags, F_HITCONFER, F_REVIVETIMER, NA, 0, NULL); + addflag(lastrace->flags, F_HITCONFERVALS, 0, 1, R_WIGHT, "rises up as a wight"); + addflag(lastrace->flags, F_HITCONFERRC, RC_HUMANOID, NA, NA, NULL); + addflag(lastrace->flags, F_HITCONFERDEADONLY, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_DRAINONHIT, 1, SC_CON, 100, "1d0+5"); + addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); + + addrace(R_WRAITHBOG, "bog wraith", 20, 'Z', C_BROWN, MT_PLANT, RC_UNDEAD, "Bog wraiths take the form of vaguely humanoid blobs of putrid mud."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -19264,7 +19345,6 @@ void initrace(void) { addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, B_APPENDYOU, "undulates"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "screeches^a screech"); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); @@ -19335,7 +19415,6 @@ void initrace(void) { addflag(lastrace->flags, F_INDUCEFEAR, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL); @@ -19366,7 +19445,6 @@ void initrace(void) { addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addrace(R_GHOST, "ghost", 50, 'p', C_GREY, MT_FLESH, RC_UNDEAD, "Wispy spirits formed when a soul refuses to depart the earthly realm after death, ghosts exist part way between dimensions. The sight of a ghost can cause fear in all who behold it, and their ethereal nature makes them immune to most attacks."); // p for sPirit @@ -19438,7 +19516,6 @@ void initrace(void) { addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); @@ -19507,7 +19584,6 @@ void initrace(void) { addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_FIRE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, 6, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_FOLLOWTIME, 30, NA, NA, NULL); @@ -19548,11 +19624,10 @@ void initrace(void) { addflag(lastrace->flags, F_CASTCHANCE, 40, NA, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, 6, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_FOLLOWTIME, 50, NA, NA, NULL); - addrace(R_REVENANT, "revenant", 60, 'Z', C_LIGHTBLUE, MT_FLESH, RC_UNDEAD, "A powerful zombie which retains full memory of its former life and abilities."); + addrace(R_REVENANT, "revenant", 60, 'Z', C_MAGENTA, MT_FLESH, RC_UNDEAD, "A powerful zombie which retains full memory of its former life and abilities."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_STR, AT_HIGH, NA, NULL); @@ -19855,7 +19930,6 @@ void initrace(void) { addflag(lastrace->flags, F_TREMORSENSE, 10, NA, NA, NULL); addflag(lastrace->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); @@ -19886,7 +19960,6 @@ void initrace(void) { addflag(lastrace->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_TREMORSENSE, 10, NA, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); @@ -19917,7 +19990,6 @@ void initrace(void) { addflag(lastrace->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_TREMORSENSE, 10, NA, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); @@ -19941,7 +20013,6 @@ void initrace(void) { addflag(lastrace->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_TREMORSENSE, 10, NA, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); @@ -20087,6 +20158,7 @@ void initrace(void) { addflag(r->flags, F_NORESTHEAL, B_TRUE, NA, NA, NULL); addflag(r->flags, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(r->flags, F_NONAUSEA, B_TRUE, NA, NA, NULL); + // +/- 15 accuracy during day/night addflag(r->flags, F_NIGHTBOOST, 15, NA, NA, NULL); addflag(r->flags, F_DAYBOOST, -15, NA, NA, NULL); diff --git a/defs.h b/defs.h index 072e23d..860b1a4 100644 --- a/defs.h +++ b/defs.h @@ -111,6 +111,9 @@ #define B_BLINDABLE (-1) +#define B_ALLOWEXTRA (-1) +#define B_NOEXTRA (0) + #define B_ADD 1 #define B_REMOVE -1 @@ -709,6 +712,7 @@ enum COLOUR { C_CARPET1, C_CARPET2, C_SMOKE, + C_STONE, C_WOOD, // dark colours C_DARKCYAN, @@ -1295,6 +1299,7 @@ enum RACE { R_DRYAD, R_EFREETI, R_EYEBAT, + R_GARGOYLE, R_GIANTHILL, R_GIANTFIRE, R_GIANTFIREFC, @@ -1512,6 +1517,7 @@ enum RACE { R_SKELETONFIRE, R_SKELLION, R_VAMPIRE, + R_WIGHT, R_WRAITHBOG, R_WRAITHICE, R_ZOMBIE, @@ -1717,6 +1723,8 @@ enum OBTYPE { OT_TRAPTELEPORT, OT_TRAPTRIP, OT_TRAPWIND, + // traps - hiding monsters + OT_GARGOYLE, // rocks OT_ASH, OT_ASHLARGE, @@ -2177,6 +2185,7 @@ enum OBTYPE { OT_A_SUCKBLOOD, OT_A_SWALLOW, OT_A_SWOOP, + OT_A_TIPTOE, OT_A_TRIPLF, // trip an opponent OT_A_EMPLOY, OT_A_EXPOSEDSTRIKE, @@ -2960,6 +2969,15 @@ enum FLAG { F_CORPSEOF, // this is a corpse of montype val0. // v1 is its level when it died // text is how it died. + F_ISMONSTER, // this object is really a mosnter staying still to + // trick you! + // v0 = race id of monster + // v1 = object id to hide as + // v2 = spot check difficulty (or NA) + // text = reveal ourselves if prey <= this distance + // + // Also see: F_PRETENDSTOBE + F_REVIVETIMER, // v0 = cur, v1 = max. v0 incremenets each tick. // when v0 == v1, this object changes into lf of race // v2. @@ -2998,6 +3016,10 @@ enum FLAG { // text F_CRITKNOCKDOWN, // lf knocks down victims on a critical hit + F_DRAINONHIT, // victims hit by this lf get v0 xplevs drained unless + // they pass a skillcheck of type v1, diff v2. + // v1 can be NA. + // if successful, lf gains 'text' hp (in dice format) F_HITCONFER, // hitting with this gives flagid=v0 // with timeleft = text ("min-max" // or NULL for permenant) @@ -3005,6 +3027,9 @@ enum FLAG { // if val1 = NA, no check. // MUST ALSO HAVE HITCONFERVALS. F_HITCONFERVALS,// specifies values for conferred flag. + F_HITCONFERRC, // hitconfer only works against victims of raceclass + // v0 + F_HITCONFERDEADONLY, // hitconfer only works on fatal hits F_ACTIVATED, // val0 = is this object turned on? F_GRENADE, // this object will drain charge when activated, then die F_EXPLODEONDEATH, // explodes when it dies, deals TEXT damage. @@ -4139,6 +4164,13 @@ enum FLAG { F_PARALYZED,// cannot do anything F_PARANOIA, // mosnters randomly appear out of sight, or random // noises happen from behind you. + F_PRETENDSTOBE, // this lf will pretend to be object v0. + // when bored, revert to an ob of type v0. + // + // if 'text' is set, then v0's contents object + // should be this. + // + // Also see: F_ISMONSTER F_PRONE, // lying on the ground F_FROZEN, // made of ice F_HEAVENARM, // prevent the next v0 damage received. @@ -4469,6 +4501,7 @@ enum ERROR { E_OBINWAY, E_TOOHEAVY, E_TOOHARD, + E_TOOSOON, E_NOHANDS, E_NOPACK, E_INSUBSTANTIAL, @@ -4565,6 +4598,7 @@ enum COMMAND { CMD_AIM, CMD_CLOSE, CMD_COMMS, + CMD_COMMSALL, CMD_DOWN, CMD_DROP, CMD_DROPMULTI, @@ -4597,7 +4631,6 @@ enum COMMAND { CMD_REST, CMD_RESTFULL, CMD_SAVEQUIT, - CMD_SLOWWALK, CMD_TAKEOFF, CMD_THROW, CMD_UP, diff --git a/god.c b/god.c index a55de0e..30b8b41 100644 --- a/god.c +++ b/god.c @@ -737,7 +737,7 @@ void dooffer(void) { if ((god->race->id == R_GODNATURE) && (o->type->obclass->id == OC_FLORA) && newcell) { - addmonster(player->cell, R_BUTTERFLY, NULL, B_FALSE, 1, B_FALSE, NULL); + addmonster(player->cell, R_BUTTERFLY, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); } if (o->type->obclass->id == OC_FLORA) { diff --git a/io.c b/io.c index ce66148..c1b840d 100644 --- a/io.c +++ b/io.c @@ -4441,160 +4441,30 @@ void doclose(void) { } } -void docomms(lifeform_t *lf) { - cell_t *where = NULL; - int i; - int askforob = B_FALSE; +void docommslf(lifeform_t *lf, char ch, lifeform_t *lf2, cell_t *targc) { + char lfname[BUFLEN], lfname2[BUFLEN]; char buf[BUFLEN]; - char lfname[BUFLEN]; - char ch; - //int moneyowing = 0; - enum ATTRBRACKET iqb; - flag_t *f; - cell_t *c; - lifeform_t *lf2 = NULL; - char lfname2[BUFLEN]; - char buf2[BUFLEN]; int count; int alignmod = 0; + cell_t *c; + flag_t *f; object_t *o, *givenob = NULL; object_t *godstone = NULL; - if (!lf) { - where = askcoords("Talk to who?", "Talk->", TT_MONSTER, player, UNLIMITED, LOF_DONTNEED, B_FALSE); - if (where && where->lf && cansee(player, where->lf)) { - lf = where->lf; - } - } - if (!lf) { - msg("Cancelled."); - return; - } + int i; + int askforob = B_FALSE; alignmod = getalignmod(lf); getlfname(lf, lfname); - - - snprintf(buf, BUFLEN, "What will you say to %s?",lfname); - initprompt(&prompt, buf); - prompt.maycancel = B_TRUE; - - iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); - - // are they friendly? - if (ispetof(lf, player)) { - if ((iqb >= IQ_ANIMAL) || isundead(lf)) { - addchoice(&prompt, 'a', "Attack something", NULL, NULL, NULL); - } - - if (!isadjacent(lf->cell, player->cell)) { - addchoice(&prompt, 'c', "Come here", NULL, NULL, NULL); - } - - addchoice(&prompt, 'g', "Go somewhere", NULL, NULL, NULL); - - if (isadjacent(lf->cell, player->cell) && !lfhasflag(lf, F_NOPACK)) { - addchoice(&prompt, 't', "Trade items with me", NULL, NULL, NULL); - } - - if (iqb >= IQ_ANIMAL) { - f = isresting(lf); - if (f) { - addchoice(&prompt, 'r', "Stop resting.", NULL, NULL, NULL); - } else { - addchoice(&prompt, 'r', "Rest until you are healed.", NULL, NULL, NULL); - } - addchoice(&prompt, '<', "Stay close.", NULL, NULL, NULL); - addchoice(&prompt, '>', "Keep your distance.", NULL, NULL, NULL); - } - } else if (ishirable(lf) ) { - if (lfhasflag(lf, F_ISPRISONER)) { - addchoice(&prompt, 'j', "Join me, and I will help you escape.", NULL, NULL, NULL); - } else if (getskill(player, SK_SPEECH) >= PR_EXPERT) { - addchoice(&prompt, 'j', "Join me on my quest!", NULL, NULL, NULL); - } - } - - if (!isgod(lf) && ispeaceful(lf) && cantalk(lf)) { - enum SKILLLEVEL slev; - job_t *j; - slev = getskill(player, SK_SPEECH); - // same race or job? - j = getjob(player); - if ((slev != PR_MASTER) && j && (j == getjob(lf))) slev++; - if ((slev != PR_MASTER) && (player->race->id == lf->race->id)) slev++; - if (slev >= PR_NOVICE) { - addchoice(&prompt, 'x', "Any dangers nearby that I should look out for?", NULL, NULL, NULL); - } - if (slev >= PR_BEGINNER) { - addchoice(&prompt, 'i', "What can you tell me about this area?", NULL, NULL, NULL); - } - if (!areallies(player, lf)) { - if (slev >= PR_SKILLED) { - addchoice(&prompt, 'k', "Care to trade knowledge?", NULL, NULL, NULL); - } - } - } - - if (isadjacent(lf->cell, player->cell)) { - if (areenemies(player, lf)) { - addchoice(&prompt, 'm', "Have mercy!", NULL, NULL, NULL); - } - // if you are allies, use 'trade items' instead - if (!areallies(player, lf)) { - if (isgod(lf)) { - // may only donate the godstone - godstone = hasob(player->pack, getopposinggodstone(lf->race->id)); - if (godstone) { - char buf[BUFLEN],obname[BUFLEN]; - getobname(godstone, obname, 1); - sprintf(buf, "(offer %s)", obname); - addchoice(&prompt, 'o', buf, NULL, NULL, NULL); - } - } else { - addchoice(&prompt, 'o', "(offer a bribe)", NULL, NULL, NULL); - } - } - } - - /* - f = lfhasflag(lf, F_OWNSSHOP); - if (f) { - int shopid; - shopid = f->val[0]; - moneyowing = getowing(player, shopid, NULL); - if (moneyowing > 0) { - snprintf(buf, BUFLEN, "(pay $%d to the shopkeeper)",moneyowing); - addchoice(&prompt, 'p', buf, NULL, NULL, NULL); - } - } - */ - - - addchoice(&prompt, 'y', "Yeeeeeaaaargh!", NULL, NULL, NULL); - addchoice(&prompt, 'n', "(nothing)", NULL, NULL, NULL); - - ch = getchoice(&prompt); - if ((ch == 'n') || (ch == '\0')) { - msg("Cancelled."); - return; + if (lf2) { + getlfname(lf2, lfname2); + } else { + strcpy(lfname2, "?noone?"); } + // process command switch (ch) { case 'a': - snprintf(buf, BUFLEN, "Tell %s to attack who?",lfname); - snprintf(buf2, BUFLEN, "%s->Attack->",lfname); - c = askcoords(buf, buf2, TT_MONSTER, lf, UNLIMITED, LOF_DONTNEED, B_FALSE); - if (c && c->lf) { - lf2 = c->lf; - } - if (!lf2) { - msg("Cancelled."); - return; - } - getlfname(lf2, lfname2); - msg("You say \"Attack %s!\" to %s.",isplayer(lf2) ? "me" : lfname2, lfname); - if (lfhasflag(lf, F_RAGE) || !canhear(lf, player->cell, SV_SHOUT, NULL)) { msg("%s doesn't respond.", lfname); break; @@ -4624,7 +4494,6 @@ void docomms(lifeform_t *lf) { } break; case 'c': - msg("You say \"Come here!\" to %s.",lfname); if (lfhasflag(lf, F_RAGE) || !canhear(lf, player->cell, SV_SHOUT, NULL)) { msg("%s doesn't respond.", lfname); break; @@ -4857,17 +4726,8 @@ void docomms(lifeform_t *lf) { break; case 'g': - snprintf(buf, BUFLEN, "Tell %s to go where?",lfname); - snprintf(buf2, BUFLEN, "%s->Goto->",lfname); - c = askcoords(buf, buf2, TT_NONE, lf, UNLIMITED, LOF_DONTNEED, B_FALSE); - if (c && cellwalkable(lf, c, NULL) ) { - } else { - msg("Cancelled."); - return; - } // stop attacking all current targets first... killflagsofid(lf->flags, F_TARGETLF); - msg("You say \"Go over there!\" to %s.", lfname); if (lfhasflag(lf, F_RAGE) || !canhear(lf, player->cell, SV_SHOUT, NULL)) { msg("%s doesn't respond.", lfname); break; @@ -4876,10 +4736,9 @@ void docomms(lifeform_t *lf) { msg("%s doesn't respond.", lfname); break; } - aigoto(lf, c, MR_OTHER, NULL, DEF_AIFOLLOWTIME); + aigoto(lf, targc, MR_OTHER, NULL, DEF_AIFOLLOWTIME); break; case 'i': - msg("You say \"What can you tell me about this area?\" to %s.", lfname); if (lfhasflag(lf, F_PHANTASM)) { msg("%s doesn't respond.", lfname); break; @@ -4891,7 +4750,6 @@ void docomms(lifeform_t *lf) { break; case 'j': // charisma check to see if they'll join you. - msg("You say \"Join me on my quest!\" to %s.", lfname); if (lfhasflag(lf, F_RAGE) || !canhear(lf, player->cell, SV_SHOUT, NULL)) { msg("%s doesn't respond.", lfname); break; @@ -4903,7 +4761,6 @@ void docomms(lifeform_t *lf) { recruit(lf); break; case 'k': // trade Knowledge - msg("You say \"Care to trade knowledge?\" to %s.", lfname); if (lfhasflag(lf, F_PHANTASM)) { msg("%s doesn't respond.", lfname); break; @@ -4911,7 +4768,6 @@ void docomms(lifeform_t *lf) { tradeknowledge(lf); break; case 'm': // mercy - msg("You say \"Have mercy!\" to %s.", lfname); if (lfhasflag(lf, F_PHANTASM)) { msg("%s doesn't respond.", lfname); break; @@ -4982,7 +4838,6 @@ void docomms(lifeform_t *lf) { break; */ case 'r': - msg("You say \"Get some rest.\" to %s.", lfname); if (lfhasflag(lf, F_RAGE) || !canhear(lf, player->cell, SV_SHOUT, NULL)) { msg("%s doesn't respond.", lfname); break; @@ -4996,7 +4851,8 @@ void docomms(lifeform_t *lf) { stopresting(lf); } else { if (needstorest(lf, NULL)) { - if (safetorest(lf)) { + enum ERROR why; + if (safetorest(lf, &why) || (why == E_TOOSOON)) { addflag(lf->flags, F_RESTUNTILBETTER, B_TRUE, NA, NA, NULL); startresting(lf, B_FALSE); } else { @@ -5040,7 +4896,6 @@ void docomms(lifeform_t *lf) { } break; case 'x': - msg("You say \"Any dangers nearby that I should look out for?\" to %s.", lfname); if (lfhasflag(lf, F_PHANTASM)) { msg("%s doesn't respond.", lfname); break; @@ -5051,11 +4906,9 @@ void docomms(lifeform_t *lf) { } break; case 'y': - msg("You shout at %s!", lfname); - noise(where, player, NC_OTHER, 3, "someone shouting!", NULL); + noise(player->cell, player, NC_OTHER, 3, "someone shouting!", NULL); break; case '<': - msg("You say \"Stay close!\" to %s.", lfname); if (lfhasflag(lf, F_RAGE) || !canhear(lf, player->cell, SV_SHOUT, NULL)) { msg("%s doesn't respond.", lfname); break; @@ -5067,7 +4920,6 @@ void docomms(lifeform_t *lf) { setfollowdistance(lf, 1, 3); break; case '>': - msg("You say \"Keep your distance!\" to %s.", lfname); if (lfhasflag(lf, F_RAGE) || !canhear(lf, player->cell, SV_SHOUT, NULL)) { msg("%s doesn't respond.", lfname); break; @@ -5079,9 +4931,173 @@ void docomms(lifeform_t *lf) { setfollowdistance(lf, 3, 5); break; } +} + +void docommsmulti(void) { + lifeform_t *ally[MAX_MAPW*MAX_MAPH],*l,*lfarg; + cell_t *cellarg,*origc = NULL; + int nallies = 0,i; + char buf[BUFLEN],ch; + + snprintf(buf, BUFLEN, "What will you command your allies to do?"); + initprompt(&prompt, buf); + prompt.maycancel = B_TRUE; + + // find all allies on the map who can hear the player + for (l = player->cell->map->lf ; l ; l = l->next) { + if ((l != player) && (getallegiance(l) == AL_FRIENDLY)) { + ally[nallies] = l; + nallies++; + // add options to say to this person + makecommslist(&prompt, l, B_TRUE); + } + } + // ask what to say to them all + ch = getchoice(&prompt); + if ((ch == 'n') || (ch == '\0')) { + msg("Cancelled."); + return; + } + + if (getcommsopts(ch, "Allies", &lfarg, &cellarg)) { + msg("Cancelled."); + return; + } + announcecomms(ch, "your allies", lfarg, cellarg); + + origc = cellarg; + for (i = 0; i < nallies; i++) { + docommslf(ally[i], ch, lfarg, cellarg); + if (ch == 'g') { + // pick a new cell + cellarg = getrandomadjcell(cellarg, &ccwalkable, B_ALLOWEXPAND); + if (!cellarg) cellarg = origc; + + } + } taketime(player, getactspeed(player)); } +void announcecomms(char ch, char *talkto, lifeform_t *lfarg, cell_t *cellarg) { + char lfname2[BUFLEN]; + if (lfarg) { + getlfname(lfarg, lfname2); + } + switch (ch) { + case 'a': + msg("You say \"Attack %s!\" to %s.",isplayer(lfarg) ? "me" : lfname2, talkto); + break; + case 'c': + msg("You say \"Come here!\" to %s.",talkto); + break; + case 'g': + msg("You say \"Go over there!\" to %s.", talkto); + break; + case 'i': + msg("You say \"What can you tell me about this area?\" to %s.", talkto); + break; + case 'j': + msg("You say \"Join me on my quest!\" to %s.", talkto); + break; + case 'k': // trade Knowledge + msg("You say \"Care to trade knowledge?\" to %s.", talkto); + break; + case 'm': // mercy + msg("You say \"Have mercy!\" to %s.", talkto); + break; + case 'r': + msg("You say \"Get some rest.\" to %s.", talkto); + break; + case 'x': + msg("You say \"Any dangers nearby that I should look out for?\" to %s.", talkto); + break; + case 'y': + msg("You shout at %s!", talkto); + break; + case '<': + msg("You say \"Stay close!\" to %s.", talkto); + break; + case '>': + msg("You say \"Keep your distance!\" to %s.", talkto); + break; + default: break; + } +} + +void docomms(lifeform_t *lf) { + cell_t *where = NULL; + char buf[BUFLEN],lfname[BUFLEN]; + char ch; + lifeform_t *lfarg; + cell_t *cellarg; + //int moneyowing = 0; + if (!lf) { + where = askcoords("Talk to who?", "Talk->", TT_MONSTER, player, UNLIMITED, LOF_DONTNEED, B_FALSE); + if (where && where->lf && cansee(player, where->lf)) { + lf = where->lf; + } + } + if (!lf) { + msg("Cancelled."); + return; + } + + getlfname(lf, lfname); + + snprintf(buf, BUFLEN, "What will you say to %s?",lfname); + initprompt(&prompt, buf); + prompt.maycancel = B_TRUE; + + + makecommslist(&prompt, lf, B_FALSE); + + ch = getchoice(&prompt); + if ((ch == 'n') || (ch == '\0')) { + msg("Cancelled."); + return; + } + + getcommsopts(ch, lfname, &lfarg, &cellarg); + + announcecomms(ch, lfname, lfarg, cellarg); + docommslf(lf, ch, lfarg, cellarg); + + taketime(player, getactspeed(player)); +} + +// return true on error +int getcommsopts(char ch, char *talkto, lifeform_t **retlf, cell_t **retcell) { + cell_t *c; + char buf[BUFLEN],buf2[BUFLEN]; + + *retlf = NULL; + *retcell = NULL; + + // get other options for comms... + switch (ch) { + case 'a': // who to attack? + snprintf(buf, BUFLEN, "Tell %s to attack who?",talkto); + snprintf(buf2, BUFLEN, "%s->Attack->",talkto); + c = askcoords(buf, buf2, TT_MONSTER, player, UNLIMITED, LOF_DONTNEED, B_FALSE); + if (c && c->lf) { + *retlf = c->lf; + } else { + return B_TRUE; + } + break; + case 'g': // go where? + snprintf(buf, BUFLEN, "Tell %s to go where?",talkto); + snprintf(buf2, BUFLEN, "%s->Goto->",talkto); + *retcell = askcoords(buf, buf2, TT_NONE, player, UNLIMITED, LOF_DONTNEED, B_FALSE); + if (! (*retcell)) { + return B_TRUE; + } + break; + } + return B_FALSE; +} + + // lf is the person (if any) who you are talking to void docomms_areainfo(char *who, flagpile_t *fp, lifeform_t *lf) { int x,y,ndone; @@ -9682,6 +9698,7 @@ void initgfx(void) { initcol(C_CARPET1, 560, 280, 136); initcol(C_CARPET2, 360, 80, 36); initcol(C_SMOKE, 250, 250, 300); + initcol(C_STONE, 800, 800, 800); initcol(C_WOOD, 384, 244, 64); // dark cols initcol(C_DARKCYAN, 0, 500, 500); @@ -10783,9 +10800,6 @@ void handleinput(void) { drawscreen(); } break; - case CMD_SLOWWALK: // slowwalk - trysneak(player, D_NONE); - break; case CMD_REST: // wait addflag(player->flags, F_LASTCMD, NA, NA, NA, temp); if (count > 1) { @@ -10878,6 +10892,9 @@ void handleinput(void) { case CMD_COMMS: // communicate docomms(NULL); break; + case CMD_COMMSALL: // communicate + docommsmulti(); + break; case CMD_EAT: // eat doeat(player->pack); break; diff --git a/io.h b/io.h index f878039..c53e749 100644 --- a/io.h +++ b/io.h @@ -14,6 +14,7 @@ void animradial(cell_t *src, int radius, int ch, int colour, int dirtype, char * void animsky(cell_t *src, char ch, int colour); //void announceob(enum OBTYPE oid); void announcearrival(lifeform_t *lf, map_t *newmap); +void announcecomms(char ch, char *talkto, lifeform_t *lfarg, cell_t *cellarg); int announceflaggain(lifeform_t *lf, flag_t *f); int announceflagloss(lifeform_t *lf, flag_t *f); int announceobflaggain(object_t *o, flag_t *f); @@ -51,6 +52,7 @@ void describespell(objecttype_t *ot); void doattackcell(int dir); void doclose(void); void docomms(lifeform_t *target); +void docommslf(lifeform_t *lf, char ch, lifeform_t *lf2, cell_t *targc); void docomms_areainfo(char *who, flagpile_t *fp, lifeform_t *lf); void docomms_areadangers(char *who, flagpile_t *fp, lifeform_t *lf); void dodrop(obpile_t *op, int wantmulti, obpile_t *dst); @@ -103,6 +105,7 @@ void forceredraw(void); enum COLOUR getattrcolour(enum ATTRBRACKET brack); char getchoice(prompt_t *prompt); char getchoicestr(prompt_t *prompt, int useshortcuts, int showlallatstart); +int getcommsopts(char ch, char *talkto, lifeform_t **retlf, cell_t **retcell); int getkey(int escseqok); enum COLOUR getskilllevelcolour(enum SKILLLEVEL slev); void handle_ctrl_y(int arg); diff --git a/lf.c b/lf.c index f2012a5..ea2838c 100644 --- a/lf.c +++ b/lf.c @@ -516,7 +516,10 @@ int calcxp(lifeform_t *lf) { // extra 'spells' if (hasflag(lf->race->flags, F_INDUCEFEAR)) { - spells += 10; + spells += 5; + } + if (hasflag(lf->race->flags, F_DRAINONHIT)) { + spells += 15; } // TOTAL: @@ -2644,9 +2647,10 @@ int checkfordrowning(lifeform_t *lf, object_t *o) { } int check_rest_ok(lifeform_t *lf) { - if (!safetorest(lf)) { + enum ERROR why; + if (!safetorest(lf, &why)) { if (isplayer(lf)) { - switch (reason) { + switch (why) { case E_LEVITATING: msg("You cannot rest while levitating in mid-air!"); break; @@ -2671,7 +2675,7 @@ int check_rest_ok(lifeform_t *lf) { lifeform_t *clonelf(lifeform_t *src, cell_t *where) { lifeform_t *lf; - lf = addmonster(where, src->race->id, NULL, B_FALSE, 1, B_FALSE, NULL); + lf = addmonster(where, src->race->id, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (lf) { killflagsofid(lf->flags, F_XPVAL); addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL); @@ -3403,6 +3407,59 @@ void die(lifeform_t *lf) { } more(); drawmsg(); + if (!vaporised) { + flag_t *retflag[MAXCANDIDATES], *f; + int nretflags,i; + + + f = hasflag(lf->flags, F_REVIVETIMER); + if (f) { + race_t *r; + r = findrace(f->val[2]); + if (f) { + strcpy(reanimateas, r->name); + } + } + + if (!strlen(reanimateas)) { + // killed by a vampire = vampire. + if (killer) { + switch (killer->race->id) { + case R_VAMPIRE: sprintf(reanimateas, "vampire"); break; + default: break; + } + } + } + + // died by eating your own race = ghoul. + // died by eating a vampire corpse = vampire. + if (!strlen(reanimateas)) { + getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE); + for (i = 0; i < nretflags; i++) { + if (retflag[i]->val[2] == R_VAMPIRE) { + sprintf(reanimateas, "vampire"); + } else if (retflag[i]->val[2] == lf->race->baseid) { + sprintf(reanimateas, "ghoul"); + } + } + } + + // hecta-worshippers often get reanimated. + if (!strlen(reanimateas) && thisisplayer && godprayedto(R_GODDEATH)) { + if (onein(3)) { + switch (rnd(1,2)) { + case 1: + sprintf(reanimateas, "zombie"); + break; + case 2: + sprintf(reanimateas, "skeleton"); + break; + } + } + } + } + + // god effects... if (!vaporised) { god = getrandomprayedgod(); @@ -3968,53 +4025,16 @@ void die(lifeform_t *lf) { addflag(lf->flags, F_LIFEOB, NA, 5, 2, cid); } } else { - // Will the dead lf get reanimated up as a monster ? + // Will the dead player get reanimated up as a monster in bones files? // if (!vaporised) { - flag_t *retflag[MAXCANDIDATES]; - int nretflags,i; - // killed by a vampire = vampire. - if (killer) { - switch (killer->race->id) { - case R_VAMPIRE: sprintf(reanimateas, "vampire"); break; - default: break; - } - } - - // died by eating your own race = ghoul. - // died by eating a vampire corpse = vampire. - if (!strlen(reanimateas)) { - getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE); - for (i = 0; i < nretflags; i++) { - if (retflag[i]->val[2] == R_VAMPIRE) { - sprintf(reanimateas, "vampire"); - } else if (retflag[i]->val[2] == lf->race->baseid) { - sprintf(reanimateas, "ghoul"); - } - } - } - - // hecta-worshippers often get reanimated. - if (!strlen(reanimateas) && thisisplayer && godprayedto(R_GODDEATH)) { - if (onein(3)) { - switch (rnd(1,2)) { - case 1: - sprintf(reanimateas, "zombie"); - break; - case 2: - sprintf(reanimateas, "skeleton"); - break; - } - } - } - // announce if (strlen(reanimateas)) { if (thisisplayer || cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s%s corpse rises up as %s %s!^n", - C_MAGENTA, + getlfcol(player, CC_VBAD), lfname, getpossessive(lfname), needan(reanimateas) ? "an" : "a", reanimateas); } @@ -4034,7 +4054,7 @@ void die(lifeform_t *lf) { // UNLESS WE ARE _SURE_ IT IS THE PLAYER (ie. thisisplayer = true) if (createrace != R_NONE) { lifeform_t *newlf; - newlf = addmonster(where, createrace, NULL, B_FALSE, 1, B_FALSE, NULL); + newlf = addmonster(where, createrace, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (newlf) { // no xp for killing killflagsofid(newlf->flags, F_XPVAL); @@ -4047,7 +4067,7 @@ void die(lifeform_t *lf) { // add two new worms nearby, with less hp. for (i = 0;i < 2; i++) { lifeform_t *newlf; - newlf = addmonster(dividecell[i], dividerace, NULL, B_FALSE, 1, B_FALSE, NULL); + newlf = addmonster(dividecell[i], dividerace, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (newlf) { // half hp f = hasflag(newlf->flags, F_HITDICE); @@ -4090,7 +4110,7 @@ void die(lifeform_t *lf) { // remove the corpse... if (corpse) killob(corpse); // add the reanimated monster - addmonster(c, R_SPECIFIED, reanimateas, B_FALSE, 1, B_FALSE, NULL); + addmonster(c, R_SPECIFIED, reanimateas, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); } } @@ -4867,7 +4887,7 @@ int eat(lifeform_t *lf, object_t *o) { setlastdam(lf, "merging with a corpse"); lf->lastdamtype = DT_DIRECT; lf->hp = 0; - newlf = makezombie(o, 0, "rises from the dead"); + newlf = makezombie(o, 0, "rises from the dead", NULL); if (newlf) { addflag(newlf->flags, F_CORPSELF, R_LINGPARASITE, NA, NA, NULL); } @@ -10639,10 +10659,14 @@ enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker) { return selbp; } -race_t *getrandomcorpserace(cell_t *c) { +race_t *getrandomcorpserace(cell_t *c, enum LFSIZE wantsize) { condset_t cs; initcondv(&cs, CC_HASCORPSE, B_TRUE, NA, CC_NONE); + if (wantsize != SZ_ANY) { + addcond(&cs, CC_HASSIZE, B_TRUE, wantsize); + } + return getrandomrace(c, NA, &cs); } @@ -11405,6 +11429,8 @@ long getxpforlev(int level) { // 2.8 float multiplier = 13; float constant = 2.8; + + if (level <= 0) return 0; // no xp needed for level 1 /* @@ -13506,13 +13532,13 @@ int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype, enum INJUR // return TRUE on failure -int leveldrain(lifeform_t *lf, int amt, int power, lifeform_t *fromlf) { +int leveldrain(lifeform_t *lf, int amt, enum CHECKTYPE sctype, int scdiff, lifeform_t *fromlf) { int resisted = B_FALSE; if (isimmuneto(lf->flags, DT_NECROTIC, B_FALSE)) resisted = B_TRUE; if (!resisted && isresistantto(lf->flags, DT_NECROTIC, B_FALSE) && onein(2)) resisted = B_TRUE; // fit check - if (!resisted && skillcheck(lf, SC_CON, 100+(power*6), 0 )) { + if (!resisted && skillcheck(lf, sctype, scdiff, 0 )) { resisted = B_TRUE; } if (resisted) { @@ -14975,7 +15001,6 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { assert(!cell->type->solid); } - m = cell->map; // add to the end of the list @@ -15564,7 +15589,7 @@ void makepeaceful(lifeform_t *who, lifeform_t *causedby) { // 'power' is 0-10, determines how poewrful the zombie is. // if 0, don't do any special changes. -lifeform_t *makezombie(object_t *o, int power, char *description) { +lifeform_t *makezombie(object_t *o, int power, char *description, lifeform_t *master) { flag_t *f; race_t *r; lifeform_t *lf; @@ -15671,6 +15696,10 @@ lifeform_t *makezombie(object_t *o, int power, char *description) { msg("^W%s %s!", obname, description); } + if (master) { + petify(lf, master); + } + return lf; } @@ -17364,7 +17393,7 @@ void loselevel(lifeform_t *lf, int amt, lifeform_t *fromlf) { statdirty = B_TRUE; drawstatus(); wrefresh(statwin); - msg("^%cWelcome back to level %d.",getlfcol(lf, CC_BAD), lf->level); + msg("^%cWelcome back to level %d.",getlfcol(lf, CC_VBAD), lf->level); } } @@ -17486,6 +17515,107 @@ void magicwoods_angry(lifeform_t *who) { } } +// make a list of choices for player to talk to a lf +// +// 'multiple' means 'we're talking to multiple people,not just one' +void makecommslist(prompt_t *p, lifeform_t *lf, int multiple) { + flag_t *f; + object_t *godstone; + enum ATTRBRACKET iqb; + iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); + // are they friendly? + if (ispetof(lf, player)) { + if ((iqb >= IQ_ANIMAL) || isundead(lf)) { + if (!haschoice(p, 'a')) addchoice(p, 'a', "Attack something", NULL, NULL, NULL); + } + + if (!isadjacent(lf->cell, player->cell)) { + if (!haschoice(p, 'c')) addchoice(p, 'c', "Come here", NULL, NULL, NULL); + } + + if (!haschoice(p, 'g')) addchoice(p, 'g', "Go somewhere", NULL, NULL, NULL); + + if (isadjacent(lf->cell, player->cell) && !lfhasflag(lf, F_NOPACK) && !multiple) { + if (!haschoice(p, 't')) addchoice(p, 't', "Trade items with me", NULL, NULL, NULL); + } + + if (iqb >= IQ_ANIMAL) { + f = isresting(lf); + if (f) { + if (!haschoice(p, 'r')) addchoice(p, 'r', "Stop resting.", NULL, NULL, NULL); + } else { + if (!haschoice(p, 'r')) addchoice(p, 'r', "Rest until you are healed.", NULL, NULL, NULL); + } + if (!haschoice(p, '"')) addchoice(p, '<', "Stay close.", NULL, NULL, NULL); + if (!haschoice(p, '"')) addchoice(p, '>', "Keep your distance.", NULL, NULL, NULL); + } + } else if (ishirable(lf) && !multiple) { + if (lfhasflag(lf, F_ISPRISONER)) { + if (!haschoice(p, 'j')) addchoice(p, 'j', "Join me, and I will help you escape.", NULL, NULL, NULL); + } else if (getskill(player, SK_SPEECH) >= PR_EXPERT) { + if (!haschoice(p, 'j')) addchoice(p, 'j', "Join me on my quest!", NULL, NULL, NULL); + } + } + + if (!isgod(lf) && ispeaceful(lf) && cantalk(lf) && !multiple) { + enum SKILLLEVEL slev; + job_t *j; + slev = getskill(player, SK_SPEECH); + // same race or job? + j = getjob(player); + if ((slev != PR_MASTER) && j && (j == getjob(lf))) slev++; + if ((slev != PR_MASTER) && (player->race->id == lf->race->id)) slev++; + if (slev >= PR_NOVICE) { + if (!haschoice(p, 'x')) addchoice(p, 'x', "Any dangers nearby that I should look out for?", NULL, NULL, NULL); + } + if (slev >= PR_BEGINNER) { + if (!haschoice(p, 'i')) addchoice(p, 'i', "What can you tell me about this area?", NULL, NULL, NULL); + } + if (!areallies(player, lf)) { + if (slev >= PR_SKILLED) { + if (!haschoice(p, 'k')) addchoice(p, 'k', "Care to trade knowledge?", NULL, NULL, NULL); + } + } + } + + if (isadjacent(lf->cell, player->cell) && !multiple) { + if (areenemies(player, lf)) { + if (!haschoice(p, 'm')) addchoice(p, 'm', "Have mercy!", NULL, NULL, NULL); + } + // if you are allies, use 'trade items' instead + if (!areallies(player, lf)) { + if (isgod(lf)) { + // may only donate the godstone + godstone = hasob(player->pack, getopposinggodstone(lf->race->id)); + if (godstone) { + char buf[BUFLEN],obname[BUFLEN]; + getobname(godstone, obname, 1); + sprintf(buf, "(offer %s)", obname); + if (!haschoice(p, 'o')) addchoice(p, 'o', buf, NULL, NULL, NULL); + } + } else { + if (!haschoice(p, 'o')) addchoice(p, 'o', "(offer a bribe)", NULL, NULL, NULL); + } + } + } + + /* + f = lfhasflag(lf, F_OWNSSHOP); + if (f) { + int shopid; + shopid = f->val[0]; + moneyowing = getowing(player, shopid, NULL); + if (moneyowing > 0) { + snprintf(buf, BUFLEN, "(pay $%d to the shopkeeper)",moneyowing); + addchoice(p, 'p', buf, NULL, NULL, NULL); + } + } + */ + + if (!multiple && !haschoice(p, 'y')) addchoice(p, 'y', "Yeeeeeaaaargh!", NULL, NULL, NULL); + if (!haschoice(p, 'n')) addchoice(p, 'n', "(nothing)", NULL, NULL, NULL); +} + void makefriendly(lifeform_t *who, int howlong) { char lfname[BUFLEN]; @@ -19921,12 +20051,12 @@ int rollstat(lifeform_t *lf, enum ATTRIB attr) { } // safe to rest? -int safetorest(lifeform_t *lf) { +int safetorest(lifeform_t *lf, enum ERROR *why) { lifeform_t *l; - reason = E_OK; + if (why) *why = E_OK; if (lfhasflag(lf, F_STASIS)) { - reason = E_STASIS; + if (why) *why = E_STASIS; return B_FALSE; } @@ -19946,7 +20076,7 @@ int safetorest(lifeform_t *lf) { } if (monsternearby) { - reason = E_MONSTERNEARBY; + if (why) *why = E_MONSTERNEARBY; return B_FALSE; } } @@ -19954,7 +20084,7 @@ int safetorest(lifeform_t *lf) { if (getlftemp(lf) <= T_VCOLD) { if (!isimmuneto(lf->flags, DT_COLD, B_FALSE) && !isresistantto(lf->flags, DT_COLD, B_FALSE)) { - reason = E_TOOCOLD; + if (why) *why = E_TOOCOLD; return B_FALSE; } } @@ -19969,6 +20099,7 @@ int safetorest(lifeform_t *lf) { f = lfhasflag(lf, F_TURNSINPEACE); if (!f || (f->val[0] < timeneeded)) { + if (why) *why = E_TOOSOON; return B_FALSE; } } @@ -22566,7 +22697,7 @@ void startlfturn(lifeform_t *lf) { // create the monster directly behind you if (monisreal) { // set autogen to false, we don't want minions appearing too. - mon = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL); + mon = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); } else { mon = addlf(c, r->id, 1); } @@ -23025,8 +23156,33 @@ void startlfturn(lifeform_t *lf) { practice(lf, SK_ENGINEERING, 1); } } - } + // object which is really a monster + f = hasflag(o->flags, F_ISMONSTER); + if (f && (f->val[2] != NA)) { + int diff; + diff = f->val[2] + distmod; + if (skillcheck(lf, SC_SEARCH, diff, mod)) { + lifeform_t *newlf; + // reveal it + newlf = reveal_pretendob(o); + if (newlf && cansee(player, newlf)) { + char obname[BUFLEN]; + char lfname[BUFLEN]; + getobname(o, obname, o->amt); + capitalise(obname); + getlfnamea(newlf, lfname); + msg("^wHey! %s %s really %s!",obname, (o->amt == 1) ? "is" : "are",lfname); + more(); + interrupt(lf); + needredraw = B_TRUE; + drawscreen(); + // train skills + practice(lf, SK_PERCEPTION, 1); + } + continue; + } + } } } } @@ -24118,7 +24274,7 @@ lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, char *ra lifeform_t *newlf = NULL; char buf[BUFLEN]; - newlf = addmonster(c, rid, racename, B_TRUE, 1, B_FALSE, NULL); + newlf = addmonster(c, rid, racename, B_TRUE, 1, B_FALSE, B_NOEXTRA, NULL); if (newlf) { if (haslos(player, c)) { //char *newbuf; @@ -24790,6 +24946,32 @@ int real_touch(lifeform_t *lf, object_t *o, int onpurpose) { return B_FALSE; } +lifeform_t *reveal_pretendob(object_t *o) { + enum RACE rid; + flag_t *f; + cell_t *c; + lifeform_t *lf; + c = getoblocation(o); + f = hasflag(o->flags, F_ISMONSTER); + assert(f); + rid = f->val[0]; + + addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); + + lf = addmonster(c, rid, NULL, B_TRUE, 1, B_FALSE, B_FALSE, NULL); + if (lf) { + f = lfhasflag(lf, F_PRETENDSTOBE); + if (f) { + char obname[BUFLEN]; + // replace text + getobnametruebase(o, obname, 1); + free(f->text); + f->text = strdup(obname); + } + } + return lf; +} + void turntoface(lifeform_t *lf, cell_t *dstcell) { if (isdead(lf)) return; if (lfhasflag(lf, F_AUTOROTATE)) return; diff --git a/lf.h b/lf.h index a9eae79..d58de10 100644 --- a/lf.h +++ b/lf.h @@ -281,7 +281,7 @@ int getracerarity(enum HABITAT hab, enum RACE rid, enum RARITY *rr); object_t *getrandomarmour(lifeform_t *lf, lifeform_t *attacker); enum BEHAVIOUR getrandombehaviour(void); enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker); -race_t *getrandomcorpserace(cell_t *c); +race_t *getrandomcorpserace(cell_t *c, enum LFSIZE wantsize); job_t *getrandomjob(int onlyplayerjobs); int getrandommonlevel(race_t *r, map_t *m); race_t *getrandomrace(cell_t *c, int forcedepth, condset_t *cs); @@ -336,7 +336,7 @@ flag_t *hasname(lifeform_t *lf); int hassoul(lifeform_t *lf); void inc_quad_range(enum QUADRANT *start, enum QUADRANT *end, int howmuch); int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype, enum INJURY forcetype); -int leveldrain(lifeform_t *lf, int amt, int power, lifeform_t *fromlf); +int leveldrain(lifeform_t *lf, int amt, enum CHECKTYPE sctype, int scdiff, lifeform_t *fromlf); int lfcanbekod(lifeform_t *lf); int lfcanbestoned(lifeform_t *lf); flag_t *lfhasflag(lifeform_t *lf, enum FLAG fid); @@ -432,13 +432,14 @@ void loselevel(lifeform_t *lf, int amt, lifeform_t *fromlf); void loseskill(lifeform_t *lf, enum SKILL skid); void magicwoods_angry(lifeform_t *who); void magicwoods_warn(lifeform_t *who); +void makecommslist(prompt_t *p, lifeform_t *lf, int multiple); void makefriendly(lifeform_t *lf, int howlong); void makeheard(lifeform_t *listener, lifeform_t *noisemaker, int showglyph, char *noisetext, int howlong); int makelearnable(lifeform_t *lf, enum SKILL skid); int makenauseated(lifeform_t *lf, int amt, int howlong, enum ERROR *why); void makenoise(lifeform_t *lf, enum NOISETYPE nid); void makepeaceful(lifeform_t *lf, lifeform_t *causedby); -lifeform_t *makezombie(object_t *o, int power, char *description); +lifeform_t *makezombie(object_t *o, int power, char *description, lifeform_t *master); void mayusespellschool(flagpile_t *fp, enum SPELLSCHOOL ss, enum FLAG how, int overridepower); int meetsallattreqs(lifeform_t *lf, object_t *o); int meetsattreq(lifeform_t *lf, flag_t *f, object_t *o, int *modpct); @@ -487,7 +488,7 @@ int startclimbing(lifeform_t *lf); int startresting(lifeform_t *lf, int willtrain); int rollattr(enum ATTRBRACKET bracket); int rollstat(lifeform_t *lf, enum ATTRIB attr); -int safetorest(lifeform_t *lf); +int safetorest(lifeform_t *lf, enum ERROR *why); int say(lifeform_t *lf, char *text, int volume); int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *text, lifeform_t *talkingto); int scare(lifeform_t *lf, lifeform_t *scarer, int howlong, int scarerbonus); @@ -537,6 +538,7 @@ int tradeknowledge(lifeform_t *lf); int tryclimb(lifeform_t *lf, cell_t *where, char *towhat, int onpurpose); int touch(lifeform_t *lf, object_t *o); int real_touch(lifeform_t *lf, object_t *o, int onpurpose); +lifeform_t *reveal_pretendob(object_t *o); void turntoface(lifeform_t *lf, cell_t *dstcell); void unequipeffects(lifeform_t *lf, object_t *o); void unpoison(lifeform_t *lf); diff --git a/map.c b/map.c index 7639ed6..6aa5f77 100644 --- a/map.c +++ b/map.c @@ -21,6 +21,8 @@ int enteringmap = B_FALSE; +extern void *rdata; + extern habitat_t *firsthabitat,*lasthabitat; extern job_t *firstjob; extern map_t *firstmap,*lastmap; @@ -251,21 +253,27 @@ map_t *addmap(void) { // if "rid" R_SPECIFIED, parse racename to get the race. // rid can also be R_RANDOM. // otherwise just use the given race. -lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok, int amt, int autogen, int *nadded) { +lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok, int amt, int autogen, int allowextras, int *nadded) { lifeform_t *lf = NULL; race_t *r; int db = B_FALSE; flagpile_t *wantflags = NULL; enum JOB wantjob = J_NONE; enum BEHAVIOUR wantbehaviour = BH_NONE; + enum ERROR why; if (nadded) *nadded = 0; /*if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); */ // ie. don't create mosnters on closed doors! - if (!cellwalkable(NULL, c, NULL)) { - return NULL; + if (!cellwalkable(NULL, c, &why)) { + object_t *o; + o = (object_t *)rdata; + if ((why == E_OBINWAY) && o && hasflag(o->flags, F_ISMONSTER)) { + } else { + return NULL; + } } wantflags = addflagpile(NULL, NULL); @@ -450,50 +458,50 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok // appears in groups? if (db) dblog("handling groups"); - if (autogen) { + if (autogen && allowextras) { f = hasflag(lf->flags, F_NUMAPPEAR); if (f) { // override amount amt = rnd(f->val[0], f->val[1]); } - if (amt > 1) { - int idx = 0; - cell_t *adjcell; - amt--; // we've already added one + } + if (amt > 1) { + int idx = 0; + cell_t *adjcell; + amt--; // we've already added one - //adjcell = c; - for ( ; amt > 0; amt--, idx++) { - lifeform_t *newlf; - // find an adjacent cell to one of the newly added monsters, - // starting with the first one - adjcell = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, NULL); - // did we find one? - if (!adjcell) break; + //adjcell = c; + for ( ; amt > 0; amt--, idx++) { + lifeform_t *newlf; + // find an adjacent cell to one of the newly added monsters, + // starting with the first one + adjcell = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, NULL); + // did we find one? + if (!adjcell) break; - newlf = addlf(adjcell, r->id, getrandommonlevel(r, adjcell->map)); - if (!newlf) { - break; - } - if (nadded) (*nadded)++; - newlf->born = B_FALSE; - - - finalisemonster(newlf, lf, wantflags, idx, BH_NONE); - - newlf->born = B_TRUE; - - // match alignment - setalignment(newlf, getalignment(lf)); - // match hostility - if (lfhasflag(lf, F_HOSTILE)) { - if (!lfhasflag(newlf, F_HOSTILE)) addflag(newlf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); - } else { - killflagsofid(newlf->flags, F_HOSTILE); - } - - // initial monster should remember its minions - addflag(lf->flags, F_MINION, newlf->id, NA, NA, NULL); + newlf = addlf(adjcell, r->id, getrandommonlevel(r, adjcell->map)); + if (!newlf) { + break; } + if (nadded) (*nadded)++; + newlf->born = B_FALSE; + + + finalisemonster(newlf, lf, wantflags, idx, BH_NONE); + + newlf->born = B_TRUE; + + // match alignment + setalignment(newlf, getalignment(lf)); + // match hostility + if (lfhasflag(lf, F_HOSTILE)) { + if (!lfhasflag(newlf, F_HOSTILE)) addflag(newlf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + } else { + killflagsofid(newlf->flags, F_HOSTILE); + } + + // initial monster should remember its minions + addflag(lf->flags, F_MINION, newlf->id, NA, NA, NULL); } } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging @@ -501,7 +509,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok // minons? // appears in groups? if (db) dblog("handling minions"); - if (autogen) { + if (autogen && allowextras) { f = hasflag(lf->flags, F_MINIONS); if (f) { if (rnd(1,100) <= f->val[0]) { @@ -636,7 +644,7 @@ int addrandomthing(cell_t *c, int obchance, int *nadded) { lifeform_t *lf; // monster //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - lf = addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, nadded); + lf = addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, B_ALLOWEXTRA, nadded); if (lf) { rv = TT_MONSTER; } @@ -2606,7 +2614,7 @@ int fix_unreach_via_doors(map_t *m) { setcelltype(c, getmapempty(m)); makedoor(c, 0); setcellreason(c, "making door to link seperated areas"); - dblog(" successfully fixed by creating a door."); + dblog(" successfully fixed by creating a door at %d,%d.",c->x, c->y); return 1; } } @@ -4527,7 +4535,7 @@ void createheaven(map_t *map, int depth, map_t *parentmap, int exitdir, object_t c = map->cell[i]; } // place god - lf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL); + lf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); assert(lf); // add to god list godlf[ngodlfs++] = lf; @@ -4964,7 +4972,8 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex c = getcell_cond(map, &ccwalkableroom); if (!c) c = getrandomcell(map); c = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); - addmonster(c, R_SPECIFIED, thing[i]->what, B_FALSE, thing[i]->value, B_TRUE, NULL); + addmonster(c, R_SPECIFIED, thing[i]->what, B_FALSE, thing[i]->value, B_TRUE, + B_ALLOWEXTRA, NULL); break; case RT_BRANCHLINK: if (db) dblog(" adding branchlink"); @@ -9850,7 +9859,7 @@ void mapentereffects(map_t *m) { //c = getrandomroomcell(m, m->room[i].id, WE_WALKABLE); c = getcell_cond(m, &cs); if (c && !hasobflagwithin(c, F_STAIRS, 15, DT_COMPASS)) { - addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, NULL); + addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, B_ALLOWEXTRA, NULL); } } } diff --git a/map.h b/map.h index 5787df8..2581c3e 100644 --- a/map.h +++ b/map.h @@ -4,7 +4,7 @@ cell_t *addcell(map_t *map, int x, int y); habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell, int thingchance, int obchance, int vaultchance, int maxvisrange, enum OBTYPE upstairtype, enum OBTYPE downstairtype, int stairsinrooms, enum TEMPERATURE temp); void addhomeobs(lifeform_t *lf, int dolevelobs); map_t *addmap(void); -lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok, int amt, int autogen, int *nadded); +lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok, int amt, int autogen, int allowextras, int *nadded); object_t *addrandomob(cell_t *c); int addrandomthing(cell_t *c, int obchance, int *nadded); region_t *addregion(enum BRANCH rtype, region_t *parent, int outlineid, int depthmod, int createdby); diff --git a/move.c b/move.c index c0ab008..56d87de 100644 --- a/move.c +++ b/move.c @@ -3286,6 +3286,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { if (door) { msg("^gThere seems to be a secret door here!"); killflagsofid(door->flags, F_SECRET); + needredraw = B_TRUE; } } //if (isblind(lf) || !haslos(lf, cell)) { @@ -3366,6 +3367,24 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { break; case E_OBINWAY: inway = (object_t *)rdata; + // walked into someone who was feigning death? + if (inway && hasflag(inway->flags, F_ISMONSTER)) { + lifeform_t *newlf; + if (haslos(lf, cell)) { + char inwayname[BUFLEN]; + getobname(inway, inwayname, 1); + msg("^B%s starts to move!", inwayname); + more(); + } + // reaveal + newlf = reveal_pretendob(inway); + if (newlf) { + turntoface(newlf, lf->cell); + aiattack(newlf, lf, aigetchasetime(newlf)); + } + if (onpurpose || fleeing) taketime(lf, getmovespeed(lf)); + return B_FALSE; + } // can we push this? if (ispushable(inway)) { if (canpush(lf, inway, dir)) { @@ -3764,6 +3783,13 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { return B_FALSE; } + // mosnters will never move on top of other monsters + // pretending to be objects (ie. gargoyles) + if (hasobwithflag(cell->obpile, F_ISMONSTER)) { + if (error) *error = E_WONT; + return B_FALSE; + } + if (celldangerous(lf, cell, B_TRUE, error)) { // if lfs are fleeing and have below average wisdom, // they will walk onto dangerous cells diff --git a/nexus.c b/nexus.c index 4f5bf7e..689f025 100644 --- a/nexus.c +++ b/nexus.c @@ -307,6 +307,7 @@ int main(int argc, char **argv) { addtempflag(player->flags, F_CANWILL, OT_A_CHECKSTAIRS, NA, NA, NULL, FROMGAMESTART); addtempflag(player->flags, F_CANWILL, OT_A_PRAY, NA, NA, NULL, FROMGAMESTART); addtempflag(player->flags, F_CANWILL, OT_A_TRAIN, NA, NA, NULL, FROMGAMESTART); + addtempflag(player->flags, F_CANWILL, OT_A_TIPTOE, NA, NA, NULL, FROMGAMESTART); addtempflag(player->flags, F_CANWILL, OT_A_DEBUG, NA, NA, NULL, FROMGAMESTART); ///////// // make the initial level @@ -1932,6 +1933,7 @@ int roll(char *string) { int rolldie(int ndice, int sides) { int i; int res = 0; + if (sides <= 0) return 0; for (i = 0; i < ndice; i++) { res += rnd(1,sides); } diff --git a/objects.c b/objects.c index a9c5c9b..f0315fd 100644 --- a/objects.c +++ b/objects.c @@ -1571,6 +1571,14 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum } } + // hiding monsters? + f = hasflag(o->flags, F_ISMONSTER); + if (f) { + object_t *oo; + oo = addobfast(o->contents, f->val[1]); + assert(oo); + } + // extra chance of bone items being cursed if (o->type->material->id == MT_BONE) { if (pctchance(15)) { @@ -1966,7 +1974,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum if (!corpserace || hasflag(corpserace->flags, F_NOCORPSE)) { // random one. - corpserace = getrandomcorpserace(NULL); + corpserace = getrandomcorpserace(NULL, wantarmsize); } o->weight = corpserace->weight; @@ -2027,13 +2035,13 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum if (!corpserace) { cell_t *where; where = getoblocation(o); - // select random race + // select random race, or correct size. if (where) { - corpserace = getrandomcorpserace(where); + corpserace = getrandomcorpserace(where, wantarmsize); } if (!corpserace) { // ie. vending machine, or inside another object/fake cell? - corpserace = getrandomcorpserace(NULL); + corpserace = getrandomcorpserace(NULL, wantarmsize); } if (corpserace->id != corpserace->baseid) corpserace = findrace(corpserace->baseid); } @@ -2090,7 +2098,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum } } else if (o->type->id == OT_JERKY) { if (!corpserace) { - corpserace = getrandomcorpserace(NULL); + corpserace = getrandomcorpserace(NULL, wantarmsize); if (corpserace->id != corpserace->baseid) corpserace = findrace(corpserace->baseid); } addflag(o->flags, F_LINKRACE, corpserace->id, NA, NA, NULL); @@ -2099,7 +2107,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, enum if (!corpserace || hasflag(corpserace->flags, F_NOCORPSE)) { // random one. - corpserace = getrandomcorpserace(NULL); + corpserace = getrandomcorpserace(NULL, wantarmsize); if (corpserace->id != corpserace->baseid) corpserace = findrace(corpserace->baseid); } @@ -5503,6 +5511,11 @@ char *getobdesc(object_t *o, char *buf) { int blind = B_FALSE; int known = B_FALSE; if (gamemode == GM_GAMESTARTED) { + if (hasflag(o->flags, F_ISMONSTER)) { + if (o->contents->first) { + o = o->contents->first; + } + } // can't see the object ? if (o->pile->owner == player) { if (!haslos(player, player->cell) || isblind(player)) { @@ -5814,6 +5827,13 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan where = getoblocation(o); + if (hasflag(o->flags, F_ISMONSTER)) { + if (o->contents->first) { + o = o->contents->first; + } + } + + f = hasflag(o->flags, F_TRAIL); if (f) { race_t *r = NULL; @@ -10465,7 +10485,7 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { where = getrandomadjcell(lf->cell, &ccwalkable, B_ALLOWEXPAND); if (where) { - mon = addmonster(where, f->val[0], NULL, B_TRUE, 1, B_FALSE, NULL); + mon = addmonster(where, f->val[0], NULL, B_TRUE, 1, B_FALSE, B_NOEXTRA, NULL); } if (mon) { char monname[BUFLEN]; @@ -10612,7 +10632,7 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { case 0: // butterflies around user willid = B_TRUE; where = getrandomadjcell(lf->cell, &ccwalkable, B_ALLOWEXPAND); - addmonster(where, R_BUTTERFLY, NULL, B_FALSE, rnd(10,20), B_FALSE, NULL); + addmonster(where, R_BUTTERFLY, NULL, B_FALSE, rnd(10,20), B_FALSE, B_NOEXTRA, NULL); if (haslos(player, where)) { msg("A swarm of butterflies appears!"); } @@ -12383,7 +12403,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE break; case OT_POT_EXPERIENCE: if (potblessed == B_CURSED) { - leveldrain(lf, 1, 99, NULL); + leveldrain(lf, 1, SC_CON, 999, NULL); } else { // gain xp! if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, 0)) { @@ -14281,6 +14301,17 @@ int real_takedamage(object_t *o, int howmuch, int damtype, int wantannounce, lif return 0; } + // monster pretending to be object? + if (hasflag(o->flags, F_ISMONSTER)) { + lifeform_t *newlf; + newlf = reveal_pretendob(o); + if (newlf && attacker) { + turntoface(newlf, attacker->cell); + aiattack(newlf, attacker, aigetchasetime(newlf)); + } + return 0; + } + // update lastdamtype f = hasflag(o->flags, F_LASTDAMTYPE); if (f) { @@ -15420,6 +15451,26 @@ void timeeffectsob(object_t *o) { killflagsofid(o->flags, F_FILLPOT); } + // mosnters pretending to be objects? + if (onground && hasflag(o->flags, F_ISMONSTER)) { + f = hasflag(o->flags, F_ISMONSTER); + if (strlen(f->text)) { + int dist; + dist = atoi(f->text); + + // player within reveal distance? + if ((getcelldist(player->cell, location) <= dist) && !haslos(player, location)) { + lifeform_t *newlf; + newlf = reveal_pretendob(o); + if (newlf) { + turntoface(newlf, player->cell); + aiattack(newlf, player, aigetchasetime(newlf)); + } + return; + } + } + } + // expire flags timeeffectsflags(o->flags); checkflagpile(o->flags); @@ -16010,7 +16061,7 @@ void timeeffectsob(object_t *o) { contagious = B_TRUE; } // turn into a zombified version of itself. - lf = makezombie(o, 10, revivetext); + lf = makezombie(o, 10, revivetext, lf); // this will do the announcement if (lf && contagious) { addflag(lf->flags, F_HITCONFER, F_REVIVETIMER, SC_POISON, 165, NULL); addflag(lf->flags, F_HITCONFERVALS, 0, 1, R_ZOMBIECON, "rises up as a zombie!"); @@ -16018,7 +16069,7 @@ void timeeffectsob(object_t *o) { addflag(lf->flags, F_HATESALL, B_TRUE, NA, NA, NULL); } else { // revive! - lf = addmonster(lfloc, f->val[2], NULL, B_FALSE, 1, B_FALSE, NULL); + lf = addmonster(lfloc, f->val[2], NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); // gain flags form corpse copyflag(lf->flags, o->flags, F_CANWILL); copyflag(lf->flags, o->flags, F_CANCAST); @@ -16029,11 +16080,11 @@ void timeeffectsob(object_t *o) { // corpse vanishes removeob(o, o->amt); - } - // announce - if (haslos(player, lfloc) || haslos(player, obloc)) { - msg("^W%s %s!^n", obname, revivetext); - interrupt(player); + // announce + if (haslos(player, lfloc) || haslos(player, obloc)) { + msg("^W%s %s!^n", obname, revivetext); + interrupt(player); + } } free(revivetext); return; @@ -16838,6 +16889,11 @@ int validateobs(void) { } } + if (hasflag(ot->flags, F_ISMONSTER) && !hasflag(ot->flags, F_IMPASSABLE)) { + printf("ERROR in object '%s' - objects with f_ismonster MUST also have f_impassable too!\n", ot->name); + goterror = B_TRUE; + } + // remember buildings if (ot->obclass->id == OC_BUILDING) { if (hasflag(ot->flags, F_RARITY)) { diff --git a/spell.c b/spell.c index ea8420b..622ebe9 100644 --- a/spell.c +++ b/spell.c @@ -2720,6 +2720,20 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // do the first one right away rest(user, B_TRUE); } + } else if (abilid == OT_A_TIPTOE) { + if (isswimming(user)) { + if (isplayer(user)) msg("You can't tiptoe while swimming!"); + return B_TRUE; + } + if (isairborne(user, NULL)) { + if (isplayer(user)) msg("You can't tiptoe while airborne!"); + return B_TRUE; + } + if (isclimbing(user)) { + if (isplayer(user)) msg("You can't tiptoe while climbing!"); + return B_TRUE; + } + trysneak(user, D_NONE); } else if (abilid == OT_A_TRIPLF) { object_t *wep; int skillmod = 0; @@ -3719,7 +3733,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } if (isplayer(user)) { - if (!safetorest(user)) { + if (!safetorest(user, NULL)) { if (getattrbracket(getattr(user, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { if (askchar("Really try to hide while in view of enemies?", "yn", "n", B_TRUE, B_FALSE) != 'y') { return B_TRUE; @@ -4387,9 +4401,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int sel,n; sel = rnd(0,nposs-1); o = poss[sel]; - newlf = makezombie(o, power, "rises from the dead"); + newlf = makezombie(o, power, "rises from the dead", target); if (newlf) { - petify(newlf, target); donesomething = B_TRUE; } // remove from list @@ -4482,7 +4495,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // add it! getobname(o, obname, 1); removeob(o, ALL); - lf = addmonster(targcell, f->val[0], NULL, B_FALSE, 1, B_FALSE, NULL); + lf = addmonster(targcell, f->val[0], NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); setlfmaterial(lf, MT_STONE, B_FALSE); if (cansee(player, lf)) { msg("%s comes to life!",obname); @@ -4528,7 +4541,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } getobname(o, obname, 1); removeob(o, ALL); - lf = addmonster(targcell, rid, NULL, B_FALSE, 1, B_FALSE, NULL); + lf = addmonster(targcell, rid, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (cansee(player, lf)) { msg("%s comes to life!",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -11420,6 +11433,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (spellid == OT_S_REVEALHIDDEN) { int i; int seen = B_FALSE; + + if (!isplayer(caster)) return B_TRUE; + if (!target) target = caster; for (i = 0 ; i < target->nlos; i++ ){ targcell = target->los[i]; @@ -11434,9 +11450,19 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ char obname[BUFLEN]; killflag(f); getobname(o, obname, o->amt); - msg("%s is magically revealed!", obname); + msg("^G%s is magically revealed!", obname); seen = B_TRUE; } + if (hasflag(o->flags, F_ISMONSTER)) { + lifeform_t *newlf; + newlf = reveal_pretendob(o); + if (newlf) { + char lfname[BUFLEN]; + getlfnamea(newlf, lfname); + msg("^G%s is magically revealed!", lfname); + seen = B_TRUE; + } + } } } @@ -15283,7 +15309,7 @@ int summonlfs(lifeform_t *caster, cell_t *where, enum RACE wantrace, enum RACECL r = findrace(poss[rnd(0,nposs-1)]); if (r) { // add it! - newlf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL); + newlf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); // not worth any xp killflagsofid(newlf->flags, F_XPVAL); addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL); diff --git a/text.c b/text.c index dac44a0..f3ffaf5 100644 --- a/text.c +++ b/text.c @@ -1012,6 +1012,7 @@ char *getcolname(enum COLOUR c) { case C_CARPET2: return "carpet2"; case C_METAL: return "metal"; case C_SMOKE: return "smoke"; + case C_STONE: return "stone"; case C_WOOD: return "wood"; case C_DARKCYAN: return "darkcyan"; case C_DARKBLUE: return "darkblue"; diff --git a/vault.c b/vault.c index 8289c34..4e51ee5 100644 --- a/vault.c +++ b/vault.c @@ -416,7 +416,7 @@ int addvaultthing(cell_t *c, vault_t *v, enum VAULTTHING vt, char *what) { break; case VT_LF: - lf = addmonster(c, R_SPECIFIED, what, B_TRUE, 1, B_TRUE, NULL); + lf = addmonster(c, R_SPECIFIED, what, B_TRUE, 1, B_TRUE, B_NOEXTRA, NULL); if (!lf) { dblog("invalid racename '%s' in vault %s", what, v->id); msg("invalid racename '%s' in vault %s", what, v->id);