diff --git a/attack.c b/attack.c index 4f198c1..f122f58 100644 --- a/attack.c +++ b/attack.c @@ -446,34 +446,34 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { // god effects... if ((attacktype == AT_LF) && isplayer(lf)) { if (attackedfriend) { - angergodmaybe(R_GODMERCY, 100); - angergodmaybe(R_GODPURITY, 100); + angergodmaybe(R_GODMERCY, 100, GA_ATTACKALLY); + angergodmaybe(R_GODPURITY, 100, GA_ATTACKALLY); switch (getalignment(attacktarget)) { case AL_EVIL: - angergodmaybe(R_GODDEATH, 20); // even more + angergodmaybe(R_GODDEATH, 20, GA_ATTACKALLY); // even more break; case AL_GOOD: - angergodmaybe(R_GODPURITY, 20); // even more + angergodmaybe(R_GODPURITY, 20, GA_ATTACKALLY); // even more break; default: break; } } else if (attackedpeaceful) { - angergodmaybe(R_GODMERCY, 50); - angergodmaybe(R_GODPURITY, 50); + angergodmaybe(R_GODMERCY, 50, GA_ASSAULT); + angergodmaybe(R_GODPURITY, 50, GA_ASSAULT); switch (getalignment(attacktarget)) { case AL_EVIL: - angergodmaybe(R_GODDEATH, 20); // even more + angergodmaybe(R_GODDEATH, 20, GA_ASSAULT); // even more break; case AL_GOOD: - angergodmaybe(R_GODPURITY, 20); // even more + angergodmaybe(R_GODPURITY, 20, GA_ASSAULT); // even more break; default: break; } } else if (attackedhelpless) { - angergodmaybe(R_GODMERCY, 100); - angergodmaybe(R_GODPURITY, 100); + angergodmaybe(R_GODMERCY, 100, GA_ATTACKHELPLESS); + angergodmaybe(R_GODPURITY, 100, GA_ATTACKHELPLESS); if (getalignment(attacktarget) != AL_EVIL) { pleasegodmaybe(R_GODTHIEVES, 5); pleasegodmaybe(R_GODDEATH, 10); @@ -481,8 +481,8 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { } if (lfhasflag(lf, F_USEDPOISON)) { killflagsofid(lf->flags, F_USEDPOISON); - angergodmaybe(R_GODPURITY, 100); - angergodmaybe(R_GODMERCY, 50); + angergodmaybe(R_GODPURITY, 100, GA_POISON); + angergodmaybe(R_GODMERCY, 50, GA_POISON); pleasegodmaybe(R_GODDEATH, 3); } } @@ -882,96 +882,24 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) // announce it if (!feigneddeath) { - int usecrittext = B_FALSE; - - if ((i == 0) && critical && !fatal) usecrittext = B_TRUE; - - if (isplayer(lf)) { - char extradambuf[BUFLEN]; - char withwep[BUFLEN]; - char *verb; - int needfree = B_FALSE; - - strcpy(extradambuf, ""); - - if (wep && !ismeleeweapon(wep)) { - snprintf(withwep, BUFLEN, " with %s", wepname); - } else { - strcpy(withwep, ""); + if (isplayer(lf) || isplayer(victim) || cansee(player, lf) || cansee(player, victim)) { + construct_hit_string(lf, victim, attackername, victimname, victimbpname, wep, damtype[i], dam[i], victim->maxhp, i, backstab, critical, fatal, isunarmed, buf); + if (strlen(buf)) { + warn("%s", buf); } - - strcpy(extradambuf, ""); - if (dam[i] == 0) { - if (getlorelevel(lf, victim->race->raceclass->id)) { - //strcpy(extradambuf, " but do no damage"); - strcpy(extradambuf, " ineffectually"); - } - } else if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT) ) { - snprintf(extradambuf, BUFLEN, " [%d dmg]",dam[i]); - } - - if (backstab && (i == 0)) { - verb = strdup("backstab"); - needfree = B_TRUE; - } else if (fatal) { - verb = getkillverb(victim, wep, damtype[i], dam[i], victim->maxhp); - } else { - if ((getlorelevel(lf, victim->race->raceclass->id) >= PR_BEGINNER) || - !ismeleedam(damtype[i])) { - verb = getattackverb(lf, wep, damtype[i], dam[i], victim->maxhp); - } else { - // always use verb for 10% - verb = getattackverb(lf, wep, damtype[i], pctof(10, victim->maxhp), victim->maxhp); - } - } - warn("^%cYou %s%s %s%s%s%s", fatal ? 'g' : 'n', - usecrittext ? "critically " : "", verb, - usecrittext ? victimbpname : victimname, withwep,extradambuf, - (fatal || backstab) ? "!" : "."); - - if (fatal && strstr(verb, "behead")) { + } + if (!isplayer(lf) && !isplayer(victim)) { + noise(lf->cell, lf, NC_OTHER, 3, "sounds of fighting.", NULL); + } + if (fatal) { + if (strstr(buf, "behead")) { + // we'll need to place the severed head object addflag(victim->flags, F_BEHEADED, B_TRUE, NA, NA, NULL); } - - if (fatal && !hasflag(victim->flags, F_NODEATHANNOUNCE)) { + if (!hasflag(victim->flags, F_NODEATHANNOUNCE)) { // don't also say "the xx dies" addflag(victim->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); } - if (needfree) { - free(verb); - } - } else { // ie. the attacker is a monster - if (cansee(player, lf) || isplayer(victim)) { - char withwep[BUFLEN]; - char attackverb[BUFLEN]; - char nodamstr[BUFLEN]; - int nodam = B_FALSE; - - // capitalise first letter - strcpy(buf, attackername); - capitalise(buf); - - if (wep && !isunarmed && (lf->race->id != R_DANCINGWEAPON) && cansee(player, lf)) { - snprintf(withwep, BUFLEN, " with %s", wepname); - } else { - strcpy(withwep, ""); - } - - strcpy(attackverb, getattackverb(lf, wep, damtype[i],dam[i],victim->maxhp)); - if ((dam[i] == 0) && (damtype[i] != DT_TOUCH)) { - nodam = B_TRUE; - strcpy(nodamstr, " ineffectually"); - } else { - strcpy(nodamstr, ""); - } - warn("^%c%s %s%s%s %s%s%s.", (isplayer(victim) && !nodam) ? 'b' : 'n', buf, - usecrittext ? "critically " : "", attackverb, - needses(attackverb) ? "es" : "s", - usecrittext ? victimbpname : victimname,withwep, nodamstr); - } - if (!isplayer(victim)) { - noise(lf->cell, lf, NC_OTHER, 3, "sounds of fighting.", NULL); - } } } @@ -1280,7 +1208,7 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { flag_t *f; int isunarmed = B_FALSE; cell_t *obloc = NULL; - char wepname[BUFLEN]; + char wepname[BUFLEN],buf[BUFLEN]; int i; //int aidb = B_TRUE; int maxhp; @@ -1329,6 +1257,10 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { for (i = 0; i < ndam; i++) { // announce the hit + + construct_hit_string(lf, NULL, attackername, obname, NULL, wep, damtype[i], dam[i], maxhp, i, B_FALSE, B_FALSE, B_FALSE, isunarmed, buf); + + /* if (isplayer(lf)) { char extradambuf[BUFLEN]; if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT) ) { @@ -1351,6 +1283,13 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { } else { noise(lf->cell, NULL, NC_OTHER, 3, "sounds of fighting.", NULL); } + */ + if (strlen(buf)) { + msg("%s", buf); + } + if (!isplayer(lf) && !cansee(player, lf)) { + noise(lf->cell, lf, NC_OTHER, 3, "sounds of fighting.", NULL); + } if ((i == 0) && (wep->type->id == OT_FISTS) && hasflag(o->flags, F_HARDNESS)) { object_t *gloves; @@ -1380,7 +1319,7 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { // object loses hp takedamage(o, dam[i], damtype[i]); if (isplayer(lf) && hasflag(o->flags, F_LOCKED)) { - angergodmaybe(R_GODTHIEVES, 25); + angergodmaybe(R_GODTHIEVES, 25, GA_MONEY); } } // end foreach damtype @@ -1621,165 +1560,6 @@ void getarrange(int arating, int *min, int *max) { *max = pctof(60, arating); } -// returns a const char * -char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) { - float pct; - enum LFSIZE ownersize = SZ_HUMAN; - flag_t *retflag[MAXCANDIDATES]; - int nretflags = 0; - - if (lf) { - ownersize = getlfsize(lf); - } - - pct = (int)(((float) dam / (float) maxhp) * 100.0); - - if (wep) { - int i; - flag_t *f; - getflags(wep->flags, retflag, &nretflags, F_ATTACKVERB, F_NONE); - for (i = 0; i < nretflags; i++) { - f = retflag[i]; - if ((f->val[0] == NA) && (f->val[1] == NA)) { - return f->text; - } else if (f->val[0]) { - if (pct >= f->val[0]) { - if (f->val[1] == NA) { - return f->text; - } else if (pct <= f->val[1]) { - return f->text; - } - } - } else if (f->val[1]) { - if (pct <= f->val[1]) { - return f->text; - } - } - } - } - - if (damtype == DT_ACID) { - return "burn"; - } else if (damtype == DT_BASH) { - if (pct <= 5) { - return "whack"; - } else if (pct <= 15) { - if (onein(2)) { - return "hit"; - } else { - return "bash"; - } - } else if (pct <= 25) { - return "pummel"; - } else if (pct <= 35) { - return "slam"; - } else { - return "clobber"; - } - } else if (damtype == DT_BITE) { - if (lf && (ownersize <= SZ_SMALL)) { - if (pct <= 5) { - return "nip"; - } else { - return "bite"; - } - } else { - if (pct <= 5) { - return "gnaw"; - } else if (pct <= 30) { - return "bite"; - } else { - return "savage"; - } - } - } else if (damtype == DT_CHOP) { - if (pct <= 5) { - return "hit"; - } else if (pct <= 15) { - return "hack"; - } else { - return "chop"; - } - } else if (damtype == DT_COLD) { - if (pct <= 10) { - return "chill"; - } else { - return "freeze"; - } - } else if (damtype == DT_CRUSH) { - return "crush"; - } else if (damtype == DT_ELECTRIC) { - if (pct <= 5) { - return "zap"; - } else if (pct <= 15) { - return "jolt"; - } else if (pct <= 20) { - return "shock"; - } else if (pct <= 30) { - return "electrify"; - } else { - return "electrocute"; - } - } else if ((damtype == DT_FIRE) || (damtype == DT_HEAT)) { - if (pct <= 5) { - return "scorch"; - } else if (pct <= 20) { - return "burn"; - } else if (pct <= 40) { - return "scald"; - } else { - return "incinerate"; - } - } else if (damtype == DT_HOLY) { - switch (rnd(1,3)) { - case 1: - return "smite"; - case 2: - return "cleanse"; - case 3: - return "purify"; - } - } else if (damtype == DT_PIERCE) { - if (pct <= 5) { - return "poke"; - } else if (pct <= 15) { - return "stab"; - } else if (pct <= 30) { - return "pierce"; - } else if (pct <= 40) { - return "spear"; - } else { - return "deeply stab"; - } - } else if (damtype == DT_POISONGAS) { - return "poison"; - } else if (damtype == DT_PROJECTILE) { - return "hit"; - } else if (damtype == DT_SLASH) { - if (pct <= 5) { - return "scratch"; - } else if (pct <= 15) { - return "hit"; - } else if (pct <= 30) { - return "slash"; - } else { - return "slice"; - } - } else if (damtype == DT_TOUCH) { - return "touch"; - } else if (damtype == DT_WATER) { - // for when water-vulnerable things go into water - return "hurt"; - } else if (damtype == DT_UNARMED) { - if (onein(2)) { - return "punch"; - } else { - return "hit"; - } - } - return "hit"; -} - /* object_t *getattackwep(lifeform_t *lf, obpile_t **unarmedpile, flag_t **unarmedflag) { object_t *wep; @@ -1867,6 +1647,22 @@ int getextradamwep(object_t *wep, int *dam, enum DAMTYPE *damtype, int *ndam) { int i; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; + lifeform_t *owner; + owner = wep->pile->owner; + + if (owner && owner->mp) { + f = hasflag(wep->flags, F_ENCHANTED); + if (f) { + if (strlen(f->text)) { + *(dam + *ndam) = roll(f->text); + } else { + *(dam + *ndam) = roll("1d2"); // default: 1d2 extra damage + } + *(damtype + *ndam) = DT_MAGIC; + (*ndam)++; + } + } + getflags(wep->flags, retflag, &nretflags, F_FROZEN, F_ONFIRE, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; @@ -1887,97 +1683,6 @@ int getextradamwep(object_t *wep, int *dam, enum DAMTYPE *damtype, int *ndam) { return *dam; } -char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) { - float pct; - pct = (int)(((float) dam / (float) maxhp) * 100.0); - - if (wep && hasflag(wep->flags, F_MERCIFUL)) { - return "knock out"; - } - - if (victim->race->id == R_DANCINGWEAPON) { - return "defeat"; - } - - if (getraceclass(victim) == RC_PLANT) { - return "destroy"; - } - - if (wep) { - flag_t *f; - int i; - flag_t *retflag[MAXCANDIDATES]; - int nretflags = 0; - getflags(wep->flags, retflag, &nretflags, F_KILLVERB, F_NONE); - for (i = 0; i < nretflags; i++) { - f = retflag[i]; - if (f->id == F_KILLVERB) { - if ((f->val[0] == NA) && (f->val[1] == NA)) { - return f->text; - } else if (f->val[0]) { - if (pct >= f->val[0]) { - if (f->val[1] == NA) { - return f->text; - } else if (pct <= f->val[1]) { - return f->text; - } - } - } else if (f->val[1]) { - if (pct <= f->val[1]) { - return f->text; - } - } - } - } - } - - if ((damtype == DT_BASH) && ismadeofice(victim)) { - return "shatter"; - } - - if (damtype == DT_CRUSH) { - return "crush"; - } - - if (damtype == DT_HOLY) { - return "smite"; - } - - if (pct >= 70) { - if (damtype == DT_PIERCE) return "impale"; - if (damtype == DT_BASH) { - if (isunconscious(victim)) { - return "kill"; - } else { - return "flatten"; - } - } - if (damtype == DT_BITE) return "gore"; - if (damtype == DT_SLASH) { - if (lfhasflagval(victim, F_NOBODYPART, BP_HEAD, NA, NA, NULL)) { - return "bisect"; - } else { - if ((getlfsize(victim) >= SZ_MEDIUM) && onein(3)) { - return "behead"; - } else { - return "bisect"; - } - } - } - } - - if (getraceclass(victim) == RC_UNDEAD) { - // can't "kill" the undead - return "destroy"; - } - - // never use 'kill' for bashing since you might just knock them out - if (damtype == DT_BASH) { - return "clobber"; - } - return "kill"; -} - void getdamrange(flag_t *f, int *min, int *max) { int mindam,maxdam; diff --git a/attack.h b/attack.h index 450b4a5..a7f42ec 100644 --- a/attack.h +++ b/attack.h @@ -12,12 +12,10 @@ int damtypecausesbleed(enum DAMTYPE dt); int damtypecausescriteffects(enum DAMTYPE dt); int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype); void getarrange(int arating, int *min, int *max); -char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp); //object_t *getattackwep(lifeform_t *lf, obpile_t **unarmedpile, flag_t **unarmedflag); enum DAMTYPE getdamtype(object_t *wep); int getextradamlf(lifeform_t *lf, int *dam, enum DAMTYPE *damtype, int *ndam); int getextradamwep(object_t *wep, int *dam, enum DAMTYPE *damtype, int *ndam); -char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp); void getdamrange(flag_t *f, int *min, int *max); //void getdamrangeunarmed(flag_t *f, int *min, int *max); //float getdamreducepct(float armourrating); diff --git a/data.c b/data.c index 39b6ecf..ea1210b 100644 --- a/data.c +++ b/data.c @@ -621,7 +621,7 @@ void initjobs(void) { addflag(lastjob->flags, F_JOBATTRMOD, A_IQ, 4, NA, NULL); addflag(lastjob->flags, F_JOBATTRMOD, A_CON, -3, NA, NULL); // initial objects - addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "wizard staff"); + addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "enchanted wizard staff"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "wizard hat"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "robe"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "2 potions of magic"); @@ -662,7 +662,7 @@ void initjobs(void) { addflag(lastjob->flags, F_LEVFLAG, 3, F_DETECTMAGIC, B_TRUE, NULL); addflag(lastjob->flags, F_LEVFLAG, 7, F_DETECTAURAS, B_TRUE, NULL); addflag(lastjob->flags, F_LEVFLAG, 10, F_CONTROL, B_TRUE, NULL); - addflag(lastjob->flags, F_LEVSPELLSCHOOL, 101, SS_NONE, B_TRUE, NULL); // new spell every 1 level + //addflag(lastjob->flags, F_LEVSPELLSCHOOL, 101, SS_NONE, B_TRUE, NULL); // new spell every 1 level addflag(lastjob->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); // monster job flags f = addflag(lastjob->flags, F_CANCAST, OT_S_FIREDART, NA, NA, NULL); addcondition(f, FC_IFMONSTER, 50); @@ -775,6 +775,8 @@ void initobjects(void) { // object modifiers - flags can be either known or not, depending on if it's obvious addobmod(OM_BLOODSTAINED,"bloodstained"); addflag_real(lastobmod->flags, F_SCARY, 2, NA, NA, NULL, PERMENANT, B_KNOWN, -1); + addobmod(OM_ENCHANTED, "enchanted"); + addflag_real(lastobmod->flags, F_ENCHANTED, NA, NA, NA, NULL, PERMENANT, B_KNOWN, -1); addobmod(OM_FLAMING,"flaming"); addflag_real(lastobmod->flags, F_ONFIRE, B_TRUE, NA, NA, NULL, PERMENANT, B_KNOWN, -1); addobmod(OM_FROZEN,"frozen"); @@ -1799,7 +1801,7 @@ void initobjects(void) { addflag(lastot->flags, F_LINKSPELL, OT_S_FLAMEBURST, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); - addot(OT_SCR_ENCHANT, "scroll of enchantment", "Magically enhances a weapon or piece of armour.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); + addot(OT_SCR_ENCHANT, "scroll of enhancement", "Magically enhances a weapon or piece of armour.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); addflag(lastot->flags, F_LINKSPELL, OT_S_ENCHANT, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_RARE, NULL); @@ -1891,7 +1893,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); /////////////////// - // death + // death / ncromancy /////////////////// // l1 addot(OT_S_STENCH, "stench", "Nauseates the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); @@ -2083,7 +2085,7 @@ void initobjects(void) { // enchantment /////////////////// // l7 - addot(OT_S_ENCHANT, "enchantment", "Magically enhances a weapon or piece of armour.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_ENCHANT, "enhancement", "Magically enhances a weapon or piece of armour.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); @@ -3178,7 +3180,7 @@ void initobjects(void) { // books addot(OT_MANUAL, "manual", "Teaches you one level of its subject matter.", MT_PAPER, 1.5, OC_BOOK, SZ_SMALL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_RARE, NULL); - addot(OT_SPELLBOOK, "spellbook", "Teaches you the spell contained within.", MT_PAPER, 1.5, OC_BOOK, SZ_SMALL); + addot(OT_SPELLBOOK, "spellbook", "Teaches you the spells contained within.", MT_PAPER, 1.5, OC_BOOK, SZ_SMALL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_RARE, NULL); // wands diff --git a/defs.h b/defs.h index 0151ca6..f3d9ae3 100644 --- a/defs.h +++ b/defs.h @@ -379,6 +379,21 @@ enum DRAINTYPE { DR_FROMWEP }; +enum GODANGERREASON { + GA_ATTACKALLY, + GA_ATTACKHELPLESS, // attacked someone who was sleeping etc + GA_ATTACKOBJECT, // attacked or broke an object + GA_ASSAULT, // attacked someone peaceful + GA_EAT, // ate something the god doesn't like + GA_HERESY, // blessed or cursed an object + GA_MERCY, // allowed something to flee + GA_MONEY, // paid money to someone + GA_MURDER, // killed someone peaceful + GA_POISON, // used poison + GA_PRAY, // pestering through constant prayer + GA_SPELL, // cast a spell from the wrong school +}; + enum NOISECLASS { NC_NONE = 0, NC_MOVEMENT = 1, @@ -502,6 +517,7 @@ enum GAMEMODE { GM_VALIDATION, GM_LOADING, GM_LOADED, + GM_CHARGEN, GM_GAMESTARTED, GM_GAMEOVER, }; @@ -1971,6 +1987,7 @@ enum FLAG { // object mods/effects F_ONFIRE, // burning, also deals extra fire damage // option text = xdx amount of damage. + F_ENCHANTED, // weapon also deals 'text' extra fire damage F_HEADLESS, // for corpses. can go on LFs too. F_MASTERWORK, // weps do higher damager, armour protects better F_SHODDY, // weps do less damage, armour protects less. @@ -2044,7 +2061,8 @@ enum FLAG { F_LINKSPELL, // val0 = spell this scroll will cast when read // v1 = spell power // book flags - F_MANUALOF, // val0 = spellschool this book trains + F_MANUALOF, // val0 = skill this book trains + F_LINKSCHOOL, // val0 = spellschool this book has spells from // ob identification flags F_HASHIDDENNAME, // whether this object class has a hidden name // text is the name if you don't know what it is @@ -3263,6 +3281,7 @@ enum RUSTINESS { enum OBMOD { OM_BLOODSTAINED, + OM_ENCHANTED, OM_FLAMING, OM_FROZEN, OM_HEADLESS, diff --git a/god.c b/god.c index 9a7b5e5..75c7fbd 100644 --- a/god.c +++ b/god.c @@ -30,7 +30,7 @@ extern lifeform_t *player; lifeform_t *godlf[MAXGODS]; int ngodlfs = 0; -void angergod(enum RACE rid, int amt) { +void angergod(enum RACE rid, int amt, enum GODANGERREASON why) { lifeform_t *god; char lfname[BUFLEN]; int piety; @@ -48,6 +48,36 @@ void angergod(enum RACE rid, int amt) { // if you HAVE prayed to this god before, something bad will probably happen. + switch (why) { + case GA_ATTACKALLY: + godsay(rid, "How dare you attack an ally!"); break; + case GA_ATTACKHELPLESS: + godsay(rid, "You would attack one who is helpless?"); break; + case GA_ATTACKOBJECT: + if (rid == R_GODTHIEVES) { + godsay(rid, "What are you, a common thug?"); break; + } else { + godsay(rid, "You dare destroy my symbols?"); break; + } + case GA_ASSAULT: + godsay(rid, "You dare attack one of my innocent servants?"); break; + case GA_EAT: + godsay(rid, "That is NOT acceptable for consumption!"); break; + case GA_HERESY: + godsay(rid, "Heresy!"); break; + case GA_MERCY: + godsay(rid, "You allowed your foe to escape!"); break; + case GA_MONEY: + godsay(rid, "Giving away money offends me..."); break; + case GA_MURDER: + godsay(rid, "You have taken a life!"); break; + case GA_PRAY: break; + case GA_POISON: + godsay(rid, "I do not condone the use of poison!"); break; + case GA_SPELL: + godsay(rid, "Your magic offends me!"); break; + } + // announce if (piety > 0) { // not angry yet. godsay(rid, "You are testing my patience, mortal!"); @@ -243,11 +273,11 @@ void angergod(enum RACE rid, int amt) { } // anger the god if you are worshippin them. -void angergodmaybe(enum RACE rid, int amt) { +void angergodmaybe(enum RACE rid, int amt, enum GODANGERREASON why) { lifeform_t *god; god = findgod(rid); if (lfhasflag(god, F_PRAYEDTO)) { - angergod(rid, amt); + angergod(rid, amt, why); } } @@ -754,7 +784,7 @@ int prayto(lifeform_t *lf, lifeform_t *god) { if (godisangry(god->race->id)) { // get even more angry - angergod(god->race->id, PIETYPRAYLOSS); + angergod(god->race->id, PIETYPRAYLOSS, GA_PRAY); return B_FALSE; } @@ -781,7 +811,7 @@ int prayto(lifeform_t *lf, lifeform_t *god) { if (piety <= 99) { // piety between 0 and 99 = ignored //godsay(god->race->id, "Stop pestering me!"); - angergod(god->race->id, 0); + angergod(god->race->id, 0, GA_PRAY); modpiety(god->race->id, -30); return B_FALSE; } diff --git a/god.h b/god.h index cccff1d..ebbbcde 100644 --- a/god.h +++ b/god.h @@ -1,7 +1,7 @@ #include "defs.h" -void angergod(enum RACE rid, int amt); -void angergodmaybe(enum RACE rid, int amt); +void angergod(enum RACE rid, int amt, enum GODANGERREASON why); +void angergodmaybe(enum RACE rid, int amt, enum GODANGERREASON why); void dooffer(void); lifeform_t *findgod(enum RACE rid); int getpiety(enum RACE rid); diff --git a/io.c b/io.c index 8994d89..a4a4516 100644 --- a/io.c +++ b/io.c @@ -4591,11 +4591,17 @@ char *makedesc_ob(object_t *o, char *retbuf) { // other extra damage or effects? f = hasflag(o->flags, F_ONFIRE); if (f) { - if (f->text) { - sprintf(buf," It also inflicts %s extra burning damage.\n", f->text); - } else { - sprintf(buf," It also inflicts extra burning damage.\n"); - } + sprintf(buf," It also inflicts %s extra burning damage.\n", strlen(f->text) ? f->text : "2-12"); + strncat(retbuf, buf, HUGEBUFLEN); + } + f = hasflag(o->flags, F_FROZEN); + if (f) { + sprintf(buf," It also inflicts %s extra cold damage.\n", strlen(f->text) ? f->text : "1-4"); + strncat(retbuf, buf, HUGEBUFLEN); + } + f = hasflag(o->flags, F_ENCHANTED); + if (f) { + sprintf(buf," It also inflicts %s extra magical damage if the user has spare mana.\n", strlen(f->text) ? f->text : "1-2"); strncat(retbuf, buf, HUGEBUFLEN); } } else { @@ -5379,15 +5385,32 @@ char *makedesc_ob(object_t *o, char *retbuf) { if (o->contents->first) { object_t *oo; + int showcontents = B_TRUE; - sprintf(buf, "\n%s currently contain%s:\n", (o->amt == 1) ? "It" : "They", (o->amt == 1) ? "s" : ""); - strncat(retbuf, buf, HUGEBUFLEN); - - for (oo = o->contents->first ;oo ; oo = oo->next) { - char contentname[BUFLEN]; - getobname(oo, contentname, oo->amt); - sprintf(buf, " - %s\n", contentname); + if ((o->type->id == OT_SPELLBOOK) && !isknown(o)) { + showcontents = B_FALSE; + } + + if (showcontents) { + if (o->type->id == OT_SPELLBOOK) { + sprintf(buf, "\n%s contain%s the following spells:\n", (o->amt == 1) ? "It" : "They", (o->amt == 1) ? "s" : ""); + } else { + sprintf(buf, "\n%s currently contain%s:\n", (o->amt == 1) ? "It" : "They", (o->amt == 1) ? "s" : ""); + } strncat(retbuf, buf, HUGEBUFLEN); + + for (oo = o->contents->first ;oo ; oo = oo->next) { + char contentname[BUFLEN]; + getobname(oo, contentname, oo->amt); + sprintf(buf, " - %s", contentname); + if (o->type->id == OT_SPELLBOOK) { + char lbuf[BUFLEN]; + sprintf(lbuf, " (Lv %d)", getspelllevel(oo->type->id)); + strcat(buf, lbuf); + } + strcat(buf, "\n"); + strncat(retbuf, buf, HUGEBUFLEN); + } } } @@ -7379,7 +7402,7 @@ int getkey(int escseqok) { if (key_code == CH_BREAK) { // ctrl-c cbreak(); - raise(SIGINT); + raise(SIGINT); // break to debugger raw(); } diff --git a/lf.c b/lf.c index aad3034..ccf239b 100644 --- a/lf.c +++ b/lf.c @@ -1502,11 +1502,11 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar switch (school) { case SS_DEATH: pleasegodmaybe(R_GODDEATH, getspelllevel(sid)); - angergodmaybe(R_GODPURITY, getspelllevel(sid)*5); + angergodmaybe(R_GODPURITY, getspelllevel(sid)*5, GA_SPELL); break; case SS_LIFE: pleasegodmaybe(R_GODPURITY, getspelllevel(sid)); - angergodmaybe(R_GODDEATH, getspelllevel(sid)*5); + angergodmaybe(R_GODDEATH, getspelllevel(sid)*5, GA_SPELL); break; default: break; @@ -2110,7 +2110,7 @@ void die(lifeform_t *lf) { } // mercy god doesn't like killing - angergodmaybe(R_GODMERCY, 1); + angergodmaybe(R_GODMERCY, 1, GA_MURDER); } } @@ -2936,7 +2936,7 @@ int eat(lifeform_t *lf, object_t *o) { // eating your pet is very bad! if (isplayer(lf) && hasflagval(o->flags, F_PETOF, player->id, NA, NA, NULL)) { - angergodmaybe(R_GODPURITY, 150); + angergodmaybe(R_GODPURITY, 150, GA_EAT); stopeating = B_TRUE; } @@ -3776,7 +3776,7 @@ int flee(lifeform_t *lf) { // player let something flee? if (isplayer(thisone)) { pleasegodmaybe(R_GODMERCY, 5); - angergodmaybe(R_GODDEATH, 25); + angergodmaybe(R_GODDEATH, 25, GA_MERCY); } killflag(f); } else { @@ -10979,7 +10979,7 @@ int makenauseated(lifeform_t *lf, int amt, int howlong) { if (!hasbp(lf, BP_HEAD)) return B_TRUE; // can't smell with no head - if (!lfhasflag(lf, F_HUMANOID)) return B_TRUE; + //if (!lfhasflag(lf, F_HUMANOID)) return B_TRUE; f = lfhasflag(lf, F_NAUSEATED); if (f) { @@ -13581,7 +13581,7 @@ void startlfturn(lifeform_t *lf) { int angeramt; angeramt = f->val[0]; limit(&angeramt, 25, NA); - angergodmaybe(R_GODTHIEVES, angeramt); + angergodmaybe(R_GODTHIEVES, angeramt, GA_MONEY); killflag(f); } } diff --git a/map.c b/map.c index 2a0a38b..0554cd2 100644 --- a/map.c +++ b/map.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -5157,6 +5158,7 @@ int linkstairs(object_t *o, object_t *o2) { msg("ERROR - stairs link to existing map %d('%s', depth %d), but it has no free stairs.",othermap->id, othermap->name, othermap->depth); more(); + raise(SIGINT); // debug } } // end if othermap } // end if !o2 diff --git a/move.c b/move.c index ea284e0..905ad99 100644 --- a/move.c +++ b/move.c @@ -2718,7 +2718,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { // purposely moving away fmor money? if (onpurpose && isplayer(lf) && srcmoney) { - angergodmaybe(R_GODTHIEVES, srcmoney); + angergodmaybe(R_GODTHIEVES, srcmoney, GA_MONEY); } if (reason != E_OK) { diff --git a/nexus.c b/nexus.c index 3d7c66e..d570683 100644 --- a/nexus.c +++ b/nexus.c @@ -198,6 +198,7 @@ int main(int argc, char **argv) { if (!startrace) { race_t *r; + gamemode = GM_CHARGEN; // ask for race initprompt(&prompt, "Select your race:"); ch = 'a'; @@ -288,6 +289,7 @@ int main(int argc, char **argv) { addchoice(&prompt, 'a', getskillname(SK_SS_AIR), NULL, findskill(SK_SS_AIR), NULL); addchoice(&prompt, 'c', getskillname(SK_SS_COLD), NULL, findskill(SK_SS_COLD), NULL); addchoice(&prompt, 'f', getskillname(SK_SS_FIRE), NULL, findskill(SK_SS_FIRE), NULL); + addchoice(&prompt, 'n', getskillname(SK_SS_DEATH), NULL, findskill(SK_SS_DEATH), NULL); addchoice(&prompt, 'w', getskillname(SK_SS_WILD), NULL, findskill(SK_SS_WILD), NULL); getchoice(&prompt); sk = (skill_t *) prompt.result; @@ -296,19 +298,28 @@ int main(int argc, char **argv) { case SK_SS_AIR: addflag(player->flags, F_CANCAST, OT_S_MIST, NA, NA, NULL); addflag(player->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL); + addob(player->pack, "spellbook of air magic"); break; case SK_SS_COLD: addflag(player->flags, F_CANCAST, OT_S_CHILL, NA, NA, NULL); + addob(player->pack, "spellbook of cold magic"); break; case SK_SS_FIRE: addflag(player->flags, F_CANCAST, OT_S_SPARK, NA, NA, NULL); + addob(player->pack, "spellbook of fire magic"); + break; + case SK_SS_DEATH: + addflag(player->flags, F_CANCAST, OT_S_STENCH, NA, NA, NULL); + addob(player->pack, "spellbook of necromancy"); break; case SK_SS_WILD: addflag(player->flags, F_CANCAST, OT_S_MANASPIKE, NA, NA, NULL); + addob(player->pack, "spellbook of wild magic"); break; default: break; } + initprompt(&prompt, "Select your secondary spell school:"); addchoice(&prompt, 'd', getskillname(SK_SS_DIVINATION), NULL, findskill(SK_SS_DIVINATION), NULL); addchoice(&prompt, 'g', getskillname(SK_SS_GRAVITY), NULL, findskill(SK_SS_GRAVITY), NULL); @@ -321,18 +332,23 @@ int main(int argc, char **argv) { switch (sk->id) { case SK_SS_DIVINATION: addflag(player->flags, F_CANCAST, OT_S_SIXTHSENSE, NA, NA, NULL); + addob(player->pack, "spellbook of divination magic"); break; case SK_SS_GRAVITY: addflag(player->flags, F_CANCAST, OT_S_TRUESTRIKE, NA, NA, NULL); + addob(player->pack, "spellbook of gravitation magic"); break; case SK_SS_MODIFICATION: addflag(player->flags, F_CANCAST, OT_S_HOLDPORTAL, NA, NA, NULL); + addob(player->pack, "spellbook of modification magic"); break; case SK_SS_SUMMONING: addflag(player->flags, F_CANCAST, OT_S_FLOATINGDISC, NA, NA, NULL); + addob(player->pack, "spellbook of summoning magic"); break; case SK_SS_TRANSLOCATION: addflag(player->flags, F_CANCAST, OT_S_APPORTATION, NA, NA, NULL); + addob(player->pack, "spellbook of translocation magic"); break; default: break; diff --git a/objects.c b/objects.c index 16fa685..b4fadad 100644 --- a/objects.c +++ b/objects.c @@ -434,7 +434,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes int bonus = 0; int nom = 0; int n; - int bookcontents = -1; + int bookcontents = -1;// a skill for manuals, or a spellschool id for spellbooks enum DEPTH wantdepth = DP_MAX; int wantlit = B_FALSE; int wantrarity = RR_NONE; @@ -778,10 +778,10 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes char *pp; pp = p + 13; if (*pp) { - objecttype_t *spelltype = NULL; - spelltype = findspelln(pp); - if (spelltype) { - bookcontents = spelltype->id; + enum SPELLSCHOOL school; + school = findspellschoolbyname(pp); + if (school != SS_NONE) { + bookcontents = school; } } ot = findot(OT_SPELLBOOK); @@ -892,9 +892,11 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes } if (ot->obclass->id == OC_SPELL) { - if (db) dblog("DB: Cannot give a spell object! object name '%s'", p ); - nretobs = 0; - return NULL; + if (where->owner || where->where) { + if (db) dblog("DB: Cannot give a spell object to a player, or a cell! object name '%s'", ot->name ); + nretobs = 0; + return NULL; + } } // override canstack if required @@ -1173,14 +1175,13 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes int numhiddennames; int n,sel; if (bookcontents == -1) { - int maxlev; - maxlev = getmapdifficulty(obloc->map) / 3; - limit(&maxlev, 1, 9); if (o->type->id == OT_SPELLBOOK) { - bookcontents = getrandomspell(maxlev); - while (!schoolappearsinbooks(getspellschool(bookcontents))) { - bookcontents = getrandomspell(maxlev); - } + // pick a random school + bookcontents = getrandomspellschool(); + //bookcontents = getrandomspell(maxlev); + //while (!schoolappearsinbooks(getspellschool(bookcontents))) { + // bookcontents = getrandomspell(maxlev); + // } } else { // ie. manual bookcontents = getrandomskill(); } @@ -1188,12 +1189,31 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes // link if (o->type->id == OT_SPELLBOOK) { - addflag(o->flags, F_LINKSPELL, bookcontents, NA, NA, NULL); + int nspells,firstlev; + // remember the book's school (used for description) + addflag(o->flags, F_LINKSCHOOL, bookcontents, NA, NA, NULL); + // add contents to the book + if (where->owner) { // giving to a lifeform? + nspells = 6; + firstlev = 1; + } else { + nspells = rnd(2,5); + firstlev = rnd(1,4); + } + for (i = 0; i < nspells; i++) { + enum OBTYPE oid; + int lev; + lev = firstlev + i; + if (lev > MAXSPELLLEV) break; + oid = getrandomspellfromschool(bookcontents,lev); + if (oid != OT_NONE) { + assert(addobfast(o->contents, oid)); + } + } } else { addflag(o->flags, F_MANUALOF, bookcontents, NA, NA, NULL); } - // count hidden names numhiddennames = 0; for (hn = firsthiddenname ; hn ; hn = hn->next) { @@ -1214,6 +1234,8 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes } } addflag(o->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, selhn->text); + // don't call sethiddenname, because it acts on OBJECT TYPES not OBJECTS. + // all books are unique } @@ -3731,6 +3753,14 @@ char *gethiddenname(object_t *o) { if (hasflag(o->flags, F_IDENTIFIED)) { return o->type->name; } + // otherwise special case for unidentified books... + if (o->type->id == OT_SPELLBOOK) { + flag_t *f; + f = hasflag(o->flags, F_HASHIDDENNAME); + if (f) { + return f->text; + } + } // otherwise check if it has a hidden name for (k = knowledge; k ; k = k->next) { if (k->id == o->type->id) { @@ -3755,6 +3785,16 @@ char *gethiddennameot(enum OBTYPE otid) { knowledge_t *k; objecttype_t *ot; ot = findot(otid); + + // otherwise special case for unidentified books... + if (ot->id == OT_SPELLBOOK) { + flag_t *f; + f = hasflag(ot->flags, F_HASHIDDENNAME); + if (f) { + return f->text; + } + } + for (k = knowledge; k ; k = k->next) { if (k->id == ot->id) { // it DOES have a hidden name. @@ -4322,14 +4362,12 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } if (o->type->obclass->id == OC_BOOK) { - if (!strcmp(basename, o->type->name)) { + if (streq(basename, o->type->name)) { if (o->type->id == OT_SPELLBOOK) { - f = hasflag(o->flags, F_LINKSPELL); + f = hasflag(o->flags, F_LINKSCHOOL); if (f) { - objecttype_t *st; - st = findot(f->val[0]); strcat(basename, " of "); - strcat(basename, st->name); + strcat(basename, getschoolname(f->val[0])); } } else { f = hasflag(o->flags, F_MANUALOF); @@ -4341,6 +4379,9 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } } } + } else if (o->type->obclass->id == OC_SPELL) { + strcpy(buf, o->type->name); + return buf; } if (!showall) { @@ -5209,17 +5250,16 @@ char *getschoolname(enum SPELLSCHOOL sch) { case SS_COLD: return "Cold Magic"; case SS_MODIFICATION: return "Modification Magic"; case SS_DEATH: return "Necromancy"; - case SS_NATURE: return "Nature"; + case SS_NATURE: return "Enviromancy"; case SS_LIFE: return "Life Magic"; case SS_DIVINATION: return "Divination Magic"; case SS_TRANSLOCATION: return "Translocation Magic"; case SS_SUMMONING: return "Summoning Magic"; case SS_GRAVITY: return "Gravitation Magic"; - case SS_LAST: return "!invalid school!"; default: break; } - return "unknown school"; + return "badschool"; } char *getschoolnameshort(enum SPELLSCHOOL sch) { @@ -5878,6 +5918,11 @@ int isknown(object_t *o) { return B_TRUE; } + // unidentified books are always unknown + if (o->type->id == OT_SPELLBOOK) { + return B_FALSE; + } + return isknownot(o->type); } @@ -8181,7 +8226,7 @@ int pour(lifeform_t *lf, object_t *o) { // god effects if (isplayer(lf)) { pleasegodmaybe(R_GODPURITY, 3); - angergodmaybe(R_GODDEATH, 15); + angergodmaybe(R_GODDEATH, 15, GA_HERESY); } } else if ((o->type->id == OT_POT_WATER) && (o->blessed == B_CURSED)) { // unholy water if (isplayer(lf)) { @@ -8190,12 +8235,12 @@ int pour(lifeform_t *lf, object_t *o) { o->blessknown = B_TRUE; // curse whatever we poured onto curseob(dst); - // we now know that this is holy water + // we now know that this is unholy water if (!isknown(o)) makeknown(o->type->id); // god effects if (isplayer(lf)) { pleasegodmaybe(R_GODDEATH, 3); - angergodmaybe(R_GODPURITY, 25); + angergodmaybe(R_GODPURITY, 25, GA_HERESY); } } else if (o->type->id == OT_POT_INVULN) { flag_t *f; @@ -8909,6 +8954,7 @@ int readsomething(lifeform_t *lf, object_t *o) { } else { readtime = SPEED_READ; } + taketime(lf, readtime); if (isplayer(lf)) maketried(o->type->id); @@ -8938,7 +8984,7 @@ int readsomething(lifeform_t *lf, object_t *o) { if (playercansee) { if (o->type->obclass->id == OC_BOOK) { // is this a spellbook? - if (linkspell) { + if (o->type->id == OT_SPELLBOOK) { // if so, always id it. // note though that we can only actually LEARN // the spell if we are skilled in that school. @@ -9303,35 +9349,66 @@ int readsomething(lifeform_t *lf, object_t *o) { } } else if (o->type->obclass->id == OC_BOOK) { // is this a spellbook? - if (linkspell) { - // if so, can we read it? + if (o->type->id == OT_SPELLBOOK) { + object_t *oo; + char ch = 'a'; + enum SPELLSCHOOL school; + enum SKILLLEVEL slev; + f = hasflag(o->flags, F_LINKSCHOOL); + school = f->val[0]; + slev = getskill(lf, getschoolskill(school)); + + if (!slev) { + if (isplayer(lf)) msg("You cannot comprehend the contents of this book."); + return B_FALSE; + } + + // we are skilled in the right school. now ask which spell to learn. + initprompt(&prompt, "Which spell will you try to learn?"); + for (oo = o->contents->first ;oo ; oo = oo->next) { + if (!hasflagval(lf->flags, F_CANCAST, oo->type->id, NA, NA, NULL)) { + char *longdesc; + char spellname[BUFLEN]; + longdesc = malloc(HUGEBUFLEN * sizeof(char)); + sprintf(spellname, "%s (Lv %d)", oo->type->name, getspelllevel(oo->type->id)); + makedesc_spell(oo->type, longdesc); + addchoice(&prompt, ch++, oo->type->name, NULL, oo->type, longdesc); + free(longdesc); + } + } + addchoice(&prompt, '-', "none", NULL, NULL, NULL); + if (prompt.nchoices == 1) { + msg("You already know all the spells in this book!"); + return B_FALSE; + } + prompt.maycancel = B_TRUE; + ch = getchoicestr(&prompt, B_FALSE, B_TRUE); + if ((ch == '\0') || (ch == '-')) { + // not 'cancelled' because we still took time + msg("You close the spellbook without reading it."); + return B_FALSE; + } + linkspell = (objecttype_t *) prompt.result; + + + // too hard? if (getspellpower(lf, linkspell->id) > 0) { if (lfhasflagval(lf, F_CANCAST, linkspell->id, NA, NA, NULL)) { if (isplayer(lf)) msg("You already know how to cast this spell!"); } else { - enum SPELLSCHOOL school; - enum SKILLLEVEL slev; - school = getspellschool(linkspell->id); - slev = getspellskill(lf, linkspell->id); - - // can we learn this? - if (slev) { - // try to learn it - int difficulty, mod; - difficulty = 15 + (getspelllevel(linkspell->id)*3); - mod = slev * 2; - if (skillcheck(lf, SC_LEARNMAGIC, difficulty, mod)) { - // learn it - addflag(lf->flags, F_CANCAST, linkspell->id, NA, NA, NULL); - } else { - msg("^bYou fail to learn %s.",linkspell->name); - } + // try to learn it + int difficulty, mod; + difficulty = 15 + (getspelllevel(linkspell->id)*3); + mod = slev * 2; + if (skillcheck(lf, SC_LEARNMAGIC, difficulty, mod)) { + // learn it + addflag(lf->flags, F_CANCAST, linkspell->id, NA, NA, NULL); } else { - msg("You are not yet skilled in %s.",getschoolnameshort(school)); + msg("^bYou fail to learn %s.",linkspell->name); } } } else { - if (isplayer(lf)) msg("You cannot comprehend the contents of this book."); + if (isplayer(lf)) msg("This spell is too hard for you to learn right now."); } } else { // manuals @@ -9518,8 +9595,10 @@ int sethiddenname(objecttype_t *ot, char *text) { int n; int gotcolour = B_FALSE; - // add knowledge for it - addknowledge(ot->id, text, B_UNKNOWN); + // add knowledge for it (unless it's a book + if (ot->obclass->id != OC_BOOK) { + addknowledge(ot->id, text, B_UNKNOWN); + } // some descriptions confer other effecst too... if (strstr(text, "glowing")) { @@ -9810,7 +9889,7 @@ int shatter(object_t *o, int hitlf, char *damstring, lifeform_t *fromlf) { case OT_POT_HEALINGMIN: case OT_POT_HEALINGMAJ: case OT_POT_AMBROSIA: - angergodmaybe(R_GODMERCY, 25); + angergodmaybe(R_GODMERCY, 25, GA_ATTACKOBJECT); break; default: break; diff --git a/spell.c b/spell.c index 507dbd1..c5bde32 100644 --- a/spell.c +++ b/spell.c @@ -5532,7 +5532,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!frompot) { pleasegodmaybe(R_GODMERCY, 3); } - angergodmaybe(R_GODDEATH, 20); + angergodmaybe(R_GODDEATH, 20, GA_HERESY); } // hostile monsters might calm down if (!frompot && donesomething && isplayer(caster) && !isplayer(target) && (getallegiance(target) == AL_HOSTILE)) { @@ -9195,6 +9195,14 @@ objecttype_t *findspelln(char *buf) { return NULL; } +enum SPELLSCHOOL findspellschoolbyname(char *buf) { + enum SPELLSCHOOL ss; + for (ss = SS_NONE; ss <= SS_LAST; ss++) { + if (!strcasecmp(buf, getschoolname(ss))) return ss; + } + return SS_NONE; +} + void fizzle(lifeform_t *caster) { if (!caster) return; if (isplayer(caster)) { @@ -9299,6 +9307,40 @@ enum OBTYPE getrandomspell(int maxlev) { return ot->id; } +// wantlev = 0 means 'any level' +enum OBTYPE getrandomspellfromschool(enum SPELLSCHOOL school, int wantlev) { + objecttype_t *ot,*poss[MAXCANDIDATES]; + int nposs = 0; + + // get list of all spells of this school + for (ot = objecttype ; ot ; ot = ot->next) { + if ((ot->obclass->id == OC_SPELL) && spellisfromschool(ot->id, school)) { + if ((wantlev == 0) || (getspelllevel(ot->id) == wantlev)) { + poss[nposs++] = ot; + } + } + } + + if (nposs <= 0) { + return OT_NONE; + } + ot = poss[rnd(0,nposs-1)]; + return ot->id; +} + +enum SPELLSCHOOL getrandomspellschool(void) { + enum SPELLSCHOOL poss[MAXCANDIDATES],ss; + int nposs = 0; + // count valid schools + for (ss = SS_NONE; ss <= SS_LAST; ss++) { + if (schoolappearsinbooks(ss) && !streq(getschoolname(ss), "badschool")) { + poss[nposs++] = ss; + } + } + // make + return poss[rnd(0,nposs-1)]; +} + enum SKILL getschoolskill(enum SPELLSCHOOL ss) { switch (ss) { case SS_ALLOMANCY: diff --git a/spell.h b/spell.h index 11afd1c..d667dc4 100644 --- a/spell.h +++ b/spell.h @@ -5,12 +5,15 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifeform_t *target, flag_t *cwflag); 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); objecttype_t *findspelln(char *buf); +enum SPELLSCHOOL findspellschoolbyname(char *buf); void fizzle(lifeform_t *caster); //int getiqreq(enum OBTYPE oid); char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr); int getmpcost(lifeform_t *lf, enum OBTYPE oid); int getmrdiff(enum OBTYPE spellid, int power); enum OBTYPE getrandomspell(int maxlev); +enum OBTYPE getrandomspellfromschool(enum SPELLSCHOOL school, int wantlev); +enum SPELLSCHOOL getrandomspellschool(void); enum SKILL getschoolskill(enum SPELLSCHOOL ss); char *getspellcosttext(lifeform_t *lf, enum OBTYPE spellid, int power, char *buf); int getspellduration(int min,int max,int blessed); diff --git a/text.c b/text.c index b4b2edf..f3940fe 100644 --- a/text.c +++ b/text.c @@ -4,7 +4,9 @@ #include #include #include +#include "attack.h" #include "defs.h" +#include "flag.h" #include "lf.h" #include "nexus.h" #include "objects.h" @@ -12,6 +14,8 @@ extern long curtime; +extern lifeform_t *player; + int needan(char *text) { if (isvowel(tolower(text[0]))) { return B_TRUE; @@ -78,6 +82,150 @@ enum COLOUR chartocol(char ch) { return C_GREY; } +char *construct_hit_string(lifeform_t *lf, lifeform_t *victim, char *attackername, char *victimname, char *victimbpname, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp, int idx, int backstab, int critical, int fatal, int isunarmed, char *retbuf) { + int usecrittext = B_FALSE; + char wepname[BUFLEN],buf[BUFLEN]; + + getobname(wep, wepname, 1); + + // initial hit... + if (idx == 0) { + if (critical && !fatal) usecrittext = B_TRUE; + + if (isplayer(lf)) { + char extradambuf[BUFLEN]; + char withwep[BUFLEN]; + char *verb; + int needfree = B_FALSE; + + strcpy(extradambuf, ""); + + if (wep && !ismeleeweapon(wep)) { + snprintf(withwep, BUFLEN, " with %s", wepname); + } else { + strcpy(withwep, ""); + } + + strcpy(extradambuf, ""); + if (dam == 0) { + if (!victim || getlorelevel(lf, victim->race->raceclass->id)) { + //strcpy(extradambuf, " but do no damage"); + strcpy(extradambuf, " ineffectually"); + } + } else if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT) ) { + snprintf(extradambuf, BUFLEN, " [%d dmg]",dam); + } + + if (backstab && (idx == 0)) { + verb = strdup("backstab"); + needfree = B_TRUE; + } else if (fatal) { + verb = getkillverb(victim, wep, damtype, dam, maxhp); + } else { + if (!victim || // atacking an object + (getlorelevel(lf, victim->race->raceclass->id) >= PR_BEGINNER) || // know about the race + !ismeleedam(damtype)) { // non-physical damage + verb = getattackverb(lf, wep, damtype, dam, maxhp); + } else { + // always use verb for 10% + verb = getattackverb(lf, wep, damtype, pctof(10, maxhp), maxhp); + } + } + snprintf(retbuf, BUFLEN, "^%cYou %s%s %s%s%s%s", fatal ? 'g' : 'n', + usecrittext ? "critically " : "", verb, + usecrittext ? victimbpname : victimname, withwep,extradambuf, + (fatal || backstab) ? "!" : "."); + + if (needfree) { + free(verb); + } + } else { // ie. the attacker is a monster + if (cansee(player, lf) || (victim && isplayer(victim))) { + char withwep[BUFLEN]; + char attackverb[BUFLEN]; + char nodamstr[BUFLEN]; + int nodam = B_FALSE; + + // capitalise first letter + strcpy(buf, attackername); + capitalise(buf); + + if (wep && !isunarmed && (lf->race->id != R_DANCINGWEAPON) && cansee(player, lf)) { + snprintf(withwep, BUFLEN, " with %s", wepname); + } else { + strcpy(withwep, ""); + } + + strcpy(attackverb, getattackverb(lf, wep, damtype,dam,maxhp)); + if ((dam == 0) && (damtype != DT_TOUCH)) { + nodam = B_TRUE; + strcpy(nodamstr, " ineffectually"); + } else { + strcpy(nodamstr, ""); + } + snprintf(retbuf, BUFLEN, "^%c%s %s%s%s %s%s%s.", (victim && isplayer(victim) && !nodam) ? 'b' : 'n', buf, + usecrittext ? "critically " : "", attackverb, + needses(attackverb) ? "es" : "s", + usecrittext ? victimbpname : victimname,withwep, nodamstr); + } + } + } else { // follow-up weapon damage (ie from a flaming sword etc) + if (victim && cansee(player, victim)) { + if (dam == 0) { // ineffectual + switch (damtype) { + case DT_COLD: + snprintf(retbuf, BUFLEN, "^n%s %s cold.", victimname, isplayer(victim) ? "don't feel" : "doesn't look"); + break; + case DT_HEAT: + case DT_FIRE: + snprintf(retbuf, BUFLEN, "^n%s %s hot.", victimname, isplayer(victim) ? "don't feel" : "doesn't look"); + break; + case DT_MAGIC: + strcpy(retbuf, ""); + break; + default: + snprintf(retbuf, BUFLEN, "^n%s shrug%s off the effects.", victimname, isplayer(victim) ? "" : "s"); + break; + } + } else if (fatal) { // fatal + switch (damtype) { + case DT_COLD: + snprintf(retbuf, BUFLEN, "^%c%s %s chilled to the bone", getlfcol(victim, CC_BAD), victimname, is(victim)); + break; + case DT_HEAT: + case DT_FIRE: + snprintf(retbuf, BUFLEN, "^%c%s %s incinerated!", getlfcol(victim, CC_BAD), victimname, is(victim)); + break; + case DT_MAGIC: + snprintf(retbuf, BUFLEN, "^%c%s %s blasted with magic!", getlfcol(victim, CC_BAD), victimname, is(victim)); + break; + default: + snprintf(retbuf, BUFLEN, "^%c%s %s killed!", getlfcol(victim, CC_BAD), victimname, is(victim)); + break; + } + } else { // normal + switch (damtype) { + case DT_COLD: + snprintf(retbuf, BUFLEN, "^n%s %s chilled!", victimname, is(victim)); + break; + case DT_HEAT: + case DT_FIRE: + snprintf(retbuf, BUFLEN, "^n%s %s burned!", victimname, is(victim)); + break; + case DT_MAGIC: + snprintf(retbuf, BUFLEN, "^nMagical energy sears %s!", victimname); + break; + default: + snprintf(retbuf, BUFLEN, "^n%s %s hurt!", victimname, is(victim)); + break; + } + } + } + } + capitalise(retbuf); + return retbuf; +} + char *dicetotext(int ndice, int nsides, int bonus, int *min, int *max, char *dicebuf, char *minmaxbuf) { int localmin, localmax; @@ -188,6 +336,168 @@ int getaccuracynum(int accpct) { return num; } +// returns a const char * +char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) { + float pct; + enum LFSIZE ownersize = SZ_HUMAN; + flag_t *retflag[MAXCANDIDATES]; + int nretflags = 0; + + if (lf) { + ownersize = getlfsize(lf); + } + + pct = (int)(((float) dam / (float) maxhp) * 100.0); + + if (wep) { + int i; + flag_t *f; + getflags(wep->flags, retflag, &nretflags, F_ATTACKVERB, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if ((f->val[0] == NA) && (f->val[1] == NA)) { + return f->text; + } else if (f->val[0]) { + if (pct >= f->val[0]) { + if (f->val[1] == NA) { + return f->text; + } else if (pct <= f->val[1]) { + return f->text; + } + } + } else if (f->val[1]) { + if (pct <= f->val[1]) { + return f->text; + } + } + } + } + + if (damtype == DT_ACID) { + return "burn"; + } else if (damtype == DT_BASH) { + if (pct <= 5) { + return "whack"; + } else if (pct <= 15) { + if (onein(2)) { + return "hit"; + } else { + return "bash"; + } + } else if (pct <= 25) { + return "pummel"; + } else if (pct <= 35) { + return "slam"; + } else { + return "clobber"; + } + } else if (damtype == DT_BITE) { + if (lf && (ownersize <= SZ_SMALL)) { + if (pct <= 5) { + return "nip"; + } else { + return "bite"; + } + } else { + if (pct <= 5) { + return "gnaw"; + } else if (pct <= 30) { + return "bite"; + } else { + return "savage"; + } + } + } else if (damtype == DT_CHOP) { + if (pct <= 5) { + return "hit"; + } else if (pct <= 15) { + return "hack"; + } else { + return "chop"; + } + } else if (damtype == DT_COLD) { + if (pct <= 10) { + return "chill"; + } else { + return "freeze"; + } + } else if (damtype == DT_CRUSH) { + return "crush"; + } else if (damtype == DT_ELECTRIC) { + if (pct <= 5) { + return "zap"; + } else if (pct <= 15) { + return "jolt"; + } else if (pct <= 20) { + return "shock"; + } else if (pct <= 30) { + return "electrify"; + } else { + return "electrocute"; + } + } else if ((damtype == DT_FIRE) || (damtype == DT_HEAT)) { + if (pct <= 5) { + return "scorch"; + } else if (pct <= 20) { + return "burn"; + } else if (pct <= 40) { + return "scald"; + } else { + return "incinerate"; + } + } else if (damtype == DT_HOLY) { + switch (rnd(1,3)) { + case 1: + return "smite"; + case 2: + return "cleanse"; + case 3: + return "purify"; + } + } else if (damtype == DT_MAGIC) { + return "blast"; + } else if (damtype == DT_PIERCE) { + if (pct <= 5) { + return "poke"; + } else if (pct <= 15) { + return "stab"; + } else if (pct <= 30) { + return "pierce"; + } else if (pct <= 40) { + return "spear"; + } else { + return "deeply stab"; + } + } else if (damtype == DT_POISONGAS) { + return "poison"; + } else if (damtype == DT_PROJECTILE) { + return "hit"; + } else if (damtype == DT_SLASH) { + if (pct <= 5) { + return "scratch"; + } else if (pct <= 15) { + return "hit"; + } else if (pct <= 30) { + return "slash"; + } else { + return "slice"; + } + } else if (damtype == DT_TOUCH) { + return "touch"; + } else if (damtype == DT_WATER) { + // for when water-vulnerable things go into water + return "hurt"; + } else if (damtype == DT_UNARMED) { + if (onein(2)) { + return "punch"; + } else { + return "hit"; + } + } + return "hit"; +} + + char *getattrabbrev(enum ATTRIB att) { switch (att) { case A_NONE: @@ -336,6 +646,98 @@ int gethitconferlifetime(char *text, int *min, int *max) { return howlong; } +char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) { + float pct; + pct = (int)(((float) dam / (float) maxhp) * 100.0); + + if (wep && hasflag(wep->flags, F_MERCIFUL)) { + return "knock out"; + } + + if (victim->race->id == R_DANCINGWEAPON) { + return "defeat"; + } + + if (getraceclass(victim) == RC_PLANT) { + return "destroy"; + } + + if (wep) { + flag_t *f; + int i; + flag_t *retflag[MAXCANDIDATES]; + int nretflags = 0; + getflags(wep->flags, retflag, &nretflags, F_KILLVERB, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if (f->id == F_KILLVERB) { + if ((f->val[0] == NA) && (f->val[1] == NA)) { + return f->text; + } else if (f->val[0]) { + if (pct >= f->val[0]) { + if (f->val[1] == NA) { + return f->text; + } else if (pct <= f->val[1]) { + return f->text; + } + } + } else if (f->val[1]) { + if (pct <= f->val[1]) { + return f->text; + } + } + } + } + } + + if ((damtype == DT_BASH) && ismadeofice(victim)) { + return "shatter"; + } + + if (damtype == DT_CRUSH) { + return "crush"; + } + + if (damtype == DT_HOLY) { + return "smite"; + } + + if (pct >= 70) { + if (damtype == DT_PIERCE) return "impale"; + if (damtype == DT_BASH) { + if (isunconscious(victim)) { + return "kill"; + } else { + return "flatten"; + } + } + if (damtype == DT_BITE) return "gore"; + if (damtype == DT_SLASH) { + if (lfhasflagval(victim, F_NOBODYPART, BP_HEAD, NA, NA, NULL)) { + return "bisect"; + } else { + if ((getlfsize(victim) >= SZ_MEDIUM) && onein(3)) { + return "behead"; + } else { + return "bisect"; + } + } + } + } + + if (getraceclass(victim) == RC_UNDEAD) { + // can't "kill" the undead + return "destroy"; + } + + // never use 'kill' for bashing since you might just knock them out + if (damtype == DT_BASH) { + return "clobber"; + } + return "kill"; +} + + char *getpoisondamverb(enum POISONTYPE ptype) { switch (ptype) { case P_FOOD: diff --git a/text.h b/text.h index 3060e0a..d7091b8 100644 --- a/text.h +++ b/text.h @@ -4,16 +4,19 @@ int needan(char *text); char *capitalise(char *text); char *capitaliseall(char *text); enum COLOUR chartocol(char ch); +char *construct_hit_string(lifeform_t *lf, lifeform_t *victim, char *attackername, char *victimname, char *victimbpname, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp, int damidx, int critical, int backstab, int fatal, int isunarmed, char *retbuf); char *dicetotext(int ndice, int nsides, int bonus, int *min, int *max, char *dicebuf, char *minmaxbuf); int flip(int ch); char *getaccuracyname(int accpct); int getaccuracymodnum(int accmodpct); int getaccuracynum(int accpct); +char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp); char *getattrabbrev(enum ATTRIB att); char *getattrname(enum ATTRIB att); char *getdirname(int dir); char *getdirnameshort(int dir); int gethitconferlifetime(char *text, int *min, int *max); +char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp); char *getpoisondamverb(enum POISONTYPE ptype); char *getpoisondesc(enum POISONTYPE ptype); char *getpoisonname(enum POISONTYPE ptype);