From da656dfa521e1f853587c2825b9a4a46626dc20c Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Wed, 14 Sep 2011 22:42:54 +0000 Subject: [PATCH] - [+] add autopop to playerstart vaults - [+] rename magic skills: Magic:Cold - [+] bug: infinite loop in poison arrow trap - make sure the arrow always dies. - [+] bug: c4 didn't kill iron door - [+] implement stamina (float). max is Fit/2 * [+] in startlfturn: - [+] sprinting drains this. - [+] ...then stop using f_sprinting for exhausted - [+] ...and remove f_tired - [+] if exhausted, stop sprinting. (in modstamina) - [+] change crushed windpipe - [+] f_stamcost for abilities. - [+] modify cancast. - [+] tumbling - [+] jumping - [+] rage - [+] swimming - [+] drains stamina like sprinting - [+] if stamina drops to 0, you start drowning. - [+] new spell: lethargy (sets stamina to 0) - [+] if a sleep spell fails, use lethergy instead. - [+] no attacking while stam = 0 ??? - [+] need to update statbar right away when casting ongoing spells. - [+] bug: reading an awareness scroll counting as an active spell! - [+] genericise magic resistance check into a function - [+] "disorient" - l1 mental spell which randomly turns lf, someitmes makes them dizzy - [+] change stun - just means you can't attack, cast spell, use abils * [+] AI shouldn't look for targets if stunned or no stamina * [+] why do mosnters end up facing -1 (d_none) ?? - [+] turn undead problem. - [+] The skeleton turns to flee from you! The skeleton bites you. - [+] crit which spins you around (bash to body) - [+] say "you attack xxx from behind" when you ar ebehidn them and they can't see you - [+] or "you attack the helpless xxx" when thye just can't see you - [+] genericise sacrifice text - [+] fix up weight of heads (8% of body mass) - [+] make attribss do more: - [+] iq: determine how soon you learn new skills (ie. modify SKILLXPPERPOINT) - [+] fit: determines stamina points. - [+] wisdom >= AT_HIGH - [+] warn before wearing/eating/drinking/weilding unknown bad/cursed objects (low chance) - [+] use isbadobject() - [+] chance: - [+] high = 10% - [+] vhigh = 30% - [+] exhigh = 50% - [-] idea: sacrifice to gods to make them happier - [+] mercy: weapons - [+] death: any corpses - [+] thieves: gold --- ai.c | 75 ++++---- ai.h | 1 + attack.c | 70 ++++++-- data/hiscores.db | Bin 5120 -> 5120 bytes defs.h | 28 ++- doc/add_god | 4 + doc/glyphs.txt | 4 +- flag.c | 49 +---- io.c | 292 +++++++++++++++++++++--------- io.h | 3 + lf.c | 383 ++++++++++++++++++++++++++-------------- lf.h | 4 + map.c | 20 ++- map.h | 1 + move.c | 25 ++- nexus.c | 1 + objects.c | 89 +++++++--- objects.h | 3 +- save.c | 2 + spell.c | 319 ++++++++++++++++++--------------- spell.h | 2 + vaults/playerstart1.vlt | 1 + vaults/playerstart2.vlt | 1 + vaults/playerstart3.vlt | 1 + vaults/playerstart4.vlt | 1 + vaults/playerstart5.vlt | 1 + vaults/playerstart6.vlt | 1 + vaults/playerstart7.vlt | 1 + 28 files changed, 881 insertions(+), 501 deletions(-) diff --git a/ai.c b/ai.c index 0c6d130..d5447af 100644 --- a/ai.c +++ b/ai.c @@ -36,6 +36,10 @@ int aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit) { db = B_TRUE; } + if (!canattack(lf)) { + return B_FALSE; + } + // mindless? if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) == IQ_MINDLESS) { if (!isundead(lf)) { @@ -1331,19 +1335,8 @@ void aiturn(lifeform_t *lf) { // to attack, etc) /////////////////////////////////////////////// - // look for any object which we want - /* - if (!isinbattle(lf)) { - if (db) dblog(".oO { looking for any ob which i want. }"); - if (lookforobs(lf, B_ANY)) { - if (db) dblog(".oO { found ob that i want. returning. }"); - return; - } - } - */ - // not attacking anyone in particular - if (db) dblog(".oO { i do not have a target or can't move towards it. looking for one. }"); + 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)) { @@ -1364,33 +1357,36 @@ void aiturn(lifeform_t *lf) { } } - // look for any race which we hate - newtarget = NULL; - for (n = 0; n < lf->nlos; n++) { - lifeform_t *who; - if (lf->los[n] != lf->cell) { - who = lf->los[n]->lf; - if (who && cansee(lf, who)) { - if (lfhasflagval(lf, F_HATESRACE, who->race->id, NA, NA, NULL) || - lfhasflagval(lf, F_HATESRACE, who->race->baseid, NA, NA, NULL) ) { - if (db) dblog(".oO { found a hated target - lfid %d (%s) ! }",who->id, who->race->name); - newtarget = who; - break; - } - } - } - } - if (!newtarget) { - // now look for enemies + if (!lfhasflag(lf, F_STUNNED)) { + if (db) dblog(".oO { looking for a target . }"); + // look for any race which we hate + newtarget = NULL; for (n = 0; n < lf->nlos; n++) { + lifeform_t *who; if (lf->los[n] != lf->cell) { - lifeform_t *who; who = lf->los[n]->lf; if (who && cansee(lf, who)) { - if (areenemies(lf, who)) { - if (db) dblog(".oO { found an enemy target - lfid %d (%s) ! }",who->id, who->race->name); + if (lfhasflagval(lf, F_HATESRACE, who->race->id, NA, NA, NULL) || + lfhasflagval(lf, F_HATESRACE, who->race->baseid, NA, NA, NULL) ) { + if (db) dblog(".oO { found a hated target - lfid %d (%s) ! }",who->id, who->race->name); newtarget = who; break; + } + } + } + } + if (!newtarget) { + // now look for enemies + for (n = 0; n < lf->nlos; n++) { + if (lf->los[n] != lf->cell) { + lifeform_t *who; + who = lf->los[n]->lf; + if (who && cansee(lf, who)) { + if (areenemies(lf, who)) { + if (db) dblog(".oO { found an enemy target - lfid %d (%s) ! }",who->id, who->race->name); + newtarget = who; + break; + } } } } @@ -1483,8 +1479,6 @@ void aiturn(lifeform_t *lf) { dorandommove(lf, B_NOBADMOVES, B_TRUE); // this function will call rest() if we cant move } - - // is the spell 'spellid' okay for AI lifeform 'lf' to cast at 'victim', for given purpose. // purpose could be F_AICASTTOFLEE or F_ATCASTTOATTACK int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG purpose) { @@ -1529,6 +1523,8 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG char why[BUFLEN]; if (reason == E_NOMP) { strcpy(why, "not enough mp"); + } else if (reason == E_NOSTAM) { + strcpy(why, "not enough stamina"); } else if (reason == E_TOOPOWERFUL) { strcpy(why, "spell too powerful"); } else if (reason == E_NOTREADY) { @@ -1756,7 +1752,7 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG if ((ot->id == OT_S_SMITEEVIL) && (getalignment(victim) != AL_EVIL)) { specificcheckok = B_FALSE; } - if ((ot->id == OT_A_SPRINT) && lfhasflag(lf, F_SPRINTING)) { + if ((ot->id == OT_A_SPRINT) && (lfhasflag(lf, F_SPRINTING) || !lf->stamina)) { specificcheckok = B_FALSE; } if ((ot->id == OT_A_STEAL) || (ot->id == OT_S_CONFISCATE)) { @@ -2073,6 +2069,13 @@ int lookforobs(lifeform_t *lf) { return B_FALSE; } +int loseaitargets(lifeform_t *lf) { + int donesomething = B_FALSE; + if (killflagsofid(lf->flags, F_TARGETLF)) donesomething = B_TRUE; + if (killflagsofid(lf->flags, F_TARGETCELL)) donesomething = B_TRUE; + return donesomething; +} + void makewantedoblist(lifeform_t *lf, int *noids, enum OBTYPE *oid, int *oidcovet,int *nwantflags, enum FLAG *wantflag, int *wantflagcovet) { int i; flag_t *f; diff --git a/ai.h b/ai.h index 7d25426..814f251 100644 --- a/ai.h +++ b/ai.h @@ -23,5 +23,6 @@ lifeform_t *gettargetlf(lifeform_t *lf); object_t *hasbetterarmour(lifeform_t *lf, obpile_t *op); object_t *hasbetterweapon(lifeform_t *lf, obpile_t *op); int lookforobs(lifeform_t *lf); +int loseaitargets(lifeform_t *lf); void makewantedoblist(lifeform_t *lf, int *noids, enum OBTYPE *oid, int *oidcovet,int *nwantflags, enum FLAG *wantflag, int *wantflagcovet); int useitemwithflag(lifeform_t *lf, enum FLAG whichflag); diff --git a/attack.c b/attack.c index 1816d07..ef2315a 100644 --- a/attack.c +++ b/attack.c @@ -17,6 +17,8 @@ extern lifeform_t *player; +extern enum ERROR reason; + int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype) { object_t *armour = NULL; int damtaken = 0; @@ -163,7 +165,6 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { int maxattacks = ALL; int attacksdone = 0; int lastweaponidx = -1; - flag_t *sf; int saysorry = B_FALSE; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; @@ -243,12 +244,24 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { } } - // stop sprinting - sf = lfhasflag(lf, F_SPRINTING); - if (sf && sf->val[0]) { - killflag(sf); + // can you actually attack? + if (!canattack(lf)) { + if (isplayer(lf)) { + switch (reason) { + case E_NOSTAM: msg("You are too tired to fight at the moment."); break; + case E_STUNNED: msg("You are too stunned to fight at the moment."); break; + default: msg("For some reason, you cannot attack."); break; + } + } + return B_TRUE; } + // lose a tiny bit of stamina + modstamina(lf, -0.2); + + // stop sprinting + stopsprinting(lf); + // ai code... if (lfhasflag(lf, F_DEMANDSBRIBE)) { if (!isplayer(lf) && (attacktype == AT_LF) && isplayer((lifeform_t *)attacktarget)) { @@ -312,10 +325,7 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { innateattacks = countinnateattacks(lf); // stop sprinting - f = lfhasflag(lf, F_SPRINTING); - if (f && f->val[0]) { - killflag(f); - } + stopsprinting(lf); // take time attacktime = getattackspeed(lf); @@ -377,8 +387,7 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { } } - // remember initial cells - + attacksdone = 0; while (attacksdone < maxattacks) { for (i = 0; (i < nweps) && (attacksdone < maxattacks); i++) { @@ -387,11 +396,26 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { if (!isdead((lifeform_t *)attacktarget)) { lifeform_t *victim; victim = (lifeform_t *)attacktarget; - // did we just attack someone by accident? - if (!isplayer(lf) && !areenemies(lf, victim) && (lf->race->raceclass->id == RC_HUMANOID) && - (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) >= A_LOW) ) { - saysorry = B_TRUE; + if (i == 0) { + // did we just attack someone by accident? + if (!isplayer(lf) && !areenemies(lf, victim) && (lf->race->raceclass->id == RC_HUMANOID) && + (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) >= A_LOW) ) { + saysorry = B_TRUE; + } + + // announce helpless attcaks + if (isplayer(lf) && attackedhelpless) { + char vname[BUFLEN]; + getlfname(victim, vname); + if (isbehind(lf, victim)) { + msg("You attack %s from behind!", vname); + } else { + msg("You attack the helpless %s!", vname); + } + } } + + if (attacklf(lf, victim, wep[i], damflag[i])) { // failed attacksdone = maxattacks; @@ -1370,6 +1394,7 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { void criticalhit(lifeform_t *lf, lifeform_t *victim, enum BODYPART hitpos, enum DAMTYPE damtype) { object_t *o; + char lfname[BUFLEN],victimname[BUFLEN]; // replace some dam types if (damtype == DT_UNARMED) damtype = DT_BASH; if (damtype == DT_BITE) damtype = DT_SLASH; @@ -1380,7 +1405,20 @@ void criticalhit(lifeform_t *lf, lifeform_t *victim, enum BODYPART hitpos, enum switch (hitpos) { default: case BP_BODY: - if (pctchance(40)) fall(victim, lf, B_TRUE); + if (pctchance(40)) { + // some kind of non-injury effect + switch (rnd(1,2)) { + case 1: fall(victim, lf, B_TRUE); break; + case 2: + if (cansee(player, lf) || cansee(player, victim)) { + getlfname(lf, lfname); + getlfname(victim, victimname); + setfacing(victim, getrandomdirexcept(DT_COMPASS, victim->facing)); + msg("%s%s blow spins %s around!", lfname, getpossessive(lfname),victimname); + } + break; + } + } if (!getarmour(victim, BP_BODY)) injure(victim, BP_BODY, damtype); break; case BP_HEAD: diff --git a/data/hiscores.db b/data/hiscores.db index 5c975bbbc44db485fcccfa26bc52d59d53caf4e1..cbbc7eca87ba25782ead60f6f98e29064f022826 100644 GIT binary patch delta 287 zcmZqBXwaA-%^18<<^?;mI9Oib7JQLZU)der{4` zs-A{2$N)`Usma{zvb+_EC3>1lAQ44#!^sO-B`5c@a&oFFLTu)Pxn*-1Ul%I?2mL^N delta 226 zcmZqBXwaA-&FHgH<^?;m0&~J-b`HnQi5z#BCN|hiPT*dm#>G^|z6HJSl%9yJeUo!4uoXD8YXv@g6S&PSzQLIv-AV0G>KQA*cT~9-pje$v6(U@V$ zl2nTdHN3dIGPDXB$z>H-jjIbelad|;lX8%UuP vL}5{4iJmDJ$aGCt=2u1eNj_yJlLgtu1tHSOsd*)t1*s{idJLP(__|mDJ@Y#r diff --git a/defs.h b/defs.h index 248a5a3..9c864f2 100644 --- a/defs.h +++ b/defs.h @@ -266,6 +266,7 @@ enum RELATIVEDIR { #define CH_TURN_NE (21) // ctrl-u #define CH_TURN_SW (2) // ctrl-b #define CH_TURN_SE (14) // ctrl-n +#define CH_HISTORY (16) // ctrl-p // SPECIAL NUMBERS/CONSTANTS #define DUMMYCELLTYPE 0xabcd @@ -1157,9 +1158,11 @@ enum OBTYPE { // -- mental / psionic OT_S_BAFFLE, OT_S_CHARM, + OT_S_DISORIENT, OT_S_HUNGER, - OT_S_MINDSCAN, + OT_S_LETHARGY, OT_S_LOWERMETAB, + OT_S_MINDSCAN, OT_S_PACIFY, OT_S_PSYARMOUR, OT_S_SLEEP, @@ -1681,6 +1684,8 @@ enum FLAG { F_NEWWATERDEPTH, // temp flag for the spread of f_deepwater obs. // v0+1 are x/y, v2 is new depth. // object flags + F_BADOBJECT, // this object is dangerous. ie. potion of poison, + // potion of sleep, etc. F_BEINGUSED, // this object is currently being used F_DEAD, // object will be removed F_ONEPERCELL, // only one of these objects can exist per cell @@ -1985,7 +1990,8 @@ enum FLAG { // for spells with this flag, the MP cost goes up // based on the power level. F_MAXPOWER, // val0 = max power of this spell (1-10) - F_MPCOST, // v0=mp cost of spell. if missing, mpcost if splev^2 + F_MPCOST, // v0=mp cost of spell. if missing, mpcost is splev^2 + F_STAMCOST, // v0=stamina cost of ability. default is 0. F_ONGOING, // this spell has an ongoing cost F_CASTINGTIME, // this spell takes v0 turns to cast F_EXTRADESC, // extra descriptions for this object @@ -2209,6 +2215,16 @@ enum FLAG { // if v0 is b_true, means this is a goddess F_GODLIKES, // text = something this god likes (ie. incs piety) F_GODDISLIKES, // text = something this god likes (ie. decs piety) + // for all sacrifice flags: + // v2: amt of piety for each sacrifice + // text: "the xxx disappears in yyyy" + // (yyyy is text) + // IS is replaced with "is" or "are" + // OB is replace with object name + F_SACRIFICEOBWITHFLAG, // v0 = can sacrifice obs with flag v0 to this go + F_SACRIFICEOB, // v0 = can sacrifice obtype v0 to this god + F_SACRIFICEOBCLASS, // v0 = can sacrifice obclass v0 to this god + F_NAME, // text = lf's name F_XPMOD, // add/subtract this much from calculated xpval F_BLOODOB, // text = type of object to drop for blood @@ -2362,6 +2378,7 @@ enum FLAG { F_SILENTMOVE, // lf makes no noise when walking/flying F_STABILITY, // doesn't slip over F_STENCH, // creatures within v0 gain f_nauseated = v1 + F_STUNNED, // cannot attack or cast spells F_TREMORSENSE, // doesn't need eyes to see, can see in dark with v0 F_TRUESTRIKE, // your attacks ALWAYS hit. turnsleft=v0 F_PRODUCESLIGHT, // produces light of val0 radius. @@ -2374,8 +2391,7 @@ enum FLAG { F_SLOWACTMOVE, // modifier for move and action speed F_XRAYVIS, //val0=num of walls we can see through F_CANSEETHROUGHMAT, //val0=kind of material you can see through - F_SPRINTING, // v0=true: you are sprinting. false=you are tired - F_TIRED, // you are too tired to sprint, rage, etc + F_SPRINTING, // you are sprinting. F_WINDSHIELD,// has a windshield protecting against missiles of speed // v0 or lower. F_DODGES, // you dodge missed attacks @@ -2580,6 +2596,7 @@ enum ERROR { E_NOAMMO, E_GRAVBOOSTED, E_NOMP, + E_NOSTAM, E_AVOIDOB, E_FROZEN, E_TOOBIG, @@ -2600,6 +2617,7 @@ enum ERROR { E_LOWWIS, E_WONT, E_OFFMAP, + E_STUNNED, // charm failure reasons // LOWIQ E_UNDEAD, @@ -2640,6 +2658,7 @@ enum COMMAND { CMD_MAGIC, CMD_MEMMAGIC, CMD_MSGHIST, + CMD_OFFER, CMD_OPERATE, CMD_PICKLOCK, CMD_PICKUP, @@ -2910,6 +2929,7 @@ typedef struct lifeform_s { int skillpoints; int hp,maxhp; int mp,maxmp; + float stamina; int alive; char *lastdam; struct material_s *material; diff --git a/doc/add_god b/doc/add_god index 2865b81..d6f09f4 100644 --- a/doc/add_god +++ b/doc/add_god @@ -1,3 +1,7 @@ +NOTE: + - make sure god names all start with different letters + - keep addlf() calls for gods in alphabetical order + In defs.h Add R_GODxxxx update MAXGODS diff --git a/doc/glyphs.txt b/doc/glyphs.txt index 9004803..a9f0914 100644 --- a/doc/glyphs.txt +++ b/doc/glyphs.txt @@ -43,8 +43,8 @@ hybrid human animal? ---------- ~ = deep liquid (water / lava) -[ = tool -] = armour +] = tool +[ = armour } = gas , = small puddle { = large puddle/pool diff --git a/flag.c b/flag.c index b293714..73b06ad 100644 --- a/flag.c +++ b/flag.c @@ -392,7 +392,6 @@ int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { case F_AWARENESS: case F_SPRINTING: case F_SLOWMOVE: - case F_TIRED: return B_TRUE; default: break; @@ -436,7 +435,7 @@ int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid) { case F_RAGE: case F_SPRINTING: case F_SLOWMOVE: - case F_TIRED: + case F_STUNNED: case F_TRAINING: return B_TRUE; default: @@ -982,18 +981,6 @@ void timeeffectsflag(flag_t *f, int howlong) { break; } } - } else if (f->lifetime == 3) { - if (isplayer(f->pile->owner)) { - switch (f->id) { - case F_SPRINTING: - if (f->val[0]) { - warn("You will have to stop sprinting soon..."); - } - break; - default: - break; - } - } } else if (f->lifetime == 2) { if (isplayer(f->pile->owner)) { switch (f->id) { @@ -1042,40 +1029,6 @@ void timeeffectsflag(flag_t *f, int howlong) { break; } } - // sprinting is special - if (f->id == F_SPRINTING) { - if (f->val[0]) { - enum SKILLLEVEL slev; - lifeform_t *who; - int tiredtime; - who = f->pile->owner; - // now you get slow - // you get tired when you finish sprinting - tiredtime = 15; - - // adjust for athletics skill. -2 per level. - slev = getskill(who, SK_ATHLETICS); - if (slev != PR_INEPT) { - tiredtime -= (2*slev); - } - // adjust for constitution - tiredtime = tiredtime - (int) ((float)tiredtime * (getstatmod(who, A_CON) / 100) ); - // enforce minimum - if (tiredtime < 1) tiredtime = 1; - f->val[0] = B_FALSE; - f->lifetime = tiredtime; - - if (isplayer(who)) { - msg("You are exhausted."); - } else if (cansee(player, who)) { - char lfname[BUFLEN]; - getlfname(who, lfname); - msg("%s looks exhausted.",lfname); - } - needredraw = B_TRUE; - statdirty = B_TRUE; - } - } } } diff --git a/io.c b/io.c index db521f4..34ab0d8 100644 --- a/io.c +++ b/io.c @@ -1586,13 +1586,9 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { msg("%s %s sprinting!",lfname, isplayer(lf) ? "start" : "starts"); donesomething = B_TRUE; break; - - case F_TIRED: - if (isplayer(lf)) { - msg("You are exhausted."); - } else if (cansee(player, lf)) { - msg("%s looks exhausted.",lfname); - } + case F_STUNNED: + msg("%s %s stunned!",lfname, is(lf)); + donesomething = B_TRUE; break; case F_TREMORSENSE: if (isplayer(lf)) { // don't know if monsters get it @@ -2131,25 +2127,13 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; break; case F_SPRINTING: - if (f->val[0]) { - if (isplayer(lf)) { // don't know if monsters lose it (but you'll see them get exhausted) - msg("You stop sprinting."); - donesomething = B_TRUE; - } - } else { - if (isplayer(lf)) { // don't know if monsters get it - msg("You are no longer exhausted."); - } else { - msg("%s looks less exhausted.",lfname); - } + if (isplayer(lf)) { // don't know if monsters lose it (but you'll see them get exhausted) + msg("You stop sprinting."); + donesomething = B_TRUE; } break; - case F_TIRED: - if (isplayer(lf)) { // don't know if monsters get it - msg("You are no longer exhausted."); - } else { - msg("%s looks less exhausted.",lfname); - } + case F_STUNNED: + msg("%s %s no longer stunned.",lfname, is(lf)); donesomething = B_TRUE; break; case F_TREMORSENSE: @@ -2317,6 +2301,35 @@ void announceobflagloss(object_t *o, flag_t *f) { } +int confirm_badfeeling(void) { + char ch; + ch = askchar("You have a bad feeling about this. Continue?", "yn", "n", B_TRUE); + if (ch == 'y') return B_TRUE; + return B_FALSE; +} + +lifeform_t *askgod(char *prompttext) { + lifeform_t *lf = NULL; + int i; + + initprompt(&prompt, prompttext); + prompt.maycancel = B_TRUE; + for (i = 0 ; i < ngodlfs; i++) { + flag_t *f; + char godof[BUFLEN],buf[BUFLEN]; + lf = godlf[i]; + real_getlfname(lf, buf, B_FALSE); + f = hasflag(lf->flags, F_GODOF); + snprintf(godof, BUFLEN, " (%s of %s)", (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); + strcat(buf, godof); + addchoice(&prompt, tolower(buf[0]), buf, NULL, lf); + } + + getchoice(&prompt); + lf = (lifeform_t *)prompt.result; + return lf; +} + object_t *askobject(obpile_t *op, char *prompt, int *count, long opts) { return doaskobject(op, prompt, count, B_TRUE, B_FALSE, opts, F_NONE); } @@ -3062,10 +3075,10 @@ void describegod(lifeform_t *god) { char godname[BUFLEN]; char goddesc[BUFLEN]; int i; - flag_t *f; - flag_t *retflag[MAXCANDIDATES]; + flag_t *f, *retflag[MAXCANDIDATES]; int nretflags; cls(); + assert(god); f = hasflag(god->flags, F_GODOF); real_getlfname(god, godname, B_FALSE); @@ -3105,6 +3118,28 @@ void describegod(lifeform_t *god) { } wprintw(mainwin, ".\n\n"); + getflags(god->flags, retflag, &nretflags, F_SACRIFICEOB, F_SACRIFICEOBCLASS, F_NONE); + if (nretflags == 0) { + wprintw(mainwin, "%s does not accept sacrifices.\n", godname); + } else { + int i; + getflags(god->flags, retflag, &nretflags, F_SACRIFICEOBCLASS, F_NONE); + for (i = 0; i < nretflags; i++) { + objectclass_t *oc; + oc = findoc(retflag[i]->val[0]); + wprintw(mainwin, "%s accepts the sacrifice of all %s.\n",godname, oc->name); + } + getflags(god->flags, retflag, &nretflags, F_SACRIFICEOB, F_NONE); + for (i = 0; i < nretflags; i++) { + objecttype_t *ot; + if (i == 0) { + wprintw(mainwin, "%s accepts the sacrifice of:\n", godname); + } + ot = findot(retflag[i]->val[0]); + wprintw(mainwin, "- %s\n", ot->name); + } + } + wrefresh(mainwin); // wait for key @@ -3879,9 +3914,6 @@ void describeob(object_t *o) { case F_WINDSHIELD: mvwprintw(mainwin, y, 0, "%s will surround you with a cyclonic shield.", buf); y++; break; - case F_TIRED: - mvwprintw(mainwin, y, 0, "%s make you tired.", buf); y++; - break; case F_VISRANGEMOD: mvwprintw(mainwin, y, 0, "%s %s your vision range.", buf, (f->val[1] > 0) ? "increases" : "descreases"); y++; break; @@ -4677,7 +4709,7 @@ void dodrop(obpile_t *op, int wantmulti, obpile_t *dst) { } void doeat(obpile_t *op) { - object_t *o; + object_t *o,*eatob = NULL; char ch; char buf[BUFLEN]; char obname[BUFLEN]; @@ -4698,20 +4730,27 @@ void doeat(obpile_t *op) { (o->amt == 1) ? "it" : "one"); ch = askchar(buf, "yn","n", B_TRUE); if (ch == 'y') { - eat(player, o); - return; + eatob = o; } } } - if (!hasedibleob(player->pack)) { - msg("You have nothing to eat!"); - return; - } + if (!eatob) { + if (!hasedibleob(player->pack)) { + msg("You have nothing to eat!"); + return; + } - o = askobject(op, "Eat what", NULL, AO_EDIBLE); - if (o) { - eat(player, o); + eatob = askobject(op, "Eat what", NULL, AO_EDIBLE); + } + if (eatob) { + if (isunknownbadobject(eatob) && skillcheck(player, A_WIS, 26, 0)) { + if (!confirm_badfeeling()) { + msg("Cancelled."); + return; + } + } + eat(player, eatob); } } @@ -4824,6 +4863,12 @@ int dowear(obpile_t *op) { int rv; o = askobject(op, "Wear what", NULL, AO_WEARABLE); if (o) { + if (isunknownbadobject(o) && skillcheck(player, A_WIS, 26, 0)) { + if (!confirm_badfeeling()) { + msg("Cancelled."); + return B_TRUE; + } + } wear(player, o); } else { msg("Cancelled."); @@ -4840,6 +4885,12 @@ int doweild(obpile_t *op) { int rv; o = askobject(op, "Weild what", &count, AO_WEILDABLE | AO_INCLUDENOTHING); if (o) { + if (isunknownbadobject(o) && skillcheck(player, A_WIS, 26, 0)) { + if (!confirm_badfeeling()) { + msg("Cancelled."); + return B_TRUE; + } + } rv = weild(player, o); } else if (reason == E_SELNOTHING) { // ie. unweild @@ -5301,6 +5352,71 @@ void domsghist(void) { restoregamewindows(); } +void dooffer(void) { + object_t *o, *nexto; + lifeform_t *god; + flag_t *retflag[MAXCANDIDATES]; + int nretflags,pietyplus = 0; + // which god? + god = askgod("To whom will you sacrifice?"); + if (!god) { + msg("Cancelled."); + return; + } + getflags(god->flags, retflag, &nretflags, F_SACRIFICEOB, F_SACRIFICEOBCLASS, F_SACRIFICEOBWITHFLAG, F_NONE); + if (nretflags == 0) { + msg("%s does not accept sacrifices.", god->race->name); + return; + } + // anything here to offer? + for (o = player->cell->obpile->first ; o ; o = nexto) { + nexto = o->next; + // does the god want this? + getflags(god->flags, retflag, &nretflags, F_SACRIFICEOB, F_SACRIFICEOBCLASS, F_SACRIFICEOBWITHFLAG, F_NONE); + int i; + for (i = 0; i < nretflags; i++) { + int ok = B_FALSE; + int thispiety = 0; + flag_t *f; + f = retflag[i]; + if ((f->id == F_SACRIFICEOB) && (f->val[0] == o->type->id)) { + ok = B_TRUE; + thispiety = f->val[2]; + } else if ((f->id == F_SACRIFICEOBCLASS) && (f->val[0] == o->type->obclass->id)) { + ok = B_TRUE; + thispiety = f->val[2]; + } else if ((f->id == F_SACRIFICEOBWITHFLAG) && hasflag(o->flags, f->val[0])) { + ok = B_TRUE; + thispiety = f->val[2]; + } + if (ok) { + char *p; + char obname[BUFLEN]; + getobname(o, obname, ALL); + p = strdup(f->text); + p = strrep(p, "OB", obname, NULL); + if (o->amt == 1) { + p = strrep(p, "IS", "is", NULL); + } else { + p = strrep(p, "IS", "are", NULL); + } + msg("%s", p); + free(p); + removeob(o, ALL); + pietyplus += thispiety; + } + } + } + + if (pietyplus) { + pleasegod(god->race->id, pietyplus); + } else { + nothinghappens(); + } + taketime(player, getactspeed(player)); +} + + void dooperate(obpile_t *op) { object_t *o; @@ -5542,32 +5658,14 @@ void dohelp(char helpmode) { describeskill(sk->id); } } else if (helpmode == 'g') { - int i; + lifeform_t *god; centre(mainwin,C_WHITE, 0, "GOD REFERENCE"); y = 2; - initprompt(&prompt, "Describe which god (ESC when done)?"); - for (i = 0 ; i < ngodlfs; i++) { - lifeform_t *lf; - flag_t *f; - char buf[BUFLEN]; - char godof[BUFLEN]; - lf = godlf[i]; - real_getlfname(lf, buf, B_FALSE); - f = hasflag(lf->flags, F_GODOF); - snprintf(godof, BUFLEN, " (%s of %s)", (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); - strcat(buf, godof); - addchoice(&prompt, 'a', buf, NULL, lf); - } - - addchoice(&prompt, '\0', "(done)", NULL, NULL); - prompt.maycancel = B_TRUE; - ch = getchoicestr(&prompt, B_FALSE, B_TRUE); - if (!ch) { + god = askgod("Describe which god (ESC when done)?"); + if (!god) { done = B_TRUE; } else { - lifeform_t *god; - god = (lifeform_t *)prompt.result; describegod(god); } } @@ -5629,7 +5727,14 @@ void doquaff(obpile_t *op) { } if (liquid) { if (canquaff(player, liquid)) { + if (isunknownbadobject(liquid) && skillcheck(player, A_WIS, 26, 0)) { + if (!confirm_badfeeling()) { + msg("Cancelled."); + return; + } + } quaff(player, liquid); + } else { switch (reason) { case E_INSUBSTANTIAL: @@ -6359,7 +6464,7 @@ void dumpspells(void) { for (ot = objecttype ; ot ; ot = ot->next) { if (ot->obclass->id == OC_SPELL) { // matches the current school & level? - if ((getspellschool(ot->id) == ss) && (getspelllevel(ot->id) == lev)) { + if (hasflagval(ot->flags, F_SPELLSCHOOL, ss, NA, NA, NULL) && (getspelllevel(ot->id) == lev)) { dblog("\t\t%s", ot->name); } } @@ -7093,7 +7198,8 @@ void handleinput(void) { case ':': // look at what's here dolook(player->cell, B_TRUE); break; - case '|': // msg history - TODO: replace with ctrl-p + case CH_HISTORY: + case '|': // msg history domsghist(); break; case '/': // explain object @@ -7190,6 +7296,9 @@ void handleinput(void) { addflag(player->flags, F_LASTCMD, NA, NA, NA, temp); dooperate(player->pack); break; + case 'O': + dooffer(); + break; // GAME FUNCTIONS case 'Q': // quit doquit(); @@ -7481,6 +7590,13 @@ void drawstatus(void) { wprintw(statwin, "- "); } + // stamina + wattron(statwin, A_BOLD); wprintw(statwin, "SP:"); wattroff(statwin, A_BOLD); + setcol(statwin, getpctcol((int)player->stamina, (int)getmaxstamina(player))); + wprintw(statwin, "%d",(int)player->stamina); + unsetcol(statwin, getpctcol((int)player->stamina, (int)getmaxstamina(player))); + wprintw(statwin, "/%d ",(int)getmaxstamina(player)); + wattron(statwin, A_BOLD); wprintw(statwin, "Xp:"); wattroff(statwin, A_BOLD); snprintf(buf, BUFLEN, "%d", player->level); @@ -7547,6 +7663,11 @@ void drawstatus(void) { wprintw(statwin, " Woozy"); unsetcol(statwin, C_YELLOW); } + if (lfhasflag(player, F_STUNNED)) { + setcol(statwin, C_YELLOW); + wprintw(statwin, " Stunned"); + unsetcol(statwin, C_YELLOW); + } // paralysed somehow? if (isresting(player)) { @@ -8126,10 +8247,17 @@ void showlfstats(lifeform_t *lf, int showall) { if (showall || (getseenlfconditioncutoff(player) == C_HEALTHY) || (lorelev >= PR_ADEPT)) { - doheadingsmall(mainwin, y, 0, ftext, "Hit Points"); + int xx,yy; + + doheadingsmall(mainwin, y, 0, ftext, "HP"); if (lorelev >= PR_ADEPT) setcol(mainwin, lorecol); - wprintw(mainwin, "%d / %d", lf->hp , lf->maxhp); y++; + wprintw(mainwin, "%d/%d ", lf->hp , lf->maxhp); if (lorelev >= PR_ADEPT) unsetcol(mainwin, lorecol); + + getyx(mainwin, yy, xx); + doheadingsmall(mainwin, y, xx, "%5s:", "Stam"); + wprintw(mainwin, " %d/%d", (int)lf->stamina , (int)getmaxstamina(lf)); + y++; } else { char hpinfo[BUFLEN]; snprintf(hpinfo, BUFLEN, "%s",getseenlfconditionname(lf, player)); @@ -8137,10 +8265,10 @@ void showlfstats(lifeform_t *lf, int showall) { doheadingsmall(mainwin, y, 0, ftext, "Hit Points"); wprintw(mainwin, "%s", hpinfo); y++; } + } if (showall) { char maxmpstr[BUFLEN]; - if (getmaxmp(lf) == lf->maxmp) { strcpy(maxmpstr, ""); } else { @@ -8148,7 +8276,8 @@ void showlfstats(lifeform_t *lf, int showall) { } doheadingsmall(mainwin, y, 0, ftext, "Mana"); - wprintw(mainwin, "%d / %d%s", lf->mp , lf->maxmp,maxmpstr); y++; + wprintw(mainwin, "%d / %d%s ", lf->mp , lf->maxmp,maxmpstr); + y++; } if (showall) { if (isplayer(lf)) { @@ -8166,7 +8295,7 @@ void showlfstats(lifeform_t *lf, int showall) { int attpoints; int pct; attpoints = getattpoints(lf); - doheadingsmall(mainwin, y, 0, ftext, "Training"); + doheadingsmall(mainwin, y, 0, ftext, "Skill Pts"); pct = ((float)lf->skillxp / (float)SKILLXPPERPOINT) * 100.0; limit(&pct, 0, 100); if (lf->skillpoints || attpoints ) { @@ -8176,7 +8305,7 @@ void showlfstats(lifeform_t *lf, int showall) { attpoints, (attpoints == 1) ? "" : "s"); */ - wprintw(mainwin, "%d skill point%s, ", lf->skillpoints, + wprintw(mainwin, "%d point%s, ", lf->skillpoints, (lf->skillpoints == 1) ? "" : "s"); } wprintw(mainwin, "%d%%", pct); @@ -8420,6 +8549,7 @@ void showlfstats(lifeform_t *lf, int showall) { // ARMOUR STUFF if (showall || (lorelev >= PR_NOVICE)) { + int min,max; //int min,max; arating = getarmourrating(lf, NULL, NULL, NULL); //min = pctof(25, arating); @@ -8434,11 +8564,10 @@ void showlfstats(lifeform_t *lf, int showall) { } */ if (lorelev >= PR_NOVICE) setcol(mainwin, lorecol); - if (arating == 0) { + getarrange(arating, &min, &max); + if (max <= 0) { wprintw(mainwin, "%d (no dmgreduce)", arating); y2++; } else { - int min,max; - getarrange(arating, &min, &max); wprintw(mainwin, "%d (dmgreduce %d-%d)", arating, min, max); y2++; } if (lorelev >= PR_NOVICE) unsetcol(mainwin, lorecol); @@ -8689,11 +8818,11 @@ void showlfstats(lifeform_t *lf, int showall) { } f = lfhasknownflag(lf, F_SPRINTING); if (f) { - if (f->val[0]) { - wrapprint(mainwin, &y, &x, "%s %s sprinting.", you(lf), is(lf)); - } else { - wrapprint(mainwin, &y, &x, "%s %s exhausted.", you(lf), is(lf)); - } + wrapprint(mainwin, &y, &x, "%s %s sprinting.", you(lf), is(lf)); + } + + if (lf->stamina == 0) { + wrapprint(mainwin, &y, &x, "%s %s exhausted.", you(lf), is(lf)); } f = lfhasknownflag(lf, F_UNDEAD); @@ -9549,12 +9678,6 @@ void showlfstats(lifeform_t *lf, int showall) { } y++; } - f = lfhasknownflag(lf, F_TIRED); - if (f) { - mvwprintw(mainwin, y, 0, "%s %s tired.", you(lf), is(lf)); - y++; - } - f = lfhasflag(lf, F_RISEASGHOST); if (f && (f->known)) { mvwprintw(mainwin, y, 0, "%s will rise as a ghost after death.", you(lf)); @@ -9578,6 +9701,11 @@ void showlfstats(lifeform_t *lf, int showall) { mvwprintw(mainwin, y, 0, "%s smell%s terrible.", you(lf), isplayer(lf) ? "" : "s"); y++; } + f = lfhasflag(lf, F_STUNNED); + if (f && (f->known)) { + mvwprintw(mainwin, y, 0, "%s %s stunned and cannot attack, cast spells or use abilities.", you(lf), is(lf)); + y++; + } if (!isblind(lf)) { f = lfhasknownflag(lf, F_SEEINDARK); diff --git a/io.h b/io.h index ce33ee8..d743f93 100644 --- a/io.h +++ b/io.h @@ -16,6 +16,8 @@ int announceflaggain(lifeform_t *lf, flag_t *f); int announceflagloss(lifeform_t *lf, flag_t *f); int announceobflaggain(object_t *o, flag_t *f); void announceobflagloss(object_t *o, flag_t *f); +int confirm_badfeeling(void); +lifeform_t *askgod(char *prompt); object_t *askobject(obpile_t *op, char *title, int *count, long opts); object_t *askobjectwithflag(obpile_t *op, char *title, int *count, long opts, enum FLAG withflag); object_t *doaskobject(obpile_t *op, char *title, int *count, int forpickup, int showpoints, long opts, ...); @@ -53,6 +55,7 @@ void dolook(cell_t *where, int onpurpose); void domagic(enum OBTYPE spellid, int cellx, int celly); void domemmagic(void); void domsghist(void); +void dooffer(void); void dooperate(obpile_t *op); int dopickup(obpile_t *op, int forceask); void dolockpick(obpile_t *op); diff --git a/lf.c b/lf.c index d596c7d..cb7e3f7 100644 --- a/lf.c +++ b/lf.c @@ -487,12 +487,27 @@ void callguards(lifeform_t *caller, lifeform_t *victim) { } } +int canattack(lifeform_t *lf) { + if (!lf->stamina) { + reason = E_NOSTAM; + return B_FALSE; + } else if (lfhasflag(lf, F_STUNNED)) { + reason = E_STUNNED; + return B_FALSE; + } + return B_TRUE; +} + int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { int castable = B_FALSE; flag_t *f; objecttype_t *ot; // TODO: check for mute? + if (lfhasflag(lf, F_STUNNED)) { + reason = E_STUNNED; + return B_FALSE; + } if (isprone(lf)) { reason = E_PRONE; return B_FALSE; @@ -559,6 +574,12 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { } + f = hasflag(ot->flags, F_STAMCOST); + if (f && (lf->stamina < f->val[0])) { + reason = E_NOSTAM; + return B_FALSE; + } + return castable; } @@ -1198,6 +1219,9 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar case E_SWIMMING: msg("You can't cast spells while swimming."); break; + case E_STUNNED: + msg("You can't cast spells while stunned."); + break; default: msg("For some reason, you can't cast that."); break; @@ -1516,7 +1540,7 @@ int checkfordrowning(lifeform_t *lf, object_t *o) { } losehp(lf, roll("6d6"), DT_DIRECT, NULL, "running water"); } - if (!slev && !lfhasflag(lf, F_BREATHWATER)) { + if ((!slev || !lf->stamina) && !lfhasflag(lf, F_BREATHWATER) ) { int damamt; // take drowning damage. generally you'll die @@ -3008,7 +3032,7 @@ void enhanceskills(lifeform_t *lf) { cost = getskilllevcost(f->val[1] + 1); if (lf->skillpoints >= cost) { char buf[BUFLEN]; - snprintf(buf, BUFLEN, "%s (%s, %d points)", getskillname(f->val[0]), + snprintf(buf, BUFLEN, "%s (%s, cost:%d points)", getskillname(f->val[0]), getskilllevelname(f->val[1] + 1), cost); addchoice(&prompt, ch++, getskillname(f->val[0]), buf, f); } @@ -3378,10 +3402,12 @@ void fightback(lifeform_t *lf, lifeform_t *attacker) { } // turn to face our attacker - if (isadjacent(lf->cell, attacker->cell)) { - turntoface(lf, attacker->cell); + if (!lfhasflag(lf, F_STUNNED)) { + if (isadjacent(lf->cell, attacker->cell)) { + turntoface(lf, attacker->cell); + } + aiattack(lf, attacker, DEF_AIFOLLOWTIME); } - aiattack(lf, attacker, DEF_AIFOLLOWTIME); // any nearby monsters which will help out? if (getallegiance(lf) != AL_FRIENDLY) { @@ -3570,13 +3596,15 @@ int flee(lifeform_t *lf) { real_getlfname(lf, lfname, B_FALSE); - // mindless? - if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) == IQ_MINDLESS) { - return B_FALSE; - } - // are we fleeing? getflags(lf->flags, retflag, &nretflags, F_FLEEFROM, F_NONE); + + // mindless? + if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) == IQ_MINDLESS) { + if (!nretflags) return B_FALSE; + } + + for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_FLEEFROM) { @@ -3717,8 +3745,7 @@ void fleefrom(lifeform_t *lf, lifeform_t *enemy, int howlong, int onpurpose) { } // stop targetting anyone or going anywhere - killflagsofid(lf->flags, F_TARGETLF); - killflagsofid(lf->flags, F_TARGETCELL); + loseaitargets(lf); } int freezelf(lifeform_t *freezee, lifeform_t *freezer, int howlong) { @@ -3905,11 +3932,17 @@ void gainxp(lifeform_t *lf, long amt) { // skill xp if (isplayer(lf)) { + int amtneeded; + int mod; + + mod = getstatmod(lf, A_IQ); + amtneeded = pctof(100 + mod, SKILLXPPERPOINT); + lf->skillxp += amt; assert(lf->skillxp >= 0); - while (lf->skillxp >= SKILLXPPERPOINT) { + while (lf->skillxp >= amtneeded) { newskillpoints++; - lf->skillxp -= SKILLXPPERPOINT; + lf->skillxp -= amtneeded; if (isplayer(lf)) statdirty = B_TRUE; } } @@ -5679,6 +5712,10 @@ float getmaxpushweight(lifeform_t *lf) { return max; } +float getmaxstamina(lifeform_t *lf) { + return (getattr(lf, A_CON) / 2); +} + int getmr(lifeform_t *lf) { int amt = 0; @@ -5756,7 +5793,7 @@ int getmovespeed(lifeform_t *lf) { // modifier? getflags(lf->flags, retflag, &nretflags, F_FASTMOVE, F_FASTACTMOVE, F_INJURY, F_SLOWMOVE, - F_SLOWACTMOVE, F_SPRINTING, F_TIRED, F_NONE); + F_SLOWACTMOVE, F_SPRINTING, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->id == F_SLOWMOVE) || (f->id == F_SLOWACTMOVE)) { @@ -5772,13 +5809,7 @@ int getmovespeed(lifeform_t *lf) { speed += 5; break; } } else if (f->id == F_SPRINTING) { - if (f->val[0]) { - speed -= 10; - } else { - speed += 5; - } - } else if (f->id == F_TIRED) { - speed += 5; + speed -= 10; } } @@ -7010,6 +7041,9 @@ void givejob(lifeform_t *lf, enum JOB jobid) { } } + // reset max stamina if required. + lf->stamina = getmaxstamina(lf); + if ((gamemode != GM_GAMESTARTED)) { autoweild(lf); } @@ -7701,7 +7735,7 @@ int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype) { break; case 3: inj = IJ_WINDPIPECRUSHED; - desc = strdup("windpipe is crushed^fitness penalty, cannot sprint"); + desc = strdup("windpipe is crushed^fitness penalty"); break; case 4: inj = IJ_NOSEBROKEN; @@ -7835,9 +7869,6 @@ int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype) { wep = getweapon(lf); if (wep) drop(wep, wep->amt); break; - case IJ_WINDPIPECRUSHED: - stopsprinting(lf); - break; default: break; } @@ -9317,6 +9348,8 @@ void initrace(void) { addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "purchasing items"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "giving away or discarding money"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "opening locked objects through force"); + // sacrifices + addflag(lastrace->flags, F_SACRIFICEOBCLASS, OC_MONEY, NA, 2, "OB IS consumed in a swirl of shadowy blackness"); addrace(R_GODDEATH, "Hecta", 100, '@', C_BOLDMAGENTA, MT_FLESH, RC_GOD); @@ -9361,8 +9394,8 @@ void initrace(void) { addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "magical healing"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "bestowing blessings"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "casting holy spells"); - - + // sacrifices + addflag(lastrace->flags, F_SACRIFICEOB, OT_CORPSE, NA, 2, "Bony claws rise up and drag OB underground."); addrace(R_GODMERCY, "Yumi", 300, '@', C_BOLDMAGENTA, MT_FLESH, RC_GOD); addflag(lastrace->flags, F_ALIGNMENT, AL_GOOD, NA, NA, NULL); @@ -9401,6 +9434,8 @@ void initrace(void) { addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "sneak attacks"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the use of poison"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the destruction of healing potions"); + // sacrifices + addflag(lastrace->flags, F_SACRIFICEOBCLASS, OC_WEAPON, NA, 2, "OB IS destroyed in a flash of power."); // monsters @@ -11521,7 +11556,7 @@ void initrace(void) { addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL); addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, ""); - addflag(lastrace->flags, F_RARITY, H_DUNGEON, 90, NA, ""); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, 80, NA, ""); addflag(lastrace->flags, F_RARITY, H_FOREST, 80, NA, ""); addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "1d4+1"); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d3"); @@ -11963,6 +11998,16 @@ int isaquatic(lifeform_t *lf) { return B_FALSE; } +// is lf behind otherlf? +int isbehind(lifeform_t *lf, lifeform_t *otherlf) { + int dir; + dir = getdirtowards(otherlf->cell, lf->cell, NULL, B_FALSE, DT_ORTH); + if (getrelativedir(otherlf, dir) == RD_BACKWARDS) { + return B_TRUE; + } + return B_FALSE; +} + // returns B_FALSE, B_TRUE, or B_FROMINJURY int isbleeding(lifeform_t *lf) { float hppct; @@ -12521,7 +12566,9 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { a->race = NULL; setrace(a, rid, B_FALSE); - + + // set stamina AFTER setrace as it depends on your attribs + a->stamina = getmaxstamina(a); // update other things cell->lf = a; @@ -12816,8 +12863,7 @@ void makepeaceful(lifeform_t *who) { addflag(who->flags, F_XPVAL, 0, NA, NA, NULL); killflagsofid(who->flags, F_HOSTILE); - killflagsofid(who->flags, F_TARGETLF); // stop targetting anyone - + loseaitargets(who); } lifeform_t *makezombie(object_t *o) { @@ -13730,14 +13776,16 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml } void losemp(lifeform_t *lf, int amt) { - if (isplayer(lf)) { - statdirty = B_TRUE; - } lf->mp -= amt; if (lf->mp < 0) { lf->mp = 0; } + if (isplayer(lf)) { + statdirty = B_TRUE; + drawstatus(); + updatestatus(); + } } @@ -13756,7 +13804,7 @@ void makefriendly(lifeform_t *who, int howlong) { if (!hasflag(who->flags, F_FRIENDLY)) { addtempflag(who->flags, F_FRIENDLY, B_TRUE, NA, NA, NULL, howlong); } - killflagsofid(who->flags, F_TARGETLF); // stop targetting anyone + loseaitargets(who); } @@ -14119,6 +14167,31 @@ float modifybystat(float num, lifeform_t *lf, enum ATTRIB att) { return newnum; } +void modstamina(lifeform_t *lf, float howmuch) { + float orig; + orig = lf->stamina; + lf->stamina += howmuch; + limitf(&(lf->stamina), 0, getmaxstamina(lf)); + if (lf->stamina != orig) { + if (isplayer(lf)) { + statdirty = B_TRUE; + drawstatus(); + updatestatus(); + if (lf->stamina == 0) msg("You are exhausted."); + } else if (cansee(player, lf)) { + if (lf->stamina == 0) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s looks exhausted.", lfname); + } + } + } + + if (lf->stamina == 0) { + stopsprinting(lf); + } +} + // if validchars is set, we will populate it with a list of valid // choice letters for asking the player how to rest. int needstorest(lifeform_t *lf, char *validchars) { @@ -14335,26 +14408,32 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nt, int volume, cha isplayer(l) ? "" : "s", isplayer(l) ? "your" : "its"); } - } - } else { // not alseep - // monsters will turn to face the sound, as long as they're - // not attacking something - if (!isplayer(l)) { - flag_t *f; - int willrespond = B_FALSE; - f = aihastarget(l); - if (f) { - // will probably ignore the sound... - if ((volume >= 5) && onein(2)) { - willrespond = B_TRUE; - } - } else willrespond = B_TRUE; + } else { // not asleep, but can hear it. + // monsters will turn to face the sound, as long as they're + // not attacking something + if (!isplayer(l)) { + flag_t *f; + int willrespond = B_FALSE; + f = aihastarget(l); + if (f) { + // will probably ignore the sound... + if ((volume >= 5) && onein(2)) { + willrespond = B_TRUE; + } + } else willrespond = B_TRUE; - if (willrespond) { - // turn to face the sound - turntoface(l, c); + if (willrespond) { + // turn to face the sound + turntoface(l, c); + if (isplayer(noisemaker) && cansee(player, l)) { + char lfname[BUFLEN]; + getlfname(l, lfname); + msg("%s turns to face you.", lfname); + } + } } } + } else { // can't hear the sound. } } // end for each lf on map return rv; @@ -15394,6 +15473,9 @@ void setattr(lifeform_t *lf, enum ATTRIB attr, int val) { } int setfacing(lifeform_t *lf, int dir) { + if (dir == D_NONE) { + dblog("xxx"); + } if (lf->facing == dir) { // already facing that way return B_TRUE; } @@ -15686,8 +15768,7 @@ void initskills(void) { addskilldesc(SK_ARMOUR, PR_SKILLED, "^gReduces armour evasion penalties by 40%.", B_FALSE); addskilldesc(SK_ARMOUR, PR_EXPERT, "^gReduces armour evasion penalties by 50%.", B_FALSE); addskilldesc(SK_ARMOUR, PR_MASTER, "^gReduces armour evasion penalties by 60%.", B_FALSE); - addskill(SK_ATHLETICS, "Athletics", "Assists with sprinting and exhaustion recovery.", 50); - addskilldesc(SK_ATHLETICS, PR_INEPT, "- Determines how far you can sprint before tiring.", B_FALSE); + addskill(SK_ATHLETICS, "Athletics", "Grants various athletic abilities.", 50); addskilldesc(SK_ATHLETICS, PR_NOVICE, "^gYou gain the 'sprint' ability.", B_FALSE); addskilldesc(SK_ATHLETICS, PR_ADEPT, "^gYou gain the 'tumble' ability.", B_FALSE); addskilldesc(SK_ATHLETICS, PR_EXPERT, "^gYou gain the 'jump' ability.", B_FALSE); @@ -15848,69 +15929,6 @@ void initskills(void) { addskilldesc(SK_SS_ALLOMANCY, PR_SKILLED, "Allows you to cast Allomancy spells up to level 4.", B_FALSE); addskilldesc(SK_SS_ALLOMANCY, PR_EXPERT, "Allows you to cast Allomancy spells up to level 5.", B_FALSE); addskilldesc(SK_SS_ALLOMANCY, PR_MASTER, "Allows you to cast Allomancy spells up to level 6.", B_FALSE); - addskill(SK_SS_AIR, "Air Magic", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_AIR, PR_NOVICE, "Allows you to cast Air Magic spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_AIR, PR_BEGINNER, "Allows you to cast Air Magic spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_AIR, PR_ADEPT, "Allows you to cast Air Magic spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_AIR, PR_SKILLED, "Allows you to cast Air Magic spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_AIR, PR_EXPERT, "Allows you to cast Air Magic spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_AIR, PR_MASTER, "Allows you to cast Air Magic spells up to level 6.", B_FALSE); - addskill(SK_SS_DEATH, "Necromancy", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_DEATH, PR_NOVICE, "Allows you to cast Necromancy spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_DEATH, PR_BEGINNER, "Allows you to cast Necromancy spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_DEATH, PR_ADEPT, "Allows you to cast Necromancy spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_DEATH, PR_SKILLED, "Allows you to cast Necromancy spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_DEATH, PR_EXPERT, "Allows you to cast Necromancy spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_DEATH, PR_MASTER, "Allows you to cast Necromancy spells up to level 6.", B_FALSE); - addskill(SK_SS_DIVINATION, "Divination", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_DIVINATION, PR_NOVICE, "Allows you to cast Divination spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_DIVINATION, PR_BEGINNER, "Allows you to cast Divination spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_DIVINATION, PR_ADEPT, "Allows you to cast Divination spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_DIVINATION, PR_SKILLED, "Allows you to cast Divination spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_DIVINATION, PR_EXPERT, "Allows you to cast Divination spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_DIVINATION, PR_MASTER, "Allows you to cast Divination spells up to level 6.", B_FALSE); - addskill(SK_SS_ENCHANTMENT, "Enchantment", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_ENCHANTMENT, PR_NOVICE, "Allows you to cast Enchantment spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_ENCHANTMENT, PR_BEGINNER, "Allows you to cast Enchantment spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_ENCHANTMENT, PR_ADEPT, "Allows you to cast Enchantment spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_ENCHANTMENT, PR_SKILLED, "Allows you to cast Enchantment spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_ENCHANTMENT, PR_EXPERT, "Allows you to cast Enchantment spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_ENCHANTMENT, PR_MASTER, "Allows you to cast Enchantment spells up to level 6.", B_FALSE); - addskill(SK_SS_FIRE, "Fire Magic", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_FIRE, PR_NOVICE, "Allows you to cast Fire Magic spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_FIRE, PR_BEGINNER, "Allows you to cast Fire Magic spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_FIRE, PR_ADEPT, "Allows you to cast Fire Magic spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_FIRE, PR_SKILLED, "Allows you to cast Fire Magic spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_FIRE, PR_EXPERT, "Allows you to cast Fire Magic spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_FIRE, PR_MASTER, "Allows you to cast Fire Magic spells up to level 6.", B_FALSE); - addskill(SK_SS_COLD, "Cold Magic", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_COLD, PR_NOVICE, "Allows you to cast Cold Magic spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_COLD, PR_BEGINNER, "Allows you to cast Cold Magic spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_COLD, PR_ADEPT, "Allows you to cast Cold Magic spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_COLD, PR_SKILLED, "Allows you to cast Cold Magic spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_COLD, PR_EXPERT, "Allows you to cast Cold Magic spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_COLD, PR_MASTER, "Allows you to cast Cold Magic spells up to level 6.", B_FALSE); - addskill(SK_SS_GRAVITY, "Gravitation Magic", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_GRAVITY, PR_NOVICE, "Allows you to cast Gravitation Magic spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_GRAVITY, PR_BEGINNER, "Allows you to cast Gravitation Magic spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_GRAVITY, PR_ADEPT, "Allows you to cast Gravitation Magic spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_GRAVITY, PR_SKILLED, "Allows you to cast Gravitation Magic spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_GRAVITY, PR_EXPERT, "Allows you to cast Gravitation Magic spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_GRAVITY, PR_MASTER, "Allows you to cast Gravitation Magic spells up to level 6.", B_FALSE); - addskill(SK_SS_LIFE, "Life Magic", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_LIFE, PR_NOVICE, "Allows you to cast Life Magic spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_LIFE, PR_BEGINNER, "Allows you to cast Life Magic spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_LIFE, PR_ADEPT, "Allows you to cast Life Magic spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_LIFE, PR_SKILLED, "Allows you to cast Life Magic spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_LIFE, PR_EXPERT, "Allows you to cast Life Magic spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_LIFE, PR_MASTER, "Allows you to cast Life Magic spells up to level 6.", B_FALSE); - addskill(SK_SS_MODIFICATION, "Modification", "Boosts casting of spells from this school.", 50); - addskilldesc(SK_SS_MODIFICATION, PR_NOVICE, "Allows you to cast Modification spells up to level 1.", B_FALSE); - addskilldesc(SK_SS_MODIFICATION, PR_BEGINNER, "Allows you to cast Modification spells up to level 2.", B_FALSE); - addskilldesc(SK_SS_MODIFICATION, PR_ADEPT, "Allows you to cast Modification spells up to level 3.", B_FALSE); - addskilldesc(SK_SS_MODIFICATION, PR_SKILLED, "Allows you to cast Modification spells up to level 4.", B_FALSE); - addskilldesc(SK_SS_MODIFICATION, PR_EXPERT, "Allows you to cast Modification spells up to level 5.", B_FALSE); - addskilldesc(SK_SS_MODIFICATION, PR_MASTER, "Allows you to cast Modification spells up to level 6.", B_FALSE); addskill(SK_SS_MENTAL, "Psionics", "Boosts casting of spells from this school.", 50); addskilldesc(SK_SS_MENTAL, PR_INEPT, "- Each rank gives you a 10%% chance to learn a new psionic ability when levelling up.", B_FALSE); addskilldesc(SK_SS_MENTAL, PR_NOVICE, "Allows you to cast Psionic spells up to level 1.", B_FALSE); @@ -15919,28 +15937,91 @@ void initskills(void) { addskilldesc(SK_SS_MENTAL, PR_SKILLED, "Allows you to cast Psionic spells up to level 4.", B_FALSE); addskilldesc(SK_SS_MENTAL, PR_EXPERT, "Allows you to cast Psionic spells up to level 5.", B_FALSE); addskilldesc(SK_SS_MENTAL, PR_MASTER, "Allows you to cast Psionic spells up to level 6.", B_FALSE); - addskill(SK_SS_NATURE, "Nature Magic", "Boosts casting of spells from this school.", 50); + addskill(SK_SS_NATURE, "Enviromancy", "Boosts casting of spells from this school.", 50); addskilldesc(SK_SS_NATURE, PR_NOVICE, "Allows you to cast Nature spells up to level 1.", B_FALSE); addskilldesc(SK_SS_NATURE, PR_BEGINNER, "Allows you to cast Nature spells up to level 2.", B_FALSE); addskilldesc(SK_SS_NATURE, PR_ADEPT, "Allows you to cast Nature spells up to level 3.", B_FALSE); addskilldesc(SK_SS_NATURE, PR_SKILLED, "Allows you to cast Nature spells up to level 4.", B_FALSE); addskilldesc(SK_SS_NATURE, PR_EXPERT, "Allows you to cast Nature spells up to level 5.", B_FALSE); addskilldesc(SK_SS_NATURE, PR_MASTER, "Allows you to cast Nature spells up to level 6.", B_FALSE); - addskill(SK_SS_SUMMONING, "Summoning", "Boosts casting of spells from this school.", 50); + addskill(SK_SS_AIR, "Magic:Air Magic", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_AIR, PR_NOVICE, "Allows you to cast Air Magic spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_AIR, PR_BEGINNER, "Allows you to cast Air Magic spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_AIR, PR_ADEPT, "Allows you to cast Air Magic spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_AIR, PR_SKILLED, "Allows you to cast Air Magic spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_AIR, PR_EXPERT, "Allows you to cast Air Magic spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_AIR, PR_MASTER, "Allows you to cast Air Magic spells up to level 6.", B_FALSE); + addskill(SK_SS_DEATH, "Magic:Necromancy", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_DEATH, PR_NOVICE, "Allows you to cast Necromancy spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_DEATH, PR_BEGINNER, "Allows you to cast Necromancy spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_DEATH, PR_ADEPT, "Allows you to cast Necromancy spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_DEATH, PR_SKILLED, "Allows you to cast Necromancy spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_DEATH, PR_EXPERT, "Allows you to cast Necromancy spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_DEATH, PR_MASTER, "Allows you to cast Necromancy spells up to level 6.", B_FALSE); + addskill(SK_SS_DIVINATION, "Magic:Divination", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_DIVINATION, PR_NOVICE, "Allows you to cast Divination spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_DIVINATION, PR_BEGINNER, "Allows you to cast Divination spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_DIVINATION, PR_ADEPT, "Allows you to cast Divination spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_DIVINATION, PR_SKILLED, "Allows you to cast Divination spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_DIVINATION, PR_EXPERT, "Allows you to cast Divination spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_DIVINATION, PR_MASTER, "Allows you to cast Divination spells up to level 6.", B_FALSE); + addskill(SK_SS_ENCHANTMENT, "Magic:Enchantment", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_ENCHANTMENT, PR_NOVICE, "Allows you to cast Enchantment spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_ENCHANTMENT, PR_BEGINNER, "Allows you to cast Enchantment spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_ENCHANTMENT, PR_ADEPT, "Allows you to cast Enchantment spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_ENCHANTMENT, PR_SKILLED, "Allows you to cast Enchantment spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_ENCHANTMENT, PR_EXPERT, "Allows you to cast Enchantment spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_ENCHANTMENT, PR_MASTER, "Allows you to cast Enchantment spells up to level 6.", B_FALSE); + addskill(SK_SS_FIRE, "Magic:Fire Magic", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_FIRE, PR_NOVICE, "Allows you to cast Fire Magic spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_FIRE, PR_BEGINNER, "Allows you to cast Fire Magic spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_FIRE, PR_ADEPT, "Allows you to cast Fire Magic spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_FIRE, PR_SKILLED, "Allows you to cast Fire Magic spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_FIRE, PR_EXPERT, "Allows you to cast Fire Magic spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_FIRE, PR_MASTER, "Allows you to cast Fire Magic spells up to level 6.", B_FALSE); + addskill(SK_SS_COLD, "Magic:Cold Magic", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_COLD, PR_NOVICE, "Allows you to cast Cold Magic spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_COLD, PR_BEGINNER, "Allows you to cast Cold Magic spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_COLD, PR_ADEPT, "Allows you to cast Cold Magic spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_COLD, PR_SKILLED, "Allows you to cast Cold Magic spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_COLD, PR_EXPERT, "Allows you to cast Cold Magic spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_COLD, PR_MASTER, "Allows you to cast Cold Magic spells up to level 6.", B_FALSE); + addskill(SK_SS_GRAVITY, "Magic:Gravitation Magic", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_GRAVITY, PR_NOVICE, "Allows you to cast Gravitation Magic spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_GRAVITY, PR_BEGINNER, "Allows you to cast Gravitation Magic spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_GRAVITY, PR_ADEPT, "Allows you to cast Gravitation Magic spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_GRAVITY, PR_SKILLED, "Allows you to cast Gravitation Magic spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_GRAVITY, PR_EXPERT, "Allows you to cast Gravitation Magic spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_GRAVITY, PR_MASTER, "Allows you to cast Gravitation Magic spells up to level 6.", B_FALSE); + addskill(SK_SS_LIFE, "Magic:Life Magic", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_LIFE, PR_NOVICE, "Allows you to cast Life Magic spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_LIFE, PR_BEGINNER, "Allows you to cast Life Magic spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_LIFE, PR_ADEPT, "Allows you to cast Life Magic spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_LIFE, PR_SKILLED, "Allows you to cast Life Magic spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_LIFE, PR_EXPERT, "Allows you to cast Life Magic spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_LIFE, PR_MASTER, "Allows you to cast Life Magic spells up to level 6.", B_FALSE); + addskill(SK_SS_MODIFICATION, "Magic:Modification", "Boosts casting of spells from this school.", 50); + addskilldesc(SK_SS_MODIFICATION, PR_NOVICE, "Allows you to cast Modification spells up to level 1.", B_FALSE); + addskilldesc(SK_SS_MODIFICATION, PR_BEGINNER, "Allows you to cast Modification spells up to level 2.", B_FALSE); + addskilldesc(SK_SS_MODIFICATION, PR_ADEPT, "Allows you to cast Modification spells up to level 3.", B_FALSE); + addskilldesc(SK_SS_MODIFICATION, PR_SKILLED, "Allows you to cast Modification spells up to level 4.", B_FALSE); + addskilldesc(SK_SS_MODIFICATION, PR_EXPERT, "Allows you to cast Modification spells up to level 5.", B_FALSE); + addskilldesc(SK_SS_MODIFICATION, PR_MASTER, "Allows you to cast Modification spells up to level 6.", B_FALSE); + addskill(SK_SS_SUMMONING, "Magic:Summoning", "Boosts casting of spells from this school.", 50); addskilldesc(SK_SS_SUMMONING, PR_NOVICE, "Allows you to cast Summoning spells up to level 1.", B_FALSE); addskilldesc(SK_SS_SUMMONING, PR_BEGINNER, "Allows you to cast Summoning spells up to level 2.", B_FALSE); addskilldesc(SK_SS_SUMMONING, PR_ADEPT, "Allows you to cast Summoning spells up to level 3.", B_FALSE); addskilldesc(SK_SS_SUMMONING, PR_SKILLED, "Allows you to cast Summoning spells up to level 4.", B_FALSE); addskilldesc(SK_SS_SUMMONING, PR_EXPERT, "Allows you to cast Summoning spells up to level 5.", B_FALSE); addskilldesc(SK_SS_SUMMONING, PR_MASTER, "Allows you to cast Summoning spells up to level 6.", B_FALSE); - addskill(SK_SS_TRANSLOCATION, "Translocation", "Boosts casting of spells from this school.", 50); + addskill(SK_SS_TRANSLOCATION, "Magic:Translocation", "Boosts casting of spells from this school.", 50); addskilldesc(SK_SS_TRANSLOCATION, PR_NOVICE, "Allows you to cast Translocation spells up to level 1.", B_FALSE); addskilldesc(SK_SS_TRANSLOCATION, PR_BEGINNER, "Allows you to cast Translocation spells up to level 2.", B_FALSE); addskilldesc(SK_SS_TRANSLOCATION, PR_ADEPT, "Allows you to cast Translocation spells up to level 3.", B_FALSE); addskilldesc(SK_SS_TRANSLOCATION, PR_SKILLED, "Allows you to cast Translocation spells up to level 4.", B_FALSE); addskilldesc(SK_SS_TRANSLOCATION, PR_EXPERT, "Allows you to cast Translocation spells up to level 5.", B_FALSE); addskilldesc(SK_SS_TRANSLOCATION, PR_MASTER, "Allows you to cast Translocation spells up to level 6.", B_FALSE); - addskill(SK_SS_WILD, "Wild Magic", "Boosts casting of spells from this school.", 50); + addskill(SK_SS_WILD, "Magic:Wild Magic", "Boosts casting of spells from this school.", 50); addskilldesc(SK_SS_WILD, PR_NOVICE, "Allows you to cast Wild Magic spells up to level 1.", B_FALSE); addskilldesc(SK_SS_WILD, PR_BEGINNER, "Allows you to cast Wild Magic spells up to level 2.", B_FALSE); addskilldesc(SK_SS_WILD, PR_ADEPT, "Allows you to cast Wild Magic spells up to level 3.", B_FALSE); @@ -16635,6 +16716,41 @@ void startlfturn(lifeform_t *lf) { } } + // either use up stamina, or gain it + if (lfhasflag(lf, F_SPRINTING)) { + modstamina(lf, -1); + } else if (isswimming(lf)) { + int lossamt; + // lose stamina based on swimming skill + switch (getskill(lf, SK_SWIMMING)) { + case PR_INEPT: lossamt = 3; break; + case PR_NOVICE: lossamt = 1; break; + case PR_BEGINNER: lossamt = 1; break; + case PR_ADEPT: lossamt = 0.5; break; + case PR_SKILLED: lossamt = 0; break; + case PR_EXPERT: lossamt = 0; break; + case PR_MASTER: lossamt = 0; break; + } + if (lossamt) modstamina(lf, -lossamt); + } else { + if (lf->stamina < getmaxstamina(lf)) { + float regenrate = 0.5; + if (lfhasflagval(lf, F_INJURY, IJ_WINDPIPECRUSHED, NA, NA, NULL)) { + regenrate = 0.2; // override everything else + } else { + if (lfhasflag(lf, F_ASLEEP)) { + regenrate = pctof(200, regenrate); + } + if (ispoisoned(lf)) { + regenrate = pctof(50, regenrate); + } + } + limitf(®enrate, 0, NA); + + modstamina(lf, regenrate); + } + } + // god piety gets restored over time if (isplayer(lf)) { @@ -17446,17 +17562,11 @@ void stopsprinting(lifeform_t *lf) { } int stun(lifeform_t *lf, int nturns) { - if (lfhasflag(lf, F_ASLEEP)) { + if (lfhasflag(lf, F_ASLEEP) || lfhasflag(lf, F_STUNNED)) { return B_TRUE; } - if (isplayer(lf)) { - msg("You are stunned!"); - } else if (cansee(player, lf)) { - char buf[BUFLEN]; - getlfname(lf, buf); - msg("%s is stunned!", buf); - } - taketime(lf, getactspeed(lf)*nturns); + addtempflag(lf->flags, F_STUNNED, B_TRUE, NA, NA, NULL, nturns); + loseaitargets(lf); return B_FALSE; } @@ -17880,7 +17990,10 @@ int touch(lifeform_t *lf, object_t *o) { } void turntoface(lifeform_t *lf, cell_t *dstcell) { - setfacing(lf, getdirtowards(lf->cell, dstcell, lf, B_FALSE, DT_COMPASS) ); + // not providing srclf, since this will make getdirtowards() not include + // directions in which the next cell is unwalkable. in this case we're + // not actually walking there, so we don't care. + setfacing(lf, getdirtowards(lf->cell, dstcell, NULL, B_FALSE, DT_ORTH) ); } @@ -17997,6 +18110,12 @@ int useability(lifeform_t *lf, enum OBTYPE aid, lifeform_t *who, cell_t *where) case E_NOTREADY: msg("This ability is not recharged yet."); break; + case E_NOSTAM: + msg("You are too tired to do that."); + break; + case E_STUNNED: + msg("You can't use abilities while stunned."); + break; default: msg("For some reason, you can't use this ability."); break; diff --git a/lf.h b/lf.h index 503b768..c82308f 100644 --- a/lf.h +++ b/lf.h @@ -28,6 +28,7 @@ long calcscore(lifeform_t *lf); int calcxp(lifeform_t *lf); int calcxprace(enum RACE rid); void callguards(lifeform_t *caller, lifeform_t *victim); +int canattack(lifeform_t *lf); int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost); int candrink(lifeform_t *lf, object_t *o); int caneat(lifeform_t *lf, object_t *o); @@ -157,6 +158,7 @@ float getmaxcarryweight(lifeform_t *lf); float getmaxliftweight(lifeform_t *lf); int getmaxmp(lifeform_t *lf); float getmaxpushweight(lifeform_t *lf); +float getmaxstamina(lifeform_t *lf); int getmr(lifeform_t *lf); int getvisrange(lifeform_t *lf, int useambient); void idxtoxy(lifeform_t *lf, int idx, int *x, int *y); @@ -240,6 +242,7 @@ void initskills(void); void interrupt(lifeform_t *lf); int isairborne(lifeform_t *lf); int isaquatic(lifeform_t *lf); +int isbehind(lifeform_t *lf, lifeform_t *otherlf); int isbleeding(lifeform_t *lf); int isblind(lifeform_t *lf); enum BURDENED isburdened(lifeform_t *lf); @@ -295,6 +298,7 @@ int meetsattreq(lifeform_t *lf, flag_t *f, object_t *o); int modattr(lifeform_t *lf, enum ATTRIB attr, int amt); void modhunger(lifeform_t *lf, int amt); float modifybystat(float num, lifeform_t *lf, enum ATTRIB att); +void modstamina(lifeform_t *lf, float howmuch); int needstorest(lifeform_t *lf, char *validchars); int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nt, int volume, char *text, char *seetext); void outfitlf(lifeform_t *lf); diff --git a/map.c b/map.c index 8405c4e..e4ef3de 100644 --- a/map.c +++ b/map.c @@ -3579,8 +3579,12 @@ void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int } else if ((dirtype == DT_ORTH) && (getcelldistorth(c,cc) <= range)) { inrange = B_TRUE; } - if (inrange && haslof(c, cc, LOF_WALLSTOP, NULL)) { - explodesinglecell(cc, dam, killwalls, o, c); + if (inrange) { + cell_t *stopcell = NULL; + // if a door stops the explosion, the door will + // take the damage. + haslof(c, cc, LOF_WALLSTOP, &stopcell); + explodesinglecell(stopcell, dam, killwalls, o, c); } } } @@ -4221,6 +4225,18 @@ int getrandomdir(int dirtype) { } } +int getrandomdirexcept(int dirtype, int exception) { + int dir; + dir = exception; + while (dir == exception) { + if (dirtype == DT_ORTH) { + dir = rnd(D_N, D_W); + } else { // ie. DT_COMPASS + dir = rnd(DC_N, DC_NW); + } + } + return dir; +} cell_t *getrandomroomcell(map_t *map, int roomid) { int npossible = 0; diff --git a/map.h b/map.h index 37788b8..31caa5a 100644 --- a/map.h +++ b/map.h @@ -86,6 +86,7 @@ cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LO cell_t *getrandomcell(map_t *map); cell_t *getrandomcelloftype(map_t *map, enum CELLTYPE id); int getrandomdir(int dirtype); +int getrandomdirexcept(int dirtype, int exception); cell_t *getrandomroomcell(map_t *map, int roomid); void getroomcells(map_t *m, int roomid, cell_t **retcell, int *ncells); int getslipperyness(cell_t *c, object_t **slipob); diff --git a/move.c b/move.c index 568b89a..a5b4768 100644 --- a/move.c +++ b/move.c @@ -1488,6 +1488,8 @@ int movetowards(lifeform_t *lf, cell_t *dst, int dirtype, int strafe) { dblog(".oO { dir from %d,%d -> %d,%d is %s }", lf->cell->x, lf->cell->y, dst->x, dst->y, getdirname(dir)); } rv = trymove(lf, dir, B_TRUE, strafe); + } else { + if (db) dblog(".oO { dir from %d,%d -> %d,%d is DT_NONE ! }", lf->cell->x, lf->cell->y, dst->x, dst->y); } return rv; } @@ -1559,6 +1561,8 @@ int opendoor(lifeform_t *lf, object_t *o) { } else { if (lf) { if (isplayer(lf)) { + int dir; + cell_t *pastdoorcell; // has known trap? if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) { if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { @@ -1572,15 +1576,14 @@ int opendoor(lifeform_t *lf, object_t *o) { } } // hear water behind it? - if (getskill(lf, SK_LISTEN)) { - int dir; - cell_t *pastdoorcell; - dir = getdirtowards(doorcell, lf->cell, NULL, B_FALSE, DT_ORTH); - pastdoorcell = getcellindir(doorcell, dir); - if (pastdoorcell && getcellwaterdepth(pastdoorcell, NULL)) { + dir = getdirtowards(doorcell, lf->cell, NULL, B_FALSE, DT_ORTH); + pastdoorcell = getcellindir(doorcell, dir); + if (pastdoorcell && getcellwaterdepth(pastdoorcell, NULL)) { + if (getskill(lf, SK_LISTEN) || haslos(lf, pastdoorcell)) { if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { char ch; - snprintf(buf, BUFLEN,"Your hear running water behind %s. Really open it?", obname); + snprintf(buf, BUFLEN,"%s running water behind %s. Really open it?", + haslos(lf, pastdoorcell) ? "There is" : "You can hear", obname); ch = askchar(buf,"yn","n", B_TRUE); if (ch != 'y') { msg("Cancelled."); @@ -1596,12 +1599,6 @@ int opendoor(lifeform_t *lf, object_t *o) { // stop sprinting stopsprinting(lf); - /* - sf = lfhasflag(lf, F_SPRINTING); - if (sf && sf->val[0]) { - killflag(sf); - } - */ } // trapped? @@ -2206,7 +2203,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { cell = getcellindir(lf->cell, dir); - if (isplayer(lf) && !lfhasflag(lf, F_SNEAK)) { + if (onpurpose && isplayer(lf) && !lfhasflag(lf, F_SNEAK)) { if (cell && celldangerous(lf, cell, B_TRUE, &errcode)) { char ques[BUFLEN]; char ch; diff --git a/nexus.c b/nexus.c index c896fc3..ca112c4 100644 --- a/nexus.c +++ b/nexus.c @@ -980,6 +980,7 @@ void initcommands(void) { addcommand(CMD_EAT, 'E', "Enhance your skills."); addcommand(CMD_MAGIC, 'm', "Use magic or abilities."); addcommand(CMD_MEMMAGIC, 'M', "Memorise a magic shortcut"); + addcommand(CMD_OFFER, 'O', "Offer a sacrifice to the gods."); addcommand(CMD_OPERATE, 'o', "Operate a tool/wand/device."); addcommand(CMD_PICKLOCK, 'p', "Pick a lock."); addcommand(CMD_POUR, 'P', "Pour a potion onto something."); diff --git a/objects.c b/objects.c index b056fd4..411e6d3 100644 --- a/objects.c +++ b/objects.c @@ -1312,7 +1312,8 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes assert(corpserace); - o->weight = corpserace->weight; + o->weight = pctof(8, corpserace->weight); + limitf(&o->weight, 0.01, NA); o->material = corpserace->material; // remember the race type @@ -4318,7 +4319,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } // override corpse name - if (o->type->id == OT_CORPSE) { + if ((o->type->id == OT_CORPSE) || (o->type->id == OT_HEAD)) { f = hasflag(o->flags, F_CORPSEOF); if (f) { race_t *corpserace; @@ -4326,10 +4327,10 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan corpserace = findrace(f->val[0]); ff = hasflag(corpserace->flags, F_NAME); if (ff) { - snprintf(basename, BUFLEN, "%s%s corpse",ff->text, getpossessive(ff->text)); + snprintf(basename, BUFLEN, "%s%s %s",ff->text, getpossessive(ff->text), o->type->name); no_a = B_TRUE; } else { - snprintf(basename, BUFLEN, "%s corpse",corpserace->name); + snprintf(basename, BUFLEN, "%s %s",corpserace->name, o->type->name); } } } else if (o->type->id == OT_FOUNTAIN) { @@ -5440,7 +5441,7 @@ object_t *hasobwithflagval(obpile_t *op, enum FLAG flagid, int val0, int val1, i } -object_t *hasobid(obpile_t *op, int id) { +object_t *hasobid(obpile_t *op, long id) { object_t *o; for (o = op->first ; o ; o = o->next) { if (o->id == id) return o; @@ -5831,7 +5832,7 @@ void initobjects(void) { addflag(lastobjectclass->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_CANHAVEOBMOD, OM_MASTERWORK, 17, NA, NULL); addflag(lastobjectclass->flags, F_CANHAVEOBMOD, OM_SHODDY, 34, NA, NULL); - addoc(OC_ARMOUR, "Armour/Clothing", "Protective gear.", ']', C_GREY); + addoc(OC_ARMOUR, "Armour/Clothing", "Protective gear.", '[', C_GREY); addocnoun(lastobjectclass, "armour"); addocnoun(lastobjectclass, "clothing"); addocnoun(lastobjectclass, "clothes"); @@ -5874,7 +5875,7 @@ void initobjects(void) { addocnoun(lastobjectclass, "tech"); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addoc(OC_TOOLS, "Tools", "Useful items, from the common to the obscure.", '[', C_GREY); + addoc(OC_TOOLS, "Tools", "Useful items, from the common to the obscure.", ']', C_GREY); addocnoun(lastobjectclass, "tool"); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); @@ -6484,11 +6485,13 @@ void initobjects(void) { addflag(lastot->flags, F_FLAMMABLE, 1, NA, NA, "medium fire"); addflag(lastot->flags, F_DTVULN, DT_FIRE, NA, NA, NULL); addflag(lastot->flags, F_EXPLODEONDAM, DT_FIRE, NA, NA, "2d6"); + addflag(lastot->flags, F_BADOBJECT, B_TRUE, NA, NA, NULL); addot(OT_POT_RESTORATION, "potion of restoration", "Restores lost abilities to the drinker.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL); addot(OT_POT_SLEEP, "potion of sleep", "Puts the drinker into a deep sleep.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 83, NA, NULL); addflag(lastot->flags, F_THROWMISSILE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BADOBJECT, B_TRUE, NA, NA, NULL); addot(OT_POT_SPEED, "potion of haste", "Temporarily increasees the drinker's speed.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL); addflag(lastot->flags, F_AIBOOSTITEM, B_TRUE, NA, NA, NULL); @@ -6507,10 +6510,12 @@ void initobjects(void) { addot(OT_POT_POISON, "potion of poison", "Poisons the drinker.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL); addflag(lastot->flags, F_THROWMISSILE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BADOBJECT, B_TRUE, NA, NA, NULL); addot(OT_POT_ACID, "flask of battery acid", "Causes massive internal burning if ingested.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "puddle of acid"); addflag(lastot->flags, F_THROWMISSILE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BADOBJECT, B_TRUE, NA, NA, NULL); addot(OT_POT_ELEMENTIMMUNE, "potion of elemental immunity", "Grants the imbiber temporary immunity to both fire and cold.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL); addot(OT_POT_BLOOD, "potion of blood", "A small quantity of blood.", MT_GLASS, 1, OC_POTION, SZ_TINY); @@ -6552,7 +6557,6 @@ void initobjects(void) { // scrolls addot(OT_SCR_AWARENESS, "scroll of awareness", "Mimics the effects of a 'heightened awareness' spell.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); - addflag(lastot->flags, F_LINKSPELL, OT_S_AWARENESS, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); addot(OT_SCR_CANINETRACKING, "scroll of canine tracking", "Mimics the effects of a 'canine tracking' spell.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); @@ -6619,6 +6623,7 @@ void initobjects(void) { addot(OT_SCR_FREEZEOB, "scroll of freezing touch", "Permenantly changes the next object touched into solid ice.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); addflag(lastot->flags, F_LINKSPELL, OT_S_FREEZEOB, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, RR_RARE, NULL); + addflag(lastot->flags, F_BADOBJECT, B_TRUE, NA, NA, NULL); addot(OT_SCR_KNOCK, "scroll of knock", "Magically opens a barrier.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); addflag(lastot->flags, F_LINKSPELL, OT_S_KNOCK, NA, NA, NULL); @@ -7421,6 +7426,12 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_DISORIENT, "disorient", "Spins the target around to face a different direction. This will sometimes also make them dizzy.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 10, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addot(OT_S_STUN, "stun", "Stuns the target, preventing them from taking action for a few seconds.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); @@ -7440,13 +7451,11 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); - addot(OT_S_BAFFLE, "baffle", "Confuses the target, causing them to lose control of their movement.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); - addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The target will be confused for ^bpower^n*4 turns."); + addot(OT_S_LETHARGY, "lethargy", "Reduces the target's stamina by ^bpower^n*2.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); - addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); - addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l3 addot(OT_S_PSYARMOUR, "psychic armour", "Mentally block incoming attacks.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The psychic armour's Armour Rating is ^bpower*4^n."); @@ -7456,11 +7465,19 @@ void initobjects(void) { addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); // TODO: hardcode how ai casts this - addot(OT_S_PACIFY, "pacify", "Induces calmness in another, preventing them from attacking.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); - addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); - addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); + addot(OT_S_BAFFLE, "baffle", "Confuses the target, causing them to lose control of their movement.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The target will be confused for ^bpower^n*4 turns."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); + addot(OT_S_SLEEP, "sleep", "Puts the target creature to sleep.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how long the sleep effect will last."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_HUNGER, "hunger", "Causes the target to become ravenously hungry.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); @@ -7469,18 +7486,17 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 + addot(OT_S_PACIFY, "pacify", "Induces calmness in another, preventing them from attacking.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_STUNMASS, "mass stun", "Stuns all creatures within sight.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); - addot(OT_S_SLEEP, "sleep", "Puts the target creature to sleep.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); - addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how long the sleep effect will last."); - addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); - addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); - addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); - addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); - addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l5 addot(OT_S_CHARM, "charm", "Causes another lifeform to temporary become friendly.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability and duration."); @@ -7778,6 +7794,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_JUMP, "jump", "You can leap large distances.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + addflag(lastot->flags, F_STAMCOST, 3, NA, NA, NULL); addot(OT_A_POLYREVERT, "revertform", "Revert to your original form.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_NOANNOUNCE, B_TRUE, NA, NA, NULL); @@ -7789,11 +7806,13 @@ void initobjects(void) { addot(OT_A_RAGE, "rage", "Enter a state of berzerker rage, gaining attack and defence bonuses.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); + addflag(lastot->flags, F_STAMCOST, 5, NA, NA, NULL); addot(OT_A_REPAIR, "repair equipment", "Repair damage done to your equipment.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_SPRINT, "sprint", "You can run at high speed over short distances.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); + addflag(lastot->flags, F_STAMCOST, 1, NA, NA, NULL); addot(OT_A_STEAL, "steal", "Try to steal an item from an enemy.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); @@ -7813,6 +7832,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_TUMBLE, "tumble", "You can tumble across the ground.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + addflag(lastot->flags, F_STAMCOST, 2, NA, NA, NULL); addot(OT_A_WARCRY, "warcry", "Inspire fear in your enemies with a mighty war cry.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); @@ -8216,7 +8236,7 @@ void initobjects(void) { addflag(lastot->flags, F_GRENADE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_TECHLEVEL, PR_BEGINNER, NA, NA, NULL); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); - addot(OT_C4, "block of c4", "A highly explosive plastic which explodes a medium time after activation.", MT_PLASTIC, 1, OC_TECH, SZ_TINY); + addot(OT_C4, "block of c4", "An extremely explosive plastic which explodes a medium time after activation.", MT_PLASTIC, 1, OC_TECH, SZ_TINY); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 76, RR_UNCOMMON, NULL); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); @@ -8227,7 +8247,7 @@ void initobjects(void) { addflag(lastot->flags, F_RECHARGEWHENOFF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_EXPLODEONDEATH, NA, B_BIG, B_IFACTIVATED, "15d2"); + addflag(lastot->flags, F_EXPLODEONDEATH, NA, B_BIG, B_IFACTIVATED, "90d2"); // 90 - 180 damage addflag(lastot->flags, F_GRENADE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_TECHLEVEL, PR_BEGINNER, NA, NA, NULL); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); @@ -9306,7 +9326,7 @@ void initobjects(void) { addflag(lastot->flags, F_ATTACKVERB, 16, 30, NA, "tear"); addflag(lastot->flags, F_ATTACKVERB, 31, 40, NA, "rake"); addflag(lastot->flags, F_ATTACKVERB, 41, 50, NA, "gouge"); - addflag(lastot->flags, F_ATTACKVERB, 51, NA, NA, "eviscerate"); + addflag(lastot->flags, F_ATTACKVERB, 51, NA, NA, "shred"); addflag(lastot->flags, F_KILLVERB, 70, NA, NA, "disembowel"); addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL); @@ -10005,6 +10025,18 @@ int isbadfood(object_t *o) { return B_FALSE; } +int isunknownbadobject(object_t *o) { + if (iscursed(o) && !o->blessknown) { + return B_TRUE; + } + if (isbadfood(o) && (getskill(player, SK_COOKING) < PR_BEGINNER)) { + return B_TRUE; + } + if (hasflag(o->flags, F_BADOBJECT) && !isknown(o)) { + return B_TRUE; + } + return B_FALSE; +} // is armour 'a' better than armour 'b'? int isbetterarmourthan(object_t *a, object_t *b) { @@ -13636,6 +13668,8 @@ int readsomething(lifeform_t *lf, object_t *o) { msg("You need to be outside to get your bearings first."); } } + } else if (o->type->id == OT_SCR_AWARENESS) { + addtempflag(lf->flags, F_AWARENESS, B_TRUE, NA, NA, NULL, getspellduration(30,60,o->blessed)); } else if (o->type->id == OT_SCR_NOTHING) { if (isplayer(lf)) { msg("The scroll crumbles to dust."); @@ -15729,9 +15763,14 @@ void trapeffects(object_t *trapob, enum OBTYPE oid, cell_t *c) { o = addob(src->obpile, "arrow"); } if (o) { + long oid; + // remember its id + oid = o->id; // dodge check will happen in fireat(). ignore results of the // one above. fireat(NULL, o, 1, c, 5, NULL); + o = hasobid(c->obpile, oid); + if (o) removeob(o, 1); } else { msg("ERROR: arrow trap failed."); dblog("ERROR: arrow trap failed."); diff --git a/objects.h b/objects.h index 85b026c..e5dd707 100644 --- a/objects.h +++ b/objects.h @@ -140,7 +140,7 @@ int hasobmod(object_t *o, obmod_t *om); object_t *hasobmulti(obpile_t *op, enum OBTYPE *oid, int noids); object_t *hasobwithflag(obpile_t *op, enum FLAG flagid); object_t *hasobwithflagval(obpile_t *op, enum FLAG flagid, int val0, int val1, int val2, char *text); -object_t *hasobid(obpile_t *op, int id); +object_t *hasobid(obpile_t *op, long id); void identify(object_t *o); void ignite(object_t *o); void initobjects(void); @@ -148,6 +148,7 @@ flag_t *isarmour(object_t *o); int isactivated(object_t *o); int isammofor(objecttype_t *ammo, object_t *gun); int isbadfood(object_t *o); +int isunknownbadobject(object_t *o); int isbetterarmourthan(object_t *a, object_t *b); int isbetterwepthan(object_t *a, object_t *b); int isblessed(object_t *o); diff --git a/save.c b/save.c index e0a3936..9aefc3e 100644 --- a/save.c +++ b/save.c @@ -203,6 +203,7 @@ lifeform_t *loadlf(FILE *f, cell_t *where) { fscanf(f, "contr: %d\n",&l->controller); fscanf(f, "hp: %d/%d\n",&l->hp, &l->maxhp); fscanf(f, "mp: %d/%d\n",&l->mp, &l->maxmp); + fscanf(f, "stamina: %f\n",&l->stamina); fscanf(f, "alive: %d\n",&l->alive); fscanf(f, "lastdamtype: %d\n",(int *)&l->lastdamtype); @@ -790,6 +791,7 @@ int savelf(FILE *f, lifeform_t *l) { fprintf(f, "contr: %d\n",l->controller); fprintf(f, "hp: %d/%d\n",l->hp, l->maxhp); fprintf(f, "mp: %d/%d\n",l->mp, l->maxmp); + fprintf(f, "stamina: %f\n", l->stamina); fprintf(f, "alive: %d\n",l->alive); fprintf(f, "lastdamtype: %d\n",l->lastdamtype); fprintf(f, "lastdam: %s\n",l->lastdam); diff --git a/spell.c b/spell.c index 920d9d1..1f286ed 100644 --- a/spell.c +++ b/spell.c @@ -55,6 +55,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef char buf[BUFLEN]; int power = 0,needgrab = B_FALSE, range = 0; char damstr[BUFLEN],racestr[BUFLEN]; + float stamcost = 0; objecttype_t *ot; flag_t *f; @@ -94,6 +95,20 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } + f = hasflag(ot->flags, F_STAMCOST); + if (f) stamcost = f->val[0]; + + if (stamcost) { + if (user->stamina < stamcost) { + if (isplayer(user)) { + msg("You are too tired to do that right now."); + } + return B_TRUE; + } + modstamina(user, -stamcost); + } + + if (abilid == OT_A_CHARGE) { cell_t *adjcell = NULL,*origcell; char targetname[BUFLEN]; @@ -786,12 +801,6 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } return B_TRUE; } - if (lfhasflag(user, F_TIRED)) { - if (isplayer(user)) { - msg("You are too tired to enter a rage right now."); - } - return B_TRUE; - } howlong = 10; addtempflag(user->flags, F_RAGE, B_TRUE, NA, NA, NULL, howlong); if (isplayer(user)) { @@ -922,8 +931,6 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // TODO: make this like eating/resting/etc ? taketime(user, getactspeed(user)); } else if (abilid == OT_A_SPRINT) { - int howlong; - int slev; flag_t *f; if (lfhasflagval(user, F_INJURY, IJ_WINDPIPECRUSHED, NA, NA, NULL)) { if (isplayer(user)) msg("You can't sprint with a crushed windpipe."); @@ -937,20 +944,8 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef f = lfhasflag(user, F_SPRINTING); if (f) { - if (f->val[0]) { - if (isplayer(user)) { - msg("You are already sprinting!"); - } - } else { - if (isplayer(user)) { - msg("You are too tired to sprint right now."); - } - } - return B_TRUE; - } - if (lfhasflag(user, F_TIRED)) { if (isplayer(user)) { - msg("You are too tired to sprint right now."); + msg("You are already sprinting!"); } return B_TRUE; } @@ -961,26 +956,19 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef return B_TRUE; } - howlong = 2; - // +2 for each athletics skill level - slev = getskill(user, SK_ATHLETICS); - if (slev > PR_INEPT) { - howlong += (2*slev); - } - // modify for constitution - howlong = modifybystat(howlong, user, A_CON); - - if (howlong <= 0) { - if (isplayer(user)) { - msg("You are too unfit to sprint."); + if (isplayer(user)) { + int dir,dirch; + dirch = askchar("Sprint in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE); + if (dirch == '.') { + msg("Cancelled."); + return B_TRUE; } - return B_TRUE; + dir = chartodir(dirch); + setfacing(user, dir); } - - addtempflag(user->flags, F_SPRINTING, B_TRUE, NA, NA, NULL, howlong); + + addflag(user->flags, F_SPRINTING, B_TRUE, NA, NA, NULL); practice(user, SK_ATHLETICS, 1); - // get hungry heaps! - modhunger(user, 50); } else if (abilid == OT_A_STINGACID) { validateabillf(user, abilid, &target); if (!target) return B_TRUE; @@ -1340,26 +1328,13 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef setrace(user, f->val[0], B_TRUE); } else if (abilid == OT_A_PRAY) { lifeform_t *lf; - char buf[BUFLEN]; - int i; if (!isplayer(user)) return B_FALSE; + // ask for which god initprompt(&prompt, "To whom will you pray?"); prompt.maycancel = B_TRUE; - for (i = 0 ; i < ngodlfs; i++) { - flag_t *f; - char godof[BUFLEN]; - lf = godlf[i]; - real_getlfname(lf, buf, B_FALSE); - f = hasflag(lf->flags, F_GODOF); - snprintf(godof, BUFLEN, " (%s of %s)", (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); - strcat(buf, godof); - addchoice(&prompt, 'a', buf, NULL, lf); - } - - getchoicestr(&prompt, B_FALSE, B_TRUE); - lf = (lifeform_t *)prompt.result; + lf = askgod("To whom will you pray?"); if (!lf) { msg("Cancelled."); return B_TRUE; @@ -1987,6 +1962,8 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } + + // returns TRUE on error int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_t *target, object_t *targob, cell_t *targcell, int blessed, int *seenbyplayer, int frompot) { char buf[BUFLEN]; @@ -2153,7 +2130,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ removedeadobs(targcell->obpile); - if (targcell->lf && !skillcheck(targcell->lf, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (targcell->lf && !spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) { // destroy only WORN metal objects, not CARRIED ones for (o = targcell->lf->pack->first ; o ; o = o->next) { if (isequipped(o) && ismetal(o->material->id)) { @@ -2391,15 +2368,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(target)) { - msg("You shrug off a mental attack."); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else if (haslos(player, target->cell)) { - getlfname(target, buf); - msg("%s shrugs off %s mental attack.", buf, isplayer(caster) ? "your" : "a"); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { return B_FALSE; } else { confuse(target, power*4); @@ -2459,7 +2428,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_FALSE; } - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { failed = B_TRUE; } @@ -2942,11 +2911,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_FALSE; } - if (skillcheck(targcell->lf, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(caster) || cansee(player, target)) { - msg("%s resists.",targetname); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); @@ -3161,13 +3126,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // saving throw - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(caster) || cansee(player, target)) { - char tname[BUFLEN]; - getlfname(target, tname); - msg("%s resists.",tname); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); @@ -3550,6 +3509,43 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else if (ndigs == 0) { if (isplayer(caster)) nothinghappens(); } + } else if (spellid == OT_S_DISORIENT) { + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; + target = targcell->lf; + + if (lfhasflag(target, F_ASLEEP) || !ischarmable(target)) { + fizzle(caster); + return B_TRUE; + } + + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { + return B_FALSE; + } else { + int newdir; + // make them face a random (different) direction + newdir = getrandomdirexcept(DT_COMPASS, target->facing); + + setfacing(target, newdir); + // ai loses target info + killflagsofid(target->flags, F_TARGETLF); + killflagsofid(target->flags, F_TARGETCELL); + + // announce + if (isplayer(target)) { + msg("You spin around to face %s.", getdirname(target->facing)); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + char tname[BUFLEN]; + getlfname(target, tname); + msg("%s spins around to face %s.", tname, getdirname(target->facing)); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + // make them dizzy? + if (pctchance(power*10)) { + confuse(target, 2); + } + + } } else if (spellid == OT_S_DETECTLIFE) { target = caster; if (isplayer(caster)) { @@ -3734,11 +3730,11 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // flag. // other people get a check anyway. if (targcell->lf == caster) { - if (getmr(targcell->lf) && skillcheck(targcell->lf, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (getmr(targcell->lf) && spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_FALSE)) { resisted = B_TRUE; } } else { - if (skillcheck(targcell->lf, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_FALSE)) { resisted = B_TRUE; } } @@ -3905,7 +3901,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; target = targcell->lf; - if ((getattr(target, A_IQ) <= 3) || skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if ((getattr(target, A_IQ) <= 3) || spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (cansee(player, target)) { getlfname(target, buf); msg("%s %s momentarily foolish.", buf, isplayer(target) ? "feel" : "looks"); @@ -4472,7 +4468,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_GASEOUSFORM) { if (!target) target = caster; - if (getmr(target) && (target->race->id != R_VAMPIRE) && skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (getmr(target) && (target->race->id != R_VAMPIRE) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily insubstantial."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -4599,7 +4595,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } - if (getmr(target) && skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (getmr(target) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target) || haslos(player, target->cell)) { getlfname(target, buf); msg("%s blur%s for a moment.",buf, isplayer(target) ? "" : "s"); @@ -4902,7 +4898,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } for (l = caster->cell->map->lf ; l ; l = l->next) { if (l != caster) { - if (isimmuneto(l->flags, DT_NECROTIC) || skillcheck(l, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (isimmuneto(l->flags, DT_NECROTIC) || + spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(l)) { msg("Luckily, the evil doesn't seem to harm you."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -4918,7 +4915,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // now hit the caster! - if (isimmuneto(caster->flags, DT_NECROTIC) || skillcheck(caster, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (isimmuneto(caster->flags, DT_NECROTIC) || spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(caster)) { msg("Luckily, the evil doesn't seem to harm you."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -5067,7 +5064,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // destroy objects right away removedeadobs(targcell->obpile); - if (targcell->lf && !skillcheck(targcell->lf, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (targcell->lf && !spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) { // destroy only WORN metal objects, not CARRIED ones for (o = targcell->lf->pack->first ; o ; o = o->next) { if (isequipped(o) && ismetal(o->material->id)) { @@ -5207,7 +5204,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { saved = B_TRUE; } else if (skillcheck(target, SC_STR, 20 + power, 0)) { saved = B_TRUE; @@ -5241,8 +5238,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_FALSE; } - // make this a constitution skill check ? - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { failed = B_TRUE; } else if (skillcheck(target, SC_CON, 20 + power, 0)) { failed = B_TRUE; @@ -5282,7 +5278,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // savingthrow - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0) || skillcheck(target, SC_CON, 30 + power, 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE) || skillcheck(target, SC_CON, 30 + power, 0)) { if (haslos(player, target->cell)) { char lfname[BUFLEN]; getlfname(target, lfname); @@ -5378,10 +5374,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // saving throw.... - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(caster) && cansee(player, target)) { - msg("%s%s mind fights off your intrusion!", targname, getpossessive(targname)); - } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { return B_FALSE; } @@ -6063,7 +6056,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_FALSE; } - if (lfhasflag(target, F_GRAVBOOSTED) || skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (lfhasflag(target, F_GRAVBOOSTED) || spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily heavier."); } else if (cansee(player, target)) { @@ -6242,7 +6235,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f->val[0] += (rnd(1,6) + power); if (f->val[0] >= f->val[1]) { - if (isplayer(caster) || cansee(player, caster)) msg("%s is completely repaied!", fullobname); + if (isplayer(caster) || cansee(player, caster)) msg("%s is completely repaired!", fullobname); f->val[0] = f->val[1]; } else { if (isplayer(caster) || cansee(player, caster)) msg("%s is repaired a little!", fullobname); @@ -6283,11 +6276,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ getlfname(target, targetname); - if (skillcheck(targcell->lf, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(caster) || cansee(player, target)) { - msg("%s resists.",targetname); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { } else { int donesomething = B_FALSE; @@ -6334,7 +6323,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } - if ((target != caster) && skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if ((target != caster) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily different."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -6582,6 +6571,27 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (ndone && isplayer(caster) && !frompot) { pleasegodmaybe(R_GODMERCY, 3); } + } else if (spellid == OT_S_LETHARGY) { + int amttolose; + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power, frompot)) return B_TRUE; + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_FALSE; + } + amttolose = power*2; + // only announce if the target will have some stamina left. + // if they drop to 0, modstamina will handle the announce. + if (target->stamina >= amttolose) { + if (isplayer(target)) { + msg("You suddenly feel very lethargic!"); + } else if (cansee(player, target)) { + char targname[BUFLEN]; + getlfname(target, targname); + msg("%s looks very lethargic!", targname); + } + } + modstamina(target, -amttolose); } else if (spellid == OT_S_REPELINSECTS) { // just announce if (isplayer(caster)) { @@ -6732,14 +6742,27 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(target)) { - msg("You yawn."); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else if (haslos(player, target->cell)) { - getlfname(target, buf); - msg("%s yawns.", buf); - if (seenbyplayer) *seenbyplayer = B_TRUE; + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { + // 1st skill check passed. make another one. + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { + // 2nd one passed - no effect.. + if (isplayer(target)) { + msg("You yawn."); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + getlfname(target, buf); + msg("%s yawns.", buf); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + } else { + // 2nd one failed - lose all stamina instead + modstamina(target, -(target->stamina)); + if (isplayer(target)) { + msg("You suddenly feel very lethargic!"); + } else if (cansee(player, target)) { + getlfname(target, buf); + msg("%s looks very lethargic!", buf); + } } return B_FALSE; } @@ -6798,7 +6821,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; target = targcell->lf; - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily slower."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -6842,13 +6865,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power, frompot)) return B_TRUE; target = haslf(targcell); if (target) { - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(caster) || cansee(player, target)) { - char tname[BUFLEN]; - getlfname(target, tname); - msg("%s resists.",tname); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); @@ -7039,7 +7056,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily nauseated."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -7195,15 +7212,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - if (isplayer(target)) { - msg("You shrug off a mental attack."); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else if (haslos(player, target->cell)) { - getlfname(target, buf); - msg("%s shrugs off %s mental attack.", buf, isplayer(caster) ? "your" : "a"); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { return B_FALSE; } else { stun(target, (power/5)+2); @@ -7226,16 +7235,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); return B_TRUE; } - if (skillcheck(thistarg, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { - donesomething = B_TRUE; - if (isplayer(thistarg)) { - msg("You shrug off a mental attack."); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else if (haslos(player, thistarg->cell)) { - getlfname(thistarg, buf); - msg("%s shrugs off %s mental attack.", buf, isplayer(caster) ? "your" : "a"); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } + if (spellresisted(thistarg, caster, spellid, power, seenbyplayer, B_TRUE)) { } else { donesomething = B_TRUE; stun(thistarg, (power/5)+2); @@ -7257,7 +7257,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (target) { int failed = B_FALSE; - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { failed = B_TRUE; } else { // they get pulled towards caster @@ -7289,7 +7289,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ target = caster; - if (getmr(target) && skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You flicker."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -7966,7 +7966,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; target = targcell->lf; - if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (cansee(player, target)) { getlfname(target, buf); msg("%s %s momentarily weaker.", buf, isplayer(target) ? "feel" : "looks"); @@ -8999,6 +8999,47 @@ void spellcloud(cell_t *srcloc, int radius, char ch, enum COLOUR col, enum OBTYP } } +int spellisfromschool(int spellid, enum SPELLSCHOOL school) { + objecttype_t *sp; + sp = findot(spellid); + if (hasflagval(sp->flags, F_SPELLSCHOOL, school, NA, NA, NULL)) { + return B_TRUE; + } + return B_FALSE; +} + +// returns true if the spell was resisted. +int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power, int *seenbyplayer, int announce) { + char text[BUFLEN],buf[BUFLEN]; + + if (announce) { + getlfname(target, buf); + if (spellisfromschool(spellid, SS_MENTAL)) { + if (isplayer(target)) { + strcpy(text, "You shrug off a mental attack."); + } else { + sprintf(text, "%s shrugs off %s mental attack.", buf, isplayer(caster) ? "your" : "a"); + } + } else { + if (isplayer(target)) { + strcpy(text, "You resist the effects of a spell."); + } else { + sprintf(text, "%s resists %s spell.", buf, isplayer(caster) ? "your" : "a"); + } + } + } + if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) { + if (isplayer(target) || haslos(player, target->cell)) { + if (announce) { + msg("%s",text); + } + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + return B_TRUE; + } + return B_FALSE; +} + void stopspell(lifeform_t *caster, enum OBTYPE spellid) { flag_t *f,*nextf; object_t *o, *nexto; diff --git a/spell.h b/spell.h index 3dba847..11afd1c 100644 --- a/spell.h +++ b/spell.h @@ -26,6 +26,8 @@ char *getvarpowerspelldesc(enum OBTYPE spellid, int power, char *buf); void pullobto(object_t *o, lifeform_t *lf); int schoolappearsinbooks(enum SPELLSCHOOL ss); void spellcloud(cell_t *srcloc, int radius, char ch, enum COLOUR col, enum OBTYPE sid, int power, int frompot); +int spellisfromschool(int spellid, enum SPELLSCHOOL school); +int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power, int *seenbyplayer, int announce); void stopspell(lifeform_t *caster, enum OBTYPE spellid); void stopallspells(lifeform_t *lf); void stopallspellsexcept(lifeform_t *lf, ...); diff --git a/vaults/playerstart1.vlt b/vaults/playerstart1.vlt index afbb180..b730417 100644 --- a/vaults/playerstart1.vlt +++ b/vaults/playerstart1.vlt @@ -20,5 +20,6 @@ goesin:dungeon playerstart atoneof(8,1)(8,3)(8,5) ob:playerstart mayrotate +autopop @end diff --git a/vaults/playerstart2.vlt b/vaults/playerstart2.vlt index d85b3d9..3fca739 100644 --- a/vaults/playerstart2.vlt +++ b/vaults/playerstart2.vlt @@ -22,5 +22,6 @@ x:exit goesin:dungeon playerstart mayrotate +autopop @end diff --git a/vaults/playerstart3.vlt b/vaults/playerstart3.vlt index f7bdda9..b05fd67 100644 --- a/vaults/playerstart3.vlt +++ b/vaults/playerstart3.vlt @@ -21,5 +21,6 @@ goesin:dungeon autodoors:50 playerstart mayrotate +autopop @end diff --git a/vaults/playerstart4.vlt b/vaults/playerstart4.vlt index 3957173..3c80174 100644 --- a/vaults/playerstart4.vlt +++ b/vaults/playerstart4.vlt @@ -20,5 +20,6 @@ x:exit goesin:dungeon playerstart mayrotate +autopop @end diff --git a/vaults/playerstart5.vlt b/vaults/playerstart5.vlt index a81893c..d337398 100644 --- a/vaults/playerstart5.vlt +++ b/vaults/playerstart5.vlt @@ -17,5 +17,6 @@ x:exit goesin:dungeon playerstart mayrotate +autopop @end diff --git a/vaults/playerstart6.vlt b/vaults/playerstart6.vlt index 218412d..6ac8eec 100644 --- a/vaults/playerstart6.vlt +++ b/vaults/playerstart6.vlt @@ -19,5 +19,6 @@ x:exit goesin:dungeon playerstart mayrotate +autopop @end diff --git a/vaults/playerstart7.vlt b/vaults/playerstart7.vlt index b92cf76..ccab1b4 100644 --- a/vaults/playerstart7.vlt +++ b/vaults/playerstart7.vlt @@ -22,5 +22,6 @@ f:ob:fountain goesin:dungeon playerstart mayrotate +autopop @end