- [+] wizard staff should deal 1 extra magic damage as long as you can

pay it
    - [+] use "enchanted" weapon mod?
    - [+] test
    - [+] why aren't obmods working on start obs?
    - [+] why isn't wizard starting with staff weilded
        - [+] because gamemode was GM_LOADING
        - [+] use something different during character gen.
- [+] use different text for followup weapon hits (extradam)
    - [+] "you hit the xxx" (first one)
    - [+] "the xxx burns" (second)
    - [+] "magic blasts the xxx"
    - [+] "the xxx is chilled"
    - [+] move all of this into construct_hit_string(lf, victim, wep,
          damtype, dam, damnum)
    - [+] test
    - [+] move getkillvebr()  and getattackverb() into text.c
    - [+] make attackob() use the same code
- [+] spellbooks now contain multiple spells
    - [+] they have an associated SKILL (not spell)
    - [+] they have contents (a list of spells)
    - [+] hiddenname isn't getting assigned
    - [+] contents aren't getitng filled in
    - [+] when you read them, you learn all the spells (if you can)...
          OR prompt what to read
    - [+] DONT give wizards spells on levelup anymore?
- [+] gods should say WHY they are angry.
    - [+] angergodmaybe, angergod need to take enum GODANGERREASON
        - [+] change definitions
        - [+] change calls
    - [+] add new godsay text into angergod()
This commit is contained in:
Rob Pearce 2011-10-20 17:15:24 +00:00
parent 168a3c49ff
commit c79a0403c2
16 changed files with 763 additions and 439 deletions

401
attack.c
View File

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

View File

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

14
data.c
View File

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

21
defs.h
View File

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

40
god.c
View File

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

4
god.h
View File

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

49
io.c
View File

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

14
lf.c
View File

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

2
map.c
View File

@ -1,6 +1,7 @@
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -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

2
move.c
View File

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

16
nexus.c
View File

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

185
objects.c
View File

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

44
spell.c
View File

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

View File

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

402
text.c
View File

@ -4,7 +4,9 @@
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#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:

3
text.h
View File

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