diff --git a/ai.c b/ai.c index cd564d7..523cd84 100644 --- a/ai.c +++ b/ai.c @@ -525,6 +525,623 @@ flag_t *aigoto(lifeform_t *lf, cell_t *c, enum MOVEREASON why, void *data, int t return f; } +int ai_attack_existing_target(lifeform_t *lf) { + lifeform_t *target; + int db = B_FALSE; + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + // do we already have a target we are attacking? + target = gettargetlf(lf); + if (!target) return B_FALSE; + + if (db) dblog(".oO { i have a target: lfid %d (%s). }", target->id, target->race->name); + + // target dead or unconscious? + if (isdead(target) || isunconscious(target)) { + if (db) dblog(".oO { my target is dead/ko'd }", target->id, target->race->name); + loseaitargets(lf); + if (areallies(lf, player) && cantalk(lf)) { + char text[BUFLEN]; + real_getlfname(target, text, B_FALSE); + sayphrase(lf, SP_ALLY_TARGETKILL, SV_SHOUT, NA, text); + } + } else { + // aquatic grabbers will try to drag their prey into the water + if (lfhasflagval(lf, F_GRABBING, target->id, NA, NA, NULL) && isaquatic(lf) ) { + if ( hasobwithflag(lf->cell->obpile, F_DEEPWATER) && + !hasobwithflag(target->cell->obpile, F_DEEPWATER)) { + // move away! + if (!moveawayfrom(lf, target->cell, DT_ORTH, B_FALSE, B_TRUE)) { + return B_TRUE; + } + } + } + // try to move towards them. + if (!aimovetolf(lf, target, B_TRUE)) { + // success + return B_TRUE; + } + } + return B_FALSE; +} + +// i am bored - look for something to do! +int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { + int db = B_FALSE,n,i; + lifeform_t *newtarget = NULL; + flag_t *retflag[MAXCANDIDATES]; + int nretflags = 0; + flag_t *f; + + // not attacking anyone in particular + if (db) dblog(".oO { i do not have a target or can't move towards it. }"); + + // shopkeepers will return to their shops + if (hasjob(lf, J_SHOPKEEPER)) { + f = lfhasflag(lf, F_OWNSSHOP); + if (f) { + int myshop; + cell_t *where; + myshop = f->val[0]; + // find the closest cell of my shop + where = getclosestroomcell(lf, myshop); + // move towards my shop. note that if the player leaves then + // re-enters this map, they will find that we have instantly + // teleported there (see mapentereffects). + if (aigoto(lf, where, MR_OTHER, NULL, PERMENANT)) { + // success + return B_TRUE; + } + } + } + + if (!lfhasflag(lf, F_STUNNED)) { + lifeform_t *hateposs[MAXCANDIDATES],*poss[MAXCANDIDATES]; + int nposs = 0, nhateposs = 0; + if (db) dblog(".oO { looking for a target . }"); + + // look for any hated lfs or enemies + newtarget = NULL; + for (n = 0; n < lf->nlos; n++) { + lifeform_t *who; + if (lf->los[n] != lf->cell) { // not ourself + who = lf->los[n]->lf; + if (who && !isdead(who) && !isunconscious(who) && cansee(lf, who)) { + int chance = 100; // chance that we ('lf') will attack 'who' + int reachpenalty; + // will usually ignore targets who we can't reach + if (!canreach(lf, who, &reachpenalty)) { + if (!aigetrangedattack(lf, who, NULL, NULL)) { // no ranged attack? + // 1 size too small = 53% chance to attack + // 1 size too small = 6% chance to attack + chance = 100 - (reachpenalty*47); + if (db) dblog(".oO { target %d (%s) is %d sizes out of reach. %d%% chance to ignore }",who->id, who->race->name, + reachpenalty, reachpenalty*47); + } + } else if (isresting(who)) { + // targets sleeping in a tent will probably be ignored + object_t *restob; + restob = getrestob(who); + if (restob) { + switch (restob->type->id) { + case OT_TENT: chance = 5; break; + case OT_MOTEL: chance = 0; break; + default: break; + } + } + } + + if (pctchance(chance)) { + if (lfhasflagval(lf, F_HATESRACE, who->race->id, NA, NA, NULL) || + lfhasflagval(lf, F_HATESRACE, who->race->baseid, NA, NA, NULL) ) { + if (nhateposs < MAXCANDIDATES) { + if (db) dblog(".oO { found a hated target - lfid %d (%s) ! }",who->id, who->race->name); + hateposs[nhateposs++] = who; + } + break; + } else if (!nhateposs && areenemies(lf, who)) { // dont check if we've already found a hated target + if (nposs < MAXCANDIDATES) { + if (db) dblog(".oO { found an enemy target - lfid %d (%s) ! }",who->id, who->race->name); + poss[nposs++] = who; + } + } else { + getflags(lf->flags, retflag, &nretflags, F_HATESRACEWITHFLAG, F_NONE); + for (i = 0; i < nretflags; i++) { + if (lfhasflag(who, retflag[i]->id)) { + if (db) dblog(".oO { found a target with hated flags - lfid %d (%s) ! }",who->id, who->race->name); + hateposs[nhateposs++] = who; + } + } + } + } + } + } + } + if (nhateposs) { + newtarget = hateposs[rnd(0,nhateposs-1)]; + } else if (nposs) { + newtarget = poss[rnd(0,nposs-1)]; + } + } + + if (newtarget) { + if (aiattack(lf, newtarget, DEF_AIFOLLOWTIME)) { + // failed for some reason. maybe target was feigning + // death? + if (db) dblog(".oO { setting a new target via aiattack failed! }"); + } else { + // then move towards them... + if (db) dblog(".oO { moving towards my new target (%d,%d) -> (%d,%d) }", lf->cell->x, lf->cell->y, + newtarget->cell->x, newtarget->cell->y); + + if (icanattack) { + if (!movetowards(lf, newtarget->cell, DT_ORTH, B_FALSE)) { + turntoface(lf, newtarget->cell); + return B_TRUE; + } + } else { + if (db) dblog(".oO { won't move towards target - i have no weapon. }"); + } + } + } else { + // god with no targets? + if (lf->race->raceclass->id == RC_GOD) { + if (onein(6)) { + // gods will planeshift away + if (!castspell(lf, OT_S_PLANESHIFT, lf, NULL, lf->cell, NULL, NULL)) { + return B_TRUE; + } + } + } + } + + /////////////////////////////////////////////// + // training + /////////////////////////////////////////////// + + // need to train skills? + if (readytotrain(lf)) { + if (safetorest(lf)) { + // 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. + enhanceskills(lf); + } + } + + // do we have armour which needs repairing? + if (getskill(lf, SK_ARMOUR) >= PR_SKILLED) { + if (db) dblog(".oO { do i have any armour to repair? }"); + // just try to use the ability - it'll fail if we have nothing + // which we can repair. + if (!useability(lf, OT_A_REPAIR, NULL, NULL)) { + if (db) dblog(".oO { yes - done. }"); + // success + return B_TRUE; + } else { + if (db) dblog(".oO { no armour to repair. }"); + } + } + + // pet movement - note that pets will only rest if their + // master is resting. the normal rest code underneath this section + // will never be called. + if (master) { + //lifeform_t *master; + //master = findlf(lf->cell->map, mf->val[0]); + if (!aimovetolf(lf, master, B_FALSE)) { + // success + return B_TRUE; + } + } + + /////////////////////////////////////////////// + // resting / healing + /////////////////////////////////////////////// + + // need to heal? + if (needstorest(lf, NULL) && safetorest(lf)) { + if (db) dblog(".oO { resting to heal }"); + rest(lf, B_TRUE); + return B_TRUE; + } + + return B_FALSE; +} + + +// returns true if we did somethign +int ai_handle_emergencies(lifeform_t *lf, enum ATTRBRACKET iqb) { + int db = B_FALSE; + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + if (lfhasflag(lf, F_RAGE)) return B_FALSE; + + if (iqb >= AT_AVERAGE) { + if (celldangerous(lf, lf->cell, B_TRUE, NULL)) { + // if our cell is dangerous, move away! + if (!dorandommove(lf, B_NOBADMOVES, B_FALSE)) { + return B_TRUE; + } + } + } + + // flying monsters not flying? + if (!isprone(lf)) { + if (hasflag(lf->race->flags, F_FLYING) && !lfhasflag(lf, F_FLYING)) { + if (cancast(lf, OT_S_FLIGHT, NULL)) { + if (!castspell(lf, OT_S_FLIGHT, lf, NULL, lf->cell, NULL, NULL)) { + return B_TRUE; + } + } + } + if (hasflag(lf->race->flags, F_LEVITATING) && !lfhasflag(lf, F_LEVITATING)) { + copyflag(lf->flags, lf->race->flags, F_LEVITATING); + taketime(lf, getmovespeed(lf)); + return B_TRUE; + } + } + return B_FALSE; +} + +int ai_healing(lifeform_t *lf) { + int db = B_FALSE; + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + // special cases + if ((lf->race->id == R_STIRGE) || (lf->race->id == R_LEECH)) { + if (ispeaceful(lf)) { + int sleepval = 18; + + if (modcounter(lf->flags, 1) >= sleepval) { + // we say that this ISNT on purpose, because otherwise + // we'll wake up as soon as we're healed. in this case + // we actually want to sleep forever (or until woken). + if (!gotosleep(lf, B_FALSE)) { + // force this since when not on purpose, gotosleep wont + // take time. + taketime(lf, getactspeed(lf)); + return B_TRUE; // success + } + } + } + } + + if (!lfhasflag(lf, F_RAGE)) { + // feigning death with enemies in sight, and hurt? + if (lfhasflag(lf, F_FEIGNINGDEATH) && !safetorest(lf)) { + if (isbleeding(lf)) { + if (db) dblog(".oO { i am feigning death and bleeding (hp=%d/%d), skipping turn. }",lf->hp,lf->maxhp); + // just wait... + rest(lf, B_TRUE); + return B_TRUE; + } + } + + // hurt gods planeshift away + if (lf->race->raceclass->id == RC_GOD) { + if (gethppct(lf) <= 10) { + if (!castspell(lf, OT_S_PLANESHIFT, lf, NULL, lf->cell, NULL, NULL)) { + return B_TRUE; + } + } + } + + // need to heal? + if (lf->hp < (lf->maxhp/2)) { + if (!useitemwithflag(lf, F_AIHEALITEM)) { + return B_TRUE; + } else { + // don't have or can't use our healing items + // no enemies in sight? + if (safetorest(lf)) { + // if it's "night time" for us, sleep forever. + // otehrwise just sleep until we're healed + if (!gotosleep(lf, issleepingtimefor(lf) ? B_TRUE : B_FALSE)) { + taketime(lf, getactspeed(lf)); // to make sure our turn ends + return B_TRUE; // success + } + } + } + } + } + return B_FALSE; +} + +int ai_housekeeping(lifeform_t *lf, lifeform_t *master) { + int i; + flag_t *f; + int db = B_FALSE; + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + if (lfhasflag(lf, F_RAGE)) return B_FALSE; + + if (lfhasflag(lf, F_ISPRISONER) && master && isplayer(master) && cansee(lf, master)) { + if (isoutdoors(lf->cell->map) && pctchance(20)) { + object_t *o; + say(lf, "Thanks for getting me out!", SV_TALK); + o = addobfast(master->pack, OT_MANUAL); + if (o) { + char obname[BUFLEN]; + say(lf, "Here, let me teach you something as a reward.", SV_TALK); + getobname(o, obname, o->amt); + msgnocap("%c - %s", o->letter, obname); + } + // no longer an ally + killflagsofid(lf->flags, F_PETOF); + killflagsofid(lf->flags, F_ISPRISONER); + } + } + + // too many objects? + i = countobs(lf->pack, B_FALSE); + if (i >= (MAXPILEOBS - 5)) { + object_t *container; + // get largest container with space + container = getbestcontainer(lf->pack); + if (container) { + object_t *o; + // find object which will fit + for (o = lf->pack->first ; o ; o = o->next) { + if ((o != container) && !isequipped(o) && obfits(o, container->contents)) { + // put it in. + moveob(o, container->contents, ALL); + if (cansee(player, lf)) { + char obname[BUFLEN]; + char lfname[BUFLEN]; + char containername[BUFLEN]; + // announce + getobname(o, obname, o->amt); + getobname(container, containername, 1); + getlfname(lf, lfname); + msg("%s puts %s into %s.", lfname, obname, containername); + } + // timetime + taketime(lf, getactspeed(lf)); + return B_TRUE; + } + } + + } + } + + // talking + f = lfhasflag(lf, F_RANDOMTALKPCT); + if (f) { + if (pctchance(f->val[0])) { + flag_t *poss[MAXCANDIDATES]; + int nposs = 0,i; + flag_t *retflag[MAXCANDIDATES]; + int nretflags = 0; + + getflags(lf->flags, retflag, &nretflags, F_RANDOMTALK, F_NONE); + for (i = 0; i < nretflags; i++) { + poss[nposs++] = retflag[i]; + } + if (nposs) { + int vol; + f = poss[rnd(0,nposs-1)]; + vol = rnd(f->val[1], f->val[2]); + if (strlen(f->text)) { + say(lf, f->text, vol); + } else { + sayphrase(lf, f->val[0], vol, NA, NULL); + } + } + } + } + return B_FALSE; +} + +int ai_inventory_mgt(lifeform_t *lf, int *canattack) { + int db = B_FALSE,i; + object_t *curwep,*bestwep, *o; + object_t *curgun,*bestgun; + enum BODYPART bp; + int icanattack = B_FALSE; + + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + if (lfhasflag(lf, F_RAGE)) return B_FALSE; + + // burdened? + if (isburdened(lf)) { + object_t *o,*heaviest = NULL; + float hevweight = 0; + + if (db) dblog(".oO { i am burdened }"); + + // drop our heaviest non-equipped object + for (o = lf->pack->first ; o ; o = o->next) { + if (!isequipped(o)) { + float thisweight; + thisweight = getobweight(o); + if (thisweight > hevweight) { + hevweight = thisweight; + heaviest = o; + } + } + } + if (heaviest) { + if (db) { + char obname[BUFLEN]; + getobname(o, obname, ALL); + dblog(".oO { i will drop %s to lower my burden }", obname); + } + if (!drop(heaviest, ALL)) { + return B_TRUE; + } + if (db) dblog(".oO { drop failed! }"); + } + if (db) dblog(".oO { couldn't drop anything }"); + } + + // do we have a better weapon we could use? + curwep = getweapon(lf); + bestwep = getbestweapon(lf); + + if ((curwep != bestwep) && !isfirearm(curwep)) { + if (db) dblog(".oO { i have a better weapon than my current one (%s > %s) }",bestwep->type->name, curwep ? curwep->type->name : "nothing"); + // weild better one + if (!weild(lf, bestwep)) return B_TRUE; + } + + // do we have a better firearm ? + curgun = getfirearm(lf); + if (curwep && hasflag(curwep->flags, F_TWOHANDED)) { + // we are using a two handed weapon. don't + // check for guns. + } else { + bestgun = getbestfirearm(lf); + + if (curgun != bestgun) { + if (db) dblog(".oO { i have a better gun than my current one (%s > %s) }",bestgun->type->name, curgun ? curgun->type->name : "nothing"); + // weild better one + if (!weild(lf, bestgun)) return B_TRUE; + } + } + + // do we have ammo for an empty gun? + if (curgun) { + object_t *curammo; + curammo = getammo(curgun); + if (!curammo) { + o = getrandomammo(lf); + if (o && !loadfirearm(lf, curgun, o)) { + // success + return B_TRUE; + } + } + } + + // do we have better armour? + for (i = 0; i < lf->race->nbodyparts; i++) { + object_t *curarm; + bp = lf->race->bodypart[i].id; + curarm = getarmour(lf, bp); + // do we have a better one? + for (o = lf->pack->first ; o ; o = o->next) { + if (canwear(lf, o, BP_NONE) && isbetterarmourthan(o, curarm)) { + // wear this armour instead + if (!wear(lf, o)) return B_TRUE; + } + } + } + + + // now check whetehr we have ANY weapon + if (curwep || lfhasflag(lf, F_HASATTACK)) { + icanattack = B_TRUE; + if (canattack) *canattack = B_TRUE; + } + + // before attacking targets, + // look for any object which we _covet_. + // ie. if we covet something, we will pick it up + // instead of attacking our target. + if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_FEIGNINGDEATH)) { + if (db) dblog(".oO { looking for covetted objects... }"); + if (lookforobs(lf)) { + if (db) dblog(".oO { found covetted object. returning. }"); + return B_TRUE; + } + } + return B_FALSE; +} + +int ai_movement(lifeform_t *lf) { + int valid = B_TRUE; + cell_t *c; + int db = B_FALSE; + flag_t *f; + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + // do we have a target cell? + f = hasflag(lf->flags, F_TARGETCELL); + if (!f) return B_FALSE; + + // is it still valid? + c = getcellat(lf->cell->map, f->val[0], f->val[1]); + if (!c) { + valid = B_FALSE; + } else if (f->val[2] == MR_LF) { + lifeform_t *who; + who = findlf(lf->cell->map, atoi(f->text)); + // lf doesn't exist? + if (!who) { + valid = B_FALSE; + } else if (cansee(lf, who) && (lf->cell != c)) { + // can see them and they're not where we are going? + valid = B_FALSE; + } + } else if (f->val[2] == MR_OB) { + object_t *what; + what = findobidinmap(lf->cell->map, atol(f->text)); + if (!what) { + valid = B_FALSE; + } else if (haslos(lf, c) && (what->pile->where != c)) { + // if you can see the cell and object isn't there anymore + valid = B_FALSE; + } else if (c->lf && !areenemies(lf, c->lf) && haslos(lf, c) && (getcelldist(lf->cell, c) == 1)) { + // can see a non-enemy on top of the object, and we are adjacent + valid = B_FALSE; + } + } + + if (valid) { + aimovetotargetcell(lf, f); + return B_TRUE; + } else { + killflag(f); + } + return B_FALSE; +} + +int ai_premovement(lifeform_t *lf) { + int db = B_FALSE; + int i; + if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; + + // need light? + if (!haslos(lf, lf->cell)) { + object_t *lamp; + lamp = hasobwithflagval(lf->pack, F_ACTIVATECONFER, F_PRODUCESLIGHT, NA, NA, NULL); + if (lamp && !isactivated(lamp)) { + if (db) dblog(".oO { it's dark and i have an inactive light source (%s) }", lamp->type->name); + if (!operate(lf, lamp, NULL)) { + if (db) dblog(".oO { successfully turned it on. }"); + return B_TRUE; + } else { + if (db) dblog(".oO { failed to turn it on. }"); + } + } + } + // ally needs ammo? + for (i = 0; i < lf->nlos; i++) { + cell_t *c; + c = lf->los[i]; + if (c->lf && (c->lf != lf) && areallies(lf, c->lf)) { + object_t *gun; + gun = getfirearm(c->lf); + if (gun && !getammo(gun)) { + object_t *o; + for (o = lf->pack->first ; o ; o = o->next) { + if (isammofor(o->type, gun) ) { + if (getcelldist(lf->cell, c) <= getmaxthrowrange(lf, o)) { + // throw it to them! + if (!throwat(lf, o, c)) { + // success + return B_TRUE; + } + } + } + } + } + } + } + return B_FALSE; +} + flag_t *aihastarget(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_TARGETLF); @@ -1026,20 +1643,10 @@ int aiobok(lifeform_t *lf, object_t *o, lifeform_t *target) { void aiturn(lifeform_t *lf) { int db = B_FALSE; - object_t *curwep,*bestwep, *o; int icanattack = B_FALSE; - object_t *curgun,*bestgun; flag_t *f; - //flag_t *nextf; - // lifeform_t *fleefrom = NULL; - lifeform_t *target,*newtarget; - enum BODYPART bp; - //cell_t *c; lifeform_t *master = NULL; enum ATTRBRACKET iqb; - int n,i; - flag_t *retflag[MAXCANDIDATES]; - int nretflags = 0; /* @@ -1085,575 +1692,44 @@ void aiturn(lifeform_t *lf) { /////////////////////////////////////////////// // emergencies / fixing up /////////////////////////////////////////////// - if (!lfhasflag(lf, F_RAGE)) { - if (iqb >= AT_AVERAGE) { - if (celldangerous(lf, lf->cell, B_TRUE, NULL)) { - // if our cell is dangerous, move away! - if (!dorandommove(lf, B_NOBADMOVES, B_FALSE)) { - return; - } - } - } - - // flying monsters not flying? - if (!isprone(lf)) { - if (hasflag(lf->race->flags, F_FLYING) && !lfhasflag(lf, F_FLYING)) { - if (cancast(lf, OT_S_FLIGHT, NULL)) { - if (!castspell(lf, OT_S_FLIGHT, lf, NULL, lf->cell, NULL, NULL)) { - return; - } - } - } - if (hasflag(lf->race->flags, F_LEVITATING) && !lfhasflag(lf, F_LEVITATING)) { - copyflag(lf->flags, lf->race->flags, F_LEVITATING); - taketime(lf, getmovespeed(lf)); - return; - } - } - } + if (ai_handle_emergencies(lf, iqb)) return; /////////////////////////////////////////////// // housekeeping - weapon changes, drop/pickup, // use items, talk,etc /////////////////////////////////////////////// - if (!lfhasflag(lf, F_RAGE)) { - if (lfhasflag(lf, F_ISPRISONER) && master && isplayer(master) && cansee(lf, master)) { - if (isoutdoors(lf->cell->map) && pctchance(20)) { - object_t *o; - say(lf, "Thanks for getting me out!", SV_TALK); - o = addobfast(master->pack, OT_MANUAL); - if (o) { - char obname[BUFLEN]; - say(lf, "Here, let me teach you something as a reward.", SV_TALK); - getobname(o, obname, o->amt); - msgnocap("%c - %s", o->letter, obname); - } - // no longer an ally - killflagsofid(lf->flags, F_PETOF); - killflagsofid(lf->flags, F_ISPRISONER); - } - } - - // too many objects? - i = countobs(lf->pack, B_FALSE); - if (i >= (MAXPILEOBS - 5)) { - object_t *container; - // get largest container with space - container = getbestcontainer(lf->pack); - if (container) { - object_t *o; - // find object which will fit - for (o = lf->pack->first ; o ; o = o->next) { - if ((o != container) && !isequipped(o) && obfits(o, container->contents)) { - // put it in. - moveob(o, container->contents, ALL); - if (cansee(player, lf)) { - char obname[BUFLEN]; - char lfname[BUFLEN]; - char containername[BUFLEN]; - // announce - getobname(o, obname, o->amt); - getobname(container, containername, 1); - getlfname(lf, lfname); - msg("%s puts %s into %s.", lfname, obname, containername); - } - // timetime - taketime(lf, getactspeed(lf)); - return; - } - } - - } - } - - // talking - f = lfhasflag(lf, F_RANDOMTALKPCT); - if (f) { - if (pctchance(f->val[0])) { - flag_t *poss[MAXCANDIDATES]; - int nposs = 0,i; - - getflags(lf->flags, retflag, &nretflags, F_RANDOMTALK, F_NONE); - for (i = 0; i < nretflags; i++) { - poss[nposs++] = retflag[i]; - } - if (nposs) { - int vol; - f = poss[rnd(0,nposs-1)]; - vol = rnd(f->val[1], f->val[2]); - if (strlen(f->text)) { - say(lf, f->text, vol); - } else { - sayphrase(lf, f->val[0], vol, NA, NULL); - } - } - } - } - } + if (ai_housekeeping(lf, master)) return; /////////////////////////////////////////////// // healing /////////////////////////////////////////////// - - // special cases - if ((lf->race->id == R_STIRGE) || (lf->race->id == R_LEECH)) { - if (ispeaceful(lf)) { - int sleepval = 18; - - if (modcounter(lf->flags, 1) >= sleepval) { - // we say that this ISNT on purpose, because otherwise - // we'll wake up as soon as we're healed. in this case - // we actually want to sleep forever (or until woken). - if (!gotosleep(lf, B_FALSE)) { - // force this since when not on purpose, gotosleep wont - // take time. - taketime(lf, getactspeed(lf)); - return; // success - } - } - } - } - - if (!lfhasflag(lf, F_RAGE)) { - // feigning death with enemies in sight, and hurt? - if (lfhasflag(lf, F_FEIGNINGDEATH) && !safetorest(lf)) { - if (isbleeding(lf)) { - if (db) dblog(".oO { i am feigning death and bleeding (hp=%d/%d), skipping turn. }",lf->hp,lf->maxhp); - // just wait... - rest(lf, B_TRUE); - return; - } - } - - // hurt gods planeshift away - if (lf->race->raceclass->id == RC_GOD) { - if (gethppct(lf) <= 10) { - if (!castspell(lf, OT_S_PLANESHIFT, lf, NULL, lf->cell, NULL, NULL)) { - return; - } - } - } - - // need to heal? - if (lf->hp < (lf->maxhp/2)) { - if (!useitemwithflag(lf, F_AIHEALITEM)) { - return; - } else { - // don't have or can't use our healing items - // no enemies in sight? - if (safetorest(lf)) { - // if it's "night time" for us, sleep forever. - // otehrwise just sleep until we're healed - if (!gotosleep(lf, issleepingtimefor(lf) ? B_TRUE : B_FALSE)) { - taketime(lf, getactspeed(lf)); // to make sure our turn ends - return; // success - } - } - } - } - - // burdened? - if (isburdened(lf)) { - object_t *o,*heaviest = NULL; - float hevweight = 0; - - if (db) dblog(".oO { i am burdened }"); - - // drop our heaviest non-equipped object - for (o = lf->pack->first ; o ; o = o->next) { - if (!isequipped(o)) { - float thisweight; - thisweight = getobweight(o); - if (thisweight > hevweight) { - hevweight = thisweight; - heaviest = o; - } - } - } - if (heaviest) { - if (db) { - char obname[BUFLEN]; - getobname(o, obname, ALL); - dblog(".oO { i will drop %s to lower my burden }", obname); - } - if (!drop(heaviest, ALL)) { - return; - } - if (db) dblog(".oO { drop failed! }"); - } - if (db) dblog(".oO { couldn't drop anything }"); - } - - // do we have a better weapon we could use? - curwep = getweapon(lf); - bestwep = getbestweapon(lf); - - if ((curwep != bestwep) && !isfirearm(curwep)) { - if (db) dblog(".oO { i have a better weapon than my current one (%s > %s) }",bestwep->type->name, curwep ? curwep->type->name : "nothing"); - // weild better one - if (!weild(lf, bestwep)) return; - } - - // do we have a better firearm ? - curgun = getfirearm(lf); - if (curwep && hasflag(curwep->flags, F_TWOHANDED)) { - // we are using a two handed weapon. don't - // check for guns. - } else { - bestgun = getbestfirearm(lf); - - if (curgun != bestgun) { - if (db) dblog(".oO { i have a better gun than my current one (%s > %s) }",bestgun->type->name, curgun ? curgun->type->name : "nothing"); - // weild better one - if (!weild(lf, bestgun)) return; - } - } - - // do we have ammo for an empty gun? - if (curgun) { - object_t *curammo; - curammo = getammo(curgun); - if (!curammo) { - o = getrandomammo(lf); - if (o && !loadfirearm(lf, curgun, o)) { - // success - return; - } - } - } - - // do we have better armour? - for (i = 0; i < lf->race->nbodyparts; i++) { - object_t *curarm; - bp = lf->race->bodypart[i].id; - curarm = getarmour(lf, bp); - // do we have a better one? - for (o = lf->pack->first ; o ; o = o->next) { - if (canwear(lf, o, BP_NONE) && isbetterarmourthan(o, curarm)) { - // wear this armour instead - if (!wear(lf, o)) return; - } - } - } - - - // now check whetehr we have ANY weapon - if (curwep || lfhasflag(lf, F_HASATTACK)) { - icanattack = B_TRUE; - } - - // before attacking targets, - // look for any object which we _covet_. - // ie. if we covet something, we will pick it up - // instead of attacking our target. - if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_FEIGNINGDEATH)) { - if (db) dblog(".oO { looking for covetted objects... }"); - if (lookforobs(lf)) { - if (db) dblog(".oO { found covetted object. returning. }"); - return; - } - } - } // end if not enraged + if (ai_healing(lf)) return; /////////////////////////////////////////////// - // attacks + // inventory management /////////////////////////////////////////////// - target = gettargetlf(lf); - // do we already have a target we are attacking? - if (target) { - if (db) dblog(".oO { i have a target: lfid %d (%s). }", target->id, target->race->name); - - // target dead or unconscious? - if (isdead(target) || isunconscious(target)) { - if (db) dblog(".oO { my target is dead/ko'd }", target->id, target->race->name); - loseaitargets(lf); - if (areallies(lf, player) && cantalk(lf)) { - char text[BUFLEN]; - real_getlfname(target, text, B_FALSE); - sayphrase(lf, SP_ALLY_TARGETKILL, SV_SHOUT, NA, text); - } - } else { - // aquatic grabbers will try to drag their prey into the water - if (lfhasflagval(lf, F_GRABBING, target->id, NA, NA, NULL) && isaquatic(lf) ) { - if ( hasobwithflag(lf->cell->obpile, F_DEEPWATER) && - !hasobwithflag(target->cell->obpile, F_DEEPWATER)) { - // move away! - if (!moveawayfrom(lf, target->cell, DT_ORTH, B_FALSE, B_TRUE)) { - return; - } - } - } - // try to move towards them. - if (!aimovetolf(lf, target, B_TRUE)) { - // success - return; - } - } - } + if (ai_inventory_mgt(lf, &icanattack)) return; + /////////////////////////////////////////////// + // attacking existing targets + /////////////////////////////////////////////// + if (ai_attack_existing_target(lf)) return; /////////////////////////////////////////////// // generic pre-movement actions. /////////////////////////////////////////////// - // need light? - if (!haslos(lf, lf->cell)) { - object_t *lamp; - lamp = hasobwithflagval(lf->pack, F_ACTIVATECONFER, F_PRODUCESLIGHT, NA, NA, NULL); - if (lamp && !isactivated(lamp)) { - if (db) dblog(".oO { it's dark and i have an inactive light source (%s) }", lamp->type->name); - if (!operate(lf, lamp, NULL)) { - if (db) dblog(".oO { successfully turned it on. }"); - return; - } else { - if (db) dblog(".oO { failed to turn it on. }"); - } - } - } - // ally needs ammo? - for (i = 0; i < lf->nlos; i++) { - cell_t *c; - c = lf->los[i]; - if (c->lf && (c->lf != lf) && areallies(lf, c->lf)) { - object_t *gun; - gun = getfirearm(c->lf); - if (gun && !getammo(gun)) { - object_t *o; - for (o = lf->pack->first ; o ; o = o->next) { - if (isammofor(o->type, gun) ) { - if (getcelldist(lf->cell, c) <= getmaxthrowrange(lf, o)) { - // throw it to them! - if (!throwat(lf, o, c)) { - // success - return; - } - } - } - } - } - } - } + if (ai_premovement(lf)) return; /////////////////////////////////////////////// // movement /////////////////////////////////////////////// - // do we have a target cell? - f = hasflag(lf->flags, F_TARGETCELL); - if (f) { - int valid = B_TRUE; - cell_t *c; - // is it still valid? - c = getcellat(lf->cell->map, f->val[0], f->val[1]); - if (!c) { - valid = B_FALSE; - } else if (f->val[2] == MR_LF) { - lifeform_t *who; - who = findlf(lf->cell->map, atoi(f->text)); - // lf doesn't exist? - if (!who) { - valid = B_FALSE; - } else if (cansee(lf, who) && (lf->cell != c)) { - // can see them and they're not where we are going? - valid = B_FALSE; - } - } else if (f->val[2] == MR_OB) { - object_t *what; - what = findobidinmap(lf->cell->map, atol(f->text)); - if (!what) { - valid = B_FALSE; - } else if (haslos(lf, c) && (what->pile->where != c)) { - // if you can see the cell and object isn't there anymore - valid = B_FALSE; - } else if (c->lf && !areenemies(lf, c->lf) && haslos(lf, c) && (getcelldist(lf->cell, c) == 1)) { - // can see a non-enemy on top of the object, and we are adjacent - valid = B_FALSE; - } - } - - if (valid) { - aimovetotargetcell(lf, f); - } else { - killflag(f); - } - return; - } + if (ai_movement(lf)) return; /////////////////////////////////////////////// // look for something to do (objects, things // to attack, etc) /////////////////////////////////////////////// - - // not attacking anyone in particular - if (db) dblog(".oO { i do not have a target or can't move towards it. }"); - - // shopkeepers will return to their shops - if (hasjob(lf, J_SHOPKEEPER)) { - f = lfhasflag(lf, F_OWNSSHOP); - if (f) { - int myshop; - cell_t *where; - myshop = f->val[0]; - // find the closest cell of my shop - where = getclosestroomcell(lf, myshop); - // move towards my shop. note that if the player leaves then - // re-enters this map, they will find that we have instantly - // teleported there (see mapentereffects). - if (aigoto(lf, where, MR_OTHER, NULL, PERMENANT)) { - // success - return; - } - } - } - - if (!lfhasflag(lf, F_STUNNED)) { - lifeform_t *hateposs[MAXCANDIDATES],*poss[MAXCANDIDATES]; - int nposs = 0, nhateposs = 0; - if (db) dblog(".oO { looking for a target . }"); - - // look for any hated lfs or enemies - newtarget = NULL; - for (n = 0; n < lf->nlos; n++) { - lifeform_t *who; - if (lf->los[n] != lf->cell) { // not ourself - who = lf->los[n]->lf; - if (who && !isdead(who) && !isunconscious(who) && cansee(lf, who)) { - int chance = 100; // chance that we ('lf') will attack 'who' - int reachpenalty; - // will usually ignore targets who we can't reach - if (!canreach(lf, who, &reachpenalty)) { - if (!aigetrangedattack(lf, who, NULL, NULL)) { // no ranged attack? - // 1 size too small = 53% chance to attack - // 1 size too small = 6% chance to attack - chance = 100 - (reachpenalty*47); - if (db) dblog(".oO { target %d (%s) is %d sizes out of reach. %d%% chance to ignore }",who->id, who->race->name, - reachpenalty, reachpenalty*47); - } - } else if (isresting(who)) { - // targets sleeping in a tent will probably be ignored - object_t *restob; - restob = getrestob(who); - if (restob) { - switch (restob->type->id) { - case OT_TENT: chance = 5; break; - case OT_MOTEL: chance = 0; break; - default: break; - } - } - } - - if (pctchance(chance)) { - if (lfhasflagval(lf, F_HATESRACE, who->race->id, NA, NA, NULL) || - lfhasflagval(lf, F_HATESRACE, who->race->baseid, NA, NA, NULL) ) { - if (nhateposs < MAXCANDIDATES) { - if (db) dblog(".oO { found a hated target - lfid %d (%s) ! }",who->id, who->race->name); - hateposs[nhateposs++] = who; - } - break; - } else if (!nhateposs && areenemies(lf, who)) { // dont check if we've already found a hated target - if (nposs < MAXCANDIDATES) { - if (db) dblog(".oO { found an enemy target - lfid %d (%s) ! }",who->id, who->race->name); - poss[nposs++] = who; - } - } else { - getflags(lf->flags, retflag, &nretflags, F_HATESRACEWITHFLAG, F_NONE); - for (i = 0; i < nretflags; i++) { - if (lfhasflag(who, retflag[i]->id)) { - if (db) dblog(".oO { found a target with hated flags - lfid %d (%s) ! }",who->id, who->race->name); - hateposs[nhateposs++] = who; - } - } - } - } - } - } - } - if (nhateposs) { - newtarget = hateposs[rnd(0,nhateposs-1)]; - } else if (nposs) { - newtarget = poss[rnd(0,nposs-1)]; - } - } - - if (newtarget) { - if (aiattack(lf, newtarget, DEF_AIFOLLOWTIME)) { - // failed for some reason. maybe target was feigning - // death? - if (db) dblog(".oO { setting a new target via aiattack failed! }"); - } else { - // then move towards them... - if (db) dblog(".oO { moving towards my new target (%d,%d) -> (%d,%d) }", lf->cell->x, lf->cell->y, - newtarget->cell->x, newtarget->cell->y); - - if (icanattack) { - if (!movetowards(lf, newtarget->cell, DT_ORTH, B_FALSE)) { - turntoface(lf, newtarget->cell); - return; - } - } else { - if (db) dblog(".oO { won't move towards target - i have no weapon. }"); - } - } - } else { - // god with no targets? - if (lf->race->raceclass->id == RC_GOD) { - if (onein(6)) { - // gods will planeshift away - if (!castspell(lf, OT_S_PLANESHIFT, lf, NULL, lf->cell, NULL, NULL)) { - return; - } - } - } - } - - /////////////////////////////////////////////// - // training - /////////////////////////////////////////////// - - // need to train skills? - if (readytotrain(lf)) { - if (safetorest(lf)) { - // 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. - enhanceskills(lf); - } - } - - // do we have armour which needs repairing? - if (getskill(lf, SK_ARMOUR) >= PR_SKILLED) { - if (db) dblog(".oO { do i have any armour to repair? }"); - // just try to use the ability - it'll fail if we have nothing - // which we can repair. - if (!useability(lf, OT_A_REPAIR, NULL, NULL)) { - if (db) dblog(".oO { yes - done. }"); - // success - return; - } else { - if (db) dblog(".oO { no armour to repair. }"); - } - } - - // pet movement - note that pets will only rest if their - // master is resting. the normal rest code underneath this section - // will never be called. - if (master) { - //lifeform_t *master; - //master = findlf(lf->cell->map, mf->val[0]); - if (!aimovetolf(lf, master, B_FALSE)) { - // success - return; - } - } - - /////////////////////////////////////////////// - // resting / healing - /////////////////////////////////////////////// - - // need to heal? - if (needstorest(lf, NULL) && safetorest(lf)) { - if (db) dblog(".oO { resting to heal }"); - rest(lf, B_TRUE); - return; - } + if (ai_bored(lf, master, icanattack)) return; // DEFAULT - try to move in a random direction if (db) dblog(".oO { default - moving randomly }"); diff --git a/ai.h b/ai.h index 42fdd10..1ff329a 100644 --- a/ai.h +++ b/ai.h @@ -9,6 +9,14 @@ object_t *aigetrangedattack(lifeform_t *lf, lifeform_t *target, enum RANGEATTACK void 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); +int ai_bored(lifeform_t *lf, lifeform_t *master, int canattack); +int ai_handle_emergencies(lifeform_t *lf, enum ATTRBRACKET iqb); +int ai_healing(lifeform_t *lf); +int ai_housekeeping(lifeform_t *lf, lifeform_t *master); +int ai_inventory_mgt(lifeform_t *lf, int *canattack); +int ai_movement(lifeform_t *lf); +int ai_premovement(lifeform_t *lf); flag_t *aihastarget(lifeform_t *lf); int aimovetolf(lifeform_t *lf, lifeform_t *target, int wantattack); void aimovetotargetcell(lifeform_t *lf, flag_t *f); diff --git a/data.c b/data.c index 169ce03..174946a 100644 --- a/data.c +++ b/data.c @@ -119,9 +119,9 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTATT, A_WIS, AT_EXHIGH, NA, NULL); addflag(lastjob->flags, F_STARTATT, A_CHA, AT_EXHIGH, NA, NULL); */ - addflag(lastjob->flags, F_STARTATT, A_STR, AT_RANDOM, NA, NULL); + addflag(lastjob->flags, F_STARTATT, A_STR, AT_EXHIGH, NA, NULL); addflag(lastjob->flags, F_STARTATT, A_AGI, AT_RANDOM, NA, NULL); - addflag(lastjob->flags, F_STARTATT, A_IQ, AT_RANDOM, NA, NULL); + addflag(lastjob->flags, F_STARTATT, A_IQ, AT_EXHIGH, NA, NULL); addflag(lastjob->flags, F_STARTATT, A_CON, AT_RANDOM, NA, NULL); addflag(lastjob->flags, F_STARTATT, A_WIS, AT_RANDOM, NA, NULL); addflag(lastjob->flags, F_STARTATT, A_CHA, AT_RANDOM, NA, NULL); @@ -1358,23 +1358,23 @@ void initobjects(void) { addflag(lastot->flags, F_SHOPMENU, 1, MA_GOTOMENU, SM_DONATE, "d:donate something"); addflag(lastot->flags, F_SHOPMENU, 2, MA_QUIT, NA, "q:leave"); for (i = 0; i < 10; i++) { - addflag(lastot->flags, F_STARTOBCLASS, 100, OC_ARMOUR, NA, NULL); + addflag(lastot->flags, F_STARTOBCLASS, 100, OC_ARMOUR, RANDOM, NULL); } addot(OT_SHOPBOOK, "book store", "A small kiosk dealing in books.", MT_METAL, 500, OC_BUILDING, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_ALL, NA, RR_COMMON, ""); addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); make_basic_shop(lastot->flags); for (i = 0; i < 10; i++) { - f = addflag(lastot->flags, F_STARTOBCLASS, 100, OC_BOOK, NA, NULL); + f = addflag(lastot->flags, F_STARTOBCLASS, 100, OC_BOOK, RANDOM, NULL); addcondition(f, FC_NOCONDITION, 33); - addaltval(f, F_STARTOBCLASS, 100, OC_SCROLL, NA, NULL); + addaltval(f, F_STARTOBCLASS, 100, OC_SCROLL, RANDOM, NULL); } addot(OT_SHOPFOOD, "food vendor", "A small kiosk dealing in food.", MT_METAL, 500, OC_BUILDING, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_ALL, NA, RR_COMMON, ""); addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); make_basic_shop(lastot->flags); for (i = 0; i < 10; i++) { - addflag(lastot->flags, F_STARTOBCLASS, 100, OC_FOOD, NA, NULL); + addflag(lastot->flags, F_STARTOBCLASS, 100, OC_FOOD, RANDOM, NULL); } addot(OT_SHOPGENERAL, "general store", "A small kiosk which sells various objects.", MT_METAL, 500, OC_BUILDING, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_ALL, NA, RR_COMMON, ""); @@ -1388,23 +1388,23 @@ void initobjects(void) { addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); make_basic_shop(lastot->flags); for (i = 0; i < 10; i++) { - f = addflag(lastot->flags, F_STARTOBCLASS, 100, OC_TOOLS, NA, NULL); + f = addflag(lastot->flags, F_STARTOBCLASS, 100, OC_TOOLS, RANDOM, NULL); addcondition(f, FC_NOCONDITION, 50); - addaltval(f, F_STARTOBCLASS, 100, OC_TECH, NA, NULL); + addaltval(f, F_STARTOBCLASS, 100, OC_TECH, RANDOM, NULL); } addot(OT_SHOPPOTION, "potion store", "A small kiosk dealing in liqour and potions.", MT_METAL, 500, OC_BUILDING, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_ALL, NA, RR_COMMON, ""); addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); make_basic_shop(lastot->flags); for (i = 0; i < 10; i++) { - addflag(lastot->flags, F_STARTOBCLASS, 100, OC_POTION, NA, NULL); + addflag(lastot->flags, F_STARTOBCLASS, 100, OC_POTION, RANDOM, NULL); } addot(OT_SHOPRING, "jewellery store", "A small kiosk dealing in rings and amulets.", MT_METAL, 500, OC_BUILDING, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_ALL, NA, RR_COMMON, ""); addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); make_basic_shop(lastot->flags); for (i = 0; i < 10; i++) { - addflag(lastot->flags, F_STARTOBCLASS, 100, OC_RING, NA, NULL); + addflag(lastot->flags, F_STARTOBCLASS, 100, OC_RING, RANDOM, NULL); } addot(OT_SHOPWEAPON, "weapon store", "A small kiosk dealing in weapons.", MT_METAL, 500, OC_BUILDING, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_ALL, NA, RR_COMMON, ""); @@ -1413,7 +1413,7 @@ void initobjects(void) { addflag(lastot->flags, F_SHOPMENU, 1, MA_GOTOMENU, SM_DONATE, "d:donate something"); addflag(lastot->flags, F_SHOPMENU, 2, MA_QUIT, NA, "q:leave"); for (i = 0; i < 10; i++) { - addflag(lastot->flags, F_STARTOBCLASS, 100, OC_WEAPON, NA, NULL); + addflag(lastot->flags, F_STARTOBCLASS, 100, OC_WEAPON, RANDOM, NULL); } addot(OT_TEMPLE, "temple", "A small structure dedicated to one of the gods.", MT_METAL, 500, OC_BUILDING, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_ALL, NA, RR_COMMON, ""); @@ -1860,7 +1860,7 @@ void initobjects(void) { addflag(lastot->flags, F_EDIBLE, B_TRUE, 200, NA, ""); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); addflag(lastot->flags, F_FEELTEXT, NA, NA, NA, "some bread"); - addot(OT_BREADGARLIC, "loaf of garlic bread", "A pungent loaf of garlic bread.", MT_FOOD, 0.5, OC_FOOD, SZ_TINY); + addot(OT_BREADGARLIC, "loaf of garlic bread", "A pungent loaf of garlic bread. Nauseates those around you and restores some health.", MT_FOOD, 0.5, OC_FOOD, SZ_TINY); addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, ""); addflag(lastot->flags, F_FEELTEXT, NA, NA, NA, "some bread"); @@ -1901,7 +1901,7 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_COMMON, NULL); addflag(lastot->flags, F_RARITY, H_FOREST, 100, RR_COMMON, NULL); addflag(lastot->flags, F_NUMAPPEAR, 1, 3, NA, ""); - addot(OT_HOTDOG, "hot dog", "A chunk of meat sandwiched between two pieces of bread.", MT_FOOD, 0.5, OC_FOOD, SZ_TINY); + addot(OT_HOTDOG, "hot dog", "A chunk of meat sandwiched between two pieces of bread. Temporarily increases strength, and provides some healing.", MT_FOOD, 0.5, OC_FOOD, SZ_TINY); addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 80, NA, ""); addflag(lastot->flags, F_ISMEAT, B_TRUE, 80, NA, ""); @@ -1924,6 +1924,8 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_SWAMP, 100, RR_FREQUENT, NULL); addflag(lastot->flags, F_NUMAPPEAR, 1, 3, NA, ""); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, "mushroom"); + addot(OT_MUSHROOMSTUFFED, "stuffed mushroom", "A large brown mushroom stuffed with breadcrumbs. This healthy food slightly increases your maximum hit points..", MT_FOOD, 0.2, OC_FOOD, SZ_TINY); + addflag(lastot->flags, F_EDIBLE, B_TRUE, 30, NA, ""); addot(OT_NUT, "peanut", "A species in the legume family.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 12, NA, ""); @@ -1935,7 +1937,7 @@ 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_ISMEAT, B_TRUE, 80, NA, ""); - addot(OT_RUMBALL, "rum ball", "A rum-filled ball of chocolate. Cures pain and restores 2-6 hit points.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); // weight normally comes from corpse type + addot(OT_RUMBALL, "rum ball", "A rum-filled ball of chocolate. Cures pain and restores 5-10 hit points.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); // weight normally comes from corpse type addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, ""); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); @@ -1946,10 +1948,10 @@ void initobjects(void) { addflag(lastot->flags, F_MISSILEDAM, 0, NA, NA, ""); addflag(lastot->flags, F_THROWMISSILE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_COMMON, NULL); - addot(OT_SANDWICHCHEESE, "cheese sandwich", "A tasty cheese sandwich. Filling, and restores all stamina.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); + addot(OT_SANDWICHCHEESE, "cheese sandwich", "A tasty cheese sandwich. Filling, and restores all stamina plus some health.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); addflag(lastot->flags, F_GLYPH, C_WHITE, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, ""); - addot(OT_SANDWICHPB, "peanut butter sandwich", "An extremely filling sandwich. Restores all stamina and boosts Fitness.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); + addot(OT_SANDWICHPB, "peanut butter sandwich", "An extremely filling sandwich. Restores all stamina, boosts Fitness, and restores some health.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); addflag(lastot->flags, F_GLYPH, C_WHITE, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 200, NA, ""); addot(OT_SUGAR, "lump of sugar", "A small block of sugar. Used for cooking.", MT_FOOD, 0.1, OC_FOOD, SZ_TINY); @@ -2079,19 +2081,19 @@ void initobjects(void) { // potions which come from cooking - addot(OT_POT_SOUPCHICKEN, "flask of chicken soup", "A watery soup which cures diseases.", MT_GLASS, 1, OC_POTION, SZ_TINY); + addot(OT_POT_SOUPCHICKEN, "flask of chicken soup", "A watery soup which cures diseases and heals you.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); killflagsofid(lastot->flags, F_HASHIDDENNAME); - addot(OT_POT_SOUPMUSHROOM, "flask of mushroom soup", "Hearty soup which restores a small amount of stamina.", MT_GLASS, 1, OC_POTION, SZ_TINY); + addot(OT_POT_SOUPMUSHROOM, "flask of mushroom soup", "Hearty soup which restores a small amount of stamina and heals you.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); killflagsofid(lastot->flags, F_HASHIDDENNAME); - addot(OT_POT_SOUPTOMATO, "flask of tomato soup", "Hearty soup which restores a small amount of stamina.", MT_GLASS, 1, OC_POTION, SZ_TINY); + addot(OT_POT_SOUPTOMATO, "flask of tomato soup", "Hearty soup which restores a small amount of stamina and heals you.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); killflagsofid(lastot->flags, F_HASHIDDENNAME); - addot(OT_POT_STROGONOFF, "flask of beef strogonoff", "A beef and mushroom stew which temporarily boosts your Fitness.", MT_GLASS, 1, OC_POTION, SZ_TINY); + addot(OT_POT_STROGONOFF, "flask of beef strogonoff", "A beef and mushroom stew which temporarily boosts your Fitness, and heals you a little.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); killflagsofid(lastot->flags, F_HASHIDDENNAME); - addot(OT_POT_SUGARWATER, "flask of sugar water", "Sweet water, saturated with sugar. Restores a medium amount of stamina.", MT_GLASS, 1, OC_POTION, SZ_TINY); + addot(OT_POT_SUGARWATER, "flask of sugar water", "Sweet water, saturated with sugar. Restores a medium amount of stamina and some health.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); killflagsofid(lastot->flags, F_HASHIDDENNAME); @@ -3222,11 +3224,19 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_PASSWALL, "passwall", "Allows the caster to temporarily walk through a single wall.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the caster can wait before entering a wall."); - addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); - addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addot(OT_S_ANIMATESTONE, "animate stone", "Temporarily transforms a statue into living flesh, under control of the caster. Also stops stoning.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell only affects stone which is in the form of a living creature."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_OBJECT|TT_PLAYER, NA, NA, NULL); + addflag(lastot->flags, F_RANGE, 1, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l5 addot(OT_S_GASEOUSFORM, "gaseous form", "Changes the caster into a cloud of gas.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); @@ -5236,70 +5246,89 @@ void initobjects(void) { addot(OT_RING_ENDURANCE, "ring of endurance", "Boosts stamina regeneration.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_STAMREGEN, NA, NA, "0.5"); + addflag(lastot->flags, F_VALUE, 250, NA, NA, NULL); addot(OT_RING_SIGHT, "ring of sight", "Allows the caster to see the invisible, and in the dark.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_SEEINVIS, NA, NA, NULL); addflag(lastot->flags, F_EQUIPCONFER, F_SEEINDARK, 3, NA, NULL); addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, 1, NA, NULL); + addflag(lastot->flags, F_VALUE, 350, NA, NA, NULL); addot(OT_RING_MANA, "ring of mana", "Increases the wearer's MP pool.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_EXTRAMP, 10, NA, NULL); + addflag(lastot->flags, F_VALUE, 300, NA, NA, NULL); addot(OT_RING_LUCK, "ring of luck", "Makes the wearer more lucky.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_EXTRALUCK, 5, NA, NULL); + addflag(lastot->flags, F_VALUE, 300, NA, NA, NULL); addot(OT_RING_PROTFIRE, "ring of fire immunity", "Grants the caster complete immunity to fire.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_DTIMMUNE, DT_FIRE, NA, NULL); + addflag(lastot->flags, F_VALUE, 350, NA, NA, NULL); addot(OT_RING_PROTCOLD, "ring of cold immunity", "Grants the caster complete immunity to cold.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_DTIMMUNE, DT_COLD, NA, NULL); + addflag(lastot->flags, F_VALUE, 350, NA, NA, NULL); addot(OT_RING_STR, "ring of strength", "Increases the wearer's strength.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 70, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, 1, NULL); // '1' is randomized during generation addflag(lastot->flags, F_IDWHENUSED, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 300, NA, NA, NULL); addot(OT_RING_IQ, "ring of intelligence", "Increases the wearer's intelligence.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 70, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_ATTRMOD, A_IQ, 1, NULL); // '1' is randomized during generation addflag(lastot->flags, F_IDWHENUSED, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 300, NA, NA, NULL); addot(OT_RING_CON, "ring of fitness", "Increases the wearer's fitness.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 70, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_ATTRMOD, A_CON, 1, NULL); // '1' is randomized during generation addflag(lastot->flags, F_IDWHENUSED, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 300, NA, NA, NULL); addot(OT_RING_DEX, "ring of dexterity", "Increases the wearer's dexterity.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 70, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_ATTRMOD, A_AGI, 1, NULL); // '1' is randomized during generation addflag(lastot->flags, F_IDWHENUSED, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 300, NA, NA, NULL); addot(OT_RING_HUNGER, "ring of hunger", "Greatly increases the metabolic rate of the wearer.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 73, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_FASTMETAB, 3, NA, NULL); addflag(lastot->flags, F_STARTBLESSED, B_CURSED, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 300, NA, NA, NULL); addot(OT_RING_WOUNDING, "ring of wounding", "Increases the damage output of the wearer.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 73, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_EXTRADAM, NA, NA, "0d0+4"); addflag(lastot->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_IDWHENUSED, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 350, NA, NA, NULL); addot(OT_RING_INVIS, "ring of invisibility", "Renders the wearer invisible.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_INVISIBLE, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 400, NA, NA, NULL); addot(OT_RING_INVULN, "ring of invulnerability", "Grants the caster complete immunity to physical harm.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_EQUIPCONFER, F_INVULNERABLE, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 450, NA, NA, NULL); addot(OT_RING_MPREGEN, "ring of recharging", "Slowly regenerates the wearer's mana.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_MPREGEN, 1, NA, NULL); + addflag(lastot->flags, F_VALUE, 250, NA, NA, NULL); addot(OT_RING_CONTROL, "ring of control", "Allows the wearer control over teleportation and polymorphic effects.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_CONTROL, NA, NA, NULL); + addflag(lastot->flags, F_VALUE, 350, NA, NA, NULL); addot(OT_RING_REGENERATION, "ring of regeneration", "Slowly regenerates the wearer's health, even when not resting.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_REGENERATES, 1, NA, NULL); + addflag(lastot->flags, F_VALUE, 350, NA, NA, NULL); addot(OT_RING_RESISTMAG, "ring of magic resistance", "Renders the wearer immune to most magical effects.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_EQUIPCONFER, F_RESISTMAG, 5, NA, NULL); + addflag(lastot->flags, F_VALUE, 350, NA, NA, NULL); addot(OT_RING_MIRACLES, "ring of miracles", "Grants a limited number of miracles to the wearer.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_UNCOMMON, ""); addflag(lastot->flags, F_CHARGES, 1, 3, NA, NULL); + addflag(lastot->flags, F_VALUE, 400, NA, NA, NULL); // unarmed weapons - note these damage/accuracys can be // overridded with the lifeform flag F_HASATTACK @@ -6179,6 +6208,10 @@ void initobjects(void) { addot(OT_PLAYERSTART, "playerstart", "starting pos for player", MT_NOTHING, 0, OC_MISC, SZ_MINI); // recipes - easy + addrecipe(OT_MUSHROOMSTUFFED, + OT_MUSHROOMSHI, 1, B_TRUE, + OT_BREADSTALE, 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, @@ -7907,7 +7940,6 @@ void initrace(void) { addflag(lastrace->flags, F_SEEINDARK, 8, NA, NA, NULL); addflag(lastrace->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL); addflag(lastrace->flags, F_AUTOCREATEOB, 1, NA, NA, "puff of smoke"); - addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, "puff of smoke"); addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL); addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL); @@ -8664,6 +8696,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_A_SUCKBLOOD, NA, NA, "dam:0d1+4;"); 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"); addflag(lastrace->flags, F_MORALE, 20, NA, NA, NULL); addrace(R_NEWT, "giant newt", 4, ':', C_BROWN, MT_FLESH, RC_ANIMAL, "An abnormally large example of the lizard family."); setbodytype(lastrace, BT_QUADRAPED); @@ -8740,6 +8773,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_COLD, NA, NA, NULL); addflag(lastrace->flags, F_DTIMMUNE, DT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); @@ -8760,6 +8794,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_COLD, NA, NA, NULL); + addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); @@ -8780,6 +8815,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_COLD, NA, NA, NULL); + addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); @@ -8802,6 +8838,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_COLD, NA, NA, NULL); + addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_CANWILL, OT_S_POISONBOLT, 4, 4, "pw:3;"); @@ -8825,6 +8862,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_COLD, NA, NA, NULL); + addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, B_APPENDYOU, "spits"); @@ -8850,6 +8888,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_COLD, NA, NA, NULL); + addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); @@ -8876,6 +8915,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_COLD, NA, NA, NULL); addflag(lastrace->flags, F_DTIMMUNE, DT_POISON, NA, NA, NULL); + addflag(lastrace->flags, F_WALKVERB, NA, NA, NA, "slither"); addflag(lastrace->flags, F_NOISETEXT, N_WALK, 1, NA, "^slithering"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 2, NA, "hisses^hissing"); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); diff --git a/defs.h b/defs.h index 8b38a19..dd34285 100644 --- a/defs.h +++ b/defs.h @@ -48,6 +48,7 @@ // Booleans #define B_FALSE (0) #define B_TRUE (-1) +#define B_FORCE (-2) #define B_ONPURPOSE (-1) #define B_CHANGEDIR (-1) #define B_VIS (1) @@ -1105,6 +1106,7 @@ enum OBTYPE { OT_JERKY, OT_MUSHROOMSHI, OT_MUSHROOMTOAD, + OT_MUSHROOMSTUFFED, OT_NUT, OT_ROASTMEAT, OT_RUMBALL, @@ -1290,6 +1292,7 @@ enum OBTYPE { OT_S_STUNMASS, OT_S_TELEKINESIS, // -- modification + OT_S_ANIMATESTONE, OT_S_DARKNESS, OT_S_ENCHANT, OT_S_GASEOUSFORM, @@ -2465,6 +2468,8 @@ enum FLAG { // TEMP FLAGS F_KILLEDBYPLAYER, // did the player kill this lf? // monster noise flags + F_WALKVERB, // text is verb for moving. 'walk' 'slither' + // 'bounce' 'hop' etc F_NOISETEXT, // val0 is a enum NOISETYPE // val1 is the volume of the noise // text is "verb^noun" @@ -2717,8 +2722,9 @@ enum FLAG { F_INTERRUPTED, // somethign interrupted our rest. stop! F_EATING, // lf is eating obid v0 F_TRAINING, // are we training? cleared on any action other than rest. - // v2 = if not NA, it is the training counter. - // when it hits 0, you finish trainign. + // v0 = current training amount + // v1 = training target. + // when v0 == v1 , you finish trainign. F_RESTUNTILBETTER, // resting until we have full mp/hp/stam //F_RESTUNTILHP, // resting until we have full hp //F_RESTUNTILMP, // resting until we have full mp diff --git a/flag.c b/flag.c index 4dffce5..dffb30a 100644 --- a/flag.c +++ b/flag.c @@ -57,6 +57,7 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, int redrawscreenatend = B_FALSE; int redrawstatatend = B_FALSE; int i; + int rv; // identified things mean all new flags are autmaticlaly known. @@ -70,9 +71,11 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, } } - if ((id == F_POISONED) && isimmuneto(fp, DT_POISON)) { - return NULL; - } + // impossible flags + if ((id == F_POISONED) && isimmuneto(fp, DT_POISON)) return NULL; + if ((id == F_VEGETARIAN) && hasflag(fp, F_CARNIVORE)) return NULL; + if ((id == F_PARTVEGETARIAN) && hasflag(fp, F_CARNIVORE)) return NULL; + if ((id == F_CARNIVORE) && (hasflag(fp, F_VEGETARIAN) || hasflag(fp, F_PARTVEGETARIAN)) ) return NULL; // overrite NA rarity if ((id == F_RARITY) && (val3 == NA)) { @@ -259,7 +262,9 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, } } // player flags which cause a redraw - if (flagcausesredraw(f->pile->owner, f->id)) redrawscreenatend = B_TRUE; + rv = flagcausesredraw(f->pile->owner, f->id); + if (rv < redrawscreenatend) redrawscreenatend = rv; + if (flagcausesstatredraw(f->pile->owner, f->id)) redrawstatatend = B_TRUE; } else if (f->pile->ob) { if (gamemode == GM_GAMESTARTED) { @@ -280,7 +285,7 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, setlosdirty(l); //precalclos(l); if (isplayer(l)) { - redrawscreenatend = B_TRUE; + if (!redrawscreenatend) redrawscreenatend = B_TRUE; } } } @@ -312,7 +317,7 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, if ((gamemode == GM_GAMESTARTED) && (redrawscreenatend || redrawstatatend || redolight)) { if (redolight) { //dblog("CALCINGLIGHT from flag\n"); - redrawscreenatend = B_TRUE; + if (!redrawscreenatend) redrawscreenatend = B_TRUE; calclight(redolight); setlosdirty(player); //precalclos(player); @@ -321,7 +326,12 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, if (redrawscreenatend) needredraw = B_TRUE; if (redrawstatatend) statdirty = B_TRUE; - drawscreen(); + + if (redrawscreenatend == B_FORCE) { + forceredraw(); + } else { + drawscreen(); + } } return f; } @@ -420,6 +430,11 @@ int flagcausesloscalc(enum FLAG fid) { } return B_FALSE; } + +// eitehr returns: +// B_FALSE - gaining/losing the flag doesn't neccessitate a redraw +// B_TRUE - need to mark the game window as dirty when you see someone gaining/losing this flag +// B_FORCE - need to CLEAR the game window to force a redraw. int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { if (!lf) return B_FALSE; @@ -437,7 +452,6 @@ int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { case F_INVISIBLE: case F_PRODUCESLIGHT: case F_PRONE: - case F_RAGE: case F_SECRET: case F_SEEINDARK: case F_SEEINVIS: @@ -445,6 +459,11 @@ int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { case F_SPRINTING: case F_SLOWMOVE: return B_TRUE; + case F_GRABBEDBY: + case F_ATTACHEDTO: + case F_RAGE: + return B_FORCE; + break; default: break; } @@ -1040,6 +1059,9 @@ void timeeffectsflag(flag_t *f, int howlong) { case F_DTVULN: warn("You feel a little less vulnerable to %s...", getdamname(f->val[0])); break; + case F_INVISIBLE: + warn("Your body is starting to reappear..."); + break; case F_MAGSHIELD: warn("Your magnetic shield is weakening..."); break; @@ -1088,6 +1110,9 @@ void timeeffectsflag(flag_t *f, int howlong) { case F_DTVULN: warn("You feel a little less vulnerable to %s...", getdamname(f->val[0])); break; + case F_INVISIBLE: + warn("Your invisibility is about to expire!"); more(); + break; case F_MAGSHIELD: warn("Your magnetic shield is about to expire!"); more(); break; diff --git a/io.c b/io.c index 571e57e..d2b42c6 100644 --- a/io.c +++ b/io.c @@ -1165,7 +1165,12 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { // we'll get no announcement when the player turns // invisible and doesn't have see invisible. if (!isplayer(lf) && !cansee(player, lf)) { - return B_FALSE; + // another exception - still show when things attach to the player, even if + // the player can't see them. + if ((f->id == F_ATTACHEDTO) && (f->val[0] == player->id)) { + } else { + return B_FALSE; + } } switch (f->id) { diff --git a/lf.c b/lf.c index a1dfedf..829388c 100644 --- a/lf.c +++ b/lf.c @@ -3186,6 +3186,7 @@ int eat(lifeform_t *lf, object_t *o) { switch (o->type->id) { case OT_BREADGARLIC: addtempflag(lf->flags, F_STENCH, 2, NA, NA, NULL, rnd(30,50)); + gainhp(lf,rnd(6,12)); break; case OT_CAKEFRUIT: setstamina(lf, getmaxstamina(lf)); @@ -3197,21 +3198,35 @@ int eat(lifeform_t *lf, object_t *o) { break; case OT_HOTDOG: addtempflag(lf->flags, F_ATTRMOD, A_STR, 3, NA, NULL, rnd(30,50)); + gainhp(lf,rnd(6,12)); + break; + case OT_MUSHROOMSTUFFED: + lf->maxhp++; break; case OT_RUMBALL: killflagsofid(lf->flags, F_PAIN); - gainhp(lf, rnd(2,6)); + gainhp(lf, rnd(5,10)); break; case OT_SANDWICHCHEESE: setstamina(lf, getmaxstamina(lf)); + gainhp(lf,rnd(1,5)); break; case OT_SANDWICHPB: setstamina(lf, getmaxstamina(lf)); addtempflag(lf->flags, F_ATTRMOD, A_CON, 3, NA, NULL, rnd(30,50)); + gainhp(lf,rnd(13,18)); break; default: break; } + + if (hasflagval(o->flags, F_CORPSEOF, R_SHADOWCAT, NA, NA, NULL)) { + int howlong; + howlong = rnd(10,20); + addtempflag(lf->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL, howlong); + addtempflag(lf->flags, F_AUTOCREATEOB, 1, NA, NA, "puff of smoke", howlong+2); + } + // remove object removeob(o, 1); } else { @@ -6541,6 +6556,7 @@ int getmovespeed(lifeform_t *lf) { } char *getmoveverb(lifeform_t *lf) { + flag_t *f; if (lfhasflag(lf, F_CLIMBING)) { return "climb"; } @@ -6555,6 +6571,11 @@ char *getmoveverb(lifeform_t *lf) { if (isswimming(lf)) { return "swim"; } + + f = lfhasflag(lf, F_WALKVERB); + if (f) { + return f->text; + } return "walk"; } @@ -8518,12 +8539,21 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { } } else if (id == F_STARTOBCLASS) { if (rnd(1,100) <= val[0]) { + int depthmod; if (db) { snprintf(buf2, BUFLEN, "calling startobclass"); } + + depthmod = val[2]; + switch (depthmod) { + case NA: depthmod = 0; break; + case RANDOM: depthmod = rnd(0,MAXDEPTH); break; + default: break; + } + //obdb = B_TRUE; //if (getrandomobwithclass(targmap, val[1], buf, val[2])) { - if (real_getrandomob(targmap, buf, getmapdifficulty(targmap) + val[2], NA, maxobsize, B_TRUE, + if (real_getrandomob(targmap, buf, getmapdifficulty(targmap) + depthmod, NA, maxobsize, B_TRUE, val[1], OC_NONE, DT_NONE)) { if (db) snprintf(buf2, BUFLEN, "finished startobclass, success."); o = addob(op, buf); @@ -13085,7 +13115,7 @@ int startclimbing(lifeform_t *lf) { } int startresting(lifeform_t *lf, int willtrain) { - int traincounter; + flag_t *f; // player can't rest while in the air, unless you're in a motel room if (isplayer(lf)) { @@ -13095,14 +13125,6 @@ int startresting(lifeform_t *lf, int willtrain) { } } - - if (willtrain) { - traincounter = 50; - traincounter = modifybystat(traincounter, player, A_IQ); - } else { - traincounter = NA; - } - // stop hiding killflagsofid(lf->flags, F_HIDING); @@ -13112,7 +13134,19 @@ int startresting(lifeform_t *lf, int willtrain) { killflagsofid(lf->flags, F_INTERRUPTED); if (willtrain) { - addflag(lf->flags, F_TRAINING, B_TRUE, NA, traincounter, NULL); + f = lfhasflag(lf, F_TRAINING); + if (f) { + int trainloss; + trainloss = 25; + trainloss = modifybystat(trainloss, player, A_IQ); + f->val[0] -= trainloss; + limit(&(f->val[0]), 0, NA); + } else { + int traincounter; + traincounter = 50; + traincounter = modifybystat(traincounter, player, A_IQ); + addflag(lf->flags, F_TRAINING, 0, traincounter, NA, NULL); + } } else { if (gotosleep(lf, B_TRUE)) { // failed @@ -13918,7 +13952,7 @@ void interrupt(lifeform_t *lf) { killflagsofid(lf->flags, F_AUTOCMD); } -int setlfmaterial(lifeform_t *lf, enum MATERIAL id) { +int setlfmaterial(lifeform_t *lf, enum MATERIAL id, int wantannounce) { if (getlfmaterial(lf) == id) { return B_TRUE; } @@ -13929,7 +13963,7 @@ int setlfmaterial(lifeform_t *lf, enum MATERIAL id) { lf->material = findmaterial(id); // announce - if (gamemode == GM_GAMESTARTED) { + if (wantannounce && (gamemode == GM_GAMESTARTED)) { if (isplayer(lf)) { msg("^wYour body %s to %s%c", (id == lf->race->material->id) ? "reverts" : "turns", lf->material->name, (id == lf->race->material->id) ? '.' : '!' ); @@ -16803,8 +16837,8 @@ int rest(lifeform_t *lf, int onpurpose) { if (training) { wantclearmsg = B_FALSE; - rf->val[2]--; - if (rf->val[2] == 0) { + rf->val[0]++; + if (rf->val[0] >= rf->val[1]) { // ask about gaining skills if (isplayer(lf)) { msg("You finish training."); diff --git a/lf.h b/lf.h index 39fedaf..678de81 100644 --- a/lf.h +++ b/lf.h @@ -374,7 +374,7 @@ void setkillverb(lifeform_t *lf, char *buf); void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph); void setlastdam(lifeform_t *lf, char *buf); //void setlftarget(lifeform_t *lf, lifeform_t *victim); -int setlfmaterial(lifeform_t *lf, enum MATERIAL id); +int setlfmaterial(lifeform_t *lf, enum MATERIAL id, int wantannounce); void setlosdirty(lifeform_t *lf); void setstamina(lifeform_t *lf, float howmuch); int shoot(lifeform_t *lf); diff --git a/map.c b/map.c index a077c0a..58674f9 100644 --- a/map.c +++ b/map.c @@ -4997,7 +4997,11 @@ int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) { *thing = c->lf; return TT_MONSTER; } - if (lfhasflagval(player, F_GRABBEDBY, c->lf->id, NA, NA, NULL)) { + f = lfhasflagval(player, F_GRABBEDBY, c->lf->id, NA, NA, NULL); + if (!f) { + f = lfhasflagval(c->lf, F_ATTACHEDTO, player->id, NA, NA, NULL); + } + if (f) { if (glyph) { glyph->ch = 'X'; glyph->colour = C_GREY; @@ -5005,7 +5009,11 @@ int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) { *thing = c->lf; if (desc) { getlfname(c->lf, desc); - strcat(desc, " (holding you)"); + if (f->id == F_GRABBEDBY) { + strcat(desc, " (holding you)"); + } else { + strcat(desc, " (attached to you)"); + } } return TT_MONSTER; } diff --git a/objects.c b/objects.c index 25d03fe..d14e893 100644 --- a/objects.c +++ b/objects.c @@ -4575,7 +4575,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan srcp = gethiddennameot(ot->id); // if this is "potion of xxx" - if (strstarts(srcp, "potion ")) { + if (strstarts(srcp, "potion ") || strstarts(srcp, "vial ") || strstarts(srcp, "flask ")) { // skip first word from potion name. // ie. we're left with " of xxx" while (*srcp != ' ') { @@ -5140,9 +5140,10 @@ char *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, obmod_t *om; flag_t *omposs[MAXCANDIDATES]; int noms = 0; - enum RARITY wantrr = RR_FREQUENT; + enum RARITY wantrr = RR_FREQUENT,origwantrr; habitat_t *hab; char habname[BUFLEN]; + int rrmoddir = -1; if (!db) db = obdb; @@ -5185,6 +5186,7 @@ char *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, // pick rr... wantrr = pickrr(TT_OBJECT); + origwantrr = wantrr; // no obclass given? pick one randomly. if (!nwantclass) { @@ -5317,17 +5319,30 @@ char *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, // already at lowest rarity? if ((raritymax >= 100) && (raritymin <= 0)) { // now lower wantrr - if (wantrr > RR_FREQUENT) { - if (db) dblog("rarity at min/max and no obs. lowering wantrr to %d.",wantrr); - wantrr--; - } else { - // give up - strcpy(buf, ""); - if (db || partdb) dblog("no possible random objects at all for habitat %s! giving up.", habname); - if (!hab) { - dblog("xxx"); + if (rrmoddir == -1) { + if (wantrr > RR_FREQUENT) { + wantrr--; + if (db) dblog("rarity at min/max and no obs. lowering wantrr to %d.",wantrr); + } else { + // wantrr is already at rr_frequent + // start increasing it now. + wantrr = origwantrr + 1; + rrmoddir = 1; + if (db) dblog("rarity got below frequent. raising to original rr + 1 (%d)",wantrr); + } + } else { + if (wantrr < RR_VERYRARE) { + wantrr++; + if (db) dblog("rarity at min/max and no obs. raising wantrr to %d.",wantrr); + } else { + // give up + strcpy(buf, ""); + if (db || partdb) dblog("no possible random objects at all for habitat %s! giving up.", habname); + if (!hab) { + dblog("xxx"); + } + return NULL; } - return NULL; } } else { // expand range and try again @@ -9284,6 +9299,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE } modhunger(lf, -((float)HUNGERCONST/4)); killflagsofid(lf->flags, F_POISONED); // cure poison + gainhp(lf,rnd(1,5)); break; case OT_POT_SOUPMUSHROOM: case OT_POT_SOUPTOMATO: @@ -9293,6 +9309,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE } modhunger(lf, -((float)HUNGERCONST/2)); modstamina(lf, 2); + gainhp(lf,rnd(1,5)); break; case OT_POT_SUGARWATER: if (isplayer(lf)) { @@ -9300,6 +9317,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE if (seen) *seen = B_TRUE; } modstamina(lf, 4); + gainhp(lf,rnd(1,5)); break; case OT_POT_STROGONOFF: if (isplayer(lf)) { @@ -9309,6 +9327,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE modhunger(lf, -((float)HUNGERCONST)); // boost fitness addtempflag(lf->flags, F_ATTRMOD, A_CON, 3, NA, NULL, rnd(50,100)); + gainhp(lf,rnd(2,12)); break; default: // nothing happens break; diff --git a/spell.c b/spell.c index bf67d9f..842bcba 100644 --- a/spell.c +++ b/spell.c @@ -1369,9 +1369,6 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } howlong = DEF_RAGETIME; addtempflag(user->flags, F_RAGE, B_TRUE, NA, NA, NULL, howlong); - if (isplayer(user)) { - forceredraw(); - } } else if (abilid == OT_A_REPAIR) { object_t *o,*helpob = NULL; enum MATERIAL repairablemats[MAXCANDIDATES]; @@ -3266,6 +3263,52 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else { fizzle(caster); } + } else if (spellid == OT_S_ANIMATESTONE) { + object_t *o; + target = targcell->lf; + if (target) { + if (lfhasflag(target, F_BEINGSTONED)) { + killflagsofid(target->flags, F_BEINGSTONED); + if (cansee(player, target)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + return B_FALSE; + } else { + fizzle(caster); + return B_FALSE; + } + } + + // otherwise find first statue + o = hasob(targcell->obpile, OT_STATUE); + if (o) { + lifeform_t *lf = NULL; + char obname[BUFLEN]; + flag_t *f; + // what is it a statue of? + f = hasflag(o->flags, F_CORPSEOF); + if (!f) { + fizzle(caster); + return B_FALSE; + } + // add it! + getobname(o, obname, 1); + removeob(o, ALL); + lf = addmonster(targcell, f->val[0], NULL, B_FALSE, 1, B_FALSE, NULL); + setlfmaterial(lf, MT_STONE, B_FALSE); + if (cansee(player, lf)) { + msg("%s comes to life!",obname); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + if (caster) { + petify(lf, caster); + } + // no corpse after death (so you can't keep reanimating it) + addflag(lf->flags, F_NOCORPSE, NA, NA, NA, NULL); + } else { + fizzle(caster); + return B_FALSE; + } } else if (spellid == OT_S_APPORTATION) { int failed = B_FALSE; float maxweight; @@ -3359,7 +3402,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ //f = addtempflag(caster->flags, F_MAGICARMOUR, power*2, NA, NA, "skin of bark", FROMSPELL); //f->obfrom = spellid; - setlfmaterial(target, MT_WOOD); + setlfmaterial(target, MT_WOOD, B_TRUE); f = addtempflag(caster->flags, F_DTVULN, DT_FIRE, NA, NA, "2d4", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_BLADEBURN) { @@ -10499,7 +10542,7 @@ void stopspell(lifeform_t *caster, enum OBTYPE spellid) { } if ((spellid == OT_S_BARKSKIN) && (getlfmaterial(caster) == MT_WOOD)) { - setlfmaterial(caster, caster->race->material->id); + setlfmaterial(caster, caster->race->material->id, B_TRUE); } else if (spellid == OT_S_FLOATINGDISC) { map_t *m; lifeform_t *lf;