diff --git a/ai.c b/ai.c index afcb233..8a37a2f 100644 --- a/ai.c +++ b/ai.c @@ -799,6 +799,12 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { hateposs[nhateposs++] = who; } break; + } else if (lfhasflagval(lf, F_HATESRACECLASS, who->race->raceclass->id, NA, NA, NULL) ) { + if ((nhateposs < MAXCANDIDATES) && !areallies(lf, who)) { + if (db) dblog(".oO { found a hated raceclass target - lfid %d (%s) ! }",who->id, who->race->name); + hateposs[nhateposs++] = who; + } + break; } else if ( ((f = lfhasflag(lf, F_TERRITORIAL)) != NULL) && (getcelldist(who->cell, lf->cell) <= f->val[0]) ) { if ((nhateposs < MAXCANDIDATES) && !areallies(lf, who)) { diff --git a/attack.c b/attack.c index 8025709..2ae4a55 100644 --- a/attack.c +++ b/attack.c @@ -19,7 +19,8 @@ extern lifeform_t *player; extern lifeform_t *godlf[]; -extern int ngodlfs; + +extern int needredraw; extern enum ERROR reason; @@ -478,59 +479,53 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { } // god effects... - if ((attacktype == AT_LF) && isplayer(lf) && attacktarget) { - if (!isgod(attacktarget)) { - if (attackedfriend) { - angergodmaybe(R_GODMERCY, 100, GA_ATTACKALLY); - angergodmaybe(R_GODPURITY, 100, GA_ATTACKALLY); - switch (getalignment(attacktarget)) { - case AL_EVIL: - angergodmaybe(R_GODDEATH, 20, GA_ATTACKALLY); // even more - break; - case AL_GOOD: - angergodmaybe(R_GODPURITY, 20, GA_ATTACKALLY); // even more - break; - default: - break; - } - } else if (attackedpeaceful) { - angergodmaybe(R_GODMERCY, 50, GA_ASSAULT); - angergodmaybe(R_GODPURITY, 50, GA_ASSAULT); - switch (getalignment(attacktarget)) { - case AL_EVIL: - angergodmaybe(R_GODDEATH, 20, GA_ASSAULT); // even more - break; - case AL_GOOD: - angergodmaybe(R_GODPURITY, 20, GA_ASSAULT); // even more - break; - default: - break; - } - } else if (attackedhelpless) { - angergodmaybe(R_GODMERCY, 50, GA_ATTACKHELPLESS); - angergodmaybe(R_GODPURITY, 50, GA_ATTACKHELPLESS); - if (getalignment(attacktarget) != AL_EVIL) { - pleasegodmaybe(R_GODTHIEVES, 5); - pleasegodmaybe(R_GODDEATH, 10); - } - } - if (lfhasflag(lf, F_USEDPOISON)) { - int i; - flag_t *f; - killflagsofid(lf->flags, F_USEDPOISON); - for (i = 0; i < ngodlfs; i++) { - if (godlf[i]) { - f = lfhasflag(godlf[i], F_GODPOISON); - if (f) { - if (f->val[0]) { - pleasegodmaybe(godlf[i]->race->id, f->val[1]); - } else { - angergodmaybe(godlf[i]->race->id, f->val[1], GA_POISON); - } - } + if (isplayer(lf) && attacktarget) { + if (attacktype == AT_LF) { + if (!isgod(attacktarget)) { + if (attackedfriend) { + angergodmaybe(R_GODMERCY, 100, GA_ATTACKALLY); + angergodmaybe(R_GODPURITY, 100, GA_ATTACKALLY); + switch (getalignment(attacktarget)) { + case AL_EVIL: + angergodmaybe(R_GODDEATH, 20, GA_ATTACKALLY); // even more + break; + case AL_GOOD: + angergodmaybe(R_GODPURITY, 20, GA_ATTACKALLY); // even more + break; + default: + break; } + } else if (attackedpeaceful) { + angergodmaybe(R_GODMERCY, 50, GA_ASSAULT); + angergodmaybe(R_GODPURITY, 50, GA_ASSAULT); + switch (getalignment(attacktarget)) { + case AL_EVIL: + angergodmaybe(R_GODDEATH, 20, GA_ASSAULT); // even more + break; + case AL_GOOD: + angergodmaybe(R_GODPURITY, 20, GA_ASSAULT); // even more + break; + default: + break; + } + } else if (attackedhelpless) { + angergodmaybe(R_GODMERCY, 50, GA_ATTACKHELPLESS); + angergodmaybe(R_GODPURITY, 50, GA_ATTACKHELPLESS); + if (getalignment(attacktarget) != AL_EVIL) { + pleasegodmaybe(R_GODTHIEVES, 5); + pleasegodmaybe(R_GODDEATH, 10); + } + } + if ( ((lifeform_t *)attacktarget)->race->raceclass->id == RC_PLANT) { + angergodmaybe(R_GODNATURE, 25, GA_ATTACKOBJECT); + } + if (lfhasflag(lf, F_USEDPOISON)) { + killflagsofid(lf->flags, F_USEDPOISON); + if (isplayer(lf)) god_usepoison_response(); } } + } else if (attacktype == AT_OB) { + angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT); } } @@ -750,6 +745,11 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) if (hit) { if (aidb) dblog(".oO { i hit! }"); + if (!cansee(victim, lf)) { + addflag(lf->flags, F_UNSEENATTACKER, victim->id, NA, NA, NULL); + if (isplayer(victim) && !isplayer(lf)) needredraw = B_TRUE; + } + // special case if (isplayer(lf) && (victim->race->id == R_GLOWBUG)) { if ((wep->type->id == OT_EMPTYFLASK) || (wep->type->id == OT_EMPTYVIAL)) { diff --git a/data.c b/data.c index ca3eb5e..0a3ba7d 100644 --- a/data.c +++ b/data.c @@ -988,9 +988,14 @@ void initobjects(void) { addflag_real(lastobmod->flags, F_RUSTED, R_TRUSTY, NA, NA, NULL, PERMENANT, B_KNOWN, -1); // brands modifiers - flags should be UNKNOWN! - // also don't double up with names of scrolls etc. + // also don't double up with names of scrolls,books etc. // ie. spellbook of flight, boots of flight. + // missile weapons + addbrand(BR_HOMING, "of seeking", BP_NONE, B_UNCURSED, 0); + addflag_real(lastbrand->flags, F_HOMING, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); + addflag_real(lastbrand->flags, F_ONLYFOROBCLASS, OC_MISSILE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); + // weapons addbrand(BR_BALANCE, "of balance", BP_WEAPON, B_UNCURSED, 0); addflag_real(lastbrand->flags, F_BALANCE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); @@ -1088,6 +1093,7 @@ void initobjects(void) { addbrand(BR_HEALTH, "of health", BP_BODY, B_UNCURSED, 0); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_CON, 15, NULL, PERMENANT, B_UNKNOWN, -1); + // materials addmaterial(MT_NOTHING, "nothing", 0); addmaterial(MT_MAGIC, "magical energy", 0); @@ -3611,6 +3617,12 @@ void initobjects(void) { addot(OT_S_EXCAVATE, "excavate", "Creates a large open area surrounding the caster. No effect on living creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addot(OT_S_SHAPESHIFT, "shapeshift", "Causes the caster to take on the form of any creature within sight.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how powerful a creature you can become."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, 10, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); // l5 addot(OT_S_GASEOUSFORM, "gaseous form", "Changes the caster into a cloud of gas.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); @@ -3633,8 +3645,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addot(OT_S_POLYMORPH, "polymorph", "Transmutes the target into a new living race.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); - addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power V, you can polymorph other creatures."); - addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power VIII, you can choose what kind of creature to polymorph into."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power V, you can choose what kind of creature to polymorph into."); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); @@ -5275,7 +5286,7 @@ void initobjects(void) { addot(OT_POISONPUFF, "puff of poison gas", "A small puff of poisonous gas.", MT_GAS, 0, OC_EFFECT, SZ_MEDIUM); addflag(lastot->flags, F_GLYPH, C_GREEN, UNI_SHADELIGHT, NA, NULL); - addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL); + addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); @@ -6215,8 +6226,8 @@ void initobjects(void) { addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL); addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_CANHAVEOBMOD, OM_POISONED, 17, NA, NULL); - addflag(lastot->flags, F_RODSHAPED, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_CANBEDIFFMAT, MT_SILVER, 15, NA, NULL); + addflag(lastot->flags, F_RODSHAPED, B_TRUE, NA, NA, NULL); addot(OT_BOLT, "bolt", "A sharp metal spike, meant for firing from a crossbow.", MT_METAL, 0.25, OC_MISSILE, SZ_SMALL); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, ""); @@ -6227,7 +6238,6 @@ void initobjects(void) { addflag(lastot->flags, F_CANHAVEOBMOD, OM_POISONED, 17, NA, NULL); addflag(lastot->flags, F_CANBEDIFFMAT, MT_SILVER, 20, NA, NULL); - addot(OT_BULLET, "bullet", "A regular gun bullet.", MT_METAL, 0.1, OC_MISSILE, SZ_MINI); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, ""); addflag(lastot->flags, F_RARITY, H_DUNGEON, NA, RR_COMMON, ""); @@ -6581,6 +6591,16 @@ void initobjects(void) { addflag(lastot->flags, F_USESSKILL, SK_POLEARMS, NA, NA, NULL); addflag(lastot->flags, F_ATTREQ, A_AGI, 65, 80, "10"); addflag(lastot->flags, F_CRITCHANCE, 1, NA, NA, NULL); + addot(OT_PITCHFORK, "pitchfork", "A multi-pronged agricultural tool designed to lift loose material. Works as a makeshift weapon.", MT_METAL, 5, OC_WEAPON, SZ_HUMAN); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 85, RR_FREQUENT, NULL); + addflag(lastot->flags, F_RARITY, H_FOREST, 85, RR_FREQUENT, NULL); + addflag(lastot->flags, F_DAM, DT_SLASH, 3, NA, NULL); + addflag(lastot->flags, F_ALTDAM, DT_BASH, 3, NA, NULL); + addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL); + addflag(lastot->flags, F_USESSKILL, SK_POLEARMS, NA, NA, NULL); + addflag(lastot->flags, F_ATTACKVERB, NA, 2, NA, "scratch"); + addflag(lastot->flags, F_ATTACKVERB, 3, 6, NA, "scrape"); + addflag(lastot->flags, F_ATTACKVERB, 7, NA, NA, "rake"); addot(OT_RANSEUR, "ranseur", "A long spear and cross hilt, resembling a pole-mounted sai. Good for disarming.", MT_METAL, 12, OC_WEAPON, SZ_HUMAN); addflag(lastot->flags, F_RARITY, H_DUNGEON, 67, NA, NULL); addflag(lastot->flags, F_RARITY, H_CAVE, 67, NA, NULL); @@ -7703,6 +7723,59 @@ void initrace(void) { addflag(lastrace->flags, F_SACRIFICEOBWITHFLAG, F_BATTLESPOILS, NA, 3, "OB explode#S into a shower of blood!"); addflag(lastrace->flags, F_SACRIFICEOB, OT_SPELLBOOK, NA, 10, "OB explode#S into a shower of blood!"); + addrace(R_GODNATURE, "Ekrub", 200, '@', C_BOLDMAGENTA, MT_FLESH, RC_GOD, "Ekrub is god of nature and creation. She appears as a female figure dressed in farming clothes. Ekrub has a burning hatred of all dragonkind, who she views as abhorrent due to their destructive nature."); + setbodytype(lastrace, BT_HUMANOID); + addflag(lastrace->flags, F_ALIGNMENT, AL_NEUTRAL, NA, NA, NULL); + addflag(lastrace->flags, F_FLEEONHPPCT, 95, NA, NA, ""); + addflag(lastrace->flags, F_STARTATT, A_STR, NA, NA, "80"); + addflag(lastrace->flags, F_STARTATT, A_AGI, NA, NA, "80"); + addflag(lastrace->flags, F_STARTATT, A_WIS, NA, NA, "85"); + addflag(lastrace->flags, F_STARTATT, A_IQ, NA, NA, "65"); + addflag(lastrace->flags, F_STARTATT, A_CON, NA, NA, "90"); + addflag(lastrace->flags, F_STARTATT, A_CHA, NA, NA, "60"); + addflag(lastrace->flags, F_STARTASLEEPPCT, 0, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUGE, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "50d4"); + addflag(lastrace->flags, F_UNIQUE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_FISTS, 6, NA, NULL); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed +5 pitchfork of dragonslaying"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed +5 longbow"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "100 arrows of seeking"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed +2 overalls"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed sun hat"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed leather gloves"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed rubber boots"); + addflag(lastrace->flags, F_STARTSKILL, SK_POLEARMS, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_CARTOGRAPHY, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_CLIMBING, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_COOKING, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_LISTEN, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_RANGED, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_SWIMMING, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_LORE_NATURE, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_LORE_DRAGONS, PR_ADEPT, NA, NULL); // ekrub hates dragons + // god abilities + addflag(lastrace->flags, F_GODOF, B_FEMALE, NA, NA, "Nature"); + // may cast all nature spells + for (ot = objecttype ; ot ; ot = ot->next) { + if ((ot->obclass->id == OC_SPELL) && (getspellschool(ot->id) == SS_NATURE)) { + addflag(lastrace->flags, F_CANWILL, ot->id, NA, NA, "pw:10;"); + } + } + addflag(lastrace->flags, F_GODPOISON, B_FALSE, 25, NA, NULL); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "killing animals when hungry"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "killing dragons"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "cooking"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "creating objects"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "casting nature spells"); + addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "damaging or destroying objects"); + addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the use of poison"); + addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "attacking plants"); + addflag(lastrace->flags, F_HATESRACECLASS, RC_DRAGON, NA, NA, NULL); + // sacrifices + addflag(lastrace->flags, F_SACRIFICEOB, OT_CORPSE, RC_ANIMAL, 10, "Writhing vines sprout up and drag OB underground."); + addflag(lastrace->flags, F_SACRIFICEOB, OT_CORPSE, RC_DRAGON, 25, "Writhing vines sprout up and tear OB to pieces!"); addrace(R_GODTHIEVES, "Felix", 300, '@', C_BOLDMAGENTA, MT_FLESH, RC_GOD, "Felix is the god of Thieves, Greed and Trickery. He generally appears as an overweight glutton carrying his contraband loot around in huge sacks. Despite this, he is amazingly agile and is said to be able to steal one's soul right out of their body."); @@ -7889,7 +7962,7 @@ void initrace(void) { addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "allowing fleeing creatures to escape"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "acts of charity"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "natural healing"); - addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "attacking the innocent"); + addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "attacking the innocent or helpless"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "sneak attacks"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the use of poison"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the destruction of healing potions"); @@ -11642,7 +11715,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_S_CHARM, 3, 3, "pw:6;"); addflag(lastrace->flags, F_CANWILL, OT_A_CHARGE, NA, NA, "range:3;"); addflag(lastrace->flags, F_CANWILL, OT_S_STUN, 5, 5, "pw:1;"); - addflag(lastrace->flags, F_CANWILL, OT_S_POLYMORPH, 3, 3, "pw:1;race:vampire bat;"); + addflag(lastrace->flags, F_CANWILL, OT_S_SHAPESHIFT, 3, 3, "pw:1;race:vampire bat;"); addflag(lastrace->flags, F_CASTCHANCE, 40, NA, NA, NULL); addflag(lastrace->flags, F_DETECTOBS, 10, OT_COFFIN, NA, NULL); addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "pile of ash"); @@ -11966,8 +12039,9 @@ void initskills(void) { addskilldesc(SK_SHIELDS, PR_MASTER, "^gShield accuracy penalties are reduced by 6.^n", B_FALSE); addskill(SK_SPEECH, "Negotiation", "Your skill at haggling prices, or swaying others through speech.", 50); addskilldesc(SK_SPEECH, PR_INEPT, "- Each skill level reduces shop prices by 5%.", B_TRUE); - addskilldesc(SK_SPEECH, PR_NOVICE, "^gYou can now question people about items on the current level.", B_TRUE); - addskilldesc(SK_SPEECH, PR_BEGINNER, "^gYou can now question people about nearby traps or monsters.", B_TRUE); + addskilldesc(SK_SPEECH, PR_NOVICE, "^gYou can now question people about nearby traps or monsters.", B_TRUE); + addskilldesc(SK_SPEECH, PR_NOVICE, "^gYou can now offer gems as payment in shops.", B_TRUE); + addskilldesc(SK_SPEECH, PR_BEGINNER, "^gYou can now question people about items on the current level.", B_TRUE); addskilldesc(SK_SPEECH, PR_ADEPT, "^gYou can now trade knowledge and spells with other people.", B_TRUE); addskilldesc(SK_SPEECH, PR_SKILLED, "^gYou can now persuade people to join to as followers.", B_TRUE); addskilldesc(SK_SPEECH, PR_EXPERT, "^gYou can now choose which skills to learn from people.", B_TRUE); diff --git a/defs.h b/defs.h index b505829..572c622 100644 --- a/defs.h +++ b/defs.h @@ -445,6 +445,7 @@ enum GODANGERREASON { GA_MURDER, // killed someone peaceful GA_POISON, // used poison GA_PRAY, // pestering through constant prayer + GA_RACE, // prayed while a hated race. GA_SPELL, // cast a spell from the wrong school }; @@ -877,7 +878,7 @@ enum RACECLASS { }; #define R_GODFIRST R_GODPURITY -#define MAXGODS 6 +#define MAXGODS 7 enum RACE { R_NONE = 0, @@ -904,6 +905,7 @@ enum RACE { R_GODTHIEVES, // felix R_GODDEATH, // hecta R_GODMERCY, // yumi + R_GODNATURE, // ekrub R_GODBATTLE, // bjorn R_GODMAGIC, // lumara // monsters @@ -1440,6 +1442,7 @@ enum OBTYPE { OT_S_POLYMORPH, OT_S_POLYMORPHRND, OT_S_QUICKENSTONE, + OT_S_SHAPESHIFT, OT_S_SIZEUP, OT_S_SIZEDOWN, // nature / enviromancy @@ -1867,6 +1870,7 @@ enum OBTYPE { OT_GUISARME, OT_HALBERD, OT_LANCE, + OT_PITCHFORK, OT_RANSEUR, OT_SCYTHE, OT_SPEAR, @@ -2098,6 +2102,7 @@ enum FLAG { // monetary value) // for object brands F_ONLYFOROBTYPE, // brand can only go on obtype v0 + F_ONLYFOROBCLASS, // brand can only go on obclass v0 F_ONLYFOROBWITHFLAG, // brand can only go on obs with flag v0 F_ONLYFORDAMTYPE, // brand can only go on obs with damtype v0 F_ONLYFORWEPSKILL, // brand can only go on obclass v0 @@ -2108,6 +2113,7 @@ enum FLAG { // equipped on _ONE OF_ the. F_BONUS, // val0=bonus/penalty to damage+accuracy/armour. ie. +1 sword F_THROWMISSILE, // weapon would make a good thrown missle - used by AI + F_CANHOME, // this object can have the 'homing' flag F_UNIQUE, // only one may appear F_GLYPH, // override the glyph with the first char of text. // v0 is either NA (white) or colourid (C_xxx). @@ -2406,6 +2412,7 @@ enum FLAG { // end gun flags F_FLAMESTRIKE, // causes fires where you hit F_BALANCE, // heals target if their maxhp < your maxhp + F_HOMING, // missile always hits F_MERCIFUL, // puts to sleep instead of killing. F_REVENGE, // causes damage based on your hp // @@ -2555,6 +2562,9 @@ enum FLAG { F_ATTRSET, // forces attribute val0 to be val1. ie. 0=A_STR,1=18 F_SIZE, // val0 = lf size (enum LFSIZE) F_SIZETIMER, // lf weill resize to LFSIZE val0 in val1 turns. + F_UNSEENATTACKER, // this lf attacked lfid v0 while lfid v0 couldn't + // see them. used to mark 'X' on the player's map + // for unseen attackers. F_USEDPOISON, // this lf used a poisoned weapon to attack F_RANDOMTALKPCT, // v0 = chance of randomly saying something each turn F_RANDOMTALK, // EITHER: @@ -2715,6 +2725,7 @@ enum FLAG { F_HATESALL, // lf will attack ALL other lfs on sight F_HATESRACE, // lf will attack lfs with race=v0 or baseid=v0 on // sight + F_HATESRACECLASS, // lf will attack lfs with raceclass->id=v0 F_HATESRACEWITHFLAG, // lf will attack lfs with flag v0 on sight F_TERRITORIAL, // lf will attack ALL other visible lfs within range v0 F_HARMLESS, // it is safe to rest around this lf @@ -2798,6 +2809,8 @@ enum FLAG { // OB is replace with object name F_SACRIFICEOBWITHFLAG, // v0 = can sacrifice obs with flag v0 to this go F_SACRIFICEOB, // v0 = can sacrifice obtype v0 to this god + // if v1 is set, object must be the + // corpse of something with raceclass v1. F_SACRIFICEOBCLASS, // v0 = can sacrifice obclass v0 to this god F_SACRIFICEOBBLESSED, // v0 = can sacrifice obs with ->blessed=v0 @@ -3939,6 +3952,7 @@ enum BRAND { BR_FLIGHT, BR_GIANTSTRENGTH, BR_HEALTH, + BR_HOMING, BR_IMPACT, BR_THINKING, BR_KNOWLEDGE, diff --git a/god.c b/god.c index 7995616..54270c9 100644 --- a/god.c +++ b/god.c @@ -57,6 +57,8 @@ void angergod(enum RACE rid, int amt, enum GODANGERREASON why) { case GA_ATTACKOBJECT: if (rid == R_GODTHIEVES) { godsay(rid, B_TRUE, "What are you, a common thug?"); break; + } else if (rid == R_GODNATURE) { + godsay(rid, B_TRUE, "You dare destroy my creations?"); break; } else { godsay(rid, B_TRUE, "You dare destroy my symbols?"); break; } @@ -77,6 +79,8 @@ void angergod(enum RACE rid, int amt, enum GODANGERREASON why) { case GA_PRAY: dosay = B_TRUE; break; case GA_POISON: godsay(rid, B_TRUE, "I do not condone the use of poison!"); break; + case GA_RACE: + godsay(rid, B_TRUE, "Your form offends me!"); break; case GA_SPELL: godsay(rid, B_TRUE, "Your magic offends me!"); break; } @@ -182,6 +186,12 @@ void angergod(enum RACE rid, int amt, enum GODANGERREASON why) { killflag(f); } break; + case R_GODNATURE: + msg("\"You have transgressed against nature!\""); + dospelleffects(NULL, OT_S_ENTANGLE, 10, NULL, NULL, player->cell, B_BLESSED, NULL, B_TRUE); + // note: you will also rot food on touch until god is appeased. + // see touch(). + break; case R_GODMERCY: // lower one attribute msg("\"Be glad that I am feeling merciful, mortal!\""); @@ -344,6 +354,22 @@ void angergod(enum RACE rid, int amt, enum GODANGERREASON why) { modattr(player, a, -1); } + break; + case R_GODNATURE: + msg("\"You have violated the cardinal laws of nature!\""); + dospelleffects(NULL, OT_S_ENTANGLE, 10, NULL, NULL, player->cell, B_BLESSED, NULL, B_TRUE); + switch (rnd(1,3)) { + case 1: + dospelleffects(NULL, OT_S_CLOUDKILL, 10, NULL, NULL, player->cell, B_BLESSED, NULL, B_TRUE); + break; + case 2: + dospelleffects(NULL, OT_S_HAILSTORM, 10, NULL, NULL, player->cell, B_BLESSED, NULL, B_TRUE); + break; + case 3: + msg("\"Destroy him, my pets!\""); + summonlfs(god, player->cell, R_SAWGRASS, RC_ANY, SZ_ANY, AL_GOOD, 8, PERMENANT, B_FALSE); + break; + } break; case R_GODPURITY: switch (rnd(1,2)) { @@ -424,8 +450,24 @@ void dooffer(void) { flag_t *f; f = retflag[i]; if ((f->id == F_SACRIFICEOB) && (f->val[0] == o->type->id)) { - ok = B_TRUE; - thispiety = f->val[2]; + int validcorpse = B_FALSE; + if (f->val[1] == NA) { + validcorpse = B_TRUE; + } else { + flag_t *ff; + ff = hasflag(o->flags, F_CORPSEOF); + if (ff) { + race_t *r; + r = findrace(ff->val[0]); + if (r->raceclass->id == f->val[1]) { + validcorpse = B_TRUE; + } + } + } + if (validcorpse) { + ok = B_TRUE; + thispiety = f->val[2]; + } } else if ((f->id == F_SACRIFICEOBCLASS) && (f->val[0] == o->type->obclass->id)) { ok = B_TRUE; thispiety = f->val[2]; @@ -508,6 +550,7 @@ enum RACE getopposinggod(enum RACE rid) { case R_GODMERCY: return R_GODDEATH; case R_GODBATTLE: return R_GODMAGIC; case R_GODMAGIC: return R_GODBATTLE; + // TODO: nature/destruction default: break; } return R_NONE; @@ -639,6 +682,23 @@ lifeform_t *godappears(enum RACE rid, cell_t *where) { return god; } +void god_usepoison_response(void) { + int i; + flag_t *f; + for (i = 0; i < ngodlfs; i++) { + if (godlf[i]) { + f = lfhasflag(godlf[i], F_GODPOISON); + if (f) { + if (f->val[0]) { + pleasegodmaybe(godlf[i]->race->id, f->val[1]); + } else { + angergodmaybe(godlf[i]->race->id, f->val[1], GA_POISON); + } + } + } + } +} + int godblocked(enum RACE rid) { lifeform_t *opposegod; enum RACE opposeid; @@ -813,6 +873,81 @@ int godgiftmaybe(enum RACE rid, int fromtemple) { } } break; + case R_GODNATURE: + while (rollagain) { + flag_t *f; + object_t *o; + objecttype_t *ot; + rollagain = B_FALSE; + switch (rnd(1,6)) { + case 1: // resist/immune poison + if (lfhasflag(player, F_DISEASEIMMUNE)) { + if (lfhasflagval(player, F_DTIMMUNE, DT_POISON, NA, NA, NULL)) { + rollagain = B_TRUE; + } else { + f = addtempflag(player->flags, F_DTIMMUNE, DT_POISON, NA, NA, NULL, FROMGODGIFT); + f->obfrom = god->race->id; + } + } else { + f = addtempflag(player->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL, FROMGODGIFT); + f->obfrom = god->race->id; + } + break; + case 2: // resist/immune cold + if (lfhasflagval(player, F_DTRESIST, DT_COLD, NA, NA, NULL)) { + if (lfhasflagval(player, F_DTIMMUNE, DT_COLD, NA, NA, NULL)) { + rollagain = B_TRUE; + } else { + f = addtempflag(player->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL, FROMGODGIFT); + f->obfrom = god->race->id; + } + } else { + f = addtempflag(player->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, FROMGODGIFT); + f->obfrom = god->race->id; + } + break; + case 3: + // good ranged weapon + if (getskill(player, SK_RANGED)) { + char obname[BUFLEN]; + if (real_getrandomob(NULL, obname, 25, NA, getlfsize(player), + SK_RANGED, B_TRUE, OC_WEAPON, OC_NONE, DT_NONE)) { + snprintf(obtogive, BUFLEN, "excellent %s", obname); + } else { + rollagain = B_TRUE; + } + } else { + rollagain = B_TRUE; + } + break; + case 4: // ammo + o = getfirearm(player); + if (o) { + ot = getrandomammofor(o); + snprintf(obtogive, BUFLEN, "%d excellent %s", rnd(20,30), ot->name); + } else { + rollagain = B_TRUE; + } + break; + case 5: // calm animals ability + if (lfhasflagval(player, F_CANWILL, OT_S_CALMANIMALS, NA, NA, NULL)) { + rollagain = B_TRUE; + } else { + f = addtempflag(player->flags, F_CANWILL, OT_S_CALMANIMALS, NA, NA, "pw:10;", FROMGODGIFT); + f->obfrom = god->race->id; + } + break; + case 6: + if (lfhasflagval(player, F_CANWILL, OT_S_SHAPESHIFT, NA, NA, "pw:1;race:hawk;")) { + rollagain = B_TRUE; + } else { + f = addtempflag(player->flags, F_CANWILL, OT_S_SHAPESHIFT, 50, 50, "pw:1;race:hawk;", FROMGODGIFT); + f->obfrom = god->race->id; + } + break; + } + } + break; case R_GODPURITY: while (rollagain) { int n,nposs,i; @@ -939,6 +1074,15 @@ int godisangry(enum RACE rid) { return B_FALSE; } +int godprayedto(enum RACE rid) { + lifeform_t *god; + god = findgod(rid); + if (hasflag(god->flags, F_PRAYEDTO)) { + return B_TRUE; + } + return B_FALSE; +} + void godsay(enum RACE rid, int says, char *format, ...) { lifeform_t *god; char godname[BUFLEN], buf[BUFLEN]; @@ -968,6 +1112,13 @@ void godsay(enum RACE rid, int says, char *format, ...) { case R_GODMERCY: strcpy(voiceverb, "washes over you"); break; + case R_GODNATURE: + if (godisangry(rid)) { + strcpy(voiceverb, "arrives with a buffeting wind"); + } else { + strcpy(voiceverb, "arrives with a gentle breeze"); + } + break; case R_GODPURITY: default: strcpy(voiceverb, "booms out from the heavens"); @@ -1016,6 +1167,9 @@ void pleasegod(enum RACE rid, int amt) { case R_GODMAGIC: msg("You feel Lumara's presense nearby."); break; + case R_GODNATURE: + msg("You feel in tune with nature."); + break; case R_GODMERCY: msg("You feel a sense of serenity."); break; @@ -1063,6 +1217,12 @@ int prayto(lifeform_t *lf, lifeform_t *god) { return B_TRUE; } + if (lfhasflagval(god, F_HATESRACECLASS, lf->race->raceclass->id, NA, NA, NULL) || + lfhasflagval(god, F_HATESRACE, lf->race->id, NA, NA, NULL)) { + angergod(god->race->id, 50, GA_RACE); + return B_TRUE; + } + // this will return 100 if we haven't prayed to this // god before piety = getpiety(god->race->id); @@ -1103,6 +1263,9 @@ int prayto(lifeform_t *lf, lifeform_t *god) { case R_GODMERCY: strcpy(assisttext, "I hear your prayer, child."); break; + case R_GODNATURE: + strcpy(assisttext, "Nature hears your call!"); + break; case R_GODMAGIC: strcpy(assisttext, "One calls upon the eldritch powers..."); break; @@ -1288,7 +1451,7 @@ int prayto(lifeform_t *lf, lifeform_t *god) { case 1: // steal from your enemies for (l = lf->cell->map->lf ; l ; l = l->next) { - if ((l != lf) && lfhasflagval(l, F_TARGETLF, lf->id, NA, NA, NULL)) { + if ((l != lf) && areenemies(l, lf)) { object_t *wep; // confiscate their weapon wep = getweapon(l); @@ -1436,7 +1599,7 @@ int prayto(lifeform_t *lf, lifeform_t *god) { castspell(god, OT_S_SATEHUNGER, player, NULL, player->cell, NULL, NULL); donesomething = B_TRUE; } - if (islowhp(lf) || !donesomething) { + if (islowhp(lf)) { msg("\"Let thy wounds be healed.\""); castspell(god, OT_S_HEALINGMAJ, player, NULL, player->cell, NULL, NULL); donesomething = B_TRUE; @@ -1445,6 +1608,121 @@ int prayto(lifeform_t *lf, lifeform_t *god) { // uncurse one equipped ob uncurse_one_equipped(lf, "\"Let thy curse be ended!\""); break; + case R_GODNATURE: + if (isinbattle(lf, B_TRUE)) { + lifeform_t *l; + int donesomething = B_FALSE; + int redo = B_TRUE; + while (redo) { + redo = B_FALSE; + switch (rnd(1,3)) { + case 1: + msg("\"My children will bind your enemies!\""); + // entangle your enemies + for (l = lf->cell->map->lf ; l ; l = l->next) { + if ((l != lf) && areenemies(l, lf)) { + dospelleffects(NULL, OT_S_ENTANGLE, 10, NULL, NULL, l->cell, B_BLESSED, NULL, B_TRUE); + } + } + break; + case 2: // summon plants + msg("\"My children will aid you!\""); + summonlfs(player, player->cell, R_NONE, RC_PLANT, SZ_ANY, AL_NONE, 5, PERMENANT, B_TRUE); + break; + case 3: // lightning + msg("\"The powers of the sky will smite your foes!\""); + for (l = lf->cell->map->lf ; l ; l = l->next) { + if ((l != lf) && areenemies(l, lf)) { + dospelleffects(god, OT_S_CALLLIGHTNING, 10, NULL, NULL, l->cell, B_BLESSED, NULL, B_TRUE); + } + } + break; + } + } + + // fighting a dragon? + for (i = 0; i < lf->nlos; i++) { + if (lf->los[i]->lf && (getraceclass(lf->los[i]->lf) == RC_DRAGON)) { + godappears(god->race->id, NULL); + aiattack(god, lf->los[i]->lf, PERMENANT); + donesomething = B_TRUE; + break; + } + } + } else {// not in battle + int donesomething = B_FALSE; + flag_t *f; + + // fix any poison potions + dospelleffects(god, OT_S_PURIFYFOOD, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + + if (ispoisoned(lf)) { + msg("\"I will cure your poison...\""); + castspell(god, OT_S_CUREPOISON, player, NULL, player->cell, NULL, NULL); + donesomething = B_TRUE; + } + if (!donesomething && (gethungerlevel(gethungerval(player)) >= H_PECKISH)) { + msg("\"I will cure your hunger...\""); + castspell(god, OT_S_SATEHUNGER, player, NULL, player->cell, NULL, NULL); + donesomething = B_TRUE; + } + if (!donesomething && islowhp(lf)) { + msg("\"I will heal your injuries...\""); + castspell(god, OT_S_HEALINGMAJ, player, NULL, player->cell, NULL, NULL); + donesomething = B_TRUE; + } + if (!donesomething) { // out of ammo? + object_t *gun; + gun = getfirearm(lf); + if (gun && !getammo(gun) && !getrandomammo(lf)) { + objecttype_t *ot; + ot = getrandomammofor(gun); + if (ot) { + char obtogive[BUFLEN],ammoname[BUFLEN]; + char gunname[BUFLEN]; + object_t *ammo; + f = hasflag(gun->flags, F_AMMOCAPACITY); + snprintf(obtogive, BUFLEN, "%d %s of seeking", f->val[0], ot->name); + ammo = addob(gun->contents, obtogive); + identify(ammo); + getobname(ammo, ammoname, ammo->amt); + getobname(gun, gunname, 1); + msg("%s appear%s in your %s!", ammoname, OBS1(ammo), noprefix(gunname)); + donesomething = B_TRUE; + } + } + } + if (!donesomething) { // mend one random armour + object_t *poss[MAXCANDIDATES]; + int nposs = 0; + for (o = lf->pack->first ; o ; o = o->next) { + if (isequipped(o) && isarmour(o) && isdamaged(o)) { + poss[nposs++] = o; + } + } + if (nposs) { + o = poss[rnd(0,nposs-1)]; + dospelleffects(lf, OT_S_MENDING, 10, NULL, o, NULL, B_BLESSED, NULL, B_FALSE); + donesomething = B_TRUE; + } + } + if (!donesomething) { // bless armour + object_t *poss[MAXCANDIDATES]; + int nposs = 0; + for (o = lf->pack->first ; o ; o = o->next) { + if (isequipped(o) && isarmour(o) && (o->blessed == B_UNCURSED)) { + poss[nposs++] = o; + } + } + if (nposs) { + o = poss[rnd(0,nposs-1)]; + msg("\"Nature's power will protect you...\""); + blessob(o); + } + donesomething = B_TRUE; + } + } + break; default: break; } diff --git a/god.h b/god.h index 23d67f6..5916324 100644 --- a/god.h +++ b/god.h @@ -11,9 +11,11 @@ int getprayedgods(lifeform_t **retgod, int *nretgods); lifeform_t *getrandomgod(void); lifeform_t *getrandomprayedgod(void); lifeform_t *godappears(enum RACE rid, cell_t *where); +void god_usepoison_response(void); int godblocked(enum RACE rid); int godgiftmaybe(enum RACE rid, int fromtemple); int godisangry(enum RACE rid); +int godprayedto(enum RACE rid); void godsay(enum RACE rid, int says, char *format, ...); void modpiety(enum RACE rid, int amt); void pleasegod(enum RACE rid, int amt); diff --git a/io.c b/io.c index 95f38f4..8d98332 100644 --- a/io.c +++ b/io.c @@ -3790,10 +3790,10 @@ void docomms(lifeform_t *lf) { enum SKILLLEVEL slev; slev = getskill(player, SK_SPEECH); if (slev >= PR_NOVICE) { - addchoice(&prompt, 'i', "What can you tell me about this area?", NULL, NULL, NULL); + addchoice(&prompt, 'x', "Any dangers nearby that I should look out for?", NULL, NULL, NULL); } if (slev >= PR_BEGINNER) { - addchoice(&prompt, 'x', "Any dangers nearby that I should look out for?", NULL, NULL, NULL); + addchoice(&prompt, 'i', "What can you tell me about this area?", NULL, NULL, NULL); } if (!areallies(player, lf)) { if (slev >= PR_ADEPT) { @@ -4841,14 +4841,22 @@ char *makedesc_god(lifeform_t *god, char *retbuf) { } getflags(god->flags, retflag, &nretflags, F_SACRIFICEOB, F_NONE); for (i = 0; i < nretflags; i++) { - objecttype_t *ot; if (i == 0) { sprintf(thisline, "%s accepts the sacrifice of:\n", godname); strncat(retbuf, thisline, HUGEBUFLEN); } - ot = findot(retflag[i]->val[0]); - sprintf(thisline, "- %s\n", ot->name); - strncat(retbuf, thisline, HUGEBUFLEN); + + if (retflag[i]->val[1] == NA) { + objecttype_t *ot; + ot = findot(retflag[i]->val[0]); + sprintf(thisline, "- %s\n", ot->name); + strncat(retbuf, thisline, HUGEBUFLEN); + } else { + raceclass_t *rc; + rc = findraceclass(retflag[i]->val[1]); + sprintf(thisline, "- %s corpses\n", rc->name); + strncat(retbuf, thisline, HUGEBUFLEN); + } } getflags(god->flags, retflag, &nretflags, F_SACRIFICEOBBLESSED, F_NONE); for (i = 0; i < nretflags; i++) { diff --git a/lf.c b/lf.c index 090085e..e79cecd 100644 --- a/lf.c +++ b/lf.c @@ -1662,6 +1662,13 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar return B_FALSE; } } + // casting a nature spell while nature god is angry at you? + if (isplayer(lf) && godprayedto(R_GODNATURE) && godisangry(R_GODNATURE) && !willflag) { + if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL)) { + msg("You seem to be cut off from the powers of nature magic."); + return B_FALSE; + } + } // adjust power? if (hasjob(lf, J_DRUID)) { @@ -1814,6 +1821,9 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar pleasegodmaybe(R_GODPURITY, getspelllevel(sid)); angergodmaybe(R_GODDEATH, getspelllevel(sid)*5, GA_SPELL); break; + case SS_NATURE: + pleasegodmaybe(R_GODNATURE, getspelllevel(sid)*2); + break; default: break; } @@ -2401,9 +2411,22 @@ void die(lifeform_t *lf) { // revert to your original form first. if (lfhasflag(lf, F_POLYMORPHED)) { if (lfhasflag(lf, F_ORIGRACE)) { + int premaxhp; + premaxhp = lf->maxhp; abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL); + // but you stay at lower hp. + if (premaxhp < lf->maxhp) { + float ratio; + ratio = (float)premaxhp / (float) lf->maxhp; + lf->maxhp = ratio * lf->maxhp; + limit(&(lf->hp), 1, lf->maxhp); + } + if (isplayer(lf)) statdirty = B_TRUE; + /* // ... but you're still dead lf->hp = 0; + */ + return; } } @@ -2527,6 +2550,19 @@ void die(lifeform_t *lf) { pleasegodmaybe(R_GODDEATH, 1); } + switch (lf->race->raceclass->id) { + case RC_ANIMAL: + if (gethungerlevel(gethungerval(player)) > H_NONE) { + pleasegodmaybe(R_GODNATURE, 3); + } + break; + case RC_DRAGON: + pleasegodmaybe(R_GODNATURE, 50); + break; + default: + break; + } + // mercy god doesn't like killing //angergodmaybe(R_GODMERCY, 1, GA_MURDER); } @@ -3446,7 +3482,7 @@ int eat(lifeform_t *lf, object_t *o) { } else { snprintf(dambuf, BUFLEN, "a bad %s",noprefix(obname)); } - poison(lf, 20, P_FOOD, 1, dambuf); + poison(lf, rnd(20,40), P_FOOD, 1, dambuf); } } @@ -12839,7 +12875,7 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml } else if (damtype == DT_POISONGAS) { if (amt > 0) { if (!skillcheck(lf, SC_POISON, 35, 0)) { // HARD. - poison(lf, rnd(5,10), P_GAS, 2, "poison gas"); + poison(lf, rnd(20,40), P_GAS, 2, "poison gas"); } } } @@ -15520,14 +15556,14 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { // announce if (reverting) { // reverting to original form.... - if (!isdead(lf)) { + //if (!isdead(lf)) { if (isplayer(lf)) { msg("^wYou revert to your original form!"); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("^wThe %s transforms back to its original form!", newrace->name); } - } + //} if ((lf->race->id == R_GASCLOUD) && (origrace->id == R_VAMPIRE)) { if (!isplayer(lf)) { @@ -16444,6 +16480,7 @@ void startlfturn(lifeform_t *lf) { if (isplayer(lf) && lfhasflag(lf, F_DRUNK)) statdirty = B_TRUE; // clear one-turn-only flags + killflagsofid(lf->flags, F_UNSEENATTACKER); killflagsofid(lf->flags, F_DONELISTEN); killflagsofid(lf->flags, F_NOSWAP); movedlastturn = 0; @@ -18137,6 +18174,19 @@ int touch(lifeform_t *lf, object_t *o) { } + if (isplayer(lf)) { + if (godprayedto(R_GODNATURE) && godisangry(R_GODNATURE) && isedible(o)) { + char obname[BUFLEN]; + getobname(o, obname, o->amt); + msg("A green miasma surrounds %s as you touch %s.", obname, + (o->amt == 1) ? "it" : "them"); + if (!hasflag(o->flags, F_TAINTED)) { + addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL); + } + } + } + + f = hasflag(o->flags, F_SHARP); if (f) { object_t *gloves; diff --git a/map.c b/map.c index 20d314f..5be9ed7 100644 --- a/map.c +++ b/map.c @@ -2852,7 +2852,7 @@ void createheaven(map_t *map, int depth, map_t *parentmap, int exitdir, object_t godlf[ngodlfs++] = lf; if (ngodlfs > MAXGODS) { dblog("Error - number of gods(%d) exceeds MAXGODS.",ngodlfs); - msg("Error - number of gods(%d) exceeds MAXGODS.",ngodlfs); + msg("Error - number of gods(%d) exceeds MAXGODS.",ngodlfs); more(); exit(1); } roomid++; @@ -6245,6 +6245,9 @@ int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) { if (!f) { f = lfhasflagval(c->lf, F_ATTACHEDTO, player->id, NA, NA, NULL); } + if (!f) { + f = lfhasflagval(c->lf, F_UNSEENATTACKER, player->id, NA, NA, NULL); + } if (f) { if (glyph) { glyph->ch = 'X'; @@ -6253,10 +6256,20 @@ int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) { *thing = c->lf; if (desc) { getlfname(c->lf, desc); - if (f->id == F_GRABBEDBY) { - strcat(desc, " (holding you)"); - } else { - strcat(desc, " (attached to you)"); + switch (f->id) { + case F_GRABBEDBY: + strcat(desc, " (holding you)"); + break; + case F_ATTACHEDTO: + strcat(desc, " (attached to you)"); + break; + case F_UNSEENATTACKER: + strcat(desc, " (unseen attacker)"); + break; + default: + // should never happen + strcat(desc, " (detected)"); + break; } } return TT_MONSTER; diff --git a/move.c b/move.c index 4ced26a..ec49900 100644 --- a/move.c +++ b/move.c @@ -1342,6 +1342,9 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } // kill object which is being crushed. removeob(o, o->amt); + if (isplayer(lf)) { + angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT); + } continue; } } // end if crushable diff --git a/nexus.c b/nexus.c index a1cf002..9e65a90 100644 --- a/nexus.c +++ b/nexus.c @@ -11,6 +11,7 @@ #include "data.h" #include "io.h" #include "flag.h" +#include "god.h" #include "lf.h" #include "map.h" #include "move.h" @@ -330,6 +331,7 @@ int main(int argc, char **argv) { if (j->id == J_DRUID) { enum OBTYPE spell; int i; + lifeform_t *god; for (i = 0;i < 3; i++) { // pick a spell which you don't already have... spell = getrandomspellfromschool(SS_NATURE, 1); @@ -339,6 +341,9 @@ int main(int argc, char **argv) { // you can now cast it. addflag(player->flags, F_CANCAST, spell, NA, NA, NULL); } + // druids always worship ekrub + god = findgod(R_GODNATURE); + addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL); } else if (j->id == J_WIZARD) { skill_t *sk; object_t *sb1,*sb2; @@ -443,13 +448,14 @@ int main(int argc, char **argv) { cell_t *c; race_t *r; lifeform_t *pet; + enum OBTYPE avoidob = OT_WATERDEEP; f = retflag[i]; r = findracebyname(f->text); assert(r); - // create it - c = getrandomadjcell(player->cell, WE_WALKABLE, B_ALLOWEXPAND); + // create pet, in view of player if possible. + c = real_getrandomadjcell(player->cell, WE_WALKABLE, B_ALLOWEXPAND, LOF_NEED, &avoidob, player); assert(c); pet = addlf(c, r->id, 1); // mark us as its master diff --git a/objects.c b/objects.c index 8deb484..b88f3ac 100644 --- a/objects.c +++ b/objects.c @@ -2833,6 +2833,23 @@ int countnames(char **list) { return count; } +// returns the value of the most valuable object with flag 'fid' +// if fid is F_NONE, just returns the most valuable object +int counthighestobflagvalue(obpile_t *op, enum FLAG fid) { + object_t *o; + int maxval = -1; + for (o = op->first ; o ; o = o->next) { + if ((fid == F_NONE) || hasflag(o->flags, fid)) { + int thisval; + thisval = getobvalue(o); + if (thisval > maxval) { + maxval = thisval; + } + } + } + return maxval; +} + int countobs(obpile_t *op, int onlyifknown) { object_t *o; @@ -3741,33 +3758,40 @@ int getobspellpower(object_t *o, lifeform_t *lf) { return power; } -// returns value of obejcts, in gold int getobvalue(object_t *o) { - float price; + return real_getobvalue(o, o->amt); +} + +// returns value of obejcts, in gold +int real_getobvalue(object_t *o, int amt) { + float price = 1; flag_t *f; - int rarity = 0,i; + int i; enum RARITY rr = RR_FREQUENT; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; if (o->type->id == OT_GOLD) { - return o->amt; + return amt; } if (o->type->id == OT_CREDITCARD) { return getcharges(o); } - - // base value: weight * material value - price = (float)getobweight(o) * (float)getmaterialvalue(o->material->id); + // rarity + getobrarity(o, &rr); + //adjustprice(o->type, &price); // fixed prices f = hasflag(o->flags, F_VALUE); if (f) { - price += f->val[0]; - } + price = f->val[0]; + } else { + // base value: weight * material value + price = (float)getobweight(o) * (float)getmaterialvalue(o->material->id); + } if (o->type->id == OT_SPELLBOOK) { price += (89*countobs(o->contents, B_FALSE)); @@ -3822,9 +3846,6 @@ int getobvalue(object_t *o) { price += 1000; } - // rarity - rarity = getobrarity(o, &rr); - // TODO: conferred intrinsics - depends on which one // TODO: conferred spells - use spell price * multiplier @@ -3838,27 +3859,30 @@ int getobvalue(object_t *o) { } // scarcity... - switch (rr) { - case RR_UNIQUE: - price *= 5; - break; - case RR_VERYRARE: - price *= 3; - break; - case RR_RARE: - price *= 2; - break; - case RR_UNCOMMON: - break; - case RR_COMMON: - price *= 0.75; - break; - case RR_FREQUENT: - price *= 0.5; - break; - default: - break; + if (!hasflag(o->flags, F_VALUE)) { + switch (rr) { + case RR_UNIQUE: + price *= 5; + break; + case RR_VERYRARE: + price *= 3; + break; + case RR_RARE: + price *= 2; + break; + case RR_UNCOMMON: + break; + case RR_COMMON: + price *= 0.75; + break; + case RR_FREQUENT: + price *= 0.5; + break; + default: + break; + } } + // blessed/cursed if (o->type->id == OT_POT_WATER) { if (isblessed(o) || iscursed(o)) price *= 29.25; @@ -3869,7 +3893,8 @@ int getobvalue(object_t *o) { // minimum limitf(&price, 1, NA); - price = ((int)price * o->amt); + price = ((int)price * amt); + dblog("price for %s is %d", o->type->name, (int)price); return (int) price; } @@ -8146,20 +8171,28 @@ int brandappliesto(brand_t *br, objecttype_t *ot) { flag_t *retflag[MAXCANDIDATES]; int i,nretflags = 0; - if (br->bp == BP_WEAPON) { - if (ot->obclass->id != OC_WEAPON) { - return B_FALSE; + if (br->bp != BP_NONE) { + if (br->bp == BP_WEAPON) { + if (ot->obclass->id != OC_WEAPON) { + return B_FALSE; + } + } else { + // differentiate shields from guns using f_onlywithwepskill below. + if (!hasflagval(ot->flags, F_GOESON, br->bp, NA, NA, NULL)) { + return B_FALSE; + } } - } else { - // TODO: how do we differentiate shields from guns? - if (!hasflagval(ot->flags, F_GOESON, br->bp, NA, NA, NULL)) { + } + + // other restrictions? + if (hasflag(br->flags, F_ONLYFOROBTYPE)) { + if (!hasflagval(br->flags, F_ONLYFOROBTYPE, ot->id, NA, NA, NULL)) { return B_FALSE; } } - // other restrictions? - if (hasflag(br->flags, F_ONLYFOROBTYPE)) { - if (!hasflagval(br->flags, F_ONLYFOROBTYPE, ot->id, NA, NA, NULL)) { + if (hasflag(br->flags, F_ONLYFOROBCLASS)) { + if (!hasflagval(br->flags, F_ONLYFOROBCLASS, ot->obclass->id, NA, NA, NULL)) { return B_FALSE; } } @@ -10937,15 +10970,18 @@ object_t *relinkob(object_t *src, obpile_t *dst) { return src; } -void removedeadobs(obpile_t *op) { +int removedeadobs(obpile_t *op) { object_t *o, *nexto; + int nremoved = 0; for (o = op->first ; o ; o = nexto) { nexto = o->next; if (hasflag(o->flags, F_DEAD)) { checkflagpile(o->flags); obdie(o); + nremoved++; } } + return nremoved; } // returns the amount left @@ -11240,6 +11276,9 @@ int shatter(object_t *o, int hitlf, char *damstring, lifeform_t *fromlf) { makeknown(o->type->id); } potioneffects(target, o->type->id, o, o->blessed, &observed); + if (fromlf && isplayer(fromlf) && (o->type->id == OT_POT_POISON)) { + god_usepoison_response(); + } } break; case OT_POT_HEALING: @@ -12163,6 +12202,11 @@ int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int sp myroll -= 20; } + // "of homing" missiles always hit. + if (hasflag(o->flags, F_HOMING)) { + myroll = acc; + } + // metal projectile versus magnetic shield? if (target && lfhasflag(target, F_MAGSHIELD) && ismetal(o->material->id)) { // announce @@ -12457,6 +12501,9 @@ int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int sp char dambuf[BUFLEN]; snprintf(dambuf, BUFLEN, "%s (%s by %s)",obname,throwverbpast, realthrowernamea); shatter(newob, youhit, dambuf, thrower); + if (thrower && isplayer(thrower)) { + angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT); + } } else { // object only gets damaged if it hit someone/something if (missiledam) { diff --git a/objects.h b/objects.h index 3fb42ae..5c48b13 100644 --- a/objects.h +++ b/objects.h @@ -42,6 +42,7 @@ void copyobprops(object_t *dst, object_t *src); int counthiddennames(enum OBCLASS ocid, char *text); int countmoney(obpile_t *op); int countnames(char **list); +int counthighestobflagvalue(obpile_t *op, enum FLAG fid); int countobs(obpile_t *op, int onlyifknown); int countobsoftype(obpile_t *op, enum OBTYPE oid); int countobswithflag(obpile_t *op, enum FLAG flagid); @@ -87,6 +88,7 @@ skill_t *getobskill(flagpile_t *fp); enum LFSIZE getobsize(object_t *o); int getobspellpower(object_t *o, lifeform_t *lf); int getobvalue(object_t *o); +int real_getobvalue(object_t *o, int amt); char *getoperateverb(object_t *o); object_t *getoutercontainer(object_t *o); object_t *getoutercontainerop(obpile_t *op); @@ -253,7 +255,7 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE int pour(lifeform_t *lf, object_t *o); void quaff(lifeform_t *lf, object_t *o); int readsomething(lifeform_t *lf, object_t *o); -void removedeadobs(obpile_t *op); +int removedeadobs(obpile_t *op); int removeob(object_t *o, int howmany); object_t *relinkob(object_t *src, obpile_t *dst); void resizeobject(object_t *o, enum LFSIZE wantsize); diff --git a/shops.c b/shops.c index 06218ba..1a8fad7 100644 --- a/shops.c +++ b/shops.c @@ -32,17 +32,18 @@ extern WINDOW *mainwin; float applyshoppricemod(float origprice, lifeform_t *lf) { float newprice; if (lf) { - float pricepctmod = 0; + float pricepct = 100; enum SKILLLEVEL slev; // price goes up/down for charisma (+/- 25%) - pricepctmod -= getstatmod(lf, A_CHA)/2; + // high bonus = lower price. + pricepct -= (getstatmod(lf, A_CHA)/2); - // modify for speech (up to -30%); + // reduce based on speech (up to -30%); slev = getskill(lf, SK_SPEECH); if (slev) { - pricepctmod -= (slev*5); + pricepct -= (slev*5); } - newprice = pctof(origprice, 100 + pricepctmod); + newprice = pctof(pricepct, origprice); } else { newprice = origprice; } @@ -52,6 +53,18 @@ float applyshoppricemod(float origprice, lifeform_t *lf) { return newprice; } +int canafford(lifeform_t *lf, int amt) { + int goldamt = 0,gemamt = 0; + goldamt = countmoney(lf->pack); + if (getskill(lf, SK_SPEECH) >= PR_NOVICE) { + gemamt = applyshoppricemod(counthighestobflagvalue(lf->pack, F_GEM), lf); // adjust using charisma etc + } + if ((goldamt >= amt ) || (gemamt >= amt) || hasob(lf->pack, OT_CREDITCARD)) { + return B_TRUE; + } + return B_FALSE; +} + int getshopblessprice(object_t *o) { int cost = 0; int remcursecost, blesscost,surcharge; @@ -616,7 +629,7 @@ enum SHOPRETURN shoppurchase(lifeform_t *lf, object_t *vm, int starty, char *top snprintf(buf, BUFLEN, "%c - %s", o->letter, obname); if (shopmode == BUY) { - if (countmoney(player->pack) >= thisprice) { + if (canafford(player, thisprice)) { col = C_GREY; } else { col = C_RED; @@ -676,80 +689,140 @@ enum SHOPRETURN shoppurchase(lifeform_t *lf, object_t *vm, int starty, char *top } answer = askchar(buf, validchars,"n", B_TRUE, B_FALSE); if (answer == 'y') { - // prompt to use a card - if (hasob(player->pack, OT_CREDITCARD)) { - char ch2; - ch2 = askchar("Charge this purchase to your credit card?","yn","n", B_TRUE, B_FALSE); - if (ch2 == 'y') { - object_t *cc; - // ask which one - initprompt(&prompt, "Which credit card will you use?"); - for (cc = player->pack->first ; cc ; cc = cc->next) { - if (cc->type->id == OT_CREDITCARD) { - char cardname[BUFLEN]; - getobname(cc, cardname, 1); - addchoice(&prompt, cc->letter, cardname, cardname, cc, NULL); + char ques[BUFLEN]; + object_t *oo; + + // do you have enough money (or a credit card)? + if (canafford(player, value)) { + object_t *money = NULL; + enum SKILLLEVEL slev; + slev = getskill(lf, SK_SPEECH); + // determine payment method + sprintf(ques, "How will you pay for %s?", obname); + initprompt(&prompt, ques); + for (oo = player->pack->first ; oo ; oo = oo->next) { + if ((oo->type->id == OT_GOLD) || (oo->type->id == OT_CREDITCARD) || hasflag(oo->flags, F_GEM)) { + char moneyname[BUFLEN]; + char fullname[BUFLEN]; + int valid = B_FALSE; + + getobname(oo, moneyname, oo->amt); + + if (oo->type->id == OT_GOLD) { + // only list gold if you have enough + if (getobvalue(oo) >= value) valid = B_TRUE; + strcpy(fullname, moneyname); + } else if ((slev >= PR_NOVICE) && hasflag(oo->flags, F_GEM)) { + // only list gems which are worth enough + int thisval; + thisval = applyshoppricemod(getobvalue(oo), player); + if (thisval >= value) { + valid = B_TRUE; + sprintf(fullname, "%s (worth $%d)", moneyname, thisval); + } + } else { + // always list cards + valid = B_TRUE; + strcpy(fullname, moneyname); + } + + if (valid) { + addchoice(&prompt, oo->letter, fullname, fullname, oo, NULL); } } - if (prompt.nchoices == 1) { - // only one card? - cc = (object_t *)prompt.choice[0].data; - } else { - addchoice(&prompt, '-', "(cancel)", "(cancel)", NULL, NULL); - prompt.maycancel = B_TRUE; - getchoice(&prompt); - cc = (object_t *)prompt.result; + } + money = NULL; + if (prompt.nchoices == 1) { + // only one possibility. + // is it gold? + money = (object_t *)prompt.choice[0].data; + if (money->type->id != OT_GOLD) { + // if not gold, still confirm it. + money = NULL; } - if (cc) { - if (getcharges(cc) >= getobvalue(o)) { - int shopamt; + } + if (!money) { + addchoice(&prompt, '-', "(cancel)", "(cancel)", NULL, NULL); + prompt.maycancel = B_TRUE; + getchoice(&prompt); + money = (object_t *)prompt.result; + } + if (money) { + int buyit = B_FALSE; + char buytext[BUFLEN]; + enum { + PM_GOLD, + PM_GEM, + PM_CARD, + } purchasemethod; + if (money->type->id == OT_CREDITCARD) { + purchasemethod = PM_CARD; + if (getcharges(money) >= value) { // you got it! - usecharges(cc, getobvalue(o)); - o->letter = '\0'; - shopamt = o->amt; // avoid "purchased: 2 apples" when you only bought 1 but were holding 1 - o = moveob(o, player->pack, ALL); - identify(o); - getobname(o, obname, shopamt); - snprintf(toptext, BUFLEN, "Charged to card: %c - %s", o->letter, obname); + buyit = B_TRUE; + usecharges(money, value); + strcpy(buytext, "Charged to card:"); if (npurchased) (*npurchased)++; - // god of thieves likes credit cards... - pleasegodmaybe(R_GODTHIEVES, (value/75)); - return SR_CONTINUE; } else { // maxed! - usecharges(cc, getcharges(o)); // use up all remaining charges + usecharges(money, getcharges(o)); // use up all remaining charges // get kicked out msg("^B\"Trying to use a maxed out card, eh? Get out of here, thief!\""); more(); // shop closes addflag(vm->flags, F_BANNEDLF, player->id, NA, NA, NULL); return SR_QUIT; } + } else { // ie gold or gem; + if (getobvalue(money) >= value) { + buyit = B_TRUE; + // lose money (do this first to avoid potential weight issues) + if (hasflag(money->flags, F_GEM)) { + int gemsneeded; + char gemname[BUFLEN]; + int valpergem; + valpergem = applyshoppricemod(real_getobvalue(money, 1), player); + gemsneeded = value / valpergem; + limit(&gemsneeded, 1, NA); // (in case gem is worth more than object) + getobname(money, gemname, gemsneeded); + // remove enough to pay... + removeob(money, gemsneeded); + purchasemethod = PM_GEM; + + // announce that we're using a gem + msg("You hand over %s.", gemname); more(); + } else { + givemoney(player, NULL, value); + purchasemethod = PM_GOLD; + } + strcpy(buytext, "Purchased"); + if (npurchased) (*npurchased)++; + } else { + msg("I'm afraid that won't cover it..."); more(); + o = NULL; + } + } + if (buyit) { + int shopamt; + // clear o->letter + o->letter = '\0'; + // give object + shopamt = o->amt; // avoid "purchased: 2 apples" when you only bought 1 but were holding 1 + o = moveob(o, player->pack, ALL); + identify(o); + getobname(o, obname, shopamt); + snprintf(toptext, BUFLEN, "%s: %c - %s", buytext, o->letter, obname); + if (npurchased) (*npurchased)++; + if (purchasemethod == PM_CARD) { + // god of thieves likes credit cards... + pleasegodmaybe(R_GODTHIEVES, (value/75)); + } else { + practice(player, SK_SPEECH, 1); + } } - } - } - // do you have enough money? - if (countmoney(player->pack) >= getobvalue(o) ) { - int shopamt; - object_t *gold; - gold = hasob(player->pack, OT_GOLD); - if (gold) { - // if so, buy it - // lose money (do this first to avoid potential weight issues) - givemoney(player, NULL, value); - // clear o->letter - o->letter = '\0'; - // give object - shopamt = o->amt; // avoid "purchased: 2 apples" when you only bought 1 but were holding 1 - o = moveob(o, player->pack, ALL); - identify(o); - getobname(o, obname, shopamt); - snprintf(toptext, BUFLEN, "Purchased: %c - %s", o->letter, obname); - if (npurchased) (*npurchased)++; - practice(player, SK_SPEECH, 1); } else { - msg("You don't seem to have any money..."); more(); + msg("Cancelled."); more(); o = NULL; - } + } // end if money } else { msg("You can't afford that!"); more(); o = NULL; diff --git a/shops.h b/shops.h index 9967acc..c3e8ac8 100644 --- a/shops.h +++ b/shops.h @@ -1,6 +1,7 @@ #include "defs.h" float applyshoppricemod(float origprice, lifeform_t *lf); +int canafford(lifeform_t *lf, int amt); int getshopblessprice(object_t *o); void shop(lifeform_t *lf, object_t *vm); enum SHOPRETURN shopabsolve(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated); diff --git a/spell.c b/spell.c index 5dace79..e582d5d 100644 --- a/spell.c +++ b/spell.c @@ -698,6 +698,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // take some time taketime(user, getactspeed(user)); practice(user, SK_COOKING, 1); + if (isplayer(user)) pleasegodmaybe(R_GODNATURE, 5); } else { // pack full? msg("You have no space to cook!"); @@ -3778,6 +3779,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // no corpse after death (so you can't keep reanimating it) addflag(lf->flags, F_NOCORPSE, NA, NA, NA, NULL); + if (isplayer(caster)) pleasegodmaybe(R_GODNATURE, 25); } else { fizzle(caster); return B_FALSE; @@ -4167,7 +4169,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // outdoors? - if (isoutdoors(target->cell->map)) { + if (caster && (getraceclass(caster) == RC_GOD)) { + losehp(target, rolldie(5,6), DT_ELECTRIC, caster, "a heavenly bolt of lightning"); + } else if (isoutdoors(target->cell->map)) { losehp(target, rolldie(4,6), DT_ELECTRIC, caster, "a bolt of lightning"); } else { losehp(target, rolldie(3,6), DT_ELECTRIC, caster, "a bolt of lightning"); @@ -4413,7 +4417,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ addobburst(targcell, radius, DT_COMPASS, "puff of poison gas", caster, LOF_WALLSTOP); if (haslos(player, targcell)) { - msg("A puff of poison gas appears!"); + msg("A cloud of poison gas appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_CHARM) { @@ -4729,6 +4733,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } + if (isplayer(caster)) pleasegodmaybe(R_GODNATURE, 5); } else if (spellid == OT_S_CREATEMONSTER) { lifeform_t *newlf; race_t *r = NULL; @@ -5081,7 +5086,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (cansee(player, c2->lf)) { char lfname[BUFLEN]; getlfname(c2->lf, lfname); - msg("%s %s showed with debris!", lfname, isplayer(c2->lf) ? "are" : "is"); + msg("%s %s showered with debris!", lfname, isplayer(c2->lf) ? "are" : "is"); } losehp(c2->lf, rnd(2,6), DT_PROJECTILE, NULL, "flying debris"); } @@ -5381,7 +5386,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } // destroy objects right away - removedeadobs(targcell->obpile); + if (removedeadobs(targcell->obpile)) { + if (isplayer(caster)) angergodmaybe(R_GODNATURE, 20, GA_ATTACKOBJECT); + } // explosion, based on size... if (totalmass > 0) { @@ -5702,6 +5709,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("A huge pool of water appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } + if (isplayer(caster)) pleasegodmaybe(R_GODNATURE, 5); } else { failed = B_TRUE; } @@ -6102,13 +6110,15 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f->val[0] = 30 + (power/2); f->val[1] = B_FALSE; // struggling doesn't damage the vine } - /// remmeber creator. if they don't have los to us, spell - // is broken and vines will vanish. - setobcreatedby(o, caster); - + if (caster) { + /// remmeber creator. if they don't have los to us, spell + // is broken and vines will vanish. + setobcreatedby(o, caster); + } } else if (spellid == OT_S_EXCAVATE) { cell_t *retcell[MAXRETCELLS],*c; int nretcells,i,radius,seenwalls = 0, seenobs = 0; + int killedobs = 0; radius = power; limit(&radius, 3, NA); getradiuscells(caster->cell, radius, DT_ORTH, B_FALSE, LOF_DONTNEED, B_FALSE, retcell, &nretcells, 0); @@ -6127,6 +6137,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ killob(o); addobfast(c->obpile, OT_ASH); if (haslos(player, c)) seenobs++; + killedobs++; } } } @@ -6134,6 +6145,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("A shockwave of destructive force rips through the air!"); setlosdirty(player); } + if (isplayer(caster) && killedobs) { + angergodmaybe(R_GODNATURE, 10*killedobs, GA_ATTACKOBJECT); + } } else if (spellid == OT_S_FLIGHT) { flag_t *f; // always targetted at caster @@ -6354,7 +6368,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_HAILSTORM) { int failed = B_FALSE; - if (isoutdoors(caster->cell->map)) { + if (caster && isoutdoors(caster->cell->map)) { power += 3; limit(&power, NA, 10); } @@ -7439,9 +7453,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ poison(target, power*3, P_VENOM, (power/4)+1, "a glob of venom"); } } - } else { - damageallobs(NULL, targcell->obpile, 0, DT_FIRE); } + if (isplayer(caster)) god_usepoison_response(); } else if (spellid == OT_S_POSSESSION) { char targname[BUFLEN]; @@ -8461,12 +8474,13 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f->obfrom = OT_S_PASSWALL; breakgrabs(target, B_TRUE, B_TRUE); - } else if (spellid == OT_S_POLYMORPH) { + } else if ((spellid == OT_S_POLYMORPH) || (spellid == OT_S_SHAPESHIFT)) { race_t *r = NULL; - target = targcell->lf; - if (frompot) { - caster = target; + if (frompot || (spellid == OT_S_SHAPESHIFT)) { + target = caster; + } else { + target = targcell->lf; } if (!target) { @@ -8474,63 +8488,109 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } - if ((target != caster) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { - if (isplayer(target)) { - msg("You feel momentarily different."); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else if (haslos(player, target->cell)) { - getlfname(target, buf); - msg("%s looks momentarily different.", buf); - if (seenbyplayer) *seenbyplayer = B_TRUE; + if (spellid != OT_S_SHAPESHIFT) { + if ((target != caster) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { + if (isplayer(target)) { + msg("You feel momentarily different."); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (haslos(player, target->cell)) { + getlfname(target, buf); + msg("%s looks momentarily different.", buf); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + return B_FALSE; } - return B_FALSE; } - if ((caster == target) && getforcedspellrace(caster, spellid, buf)) { + //if ((caster == target) && getforcedspellrace(caster, spellid, buf)) { + if (caster && getforcedspellrace(caster, spellid, buf)) { r = findracebyname(buf); } else { - if (lfhasflag(caster, F_CONTROL)) { - if (power < 5) { - power = 5; + if (spellid == OT_S_POLYMORPH) { + if (lfhasflag(caster, F_CONTROL)) { + if (power < 5) { + power = 5; + } + } + + if (isplayer(caster) && (power >= 5)) { + if (isplayer(target)) { // ie. polymorphing yourself + askstring("What will you become", '?', buf, BUFLEN, NULL); + } else { + char buf2[BUFLEN]; + char targname[BUFLEN]; + getlfname(target, targname); + snprintf(buf2, BUFLEN, "What will you transform %s into", targname); + askstring(buf2, '?', buf, BUFLEN, NULL); + } + r = findracebyname(buf); + } else { // random + if (isplayer(target) && lfhasflag(target, F_CONTROL)) { + askstring("What will you become", '?', buf, BUFLEN, NULL); + r = findracebyname(buf); + + // make sure race is valid: + if (r && !canpolymorphto(r->id)) r = NULL; + } + + if (!r) { + // random race, but not the same! + r = target->race; + while ((r == target->race) || !canpolymorphto(r->id)) { + r = getrandomrace(NULL, NA); + } + } + } + } else if (spellid == OT_S_SHAPESHIFT) { + int i,ch = 'a'; + // get list of all lfs in sight + initprompt(&prompt, "What will you become?"); + for (i = 0; i < target->nlos; i++) { + if (target->los[i]->lf && cansee(target, target->los[i]->lf)) { + race_t *potrace; + int n; + potrace = target->los[i]->lf->race; + // same race? + if (potrace == target->race) continue; + // too powerful? + if (gethitdicerace(potrace) > power) { + continue; + } + // already in the list? + for (n = 0; n < prompt.nchoices; n++) { + if (prompt.choice[n].data == potrace) { + continue; + } + } + addchoice(&prompt, ch++, potrace->name, NULL, potrace, NULL); + } + } + addchoice(&prompt, '-', "(cancel)", NULL, NULL, NULL); + prompt.maycancel = B_TRUE; + if (prompt.nchoices == 1) { + if (isplayer(caster)) { + msg("You cannot see any forms to copy!"); + } else { + fizzle(caster); + } + return B_TRUE; + } else { + if (isplayer(caster)) { + getchoice(&prompt); + r = (race_t *)prompt.result; + } else { + r = (race_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; + } } } - if (isplayer(caster) && (power >= 5)) { - if (isplayer(target)) { // ie. polymorphing yourself - askstring("What will you become", '?', buf, BUFLEN, NULL); - } else { - char buf2[BUFLEN]; - char targname[BUFLEN]; - getlfname(target, targname); - snprintf(buf2, BUFLEN, "What will you transform %s into", targname); - askstring(buf2, '?', buf, BUFLEN, NULL); - } - r = findracebyname(buf); - - // make sure race is valid: - // - can't turn into monsters which aren't randomly generated. - // - can't turn into unique monsters - // - can't turn into undead monsters - if (r && !canpolymorphto(r->id) && !hasjob(caster, J_GOD)) { - if (isplayer(caster)) msg("As you think of a %s, magic energy dampens around you.", r->name); - r = NULL; - } - } else { // random - if (isplayer(target) && lfhasflag(target, F_CONTROL)) { - askstring("What will you become", '?', buf, BUFLEN, NULL); - r = findracebyname(buf); - - // make sure race is valid: - if (r && !canpolymorphto(r->id)) r = NULL; - } - - if (!r) { - // random race, but not the same! - r = target->race; - while ((r == target->race) || !canpolymorphto(r->id)) { - r = getrandomrace(NULL, NA); - } - } + // make sure race is valid: + // - can't turn into monsters which aren't randomly generated. + // - can't turn into unique monsters + // - can't turn into undead monsters + if (r && !canpolymorphto(r->id) && !hasjob(caster, J_GOD)) { + if (isplayer(caster)) msg("As you think of a %s, magic energy dampens around you.", r->name); + r = NULL; } } // end if forcepoly @@ -8564,8 +8624,16 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ setrace(target, r->id, B_TRUE); // if someone cast the spell at themself and it's controlled, they can change back at will. - if ((target == caster) && (power >= 5)) { - addflag(target->flags, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL); + if (target == caster) { + int canrevert = B_FALSE; + if ((spellid == OT_S_POLYMORPH) && (power >= 5)) { + canrevert = B_TRUE; + } else if (spellid == OT_S_SHAPESHIFT) { + canrevert = B_TRUE; + } + if (canrevert) { + addflag(target->flags, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL); + } } if (haslos(player, target->cell)) { @@ -8577,59 +8645,69 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } } else if (spellid == OT_S_PURIFYFOOD) { - object_t *o; + object_t *o,*nexto; + int ndone = 0; - if (targob) { - o = targob; - } else { - // ask for an object - o = askobject(caster->pack, "Purify what?", NULL, '\0', AO_EDIBLE | AO_DRINKABLE); + if (!target) { + target = caster; } if (!o) { fizzle(caster); return B_TRUE; } - if (isplayer(caster)) { + for (o = target->pack->first ; o ; o = nexto) { char obname[BUFLEN]; - flag_t *f; int donesomething = B_FALSE; + flag_t *f; + nexto = o->next; getobname(o, obname, o->amt); if (o->type->id == OT_POT_POISON) { - msg("Your %s sparkles for a while.", noprefix(obname)); - makeknown(o->type->id); // you now know that it was poison. + if (isplayer(target)) { + msg("Your %s sparkles for a while.", noprefix(obname)); + makeknown(o->type->id); // you now know that it was poison. + } o->type = findot(OT_POT_WATER); - makeknown(o->type->id); // you now know what water is too. + if (isplayer(target)) { + makeknown(o->type->id); // you now know what water is too. + } donesomething = B_TRUE; + ndone++; } else { f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = f->val[1]; f = hasflagval(o->flags, F_OBHPDRAIN, NA, DT_DECAY, NA, NULL); - if (f) killflag(f); - donesomething = B_TRUE; + if (f) { + killflag(f); + donesomething = B_TRUE; + ndone++; + } } if (killflagsofid(o->flags, F_TAINTED)) { donesomething = B_TRUE; + ndone++; } } if (donesomething) { - if (isdrinkable(o)) { - msg("Your %s looks more clean now.", noprefix(obname)); - } else { - msg("Your %s looks more fresh now.", noprefix(obname)); + if (isplayer(target)) { + if (isdrinkable(o)) { + msg("Your %s looks more clean now.", noprefix(obname)); + } else { + msg("Your %s looks more fresh now.", noprefix(obname)); + } + if (seenbyplayer) *seenbyplayer = B_TRUE; } - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else { - nothinghappens(); - return B_TRUE; } - } else { - // monsters can't purify things! + } + + if (!ndone) { + fizzle(caster); + return B_TRUE; } } else if (spellid == OT_S_PYROMANIA) { int i,donesomething = B_FALSE; @@ -10308,8 +10386,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("A wall of ice appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } - - + if (isplayer(caster)) pleasegodmaybe(R_GODNATURE, 5); } else if (spellid == OT_S_WATERJET) { char lfname[BUFLEN]; cell_t *retcell[MAXRETCELLS]; @@ -10517,7 +10594,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f = addtempflag(caster->flags, F_WINDSHIELD, power, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_WISHLIMITED) { - object_t *o; + object_t *o = NULL; char obname[BUFLEN]; char ch; if (!target) target = caster; @@ -10707,6 +10784,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (isplayer(target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } + if (o && isplayer(caster)) pleasegodmaybe(R_GODNATURE, 5); } else if ((spellid == OT_S_WISH) || (spellid == OT_S_GIFT)) { object_t *o; if (isplayer(caster)) { @@ -10732,6 +10810,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ askstring(question, '?', buf, BUFLEN, NULL); addob(target->cell->obpile, buf); if (nretobs) { + int ncreated = 0; for (i = 0; i < nretobs; i++) { char obname[BUFLEN]; o = retobs[i]; @@ -10744,6 +10823,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else { msg("%s is gifted with %s.", lfname, obname); } + ncreated++; } else { // can't pick this up... @@ -10759,6 +10839,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!isblind(caster)) { msg("%s appear%s on the ground!", obname, OBS1(o)); } + ncreated++; } else { // ob exists but couldn't make it appear msg("The air in front of %s seems to ripple for a moment.", lfname); @@ -10766,6 +10847,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } + if (ncreated) { + if (isplayer(caster)) pleasegodmaybe(R_GODNATURE, 10); + } } else { // couldn't make it appear - ob doesn't exist msg("The air in front of %s seems to ripple for a while.", lfname);