From 81c7f37eff56f9b45d2fdefe486ab4dbd36f3c78 Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Wed, 3 Aug 2011 18:43:05 +0000 Subject: [PATCH] - [+] outdoor bug: at -1,-1. travel south. end up at 0,0!!! - [+] blink should go to random place within lof, not los - [+] sunglasses shuld reduce nightvis range, not visrange * [+] since jolt needs adjacency, make it more powerful - [+] use colours for spell descriptions - [+] replace 'call wind' with 'zephyr' * [+] add vault commonality. - [+] add more common vaults - roundabout etc * [+] change "addmonster" to take a string arg instrad of RACEID - [+] pouring cursed water on blessed scroll didn't work..... - [+] append "...god of xxx" to rc_god lifeforms - [+] try to attack with f_pain -> INFINITE LOOP. - [+] ARMOURPIERCE shouldn't hurt armour - [+] ghast touchparalyze attack isn't working. * [+] bug: when praying to gods, ones not in heaven aren't listed. - [+] when a god apepars..."Hecta appears" x2 - [+] attacking fleeing lf should count as a backstab (if you have the skill) * [+] "nothing happens" during god spell effects * [+] allow hunger to work on monsters - [+] pressing 'a' on @Magic has weird results. - [+] bug: got a gift form yumi during combat???? what for? healing? initial vampire implementation initial god implementation - [+] they all start off in "heaven" map. - [+] gods ahve f_piety. starts at 0 - [+] keep track of piety with each god. starts at 0 for each one. * [+] let planeshift take you to heaven (for debugging) * [+] everyone has 'pray' ability. - [+] modpiety(rid, amt) * [+] piety vals * [+] when you pray - [+] isangry(god) - ie is piety < 0 * [+] if you pray when god is angry, bad! * [+] once piety gets over 200 or so, praying might give you a gift. * [+] god_appears(lifeform_t *victim) - [+] if >=0 (indiff / pleased), goes up to indiff over time(1 per turn). - [+] regain through doing actions ... in progress - [+] lose through doing actions only if you have prayed to this god before (angergodmaybe) - [+] special: gods don't need LOS to cast spells. - [+] gods planeshift away if not doing anything else. * [+] @g to see your piety levels for each god - [+] ?g for help on gods - [+] IN general: - [+] prey = oneoff effect - [+] gift = ongoing * [+] makeangry() vs modpiety * [+] Yumi - fem,mercy/healing - if you die within the first few turns! Hecta - partial Avamon - partial --- Makefile | 4 +- ai.c | 54 +++- attack.c | 78 ++++- data/hiscores.db | Bin 3072 -> 3072 bytes defs.h | 50 ++- doc/add_god | 13 + doc/glyphs.txt | 1 + doc/vaults.txt | 11 +- god.c | 611 +++++++++++++++++++++++++++++++++++++ god.h | 16 + io.c | 352 ++++++++++++++++----- io.h | 3 +- lf.c | 294 +++++++++++++++--- lf.h | 3 +- map.c | 209 ++++++++++--- map.h | 4 +- nexus.c | 13 +- objects.c | 108 +++++-- save.c | 4 +- spell.c | 367 +++++++++++++--------- spell.h | 2 +- text.c | 21 +- text.h | 1 + vaults/cavein.vlt | 1 + vaults/circle.vlt | 2 + vaults/crosshatch.vlt | 1 + vaults/diagcross.vlt | 1 + vaults/diningroom.vlt | 1 + vaults/floodroom1.vlt | 1 + vaults/floodroom2.vlt | 1 + vaults/jimbo.vlt | 1 + vaults/labyrinth.vlt | 3 + vaults/lair_cockatrice.vlt | 1 + vaults/monsterzoo.vlt | 1 + vaults/mudroom.vlt | 1 + vaults/pillar.vlt | 1 + vaults/pillarglass.vlt | 1 + vaults/riverroom.vlt | 1 + vaults/traproom.vlt | 1 + vaults/uturn.vlt | 1 + vaults/vault.vlt | 1 + 41 files changed, 1884 insertions(+), 356 deletions(-) create mode 100644 doc/add_god create mode 100644 god.c create mode 100644 god.h diff --git a/Makefile b/Makefile index c3beee8..cac5531 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ -nexus: Makefile defs.h nexus.c nexus.h ai.c ai.h attack.c attack.h flag.c flag.h io.c io.h lf.c lf.h map.c map.h move.c move.h objects.c objects.h text.c text.h save.c save.h spell.c spell.h vault.c vault.h - gcc -Wall -g -pg -o nexus nexus.c ai.c attack.c flag.c io.c lf.c map.c move.c objects.c text.c save.c spell.c vault.c vault.h -lncurses -lsqlite3 +nexus: Makefile defs.h nexus.c nexus.h ai.c ai.h attack.c attack.h flag.c flag.h god.c god.h io.c io.h lf.c lf.h map.c map.h move.c move.h objects.c objects.h text.c text.h save.c save.h spell.c spell.h vault.c vault.h + gcc -Wall -g -pg -o nexus nexus.c ai.c attack.c flag.c god.c io.c lf.c map.c move.c objects.c text.c save.c spell.c vault.c vault.h -lncurses -lsqlite3 diff --git a/ai.c b/ai.c index 51eae6b..8748ad9 100644 --- a/ai.c +++ b/ai.c @@ -5,6 +5,7 @@ #include "attack.h" #include "defs.h" #include "flag.h" +#include "god.h" #include "io.h" #include "lf.h" #include "map.h" @@ -28,6 +29,7 @@ void addignorecell(lifeform_t *lf, cell_t *c) { // returns true on failure int aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit) { int db = B_FALSE; + int innocentattack = 0; flag_t *f; if (lfhasflag(lf, F_DEBUG)) { @@ -85,18 +87,24 @@ int aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit) { // become hostile? if (!hasflag(lf->flags, F_HOSTILE)) { addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + innocentattack = 1; } // change allegience ? if (!areenemies(lf, victim)) { if (getallegiance(victim) == AL_FRIENDLY) { killflagsofid(lf->flags, F_FRIENDLY); + innocentattack = 3; } } // no longer a pet f = lfhasflagval(lf, F_PETOF, victim->id, NA, NA, NULL); - if (f) killflag(f); + if (f) { + innocentattack = 3; + killflag(f); + } + return B_FALSE; } @@ -817,6 +825,10 @@ void aimovetotargetcell(lifeform_t *lf, flag_t *f) { } int aipickup(lifeform_t *lf, object_t *o) { + // special case + if ((o->type->id == OT_COFFIN) && (lf->race->id == R_GASCLOUD) && lfhasflagval(lf, F_ORIGRACE, R_VAMPIRE, NA, NA, NULL)) { + return rest(lf, B_TRUE); + } if (isedible(o)) { return eat(lf, o); } else { @@ -829,6 +841,11 @@ int aipickup(lifeform_t *lf, object_t *o) { int aipickupok(lifeform_t *lf, object_t *o) { int ok = B_FALSE; + // special case + if ((o->type->id == OT_COFFIN) && (lf->race->id == R_GASCLOUD) && lfhasflagval(lf, F_ORIGRACE, R_VAMPIRE, NA, NA, NULL)) { + return B_TRUE; + } + if (hasflag(o->flags, F_SHOPITEM)) { return B_FALSE; } @@ -899,14 +916,12 @@ void aiturn(lifeform_t *lf) { dblog("AIMOVE: %s", lfname); } - /* // if lifeform isn't alive, skip turn if (isdead(lf)) { if (db) dblog(".oO { i am not alive, skipping turn. }"); taketime(lf, SPEED_DEAD); return; } - */ /////////////////////////////////////////// // info gathering @@ -1035,6 +1050,15 @@ void aiturn(lifeform_t *lf) { return; } + // hurt gods planeshift away + if (lf->race->raceclass->id == RC_GOD) { + if (gethppct(lf) <= 10) { + if (!castspell(lf, OT_S_PLANESHIFT, lf, NULL, lf->cell)) { + return; + } + } + } + // need to heal? if (lf->hp < (lf->maxhp/2)) { if (!useitemwithflag(lf, F_AIHEALITEM)) { @@ -1289,8 +1313,7 @@ void aiturn(lifeform_t *lf) { return; } } - } - + } // look for any race which we hate newtarget = NULL; @@ -1335,6 +1358,16 @@ void aiturn(lifeform_t *lf) { if (db) dblog(".oO { won't move towards target - i have no weapon. }"); } } + } else { + // god with no targets? + if (lf->race->raceclass->id == RC_GOD) { + if (onein(6)) { + // gods will planeshift away + if (!castspell(lf, OT_S_PLANESHIFT, lf, NULL, lf->cell)) { + return; + } + } + } } /////////////////////////////////////////////// @@ -1590,6 +1623,9 @@ int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG if ((ot->id == OT_S_BLINDNESS) && isblind(victim)) { specificcheckok = B_FALSE; } + if ((ot->id == OT_S_DISPERSAL) && (lf->race->raceclass->id == RC_GOD)) { + specificcheckok = B_FALSE; + } if ((ot->id == OT_S_DRAINLIFE) && isimmuneto(victim->flags, DT_NECROTIC)) { specificcheckok = B_FALSE; } @@ -1789,6 +1825,14 @@ int lookforobs(lifeform_t *lf) { } } + if (hasflag(lf->flags, F_HUNGER)) { + // monsters dont normaly need to eat. if they have hunger, it's due + // to a spell. + wantflag[nwantflags] = F_EDIBLE; + wantflagcovet[nwantflags] = B_TRUE; + nwantflags++; + } + // current cell has an object we want? o = hasobmulti(lf->cell->obpile, oid, noids); if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o)) { diff --git a/attack.c b/attack.c index e0b3582..0d50664 100644 --- a/attack.c +++ b/attack.c @@ -6,6 +6,7 @@ #include "attack.h" #include "defs.h" #include "flag.h" +#include "god.h" #include "io.h" #include "lf.h" #include "map.h" @@ -24,6 +25,9 @@ int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damty int damtaken = 0; // figure out what bit of armour was hit + if (dam == 0) { + return 0; + } // special case - missiles always hit flak jacket if (damtype == DT_PROJECTILE) { @@ -56,7 +60,6 @@ int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damty // adjust how much damage to do to armour if ( ((armour->type->id == OT_FLAKJACKET) && (damtype == DT_PROJECTILE)) || (damtype == DT_ACID) || - hasflag(wep->flags, F_ARMOURPIERCE) || rust ) { // ALL of damage reduction goes towards armour } else { @@ -353,10 +356,18 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) >= A_LOW) ) { saysorry = B_TRUE; } - if (attacklf(lf, victim, wep[i], damflag[i])) break; + if (attacklf(lf, victim, wep[i], damflag[i])) { + // failed + attacksdone = maxattacks; + break; + } } } else if (attacktype == AT_OB) { - if (attackob(lf, (object_t *)attacktarget, wep[i], damflag[i])) break; + if (attackob(lf, (object_t *)attacktarget, wep[i], damflag[i])) { + // failed + attacksdone = maxattacks; + break; + } } attacksdone++; @@ -399,6 +410,8 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) int deflected = B_FALSE; int weppassthrough = B_FALSE; int firstisbackstab = B_FALSE; + int attackedhelpless = B_FALSE; + int attackedfriend = B_FALSE; int hit = B_FALSE; int critical = 0; char wepname[BUFLEN]; @@ -573,14 +586,25 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) // backstab? if ((damtype[0] == DT_PIERCE) && // using a stabbing weapon - getskill(lf, SK_BACKSTAB) && // able to backstab - !cansee(victim, lf) && // victim can't see us - !lfhasflagval(victim, F_STABBEDBY, lf->id, NA, NA, NULL) && // haven't stabbed them before - !lfhasflagval(victim, F_TARGETLF, lf->id, NA, NA, NULL) // victim isnt attacking us - ) { - addflag(victim->flags, F_STABBEDBY, lf->id, NA, NA, NULL); - dam[0] *= (getskill(lf, SK_BACKSTAB)); - firstisbackstab = B_TRUE; + getskill(lf, SK_BACKSTAB)) { // able to backstab + if (!lfhasflagval(victim, F_STABBEDBY, lf->id, NA, NA, NULL) && // haven't stabbed them before + !lfhasflagval(victim, F_TARGETLF, lf->id, NA, NA, NULL)) { // victim isnt attacking us + + if (!cansee(victim, lf) || // victim can't see us + isfleeing(victim)) { + addflag(victim->flags, F_STABBEDBY, lf->id, NA, NA, NULL); + dam[0] *= (getskill(lf, SK_BACKSTAB)); + firstisbackstab = B_TRUE; + attackedhelpless = B_TRUE; + } + } + } + + if (!cansee(victim, lf)) { + attackedhelpless = B_TRUE; + } + if (areallies(lf, victim)) { + attackedfriend = B_TRUE; } // extra damage for being skilled? if (wepsk) { @@ -767,9 +791,6 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } strcpy(attackverb, getattackverb(lf, wep, damtype[i],dam[i],victim->maxhp)); - if (strstr(attackverb, "hit")) { - dblog("xxx"); - } if ((dam[i] == 0) && (damtype[i] != DT_TOUCH)) { nodam = B_TRUE; strcpy(nodamstr, " but does no damage"); @@ -805,6 +826,16 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } else { char attackername2[BUFLEN]; real_getlfname(lf, attackername2, B_FALSE); + if (lf->race->raceclass->id == RC_GOD) { + flag_t *gf; + gf = lfhasflag(lf, F_GODOF); + if (gf->val[0] == B_FEMALE) { + strcat(attackername2, " the Goddess of "); + } else { + strcat(attackername2, " the God of "); + } + strcat(attackername2, gf->text); + } // victim loses hp // don't adjust damage - we've already done that if (wep && !isunarmed) { @@ -1045,6 +1076,18 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } } + if (isplayer(lf)) { + if (attackedfriend) { + angergodmaybe(R_GODMERCY, 100); + } else if (attackedhelpless) { + angergodmaybe(R_GODMERCY, 50); + } else if (lfhasflag(lf, F_USEDPOISON)) { + killflagsofid(lf->flags, F_USEDPOISON); + angergodmaybe(R_GODMERCY, 50); + } + + } + if (aidb) dblog(".oO { doattack about to return B_FALSE }"); return B_FALSE; } @@ -1206,6 +1249,10 @@ int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE d //int pctrange; object_t *o; + if (hasflag(wep->flags, F_ARMOURPIERCE)) { + reduceamt = 0; + } + ar = getarmourrating(lf, NULL, NULL, NULL); // between 25% and 75% of AR. @@ -2105,6 +2152,9 @@ void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam) { // was this from a poisoned weapon? if so the poison vanishes if ((f->val[0] == F_POISONED) && (f->lifetime == FROMOBMOD)) { killflag(f); + if (owner && isplayer(owner)) { + addflag(owner->flags, F_USEDPOISON, B_TRUE, NA, NA, NULL); + } } } } // end if (fid == hitconfer) diff --git a/data/hiscores.db b/data/hiscores.db index 15ea1de669e5829886be7a2424972e0d7917d328..aede53a35d0ca9ee28bd8c9dd1632b2d7a07affe 100644 GIT binary patch literal 3072 zcmeH`O-vI(6vt=xm4bXjp#_pA)0ogw%MwxuA;yn_d)hXhc z=T%Fg=M7$97EKZ#5d?CQQbNdx{j2|)?1@lks2|sv_YOPY2N7Y8z#MGtU2Rwz75LvP zur3PCp>SAOU$&U6DXXfPF}Pw@|Hav}>C{r1E~RGX)3j=hsadM(7AwdKIeRHVbyiSlneogj;|Y4fkZY5?s%c7Au2d}}W1>Ga8y1MFXO%VcqGrGA6;`qg zXTDNBb!EId0=w$I4zWanWQxE7M8t36wzwf47b~z0_rzYfEPNC;g&RUnI3~1{9rA=+ zBN-beBtGOPVjwbgXqX%Fe0hQ{tCtwho_1oQc2K6pc$rzMp-b^WKM?|v=>-QJ--9xm zQ<+@{&MYZ5;P_3FSDtX*kPR&>MMI9)hqANUCp|ZwI zJK8=chRGQZzG$eXp{sgMiUr&t?s}k%R`crJ&CAQ@FL1QS0Z({?yJa08Ld>1e~)Gj z`^=9?hNl-j4zHycZ$dJftn7EXdv^(+Q;sBg?!tdBhfBx0kd7qDRhRzr4Ke!_Ia$Rz zT=I%1>G~XNM>2Ypyxb>TyNlO~nqlJOY_;zq2sa6Qh7Yg<@8KQ1fw%A-zQPxH4KLs+ zY{FxB1P|dp+=1J0%Z3|YqXLZz{G$r^fCxhOkH1kIW4(4+V%x{>VGe2U!%Y1|R9P?%7-$3%*W=D=d Z=7|l7lh1K0^8*d!Vg?aREX)jyJOC)CH%$Nl diff --git a/defs.h b/defs.h index 235d876..9fb8430 100644 --- a/defs.h +++ b/defs.h @@ -268,6 +268,7 @@ enum SENSE { #define FROMBRAND (-7865) #define FROMOBMOD (-7864) #define FROMSKILL (-7863) +#define FROMGODGIFT (-7862) #define FROMEXTERNAL_LOW (-7999) @@ -660,8 +661,11 @@ enum RACECLASS { RC_UNDEAD, }; +#define MAXGODS 3 + enum RACE { - R_NONE, R_RANDOM, + R_NONE = 0, + R_RANDOM, R_SPECIFIED, // unique monstesr R_JAILER, // human monsters @@ -673,7 +677,9 @@ enum RACE { R_PRISONER, R_TOWNGUARD, // gods + R_GODDEATH, R_GODGREED, + R_GODMERCY, // monsters R_BEHOLDER, R_BUGBEAR, @@ -712,6 +718,7 @@ enum RACE { R_SPRITEFIRE, R_TROGLODYTE, R_TROLL, + R_VAMPIRE, R_XAT, // fish R_CRAB, @@ -1082,6 +1089,7 @@ enum OBTYPE { OT_S_QUENCH, OT_S_LESSENPOISON, OT_S_REPELINSECTS, + OT_S_SATEHUNGER, OT_S_SLEETSTORM, OT_S_SOFTENEARTH, OT_S_STICKTOSNAKE, @@ -1132,6 +1140,7 @@ enum OBTYPE { OT_A_CHARGE, OT_A_CRUSH, OT_A_JUMP, + OT_A_PRAY, OT_A_RAGE, OT_A_REPAIR, OT_A_SPRINT, @@ -1210,6 +1219,7 @@ enum OBTYPE { OT_XRAYGOGGLES, // furniture OT_CANDELABRUM, + OT_COFFIN, OT_FIREPLACE, OT_WEAPONRACK, OT_WOODENTABLE, @@ -1534,6 +1544,7 @@ enum FLAG { F_ONEPERCELL, // only one of these objects can exist per cell F_CREATEDBY, // object was made by lf id v0, text=real lfname F_ENCHANTABLE, // object can get +1/-1 ect + F_GODGIFT, // this was a gift form god with race v0. F_NOSHATTER, // object will not shatter, even if it's material should. F_STACKABLE, // can stack multiple objects togethr F_NO_PLURAL, // this obname doesn't need an 's' for plurals (eg. gold, money) @@ -1842,6 +1853,8 @@ enum FLAG { F_DONEDARKMSG, // tells the game not to say 'it is very dark here' F_DONELISTEN, // supress further 'you hear xx' messages this turn. // lifeform flags / lf flags + F_PIETY, // for god lifeforms - tracks player's piety with them + F_PRAYEDTO, // player has prayed to this god before. F_COUNTER, // generic counter flag for race abilities. F_DEBUG, // debugging enabled F_ACCURACYMOD, // modify your accuracy by val0 @@ -1859,6 +1872,7 @@ enum FLAG { F_ATTRMOD, // modify attribute val0 by val1. ie. 0=A_STR,1=-3 F_ATTRSET, // forces attribute val0 to be val1. ie. 0=A_STR,1=18 F_SIZE, // val0 = lf size (enum LFSIZE) + F_USEDPOISON, // this lf used a poisoned weapon to attack F_RANDOMTALKPCT, // v0 = chance of randomly saying something each turn F_RANDOMTALK, // v0 = sp_xxx for what to say when we randomly talk. // v1/v2 are min/max volume @@ -1909,6 +1923,7 @@ enum FLAG { F_LFSUFFIX, // text = suffix. eg. "skeleton" F_VISRANGE, // how far you can see (in the light) F_VISRANGEMOD, // modifications to visrange + F_NIGHTVISRANGEMOD, // modifications to nightvisrange F_GUNTARGET, // current projectile weapon target F_CASTINGSPELL, // set while the player is casting a spell // for instant spells: @@ -1959,6 +1974,8 @@ enum FLAG { F_PETOF, // this lf is a pet of lfid v0 // v1/2 = last known location of my owner. // optional text is last known movement dir. + // note: can also go on corpse objects to show that this + // is the corpse of a pet. F_SUMMONEDBY, // this lf was summoned by lfid v0. if they die, we // vanish. // v1 is lifetime left. this decrements each turn. @@ -2020,6 +2037,9 @@ enum FLAG { F_MPDICE, // val0: # d4 to roll for maxmp per level. val1: +xx F_JOB, // val0 = player's class/job F_GODOF, // text = what this lf is the god of. use capitals. + // if v0 is b_true, means this is a goddess + F_GODLIKES, // text = something this god likes (ie. incs piety) + F_GODDISLIKES, // text = something this god likes (ie. decs piety) F_NAME, // text = lf's name F_XPMOD, // add/subtract this much from calculated xpval F_BLOODOB, // text = type of object to drop for blood @@ -2243,6 +2263,7 @@ enum FLAG { F_VAULTISSHOP, // this vault is a shop, so add f_shopitem to objects // here. F_VAULTISSHRINE, // this vault is a godstone shrine + F_VAULTRARITY, // v0=rr_xxx F_VAULTSCATTER, // v0=thingtype, v1=pctchance // text=x1,y1,x2,y2,mincount-maxcount,thingname // if maxcount is PCT, mincount is a percentage @@ -2276,6 +2297,21 @@ enum HUNGER { H_STARVED = 5 }; +#define PIETYPRAYLOSS 75 + +#define PIETY_MIN -200 +#define PIETY_MAX 400 +enum PIETYLEV { + PL_ENRAGED = -4, + PL_FURIOUS = -3, + PL_ANGRY = -2, + PL_TOLERATED = -1, + PL_INDIFFERENT = 0, + PL_PLEASED = 1, + PL_DELIGHTED = 2, + PL_ECSTATIC = 3, +}; + // probabilities //#define CH_DEADENDOB 35 //#define CH_EMPTYCELLOB 3 @@ -2286,6 +2322,9 @@ enum HUNGER { #define B_FALSE (0) #define B_TRUE (-1) +#define B_MALE (0) +#define B_FEMALE (-1) + #define B_MAYCHASE (-1) #define B_NODOORS (0) @@ -2487,14 +2526,16 @@ typedef struct command_s { enum REGIONTYPE { RG_WORLDMAP, RG_FIRSTDUNGEON, + RG_HEAVEN, RG_PIT, }; enum HABITAT { H_DUNGEON = 1, H_FOREST = 2, - H_PIT = 3, - H_VILLAGE = 4, + H_HEAVEN = 3, + H_PIT = 4, + H_VILLAGE = 5, H_ALL = 999 }; @@ -2534,6 +2575,7 @@ typedef struct regionoutline_s { struct regionoutline_s *next, *prev; } regionoutline_t; +// a region is a link of a regiontype and an outline typedef struct region_s { int id; regiontype_t *rtype; @@ -2781,6 +2823,8 @@ typedef struct flag_s { int chance; long obfrom; // for conferred flags, link to object->id. -1 if not conferred. + // also used for godgifts, in which case it is thr race->id of + // the god who gifted you this flag. // int known; diff --git a/doc/add_god b/doc/add_god new file mode 100644 index 0000000..2865b81 --- /dev/null +++ b/doc/add_god @@ -0,0 +1,13 @@ +In defs.h + Add R_GODxxxx + update MAXGODS + +In lf.c + addrace() for the new god + include f_godlikes/f_goddislikes + +In god.c: + prayto() - add help effects. + prayto() - add gift effects. + angergod() - add angry effects (minor). + angergod() - add angry effects (major). diff --git a/doc/glyphs.txt b/doc/glyphs.txt index 3fda87b..9004803 100644 --- a/doc/glyphs.txt +++ b/doc/glyphs.txt @@ -33,6 +33,7 @@ h = humanoid H = large humanoid U = unearthly/demonic/horrific creature w = worm +V = vampire z = lizard-like creature Z = undead diff --git a/doc/vaults.txt b/doc/vaults.txt index 160fbfe..eaef231 100644 --- a/doc/vaults.txt +++ b/doc/vaults.txt @@ -18,7 +18,7 @@ General format: Legend is: c:type:what[:pct] OR - c:exit + c:exit (denotes an exit, for use by linkexits) c = any letter @@ -64,6 +64,15 @@ Flags can be: mayrotate // vault can be rotated randomly + rarity:xxx // how common this vault is. + // xxx can be: + // common (default) + // uncommon + // rare + // vrare / veryrare + // never + + norandom // this vault doesn't appear randomly. it will only // appear when specifically requested via a region's // outline. diff --git a/god.c b/god.c new file mode 100644 index 0000000..475de47 --- /dev/null +++ b/god.c @@ -0,0 +1,611 @@ +#include +#include +#include +#include +#include +#include +#include "ai.h" +#include "attack.h" +#include "defs.h" +#include "flag.h" +#include "god.h" +#include "io.h" +#include "lf.h" +#include "map.h" +#include "move.h" +#include "nexus.h" +#include "objects.h" +#include "spell.h" +#include "text.h" + +extern map_t *firstmap; +extern map_t *heaven; +extern race_t *firstrace, *lastrace; +extern raceclass_t *firstraceclass, *lastraceclass; +extern job_t *firstjob, *lastjob; +extern skill_t *firstskill, *lastskill; +extern objecttype_t *objecttype; +extern lifeform_t *player; + +lifeform_t *godlf[MAXGODS]; +int ngodlfs = 0; + +void angergod(enum RACE rid, int amt) { + lifeform_t *god; + char lfname[BUFLEN]; + int piety; + + god = findgod(rid); + real_getlfname(god, lfname, B_FALSE); + + modpiety(rid, -amt); + piety = getpiety(rid); + + // if you have never prayed to this god before, finish now. + if (!lfhasflag(god, F_PIETY)) { + return; + } + + // if you HAVE prayed to this god before, something bad will probably happen. + + // announce + if (piety > 0) { // not angry yet. + godsay(rid, "You are testing my patience, mortal!"); + } else if (piety > -100) { // minor bad stuff + godsay(rid, "You have angered me, mortal!"); + } else if (piety > -200) { // major bad stuff + godsay(rid, "You go too far, mortal!"); + } else { // god will attack + godsay(rid, "You will regret that!"); + } + + // bad stuff + if (piety <= 0) { + void *poss[MAXCANDIDATES]; + object_t *o; + flag_t *f; + int isflag[MAXCANDIDATES]; + int nposs = 0; + enum ATTRIB a; + // lose at least one god gift + for (o = player->pack->first ; o ; o = o->next) { + if (hasflagval(o->flags, F_GODGIFT, rid, NA, NA, NULL)) { + poss[nposs] = o; + isflag[nposs] = B_FALSE; + nposs++; + } + } + for (f = player->flags->first ; f ; f = f->next) { + if ((f->lifetime == FROMGODGIFT) && (f->obfrom == god->race->id)) { + poss[nposs] = f; + isflag[nposs] = B_TRUE; + nposs++; + } + } + if (nposs) { + int sel; + msg("\"You are unworthy of my gifts, mortal!\""); + sel = rnd(0,nposs-1); + if (isflag[sel]) { + killflag((flag_t *)poss[sel]); + } else { + obdie((object_t *)poss[sel]); + } + } + + // then... + if (piety > -100) { + // minor bad stuff + switch (rid) { + case R_GODDEATH: + castspell(god, OT_S_DRAINLIFE, player, NULL, player->cell); + castspell(god, OT_S_PAIN, player, NULL, player->cell); + break; + case R_GODGREED: + // take a random object + msg("\"Yoink!\""); + castspell(god, OT_S_CONFISCATE, player, NULL, player->cell); + break; + case R_GODMERCY: + // lower one attribute + msg("\"Be glad that I am feeling merciful, mortal!\""); + a = rnd(0,MAXATTS-1); + modattr(player, a, -1); + break; + default: + break; + } + } else if (piety > -200) { + object_t *o; + // major bad stuff + switch (god->race->id) { + case R_GODDEATH: + castspell(god, OT_S_PAIN, player, NULL, player->cell); + castspell(god, OT_S_DRAINLIFE, player, NULL, player->cell); + switch (rnd(1,2)) { + case 1: + msg("\"This will teach you some humility, mortal!\""); + if (getattr(player, A_IQ) > getattr(player, A_STR)) { + castspell(god, OT_S_FEEBLEMIND, player, NULL, player->cell); + } else { + castspell(god, OT_S_WEAKEN, player, NULL, player->cell); + } + break; + case 2: + // summon undead + msg("\"Destroy him, my pets!\""); + summonlfs(god, player->cell, RC_UNDEAD, SZ_ANY, 3, PERMENANT); + break; + } + break; + case R_GODGREED: + o = getweapon(player); + msg("\"Allow me to lighten your load a little...\""); + if (o) { // take player's weapon + castspell(god, OT_S_CONFISCATE, player, o, player->cell); + } else { // take 3 objects + int i; + for (i = 0; i < 3; i++) { + castspell(god, OT_S_CONFISCATE, player, NULL, player->cell); + } + } + break; + case R_GODMERCY: + msg("\"Even my mercy has its limits!\""); + // age + age(player, 15); + // lower every attribute by one + for (a = 0; a < MAXATTS; a++) { + modattr(player, a, -1); + } + + break; + default: + break; + } + } else { + // god attacks! + godappears(god->race->id, NULL); + aiattack(god, player, PERMENANT); + } + } +} + +// anger the god if you are worshippin them. +void angergodmaybe(enum RACE rid, int amt) { + lifeform_t *god; + god = findgod(rid); + if (lfhasflag(god, F_PRAYEDTO)) { + angergod(rid, amt); + } +} + +lifeform_t *findgod(enum RACE rid) { + lifeform_t *lf; + // search heaven first + for (lf = heaven->lf ; lf ; lf = lf->next) { + if (lf->race->id == rid) return lf; + } + // now search other maps + lf = findlfunique(rid); + return lf; +} + +int getpiety(enum RACE rid) { + lifeform_t *god; + flag_t *f; + god = findgod(rid); + f = lfhasflag(god, F_PIETY); + if (f) { + return f->val[0]; + } else { + // ie. haven't prayed to them before. + return 100; + } + return 0; +} + +enum PIETYLEV getpietylev(enum RACE rid, enum COLOUR *col, char *happiness) { + int piety; + /// figure out piety bracket + // min = -200 + // max = 400 + // range = 600 + piety = getpiety(rid); + if (piety <= -200) { // -200 - -100 + if (col) *col = C_ORANGE; + if (happiness) strcpy(happiness, "Enraged"); + return PL_ENRAGED; + } else if (piety <= -100) { // -199 - -100 + if (col) *col = C_RED; + if (happiness) strcpy(happiness, "Furious"); + return PL_FURIOUS; + } else if (piety < 0) { // -99 - 0 + if (col) *col = C_YELLOW; + if (happiness) strcpy(happiness, "Angry"); + return PL_ANGRY; + } else if (piety <= 99) { // 0 - 99 + if (col) *col = C_BROWN; + if (happiness) strcpy(happiness, "Tolerated"); + return PL_TOLERATED; + } else if (piety <= 199) { // 100 - 199 + if (col) *col = C_GREY; + if (happiness) strcpy(happiness, "Indifferent"); + return PL_INDIFFERENT; + } else if (piety <= 299) { // 200 - 299 + if (col) *col = C_GREEN; + if (happiness) strcpy(happiness, "Pleased"); + return PL_PLEASED; + } else if (piety <= 399) { // 300 - 399 + if (col) *col = C_BOLDGREEN; + if (happiness) strcpy(happiness, "Delighted"); + return PL_DELIGHTED; + } else { // 400+ + if (col) *col = C_BOLDCYAN; + if (happiness) strcpy(happiness, "Ecstatic"); + return PL_ECSTATIC; + } + return PL_INDIFFERENT; +} + +lifeform_t *godappears(enum RACE rid, cell_t *where) { + lifeform_t *god; + char killedname[BUFLEN],godname[BUFLEN]; + god = findgod(rid); + real_getlfname(god, godname, B_FALSE); + strcpy(killedname, ""); + if (!where) { + // somewhere next to the player. + where = getrandomadjcell(player->cell, WE_WALKABLE, B_FALSE); + if (!where) { + where = getrandomadjcell(where, B_FALSE, B_FALSE); + } + } + + // now see if anyone is there. + if (where->lf) { + // kill them. + getlfname(where->lf, killedname); + killlf(where->lf); + if (where->type->solid) { + setcelltype(where, where->habitat->emptycelltype); + } + teleportto(god, where, B_TRUE); + if (haslos(player, where) && strlen(killedname)) { + msg("%s transforms into %s!", killedname, godname); + } + + } else { + teleportto(god, where, B_TRUE); + } + + return god; +} + +// maybe get a gift +int godgiftmaybe(enum RACE rid) { + lifeform_t *god; + int piety,gotgift = B_FALSE; + god = findgod(rid); + piety = getpiety(rid); + + if (piety >= 100) { + int chance; + // ie. 100 -> 2% + // ie. 500 -> 10% + chance = piety / 5; + if (pctchance(chance)) { // if this is true, you get a gift. + char obtogive[BUFLEN]; + int rollagain = B_TRUE; + gotgift = B_TRUE; + + godsay(god->race->id, "I bestow a gift upon you, mortal!"); + strcpy(obtogive, ""); + switch (god->race->id) { + case R_GODDEATH: + while (rollagain) { + flag_t *f; + object_t *wep; + rollagain = B_FALSE; + switch (rnd(5,5)) { + case 1: + sprintf(obtogive, "3-5 cursed potions of water"); + break; + case 2: + sprintf(obtogive, "cursed branded weapon"); + break; + case 3: // poison your weapon + wep = getweapon(player); + if (wep && canbepoisoned(wep->type->id)) { + applyobmod(wep, findobmod(OM_POISONED)); + msg("A layer of venom covers your weapon!"); + } else { + rollagain = B_TRUE; + } + break; + case 4: // resistant/immune to necrotic + if (lfhasflagval(player, F_DTRESIST, DT_NECROTIC, NA, NA, NULL)) { + if (lfhasflagval(player, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL)) { + rollagain = B_TRUE; + } else { + f = addtempflag(player->flags, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL, FROMGODGIFT); + f->obfrom = god->race->id; + } + } else { + f = addtempflag(player->flags, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL, FROMGODGIFT); + f->obfrom = god->race->id; + } + break; + case 5: + msg("\"Go forth and kill in my name!\""); + setrace(player, R_VAMPIRE, B_TRUE); + break; + } + } + break; + case R_GODGREED: + break; + case R_GODMERCY: + while (rollagain) { + flag_t *f; + rollagain = B_FALSE; + switch (rnd(1,4)) { + case 1: + sprintf(obtogive, "3 vials of ambrosia"); + break; + case 2: + sprintf(obtogive, "ring of regeneration"); + break; + case 3: + sprintf(obtogive, "ring of miracles"); + break; + case 4: // immune to disease / 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; + } + } + break; + default: + break; + } // end switch + + if (strlen(obtogive)) { + object_t *o; + o = addob(player->cell->obpile, obtogive); + if (o) { + char buf[BUFLEN]; + addflag(o->flags, F_GODGIFT, god->race->id, NA, NA, NULL); + if (haslos(player, player->cell)) { + getobname(o, buf, o->amt); + msg("%s appear%s!", buf, (o->amt == 1) ? "s" : ""); + } else { + msg("You hear something hitting the ground."); + } + } + } + + // since you got a gift, lower piety back to 100 (slightly pleased) + setpiety(rid, 101); + } // end if (pctchance enough to get a gift) + } // end if (piety > 100) + return gotgift; +} + +int godisangry(enum RACE rid) { + if (getpiety(rid) < 0) { + return B_TRUE; + } + return B_FALSE; +} + +void godsay(enum RACE rid, char *format, ...) { + lifeform_t *god; + char godname[BUFLEN], buf[BUFLEN]; + va_list args; + + va_start(args, format); + vsprintf( buf, format, args ); + va_end(args); + + god = findgod(rid); + real_getlfname(god, godname, B_FALSE); + + msg("%s%s voice booms out from the heavens:", godname, getpossessive(godname)); more(); + msg("\"%s\"", buf); +} + +void modpiety(enum RACE rid, int amt) { + lifeform_t *god; + flag_t *f; + god = findgod(rid); + f = lfhasflag(god, F_PIETY); + if (!f) return; + f->val[0] += amt; + + limit(&f->val[0], PIETY_MIN, PIETY_MAX); +} + +void pleasegod(enum RACE rid, int amt) { + lifeform_t *lf; + char lfname[BUFLEN]; + lf = findgod(rid); + real_getlfname(lf, lfname, B_FALSE); + + modpiety(rid, amt); + + // announce + //msg("You feel like %s approves of your actions.", lfname); + godgiftmaybe(rid); +} + +void pleasegodmaybe(enum RACE rid, int amt) { + enum PIETYLEV modplev; + int chance; + modplev = abs(getpietylev(rid, NULL, NULL)); + + // the angrier or more happy the god gets, the harder it + // is to please them. + // ie. INDIFFERENT = 1 in 1 (always) + // ie. PLEASED/TOLERATED = 1 in 2 + // ie. DELIGHTED/ANGRY = 1 in 3 + // ie. ECSTATIC/FURIOUS = 1 in 4 + // ie. ENRAGED = 1 in 5 + chance = modplev + 1; + if (onein(chance)) { + pleasegod(rid, amt); + } +} + +int prayto(lifeform_t *lf, lifeform_t *god) { + int piety,i; + + taketime(lf, getactspeed(lf)); + + // this will return 100 if we haven't prayed to this + // god before + piety = getpiety(god->race->id); + + // remember that we have now prayed to this god. + // ie. player is expected to follow the god's rules. + if (!hasflag(god->flags, F_PRAYEDTO)) { + addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL); + } + + if (godisangry(god->race->id)) { + // get even more angry + angergod(god->race->id, PIETYPRAYLOSS); + + return B_FALSE; + } else if (piety <= 99) { + // piety between 0 and 99 = ignored + //godsay(god->race->id, "Stop pestering me!"); + angergod(god->race->id, 0); + modpiety(god->race->id, -30); + return B_FALSE; + } + + // if we get here, piety is >= 100. + // you get some help... + godsay(god->race->id, "You appear in need of assistance, mortal!"); + switch (god->race->id) { + lifeform_t *l; + int donesomething = B_FALSE; + case R_GODDEATH: + msg("\"Behold, the power of death!\""); + for (l = lf->cell->map->lf ; l ; l = l->next) { + if ((l != lf) && lfhasflagval(l, F_TARGETLF, lf->id, NA, NA, NULL)) { + castspell(god, OT_S_PAIN, l, NULL, l->cell); + } + } + dospelleffects(god, OT_S_ANIMATEDEAD, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + dospelleffects(god, OT_S_DARKNESS, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + break; + case R_GODGREED: + if (isinbattle(lf)) { + lifeform_t *l; + int donesomething = B_FALSE; + if (isbleeding(lf)) { + // teleport away + msg("\"Nothing like a quick getaway!\""); + dospelleffects(NULL, OT_S_DISPERSAL, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE); + } else { + // 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)) { + object_t *wep; + // confiscate their weapon + wep = getweapon(l); + if (wep) { + if (!donesomething) { + msg("\"I'll take that...\""); + donesomething = B_TRUE; + } + castspell(god, OT_S_CONFISCATE, l, wep, l->cell); + } + } + } + } + if (!donesomething) { + // teleport away + msg("\"Nothing like a quick getaway!\""); + dospelleffects(NULL, OT_S_DISPERSAL, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE); + } + } else { + int i,first = B_TRUE; + msg("\"Allow me to reveal your surroundings...\""); + dospelleffects(lf, OT_S_MAPPING, 5, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + dospelleffects(lf, OT_S_REVEALHIDDEN, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + // unlock doors + for (i = 0; i < player->nlos; i++) { + cell_t *c; + object_t *o; + c = player->los[i]; + o = hascloseddoor(c); + if (o && hasflag(o->flags, F_LOCKED)) { + if (first) { + msg("\"Access granted!\""); + first = B_FALSE; + } + killflagsofid(o->flags, F_LOCKED); + noise(c, NULL, NC_OTHER, 2, "the click of a lock.", NULL); + } + } + } + break; + case R_GODMERCY: + if (ispoisoned(lf)) { + msg("\"Let thy body be purged of toxins.\""); + castspell(god, OT_S_CUREPOISON, player, NULL, player->cell); + donesomething = B_TRUE; + } + if (gethungerlevel(gethungerval(player)) >= H_PECKISH) { + msg("\"Let thy stomach be satisfied.\""); + castspell(god, OT_S_SATEHUNGER, player, NULL, player->cell); + donesomething = B_TRUE; + } + if (isbleeding(lf) || !donesomething) { + msg("\"Let thy wounds be healed.\""); + castspell(god, OT_S_HEALINGMAJ, player, NULL, player->cell); + donesomething = B_TRUE; + } + break; + default: + break; + } + + + if (!godgiftmaybe(god->race->id)) { + // if you didn't get a gift, lower piety for this god + modpiety(god->race->id, -PIETYPRAYLOSS); + } + + // lower piety for other related gods + for (i = 0; i < ngodlfs; i++) { + if (godlf[i] != god) { + modpiety(godlf[i]->race->id, -25); + } + } + + return B_FALSE; +} + + +void setpiety(enum RACE rid, int amt) { + lifeform_t *god; + flag_t *f; + god = findgod(rid); + f = lfhasflag(god, F_PIETY); + if (!f) return; + f->val[0] = amt; + limit(&f->val[0], PIETY_MIN, PIETY_MAX); +} diff --git a/god.h b/god.h new file mode 100644 index 0000000..e4dbee9 --- /dev/null +++ b/god.h @@ -0,0 +1,16 @@ +#include "defs.h" + +void angergod(enum RACE rid, int amt); +void angergodmaybe(enum RACE rid, int amt); +lifeform_t *findgod(enum RACE rid); +int getpiety(enum RACE rid); +enum PIETYLEV getpietylev(enum RACE rid, enum COLOUR *col, char *happiness); +lifeform_t *godappears(enum RACE rid, cell_t *where); +int godgiftmaybe(enum RACE rid); +int godisangry(enum RACE rid); +void godsay(enum RACE rid, char *format, ...); +void modpiety(enum RACE rid, int amt); +void pleasegod(enum RACE rid, int amt); +void pleasegodmaybe(enum RACE rid, int amt); +int prayto(lifeform_t *lf, lifeform_t *god); +void setpiety(enum RACE rid, int amt); diff --git a/io.c b/io.c index bc02fca..abab4ef 100644 --- a/io.c +++ b/io.c @@ -10,6 +10,7 @@ #include "attack.h" #include "defs.h" #include "flag.h" +#include "god.h" #include "io.h" #include "lf.h" #include "map.h" @@ -41,6 +42,12 @@ extern enum ERROR reason; extern char msghist[MAXHISTORY][BUFLEN]; extern int nmsghist; +extern lifeform_t *godlf[]; +extern int ngodlfs; + +extern flag_t *retflag[]; +extern int nretflags; + extern prompt_t prompt; extern object_t *retobs[MAXPILEOBS+1]; @@ -627,6 +634,14 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src strcat(buf, " the "); strcat(buf, j->name); } + } else if (c->lf->race->raceclass->id == RC_GOD) { + f = lfhasflag(c->lf, F_GODOF); + if (f->val[0] == B_FEMALE) { + strcat(buf, " the Goddess of "); + } else { + strcat(buf, " the God of "); + } + strcat(buf, f->text); } // level /* @@ -1177,7 +1192,7 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { enum SPELLSCHOOL school; school = getspellschoolknown(lf, ot->id); msg("^gYou have learned the %s '%s'.", - (school = SS_MENTAL) ? "psionic power" : "spell", ot->name); + (school == SS_MENTAL) ? "psionic power" : "spell", ot->name); donesomething = B_TRUE; } } @@ -2772,16 +2787,27 @@ void centre(WINDOW *win, enum COLOUR col, int y, char *format, ... ) { char buf[BUFLEN]; va_list args; int startx; + int len; + char *p; + int colcount = 0; va_start(args, format); vsprintf( buf, format, args ); va_end(args); + for (p = buf ; *p ; p++) { + if (*p == '^') colcount++; + } + len = strlen(buf) - (colcount*2); + + w = getmaxx(win); - startx = (w/2) - (strlen(buf)/2); + startx = (w/2) - (len/2); if (startx < 0) startx = 0; if (col != C_NONE) setcol(win, col); - mvwprintw(win, y, (w/2) - (strlen(buf)/2), buf); + wmove(win, y, (w/2) - (len/2)); + textwithcol(win, buf); + //mvwprintw(win, y, (w/2) - (strlen(buf)/2), buf); if (col != C_NONE) unsetcol(win, col); } @@ -2923,6 +2949,60 @@ void cls(void) { statdirty = B_TRUE; } +void describegod(lifeform_t *god) { + char buf[BUFLEN]; + char godname[BUFLEN]; + char goddesc[BUFLEN]; + int i; + flag_t *f; + cls(); + f = hasflag(god->flags, F_GODOF); + + real_getlfname(god, godname, B_FALSE); + sprintf(goddesc, "(%s of %s)", (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); + + // title + sprintf(buf, "God::%s %s",godname, goddesc); + wattron(mainwin, A_BOLD); + mvwprintw(mainwin, 0, 0, buf); + wattroff(mainwin, A_BOLD); + + wmove(mainwin, 2, 0); + wprintw(mainwin, "%s likes ", godname); + getflags(god->flags, F_GODLIKES, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if (i == 0) { + wprintw(mainwin, "%s", f->text); + } else if (i == nretflags - 1) { + wprintw(mainwin, " and %s", f->text); + } else { + wprintw(mainwin, ", %s", f->text); + } + } + wprintw(mainwin, ".\n\n"); + wprintw(mainwin, "%s dislikes ", godname); + getflags(god->flags, F_GODDISLIKES, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if (i == 0) { + wprintw(mainwin, "%s", f->text); + } else if (i == nretflags - 1) { + wprintw(mainwin, " and %s", f->text); + } else { + wprintw(mainwin, ", %s", f->text); + } + } + wprintw(mainwin, ".\n\n"); + + wrefresh(mainwin); + + // wait for key + getch(); + real_clearmsg(B_TRUE); + restoregamewindows(); +} + void describeob(object_t *o) { char buf[BIGBUFLEN]; char buf2[BUFLEN]; @@ -3822,16 +3902,26 @@ void describespell(objecttype_t *ot) { char buf[BUFLEN]; flag_t *f; int i; - int power; + int power,range; cls(); + if (ot->obclass->id == OC_SPELL) { + power = getspellpower(player, ot->id); + } else { + power = -1; + } + + range = getspellrange(ot->id, power); + // title sprintf(buf, "%s",ot->name); capitalise(buf); mvwprintw(mainwin, 0, 0, buf); - mvwprintw(mainwin, 2, 0,"%s\n\n", ot->desc); + wmove(mainwin, 2, 0); + sprintf(buf, "%s\n\n", ot->desc); + textwithcol(mainwin, buf); // properties @@ -3862,12 +3952,15 @@ void describespell(objecttype_t *ot) { wprintw(mainwin, "\n"); if (ot->obclass->id == OC_SPELL) { - power = getspellpower(player, ot->id); if (power > 0) { wprintw(mainwin, "You can cast it at power level %d (maximum %d).\n",power, getspellmaxpower(ot->id)); } else { wprintw(mainwin, "It is too powerful for you to cast (maximum power %d).\n",power, getspellmaxpower(ot->id)); } + + if (range != UNLIMITED) { + wprintw(mainwin, "At this power, it has a range of %d cell%s.",range, (range == 1) ? "" : "s"); + } } wrefresh(mainwin); @@ -4085,7 +4178,7 @@ void docomms(lifeform_t *lf) { fightback(lf, player); } else if (i <= 10) { // limited wish // change to a god. - setrace(lf, R_GODGREED, B_TRUE); + lf = godappears(R_GODGREED, lf->cell); // behold! i am me! say(lf, "Behold mortal! It is I, Avamon!", SV_SHOUT); say(lf, "For your selfless act, I grant you a wish.", SV_TALK); @@ -5163,16 +5256,11 @@ void dofire(void) { } } -void dohelp(void) { +void dohelp(char helpmode) { int h; int y; command_t *c; char *ftext= "%c %s"; - enum { - HM_COMMANDS, - HM_MAIN, - HM_SKILLS, - } helpmode = HM_MAIN; int done = B_FALSE; int ch; @@ -5180,25 +5268,24 @@ void dohelp(void) { while (!done) { cls(); - if (helpmode == HM_MAIN) { + if (helpmode == '?') { initprompt(&prompt, "What would you like help with (ESC when done)?"); addchoice(&prompt, '?', "Keyboard Commands", NULL, NULL); - addchoice(&prompt, 's', "Skill Descriptions", NULL, NULL); + addchoice(&prompt, 'g', "Skill Descriptions", NULL, NULL); + addchoice(&prompt, 's', "God Descriptions", NULL, NULL); addchoice(&prompt, '-', "(done)", NULL, NULL); prompt.maycancel = B_TRUE; ch = getchoice(&prompt); switch (ch) { case '?': - helpmode = HM_COMMANDS; - break; + case 'g': case 's': - helpmode = HM_SKILLS; - break; + helpmode = ch; default: done = B_TRUE; break; } - } else if (helpmode == HM_COMMANDS) { + } else if (helpmode == 'c') { centre(mainwin,C_WHITE, 0, "KEYBOARD COMMAND REFERENCE"); y = 2; @@ -5213,8 +5300,8 @@ void dohelp(void) { } centre(mainwin,C_WHITE, h-1, "[Press any key]"); getch(); - helpmode = HM_MAIN; - } else if (helpmode == HM_SKILLS) { + done = B_TRUE; + } else if (helpmode == 's') { skill_t *sk; centre(mainwin,C_WHITE, 0, "SKILL REFERENCE"); y = 2; @@ -5227,11 +5314,40 @@ void dohelp(void) { prompt.maycancel = B_TRUE; ch = getchoicestr(&prompt, B_FALSE, B_TRUE); if (!ch) { - helpmode = HM_MAIN; + done = B_TRUE; } else { sk = (skill_t *)prompt.result; describeskill(sk->id); } + } else if (helpmode == 'g') { + int i; + centre(mainwin,C_WHITE, 0, "SKILL REFERENCE"); + y = 2; + + initprompt(&prompt, "Describe which god (ESC when done)?"); + for (i = 0 ; i < ngodlfs; i++) { + lifeform_t *lf; + flag_t *f; + char buf[BUFLEN]; + char godof[BUFLEN]; + lf = godlf[i]; + real_getlfname(lf, buf, B_FALSE); + f = hasflag(lf->flags, F_GODOF); + sprintf(godof, " (%s of %s)", (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); + strcat(buf, godof); + addchoice(&prompt, 'a', buf, NULL, lf); + } + + addchoice(&prompt, '\0', "(done)", NULL, NULL); + prompt.maycancel = B_TRUE; + ch = getchoicestr(&prompt, B_FALSE, B_TRUE); + if (!ch) { + done = B_TRUE; + } else { + lifeform_t *god; + god = (lifeform_t *)prompt.result; + describegod(god); + } } } @@ -6696,7 +6812,7 @@ void handleinput(void) { doinventory(player->pack); break; case '?': // help - dohelp(); + dohelp('?'); break; case '@': // display player stats showlfstats(player, B_FALSE); @@ -7637,8 +7753,9 @@ void showlfstats(lifeform_t *lf, int showall) { } if (showall) { - sprintf(prompt, "[@=stats S=skills/abilities M=magic E=effects %sESC=quit]", isplayer(lf) ? "" : "I=items " ); - sprintf(cmdchars, "@asme%s",isplayer(lf) ? "" : "i"); + sprintf(prompt, "^h[^W@^n=stats ^WS^nkills ^WA^nbils ^WM^nagic ^WE^nffects ^WG^nods %s^W?^n=help ^WESC^n=quit^h]", + isplayer(lf) ? "" : "^WI^ntems " ); + sprintf(cmdchars, "@asmeg%s",isplayer(lf) ? "" : "i"); } else { sprintf(prompt, "%s", "[ESC=quit]"); sprintf(cmdchars, "%s", "@"); @@ -8457,10 +8574,7 @@ void showlfstats(lifeform_t *lf, int showall) { } } - } else if (mode == 's') { - int exitnow = B_FALSE; - - // now show intrinsics on next page + } else if (mode == 'a') { centre(mainwin, C_WHITE, 0, "ABILITIES"); y = 2; @@ -8505,7 +8619,6 @@ void showlfstats(lifeform_t *lf, int showall) { wprintw(mainwin, buf); if (downline(&y, h, "ABILITIES", NULL, prompt, cmdchars, &ch)) { - exitnow = B_TRUE; break; } } @@ -8513,55 +8626,55 @@ void showlfstats(lifeform_t *lf, int showall) { } y++; - if (!exitnow) { - char skilltitle[BUFLEN]; - flag_t *known[MAXSKILLS], *available[MAXSKILLS]; - int numknown = 0, numavailable = 0; - int n; - enum SKILLLEVEL slev; + } else if (mode == 's') { + char skilltitle[BUFLEN]; + flag_t *known[MAXSKILLS], *available[MAXSKILLS]; + int numknown = 0, numavailable = 0; + int n; + enum SKILLLEVEL slev; - // get available skills + centre(mainwin, C_WHITE, 0, "SKILLS"); + + // get available skills + for (f = lf->flags->first ; f ; f = f->next) { + if (f->id == F_CANLEARN) { + if (!getskill(lf, f->val[0])) { + available[numavailable++] = f; + } + } + } + // get known skills, in order + for (slev = PR_MASTER ; slev >= PR_NOVICE; slev--) { for (f = lf->flags->first ; f ; f = f->next) { - if (f->id == F_CANLEARN) { - if (!getskill(lf, f->val[0])) { - available[numavailable++] = f; - } + if ((f->id == F_HASSKILL) && (f->val[1] == slev)) { + known[numknown++] = f; } } - // get known skills, in order - for (slev = PR_MASTER ; slev >= PR_NOVICE; slev--) { - for (f = lf->flags->first ; f ; f = f->next) { - if ((f->id == F_HASSKILL) && (f->val[1] == slev)) { - known[numknown++] = f; - } - } + } + + + //centre(mainwin, y, "SKILLS"); y ++; + sprintf(skilltitle, "%-40s%-40s","AVAILABLE SKILLS", "KNOWN SKILLS"); + doheading(mainwin, &y, 0, skilltitle); + for (n = 0; n < MAXOF(numknown,numavailable); n++) { + if (n < numavailable) { + setcol(mainwin, C_RED); + mvwprintw(mainwin, y, 0, "- %s", + getskillname(available[n]->val[0]) ); + unsetcol(mainwin, C_RED); } - - - //centre(mainwin, y, "SKILLS"); y ++; - sprintf(skilltitle, "%-40s%-40s","AVAILABLE SKILLS", "KNOWN SKILLS"); - doheading(mainwin, &y, 0, skilltitle); - for (n = 0; n < MAXOF(numknown,numavailable); n++) { - if (n < numavailable) { - setcol(mainwin, C_RED); - mvwprintw(mainwin, y, 0, "- %s", - getskillname(available[n]->val[0]) ); - unsetcol(mainwin, C_RED); - } - if (n < numknown) { - setcol(mainwin, getskilllevelcolour(known[n]->val[1])); - mvwprintw(mainwin, y, 40, "%c %s (%s)%s", - ismaxedskill(lf, known[n]->val[0]) ? '*' : '-', - getskillname(known[n]->val[0]), - getskilllevelname(known[n]->val[1]), - //ismaxedskill(lf, known[n]->val[0]) ? "/MAX" : "", - (known[n]->lifetime == FROMSPELL) ? "[spell]" : ""); - unsetcol(mainwin, getskilllevelcolour(known[n]->val[1])); - } - if (downline(&y, h, "SKILLS", skilltitle, prompt, cmdchars, &ch)) { - exitnow = B_TRUE; - break; - } + if (n < numknown) { + setcol(mainwin, getskilllevelcolour(known[n]->val[1])); + mvwprintw(mainwin, y, 40, "%c %s (%s)%s", + ismaxedskill(lf, known[n]->val[0]) ? '*' : '-', + getskillname(known[n]->val[0]), + getskilllevelname(known[n]->val[1]), + //ismaxedskill(lf, known[n]->val[0]) ? "/MAX" : "", + (known[n]->lifetime == FROMSPELL) ? "[spell]" : ""); + unsetcol(mainwin, getskilllevelcolour(known[n]->val[1])); + } + if (downline(&y, h, "SKILLS", skilltitle, prompt, cmdchars, &ch)) { + break; } } } else if (mode == 'm') { @@ -9155,6 +9268,82 @@ void showlfstats(lifeform_t *lf, int showall) { } else { mvwprintw(mainwin, y, 0, "It is not carrying anything."); } + } else if (mode == 'g') { + int i; + char line[BUFLEN]; + cls(); + centre(mainwin, C_WHITE, 0, "GODS"); + y = 2; + + sprintf(line, "%-26s Prayed? %-22s %s","God","Piety", "Happiness"); + + doheading(mainwin, &y, 0, line); + + for (i = 0; i < ngodlfs; i++) { + lifeform_t *god; + flag_t *f; + char godname[BUFLEN]; + char pietybuf[BUFLEN]; + char happiness[BUFLEN]; + int prayedto = B_FALSE; + char colbuf[3]; + enum PIETYLEV plev; + enum COLOUR col; + int piety,n; + god = godlf[i]; + real_getlfname(god, godname, B_FALSE); + f = lfhasflag(god, F_GODOF); + if (f->val[0] == B_FEMALE) { + strcat(godname, " (Goddess of "); + } else { + strcat(godname, " (God of "); + } + strcat(godname, f->text); + strcat(godname, ")"); + + if (lfhasflag(god, F_PRAYEDTO)) { + prayedto = B_TRUE; + } + + piety = getpiety(god->race->id); + plev = getpietylev(god->race->id, &col, happiness); + + // TODO: construct piety buf [---+--|--------------------] + + + sprintf(pietybuf, "^n["); + switch (col) { + case C_ORANGE: strcpy(colbuf, "^T"); break; + case C_RED: strcpy(colbuf, "^B"); break; + case C_YELLOW: strcpy(colbuf, "^w"); break; + case C_BROWN: strcpy(colbuf, "^b"); break; + case C_GREEN: strcpy(colbuf, "^g"); break; + case C_BOLDGREEN: strcpy(colbuf, "^G"); break; + case C_BOLDCYAN: strcpy(colbuf, "^E"); break; + default: case C_GREY: strcpy(colbuf, "^n"); break; + } + + strcat(pietybuf, colbuf); + + for (n = PIETY_MIN; n <= PIETY_MAX; n += 25) { + if ((piety >= n) && (piety < n+25)){ + strcat(pietybuf, "+"); + } else { + strcat(pietybuf, "-"); + } + } + strcat(pietybuf, "^n]"); + sprintf(line, "%s%-26s%-9s%s %s%s", + prayedto ? "^g" : "^n", + godname, + prayedto ? " Yes " : " No ", + pietybuf, colbuf, happiness); + + + wmove(mainwin, y, 0); + textwithcol(mainwin, line); + y++; + } } // wait for key @@ -9169,14 +9358,23 @@ void showlfstats(lifeform_t *lf, int showall) { case '@': mode = ch; break; - case 's': - case 'm': + case 'a': case 'e': + case 'g': + case 'm': + case 's': if (showall) mode = ch; break; case 'i': if (showall && !isplayer(lf)) mode = ch; break; + case '?': + if (mode == 'g') { // help on gods + dohelp('g'); + } else if (mode == 's') { // help on skills + dohelp('s'); + } + break; default: break; } diff --git a/io.h b/io.h index c2ab09b..173a297 100644 --- a/io.h +++ b/io.h @@ -33,6 +33,7 @@ void real_clearmsg(int force); void clearretobs(void); void cls(void); int contains(enum OBCLASS *array, int nargs, enum OBCLASS want); +void describegod(lifeform_t *god); void describeob(object_t *o); void describeskill(enum SKILL skid); void describespell(objecttype_t *ot); @@ -45,7 +46,7 @@ void doenter(lifeform_t *lf); void doexplain(char *question); void dofinaloblist(obpile_t *op); void dofire(void); -void dohelp(void); +void dohelp(char helpmode); void doinventory(obpile_t *op); void doknowledgelist(void); void dolook(cell_t *where, int onpurpose); diff --git a/lf.c b/lf.c index 2e56214..51be126 100644 --- a/lf.c +++ b/lf.c @@ -8,6 +8,7 @@ #include "attack.h" #include "defs.h" #include "flag.h" +#include "god.h" #include "io.h" #include "lf.h" #include "map.h" @@ -19,6 +20,7 @@ extern FILE *logfile; + extern map_t *firstmap; extern race_t *firstrace, *lastrace; extern raceclass_t *firstraceclass, *lastraceclass; @@ -53,6 +55,9 @@ extern int loading; extern int obdb; +extern lifeform_t *godlf[]; +extern int ngodlfs; + extern enum ERROR reason; @@ -70,9 +75,6 @@ void autoweild(lifeform_t *lf) { pretimespent = lf->timespent; - if (isplayer(lf)) { - dblog("xxx"); - } // weild weapons if required bestwep = getbestweapon(lf); if (bestwep) { @@ -1671,6 +1673,25 @@ void die(lifeform_t *lf) { } } + if (lf->race->id == R_VAMPIRE) { + // if are asleep, we will die normally + if (lfhasflag(lf, F_ASLEEP)) { + noise(lf->cell, lf, NC_OTHER, 4, "a horrified scream!", "screams in horror!"); + } else if (findobinmap(lf->cell->map, OT_COFFIN)) { // coffin around? + // restore 1 hp + lf->hp = 1; + killflagsofid(lf->flags, F_DEAD); + // convert into a gas cloud! + dospelleffects(NULL, OT_S_GASEOUSFORM, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + // ai will now look for our coffin + if (!isplayer(lf)) { + addflag(lf->flags, F_WANTS, OT_COFFIN, B_COVETS, NA, NULL); + addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + } + return; + } + } + // revert to your original form first. if (lfhasflag(lf, F_POLYMORPHED)) { if (lfhasflag(lf, F_ORIGRACE)) { @@ -1816,6 +1837,11 @@ void die(lifeform_t *lf) { colourmatchob(corpse, lf); } + // corpse of a player pet? + if (ispetof(lf, player)) { + addflag(corpse->flags, F_PETOF, player->id, NA, NA, NULL); + } + // add extra flags ? getflags(lf->flags, F_CORPSEFLAG, F_NONE); for (i = 0; i < nretflags; i++) { @@ -2477,6 +2503,12 @@ int eat(lifeform_t *lf, object_t *o) { stopeating = B_TRUE; } + + if (isplayer(lf) && hasflagval(o->flags, F_PETOF, player->id, NA, NA, NULL)) { + angergodmaybe(R_GODMERCY, 150); + stopeating = B_TRUE; + } + // stop eating if we are full if (!stopeating && !fullyeaten && (posthlev != hlev) && (posthlev <= H_FULL)) { if (isplayer(lf) && (posthlev != H_STUFFED)) { @@ -3239,9 +3271,12 @@ int flee(lifeform_t *lf) { if (f->lifetime == PERMENANT) { killflag(f); } else { - // if the flag is temporary, wait for it to time out normally + // if the flag is temporary, keep fleeing and wait for it to time out normally fleefrom = thisone; } + if (isplayer(thisone)) { + pleasegodmaybe(R_GODMERCY, 5); + } } } } @@ -4656,7 +4691,7 @@ int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions) { } int getnightvisrange(lifeform_t *lf) { - int range = 1; // default + int range = 1,i; // default flag_t *f; f = lfhasflag(lf, F_SEEINDARK); @@ -4672,6 +4707,16 @@ int getnightvisrange(lifeform_t *lf) { return f->val[0]; } } + // modifications? + getflags(lf->flags, F_NIGHTVISRANGEMOD, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if (f->id == F_NIGHTVISRANGEMOD) { + range += f->val[0]; + } + } + + if (range < 0) range = 0; return range; } @@ -5546,7 +5591,7 @@ race_t *getrandomrace(cell_t *c, int forcedepth) { if (forcedepth != NA) { depth = forcedepth; } else { - depth = getmapdifficulty(c ? c->map : NULL); + depth = getmapdifficulty((c && c->map) ? c->map : NULL); } getrarityrange(depth, &raritymin, &raritymax, RARITYVARIANCELF, B_TRUE); @@ -7898,6 +7943,7 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SEWING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SPEECH, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SS_AIR, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SS_DEATH, NA, NA, NULL); @@ -7941,6 +7987,7 @@ void initjobs(void) { void initrace(void) { flag_t *f; + objecttype_t *ot; // race classes addraceclass(RC_OTHER, "misc. creature", "miscellaneous creatures", SK_NONE); addraceclass(RC_ANIMAL, "animal", "animals and insects", SK_LORE_NATURE); @@ -8135,6 +8182,38 @@ void initrace(void) { addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); // gods + addrace(R_GODDEATH, "Hecta", 100, '@', C_BOLDMAGENTA, MT_FLESH, RC_GOD); + addflag(lastrace->flags, F_STARTATT, A_STR, NA, NA, "16"); + addflag(lastrace->flags, F_STARTATT, A_DEX, NA, NA, "20"); + addflag(lastrace->flags, F_STARTATT, A_WIS, NA, NA, "15"); + addflag(lastrace->flags, F_STARTATT, A_IQ, NA, NA, "18"); + addflag(lastrace->flags, F_STARTATT, A_CON, NA, NA, "6"); + addflag(lastrace->flags, F_STARTATT, A_CHA, NA, NA, "6"); + addflag(lastrace->flags, F_STARTASLEEPPCT, 0, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 30, NA, NA, NULL); + addflag(lastrace->flags, F_UNIQUE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "2d6"); + addflag(lastrace->flags, F_HASATTACK, OT_TOUCHPARALYZE2, NA, NA, "1d6"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "cloak of shadows"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "bone helmet"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "10-20 bones"); + //addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_SPELLCASTING, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_SS_DEATH, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "extends a skeletal finger"); + // god abilities + addflag(lastrace->flags, F_GODOF, NA, NA, NA, "Death"); + addflag(lastrace->flags, F_FLEEONHPPCT, 10, NA, NA, NULL); + // may cast all death spells + for (ot = objecttype ; ot ; ot = ot->next) { + if ((ot->obclass->id == OC_SPELL) && (getspellschool(ot->id) == SS_DEATH)) { + addflag(lastrace->flags, F_CANWILL, ot->id, NA, NA, "pw:10;"); + } + } + addflag(lastrace->flags, F_CANWILL, OT_S_WISHLIMITED, NA, NA, "pw:10;"); + addflag(lastrace->flags, F_CANWILL, OT_S_PLANESHIFT, NA, NA, "pw:1;"); + addrace(R_GODGREED, "Avamon", 300, '@', C_BOLDMAGENTA, MT_FLESH, RC_GOD); addflag(lastrace->flags, F_STARTATT, A_STR, NA, NA, "20"); addflag(lastrace->flags, F_STARTATT, A_DEX, NA, NA, "10"); @@ -8149,23 +8228,64 @@ void initrace(void) { addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_SLOW, NA, NA, NULL); addflag(lastrace->flags, F_HASATTACK, OT_FISTS, NA, NA, "1d2"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "+5 dagger of sharpness"); addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed ring of hunger"); addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "10 huge bags of holding"); //addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_RARITY, NA, NA, NULL); // ie. everything - addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTSKILL, SK_THIEVERY, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_STEALTH, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_BACKSTAB, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_THROWING, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_STARTSKILL, SK_CLIMBING, PR_MASTER, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "waves his hand"); // god abilities - addflag(lastrace->flags, F_GODOF, NA, NA, NA, "Greed"); - addflag(lastrace->flags, F_NOFLEE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_GODOF, NA, NA, NA, "Thieves"); + addflag(lastrace->flags, F_FLEEONHPPCT, 10, NA, NA, NULL); addflag(lastrace->flags, F_OBESE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_HIDE, NA, NA, "pw:10;"); addflag(lastrace->flags, F_CANWILL, OT_S_CALLWIND, NA, NA, "pw:10;"); addflag(lastrace->flags, F_CANWILL, OT_S_CONFISCATE, NA, NA, "pw:10;"); addflag(lastrace->flags, F_CANWILL, OT_S_WISHLIMITED, NA, NA, "pw:10;"); addflag(lastrace->flags, F_CANWILL, OT_S_HUNGER, NA, NA, "pw:1;"); addflag(lastrace->flags, F_CANWILL, OT_S_PLANESHIFT, NA, NA, "pw:1;"); + addrace(R_GODMERCY, "Yumi", 300, '@', C_BOLDMAGENTA, MT_FLESH, RC_GOD); + addflag(lastrace->flags, F_FLEEONHPPCT, 95, NA, NA, ""); + addflag(lastrace->flags, F_STARTATT, A_STR, NA, NA, "10"); + addflag(lastrace->flags, F_STARTATT, A_DEX, NA, NA, "10"); + addflag(lastrace->flags, F_STARTATT, A_WIS, NA, NA, "18"); + addflag(lastrace->flags, F_STARTATT, A_IQ, NA, NA, "17"); + addflag(lastrace->flags, F_STARTATT, A_CON, NA, NA, "10"); + addflag(lastrace->flags, F_STARTATT, A_CHA, NA, NA, "17"); + addflag(lastrace->flags, F_STARTASLEEPPCT, 0, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 50, NA, NA, NULL); + addflag(lastrace->flags, F_UNIQUE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_FISTS, NA, NA, "1d2"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "10 blessed vials of ambrosia"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "2 rings of regeneration"); + addflag(lastrace->flags, F_STARTSKILL, SK_FIRSTAID, PR_MASTER, NA, NULL); + addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "raises her hand"); + // god abilities + addflag(lastrace->flags, F_GODOF, B_FEMALE, NA, NA, "Mercy"); + addflag(lastrace->flags, F_FLEEONHPPCT, 10, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_S_CUREPOISON, NA, NA, "pw:10;"); + addflag(lastrace->flags, F_CANWILL, OT_S_HEALINGMAJ, NA, NA, "pw:10;"); + addflag(lastrace->flags, F_CANWILL, OT_S_WISHLIMITED, NA, NA, "pw:10;"); + addflag(lastrace->flags, F_CANWILL, OT_S_SATEHUNGER, NA, NA, "pw:10;"); + addflag(lastrace->flags, F_CANWILL, OT_S_DISPERSAL, NA, NA, "pw:10;"); + addflag(lastrace->flags, F_CANWILL, OT_S_SLEEP, NA, NA, "pw:10;"); + addflag(lastrace->flags, F_CANWILL, OT_S_PLANESHIFT, NA, NA, "pw:1;"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "the successful casting of healing spells"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "allowing fleeing creatures to escape"); + 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, "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"); + + // monsters addrace(R_BEHOLDER, "beholder", 5, 'e', C_MAGENTA, MT_FLESH, RC_MAGIC); addflag(lastrace->flags, F_STARTATT, A_IQ, AT_VHIGH, NA, NULL); @@ -9170,8 +9290,7 @@ void initrace(void) { addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, AT_LOW, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL); - addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6"); - addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6"); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "2d6"); addflag(lastrace->flags, F_REGENERATES, 2, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); @@ -10280,7 +10399,8 @@ void initrace(void) { addflag(lastrace->flags, F_HITDICE, 4, 2, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); - addflag(lastrace->flags, F_HASATTACK, OT_TOUCHPARALYZE2, NA, NA, "0d1+0"); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6"); + addflag(lastrace->flags, F_HASATTACK, OT_TOUCHPARALYZE2, NA, NA, "1d1"); addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL); addflag(lastrace->flags, F_LEVITATING, B_TRUE, NA, NA, NULL); @@ -10321,7 +10441,7 @@ void initrace(void) { addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL); addflag(lastrace->flags, F_ACTIONSPEED, SP_SLOW, NA, NA, ""); addflag(lastrace->flags, F_HASATTACK, OT_TEETH, NA, NA, "1d5+3"); - addflag(lastrace->flags, F_HASATTACK, OT_TOUCHPARALYZE, NA, NA, "0d1+0"); + addflag(lastrace->flags, F_HASATTACK, OT_TOUCHPARALYZE, NA, NA, "1d1"); addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL); addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); @@ -10329,6 +10449,35 @@ void initrace(void) { addflag(lastrace->flags, F_SEEINDARK, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); + addrace(R_VAMPIRE, "vampire", 75, 'V', C_BLUE, MT_FLESH, RC_UNDEAD); + addflag(lastrace->flags, F_STARTATT, A_STR, AT_EXHIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_DEX, AT_VHIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_WIS, AT_GTAVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, AT_EXHIGH, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CON, AT_AVERAGE, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_CHA, AT_VHIGH, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, RR_RARE, NULL); + addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_SEEINVIS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_FAST, NA, NA, NULL); + addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 8, 3, NA, NULL); + addflag(lastrace->flags, F_ARMOURRATING, 5, NA, NA, NULL); + addflag(lastrace->flags, F_EVASION, -10, NA, NA, NULL); + addflag(lastrace->flags, F_HOMEOB, NA, NA, NA, "coffin"); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6+4"); + addflag(lastrace->flags, F_CANWILL, OT_S_CHARM, 3, 3, "pw:6;"); + addflag(lastrace->flags, F_CANWILL, OT_A_CHARGE, 5, 5, "range:3;"); + addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "pile of ash"); + // special: change to gas cloud with 1 hp on death, if not asleep + // TODO: can shapeshift to bat + // TODO: flee from holy symbols / garlic + addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); + // special monsters addrace(R_GASCLOUD, "cloud of gas", 0.1, '}', C_GREY, MT_GAS, RC_OTHER); addflag(lastrace->flags, F_MOVESPEED, SP_ULTRAFAST, NA, NA, ""); @@ -10355,6 +10504,7 @@ void initrace(void) { addflag(lastrace->flags, F_SILENTMOVE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); + // special: fully heal if our origrace is a vampire, and we are resting over a coffin addrace(R_DANCINGWEAPON, "dancing weapon", 0, ')', C_GREY, MT_METAL, RC_OTHER); addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL); @@ -11242,7 +11392,12 @@ void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype) { *amt -= rnd(0,3); } - limit(amt, 0, NA); + if (lf->race->raceclass->id == RC_GOD) { + // immortal + limit(amt, 0, lf->hp-1); + } else { + limit(amt, 0, NA); + } } void makepeaceful(lifeform_t *who) { @@ -11284,6 +11439,9 @@ lifeform_t *makezombie(object_t *o) { where = getoblocation(o); getobname(o, obname, 1); + if (!cellwalkable(NULL, where, NULL)) { + where = getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND); + } lf = addlf(where, r->id, 1); addflag(lf->flags, F_LFSUFFIX, B_TRUE, NA, NA, "zombie"); @@ -11384,6 +11542,16 @@ int areenemies(lifeform_t *lf1, lifeform_t *lf2) { return B_TRUE; } +void age(lifeform_t *lf, int pct) { + lf->maxhp -= pctof(pct,lf->maxhp); + limit(&lf->maxhp, 0, NA); + limit(&lf->hp, NA, lf->maxhp); + if (isplayer(lf)) { + msg("^BYour body ages unnaturally!"); + statdirty = B_TRUE; + } +} + void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o) { flag_t *fromlfflag; lifeform_t *fromlf = NULL; @@ -12462,6 +12630,11 @@ void modhunger(lifeform_t *lf, int amt) { (needexclam) ? '!' : '.'); statdirty = B_TRUE; + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + gethungername(posthlev, buf); + msg("^%c%s looks %s%c", getlfcol(lf, CC_BAD), lfname, buf, (needexclam) ? '!' : '.'); } if ((posthlev >= H_VHUNGRY) && (amt > 0)) { @@ -12472,6 +12645,13 @@ void modhunger(lifeform_t *lf, int amt) { // reset hunger f->val[0] = 0; } + + // ai doesn't get hungry anymore after they have + // satisfied their hunger. + if (!isplayer(lf) && (posthlev <= H_NONE)) { + killflag(f); + return; + } } } } @@ -13644,6 +13824,13 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { } } + if ((lf->race->id == R_GASCLOUD) && (origrace->id == R_VAMPIRE)) { + if (!isplayer(lf)) { + f = lfhasflagval(lf, F_WANTS, OT_COFFIN, NA, NA, NULL); + if (f) killflag(f); + } + } + f = lfhasflagval(lf, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL); if (f) { killflag(f); @@ -13657,7 +13844,8 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { getlfname(lf, buf); f = lfhasflag(lf, F_GODOF); if (f) { - msg("^w%s transforms into %s, the God of %s!", buf, newrace->name, f->text); + msg("^w%s transforms into %s, the %s of %s!", buf, newrace->name, + (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); } else { msg("^w%s transforms into %s %s!", buf, isvowel(newrace->name[0]) ? "an" : "a", newrace->name); } @@ -13798,6 +13986,14 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { moveob(o, lf->cell->obpile, o->amt); } } + + + if (isplayer(lf)) { + needredraw = B_TRUE; + statdirty = B_TRUE; + } else if (cansee(player, lf)) { + needredraw = B_TRUE; + } } // end if gamestarted } @@ -14771,35 +14967,20 @@ void stopsprinting(lifeform_t *lf) { // and make it worth zero xp. // // for unique monsters, they move from their current position. -lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, int randomjobsok, job_t *forcejob, int lifetime, int wantfriendly) { +lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, char *racename, int lifetime, int wantfriendly) { lifeform_t *newlf = NULL; char buf[BUFLEN]; - race_t *r; - r = findrace(rid); - if (r && hasflag(r->flags, F_UNIQUE)) { - // does it already exist? - newlf = findlfunique(rid); - - // if so, move it here, then exit. - if (newlf) { - teleportto(newlf, c, B_FALSE); + newlf = addmonster(c, rid, racename, B_FALSE, 1, B_FALSE, NULL); + if (newlf) { + if (haslos(player, c)) { + //char *newbuf; + getlfnamea(newlf, buf); + capitalise(buf); + msg("%s appears!", buf); } - } - if (!newlf) { - newlf = addmonster(c, rid, randomjobsok, 1, B_FALSE, NULL); - if (newlf) { - // assign job if required - if (forcejob) { - givejob(newlf, forcejob->id); - } - if (haslos(player, c)) { - //char *newbuf; - getlfnamea(newlf, buf); - capitalise(buf); - msg("%s appears!", buf); - } + if (!hasflag(newlf->flags, F_UNIQUE)) { // summoned if (caster) { addflag(newlf->flags, F_SUMMONEDBY, caster->id, lifetime, NA, NULL); @@ -15206,6 +15387,16 @@ void turneffectslf(lifeform_t *lf) { } + // god piety gets restored over time + if (isplayer(lf)) { + for (i = 0; i < ngodlfs; i++) { + if (getpietylev(godlf[i]->race->id, NULL, NULL) == PL_TOLERATED) { + // slowly tick upwards + if (onein(2)) modpiety(godlf[i]->race->id, 1); + } + } + } + if (hasactivespell(lf, OT_S_SUMMONWEAPON)) { if (!hasob(lf->pack, OT_ENERGYBLADE)) { stopspell(lf, OT_S_SUMMONWEAPON); @@ -16345,6 +16536,9 @@ int validateraces(void) { } else if (r->raceclass->id == RC_DEMON) { addflag(r->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); } else if (r->raceclass->id == RC_GOD) { + addflag(r->flags, F_PIETY, 100, NA, NA, NULL); + addflag(r->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + addflag(r->flags, F_MORALE, 10, NA, NA, NULL); addflag(r->flags, F_BREATHWATER, B_TRUE, NA, NA, NULL); addflag(r->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(r->flags, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL); @@ -16536,6 +16730,23 @@ int rest(lifeform_t *lf, int onpurpose) { int resting = B_FALSE; object_t *restob = NULL; + // special case + if ((lf->race->id == R_GASCLOUD) && lfhasflagval(lf, F_ORIGRACE, R_VAMPIRE, NA, NA, NULL)) { + if (hasob(lf->cell->obpile, OT_COFFIN)) { + // restore original form. + abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL); + // restore full hp + lf->hp = lf->maxhp; + // fall asleep for a while + fallasleep(lf, 50); + // mark screen as dirty + needredraw = B_TRUE; + if (isplayer(lf)) { + statdirty = B_TRUE; + } + return B_FALSE; + } + } rf = lfhasflag(lf, F_TRAINING); if (rf) { @@ -16613,10 +16824,13 @@ int rest(lifeform_t *lf, int onpurpose) { } //if (isplayer(lf)) msg("hp given."); - if (lf->hp < lf->maxhp) { + if (isplayer(lf) && (lf->hp < lf->maxhp)) { // pass a skill check to regain hp if (skillcheck(lf, SC_CON, difficulty, getskill(lf, SK_FIRSTAID))) { gainhp(lf, hpheal); + if (isplayer(lf)) { + pleasegodmaybe(R_GODMERCY, 1); + } } } diff --git a/lf.h b/lf.h index 018dd3a..7fa9191 100644 --- a/lf.h +++ b/lf.h @@ -10,6 +10,7 @@ void addskilldesc(enum SKILL id, enum SKILLLEVEL lev, char *text, int wantmsg); void addtrail(lifeform_t *lf, int dir); void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype); void adjustspeedforwater(lifeform_t *lf, int *speed); +void age(lifeform_t *lf, int pct); void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o); int areallies(lifeform_t *lf1, lifeform_t *lf2); int areenemies(lifeform_t *lf1, lifeform_t *lf2); @@ -311,7 +312,7 @@ void stopeating(lifeform_t *lf); void stopresting(lifeform_t *lf); void stoprunning(lifeform_t *lf); void stopsprinting(lifeform_t *lf); -lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, int randomjobsok, job_t *forcejob, int lifetime, int wantfriendly); +lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, char *racename, int lifetime, int wantfriendly); //int testammo(lifeform_t *lf, object_t *o); int takeoff(lifeform_t *lf, object_t *o); void taketime(lifeform_t *lf, long howlong); diff --git a/map.c b/map.c index a2e1f36..db41445 100644 --- a/map.c +++ b/map.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -15,15 +16,20 @@ #include "vault.h" extern habitat_t *firsthabitat,*lasthabitat; +extern job_t *firstjob; extern map_t *firstmap,*lastmap; extern region_t *firstregion,*lastregion; extern regionoutline_t *firstregionoutline,*lastregionoutline; extern regiontype_t *firstregiontype,*lastregiontype; extern celltype_t *firstcelltype, *lastcelltype; extern objecttype_t *objecttype,*lastobjecttype; +extern race_t *firstrace; extern int viewx,viewy,vieww,viewh; extern lifeform_t *player; +extern lifeform_t *godlf[]; +extern int ngodlfs; + extern glyph_t tempglyph; extern enum OBCLASS sortorder[]; @@ -148,10 +154,14 @@ map_t *addmap(void) { // when monsters are made during level generation, autogen will be true. otherwise false; -lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen, int *nadded) { +// if "rid" RR_NONE, paste racename to get the race. +// otherwise just use the given race. +lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int amt, int autogen, int *nadded) { lifeform_t *lf = NULL; race_t *r; int db = B_FALSE; + flagpile_t *wantflags = NULL; + enum JOB wantjob = J_NONE; if (nadded) *nadded = 0; @@ -160,21 +170,31 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto return NULL; } - if ((raceid == R_NONE) || (raceid == R_RANDOM)) { - r = getrandomrace(c, NA); + if (rid != R_SPECIFIED) { + if (rid == R_RANDOM) { + r = getrandomrace(c, NA); + } else { + r = findrace(rid); + } } else { - r = findrace(raceid); + // get params + wantflags = addflagpile(NULL, NULL); + + rid = parserace(racename, wantflags, &wantjob); + + if (rid == R_RANDOM) { + r = getrandomrace(c, NA); + } else { + r = findrace(rid); + } } if (!r) { - r = getreallyrandomrace(RC_ANY); + return NULL; } - - assert(r); - if (db) { char buf[BUFLEN]; - sprintf(buf, "start addmonster for %s",r->name); + sprintf(buf, "addmonster for '%s'->%s",racename, r->name); dbtimestart(buf); } @@ -182,6 +202,18 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto if (r) { if (db) dbtime("doing lf addition"); + + if (hasflag(r->flags, F_UNIQUE)) { + // does it already exist? + lf = findlfunique(rid); + + // if so, move it here, then exit. + if (lf) { + teleportto(lf, c, B_FALSE); + return lf; + } + } + lf = addlf(c, r->id, getrandommonlevel(r, c->map)); if (db) dbtime("finished lf addition"); if (lf) { @@ -191,24 +223,28 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto if (db) dbtime("checking for job"); lf->born = B_FALSE; - if (jobok) { - for (f = lf->flags->first ; f ; f = f->next) { - // has a job? - if (f->id == F_STARTJOB) { - if (rnd(1,100) <= f->val[0]) { - enum JOB wantjob; - if (f->val[1] == J_RANDOM) { - job_t *j; - j = getrandomjob(B_TRUE); - wantjob = j->id; - } else { - wantjob = f->val[1]; + + if (wantjob == J_NONE) { + if (jobok) { + for (f = lf->flags->first ; f ; f = f->next) { + // has a job? + if (f->id == F_STARTJOB) { + if (rnd(1,100) <= f->val[0]) { + if (f->val[1] == J_RANDOM) { + job_t *j; + j = getrandomjob(B_TRUE); + wantjob = j->id; + } else { + wantjob = f->val[1]; + } + givejob(lf, wantjob); + break; } - givejob(lf, wantjob); - break; } } } + } else { + givejob(lf, wantjob); } if (autogen) { @@ -360,11 +396,21 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto } } } - + + if (wantflags) { + // wantflags? + copyflags(lf->flags, wantflags, NA); + } + lf->born = B_TRUE; } // end if lf } + // free mem + if (wantflags) { + killflagpile(wantflags); + } + if (db) dbtimeend("finished addmonster"); return lf; @@ -401,7 +447,7 @@ int addrandomthing(cell_t *c, int obchance, int *nadded) { } } else { // monster - if (addmonster(c, R_RANDOM, B_TRUE, 1, B_TRUE, nadded)) { + if (addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, nadded)) { rv = TT_MONSTER; } } @@ -1907,16 +1953,53 @@ void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir, object_ case H_VILLAGE: createforest(map, depth, parentmap, exitdir, entryob, 0); break; + case H_HEAVEN: + createheaven(map, depth, parentmap, exitdir, entryob); + break; case H_PIT: createpit(map, depth, parentmap, exitdir, entryob); break; - break; default: dblog("ERROR - createhabitat with invalid habitat!"); msg("ERROR - createhabitat with invalid habitat!"); } } +void createheaven(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { + int x,y; + enum CELLTYPE emptycell; + cell_t *c; + race_t *r; + lifeform_t *lf; + //object_t *o; + + // what kind of cells will 'empty' ones be? + emptycell = map->habitat->emptycelltype; + // fill entire maze with corridors + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + c = addcell(map, x, y); + setcelltype(c, emptycell); + } + } + + // add one of each god. + for (r = firstrace ; r ; r = r->next) { + if (r->raceclass->id == RC_GOD) { + // find a position + c = getrandomcell(map); + while (!cellwalkable(NULL, c, NULL)) { + c = getrandomcell(map); + } + // place god + lf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL); + assert(lf); + // add to god list + godlf[ngodlfs++] = lf; + } + } +} + /* seed = random number seed @@ -2297,7 +2380,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex // village guards for (i = 0; i < 1; i++) { if ((c->x == guardx[dir][i]) && (c->y == guardy[dir][i])) { - addmonster(c, R_TOWNGUARD, B_FALSE, 1, B_TRUE, NULL); + addmonster(c, R_TOWNGUARD, NULL, B_FALSE, 1, B_TRUE, NULL); } } } @@ -2371,25 +2454,25 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex int thisx,thisy; getmapcoords(m, &thisx, &thisy); if (map->nextmap[D_N] == -1) { - if (thisy == (y - 1)) { + if ((thisy == (y - 1)) && (thisx == x)) { map->nextmap[D_N] = m->id; if (db) dblog(" linked to map %d (dir N)",m->id); } } if (map->nextmap[D_E] == -1) { - if (thisx == (x + 1)) { + if ((thisx == (x + 1)) && (thisy == y)) { map->nextmap[D_E] = m->id; if (db) dblog(" linked to map %d (dir E)",m->id); } } if (map->nextmap[D_S] == -1) { - if (thisy == (y + 1)) { + if ((thisy == (y + 1)) && (thisx == x)) { map->nextmap[D_S] = m->id; if (db) dblog(" linked to map %d (dir S)",m->id); } } if (map->nextmap[D_W] == -1) { - if (thisx == (x - 1)) { + if ((thisx == (x - 1)) && (thisy == y)) { map->nextmap[D_W] = m->id; if (db) dblog(" linked to map %d (dir W)",m->id); } @@ -3324,6 +3407,9 @@ char *getregionname(char *buf, map_t *m, int withlevel) { case RG_FIRSTDUNGEON: sprintf(buf, "dungeon L%d", m->depth); break; + case RG_HEAVEN: + sprintf(buf, "the realm of gods"); + break; case RG_PIT: sprintf(buf, "a pit L%d", m->depth); break; @@ -3339,6 +3425,9 @@ char *getregionname(char *buf, map_t *m, int withlevel) { case RG_FIRSTDUNGEON: strcpy(buf, "a dungeon"); break; + case RG_HEAVEN: + sprintf(buf, "the realm of gods"); + break; case RG_PIT: sprintf(buf, "a pit"); break; @@ -3654,8 +3743,9 @@ void initmap(void) { int vx[4],vy[4],i; // habitats // thingchance, obchance, vaultchance - addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 3, 50, 10); + addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 3, 50, 30); addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALL, 3, 75, 0); + addhabitat(H_HEAVEN, "heaven", CT_CORRIDOR, CT_WALL, 0, 0, 0); addhabitat(H_PIT, "pit", CT_CORRIDOR, CT_WALL, 0, 0, 0); addhabitat(H_VILLAGE, "village", CT_GRASS, CT_WALL, 3, 70, 0); @@ -3676,7 +3766,8 @@ void initmap(void) { // region types addregiontype(RG_WORLDMAP, "World map", H_FOREST, 10, 0, D_NONE, B_TRUE); - addregiontype(RG_FIRSTDUNGEON, "First Dungeon", H_DUNGEON, 20, 3, D_DOWN, B_TRUE); + addregiontype(RG_FIRSTDUNGEON, "First Dungeon", H_DUNGEON, 10, 3, D_DOWN, B_TRUE); + addregiontype(RG_HEAVEN, "Realm of Gods", H_HEAVEN, 1, 0, D_NONE, B_FALSE); addregiontype(RG_PIT, "Pit", H_PIT, 1, 1, D_DOWN, B_FALSE); // region definitions (outlines) @@ -3709,7 +3800,7 @@ void initmap(void) { //vx = 0; vy = -1; addregionoutline(RG_FIRSTDUNGEON); addregionthing(lastregionoutline, 6, NA, NA, RT_VAULT, NA, "jimbos_lair"); - addregionthing(lastregionoutline, 20, NA, NA, RT_RNDVAULTWITHFLAG, F_VAULTISSHRINE, NULL); // godstone on last floor + addregionthing(lastregionoutline, 10, NA, NA, RT_RNDVAULTWITHFLAG, F_VAULTISSHRINE, NULL); // godstone on last floor } int isadjacent(cell_t *src, cell_t *dst) { @@ -3921,7 +4012,7 @@ int isonmap(map_t *map, int x, int y) { } int isoutdoors(map_t *m) { - if (m->region->rtype->id == RG_WORLDMAP) { + if (m->region && m->region->rtype->id == RG_WORLDMAP) { return B_TRUE; } return B_FALSE; @@ -4222,12 +4313,58 @@ void mapentereffects(map_t *m) { if (c->vault && streq(c->vault->id, "inn") && c->lf && (c->lf->race->id == R_HUMAN)) { lifeform_t *lf; killlf(c->lf); - lf = addmonster(c, R_HUMAN, B_TRUE, 1, B_FALSE, NULL); + lf = addmonster(c, R_HUMAN, NULL, B_TRUE, 1, B_FALSE, NULL); addflag(lf->flags, F_STAYINROOM, c->roomid, B_MAYCHASE, NA, NULL); } } } +enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob) { + int donesomething; + char *p; + job_t *j; + + // get params + donesomething = B_TRUE; + p = name; + while (donesomething) { + donesomething = B_FALSE; + if (strstarts(p, "sleeping ")) { + p += strlen("sleeping "); + if (wantflags) addflag(wantflags, F_ASLEEP, NA, NA, NA, NULL); + donesomething = B_TRUE; + } + } + + // try removing suffixes for jobs + for (j = firstjob ; j ; j = j->next) { + char *ep; + char jobname[BUFLEN]; + sprintf(jobname, " %s", j->name); + jobname[1] = tolower(jobname[1]); + ep = strends(name, jobname); + if (ep) { + // got it + if (wantjob) *wantjob = j->id; + // now strip the suffix off, starting at the space before it + *ep = '\0'; + break; + } + } + + // now get raceid + if (streq(p, "random")) { + return R_RANDOM; + } else { + race_t *r; + r = findracebyname(p); + if (r) { + return r->id; + } + } + return R_NONE; +} + void setcellknown(cell_t *cell, int forcelev) { enum SKILLLEVEL slev; object_t *o; diff --git a/map.h b/map.h index 7967eef..90f7ccc 100644 --- a/map.h +++ b/map.h @@ -4,7 +4,7 @@ cell_t *addcell(map_t *map, int x, int y); habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell, int thingchance, int obchance, int vaultchance); void addhomeobs(lifeform_t *lf); map_t *addmap(void); -lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen, int *nadded); +lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int amt, int autogen, int *nadded); object_t *addrandomob(cell_t *c); int addrandomthing(cell_t *c, int obchance, int *nadded); region_t *addregion(enum REGIONTYPE rtype, region_t *parent, int outlineid); @@ -39,6 +39,7 @@ int countcellexitsfor(lifeform_t *lf); void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); void createforest(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob, int nclearings); void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); +void createheaven(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int exitdir, object_t *entryob); void createpit(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); void createregionlink(map_t *m, cell_t *c, object_t *o, char *obname, enum REGIONTYPE newregiontype, region_t *parent); @@ -107,6 +108,7 @@ void makedoor(cell_t *cell, int openchance); void makelit(cell_t *c, enum LIGHTLEV how, int howlong); void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong); void mapentereffects(map_t *m); +enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob); void setcellknown(cell_t *cell, int forcelev); void setcellknownradius(cell_t *centre, int forcelev, int radius, int dirtype); void setcelltype(cell_t *cell, enum CELLTYPE id); diff --git a/nexus.c b/nexus.c index 632f659..ca031c6 100644 --- a/nexus.c +++ b/nexus.c @@ -33,6 +33,7 @@ job_t *firstjob = NULL,*lastjob = NULL; skill_t *firstskill = NULL,*lastskill = NULL; habitat_t *firsthabitat = NULL,*lasthabitat = NULL; map_t *firstmap = NULL,*lastmap = NULL; +map_t *heaven = NULL; region_t *firstregion = NULL,*lastregion = NULL; regionoutline_t *firstregionoutline = NULL,*lastregionoutline = NULL; regiontype_t *firstregiontype = NULL,*lastregiontype = NULL; @@ -187,19 +188,23 @@ int main(int argc, char **argv) { // if no maps (ie. ALWAYS now that maps aren't persistent), // make the initial level if (!firstmap) { - region_t *wregion, *dregion; + region_t *wregion, *dregion,*hregion; newworld = B_TRUE; // create world map. wregion = addregion(RG_WORLDMAP, NULL, -1); assert(wregion); addmap(); createmap(firstmap, 1, wregion, NULL, D_NONE, NULL); - //createmap(firstmap, 1, RG_FIRSTDUNGEON, H_DUNGEON, NULL, D_NONE); // create first dungeon dregion = findregionbytype(RG_FIRSTDUNGEON); assert(dregion); dmap = addmap(); createmap(dmap, 1, dregion, firstmap, D_DOWN, NULL); + // create heaven + hregion = addregion(RG_HEAVEN, NULL, -1); + assert(hregion); + heaven = addmap(); + createmap(heaven, 1, hregion, NULL, D_NONE, NULL); } // find staircase @@ -214,6 +219,9 @@ int main(int argc, char **argv) { where = real_getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL); real_addlf(where, R_HUMAN, 1, C_PLAYER); // this will assign 'player' + // add abilities which the player always has + addflag(player->flags, F_CANWILL, OT_A_PRAY, NA, NA, NULL); + user = getenv("USER"); if (user) { char pname[MAXPNAMELEN]; @@ -249,7 +257,6 @@ int main(int argc, char **argv) { } - // read cheat info from player file if (playerfile) { if (parseplayerfile(playerfile, player)) { diff --git a/objects.c b/objects.c index 7ac0089..e0ec4f1 100644 --- a/objects.c +++ b/objects.c @@ -7,6 +7,7 @@ #include "attack.h" #include "defs.h" #include "flag.h" +#include "god.h" #include "io.h" #include "lf.h" #include "map.h" @@ -2149,7 +2150,6 @@ int canbepoisoned(enum OBTYPE oid) { if (hasflagval(ot->flags, F_CANHAVEOBMOD, OM_POISONED, NA, NA, NULL)) { return B_TRUE; - } return B_FALSE; } @@ -6688,7 +6688,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_ADJSELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - addot(OT_S_CALLWIND, "call wind", "Conjures a friendly wind, carrying a single object to the caster's hands.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_CALLWIND, "zephyr", "Conjures a friendly wind, carrying a single object to the caster's hands.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); @@ -6702,7 +6702,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); // l2 - addot(OT_S_GUSTOFWIND, "gust of wind", "Causes a gust of wind to blow the target's objects away.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_GUSTOFWIND, "gust of wind", "Causes a gust of wind to blow up to ^bpower^n of the target's objects away.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -6898,15 +6898,17 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l3 - addot(OT_S_CALLLIGHTNING, "call lightning", "Blasts a single enemy with a bolt of lightning from the sky.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_CALLLIGHTNING, "call lightning", "Blasts a single enemy with a bolt of lightning from the sky, dealing 3d6 damage (4d6 if outdoors).", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - addot(OT_S_LIGHTNINGBOLT, "lightning bolt", "Fires electricity through multiple enemies.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_LIGHTNINGBOLT, "electricity bolt", "Fires a bolt of electricity through multiple enemies, dealing 2d6 damage to each.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); addot(OT_S_ENDUREELEMENTS, "endure elements", "Provides resistance to fire and cold.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); @@ -6973,6 +6975,11 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_SATEHUNGER, "sate hunger", "Immediately satisfies the hunger of one living creature.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 10, NA, NA, NULL); addot(OT_S_SUMMONANIMALSMD, "summon medium animals", "Summons 2-3 medium animals.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); @@ -7092,12 +7099,6 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); - addot(OT_S_HUNGER, "hunger", "Causes the target to become revenously hungry.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); - addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); - addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); - addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); - addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); - addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l3 addot(OT_S_PSYARMOUR, "psychic armour", "Mentally block incoming attacks.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); @@ -7111,6 +7112,12 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_HUNGER, "hunger", "Causes the target to become ravenously hungry.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 addot(OT_S_SLEEP, "sleep", "Puts the target creature to sleep.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); @@ -7254,6 +7261,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l6 addot(OT_S_GATE, "gate", "Creates a portal to a different dungeon level.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); @@ -7375,7 +7383,7 @@ void initobjects(void) { addot(OT_A_HURRICANESTRIKE, "hurricane strike", "A sweeping attack aginst everything nearby.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); - addot(OT_A_INSPECT, "inspect", "Try to identify an unknown object from your pack.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addot(OT_A_INSPECT, "inspect", "Try to identify an unknown scroll, book, wand or ring from your pack.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_JUMP, "jump", "You can leap large distances.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); @@ -7384,6 +7392,8 @@ void initobjects(void) { addflag(lastot->flags, F_NOANNOUNCE, B_TRUE, NA, NA, NULL); addot(OT_A_QUIVERINGPALM, "quivering palm", "A deadly palm strike which knocks the molecules in the target's body out of alignment.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + addot(OT_A_PRAY, "pray", "Ask for help from a higher being.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_RAGE, "rage", "Enter a state of berzerker rage, gaining attack and defence bonuses.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); @@ -8186,6 +8196,17 @@ void initobjects(void) { addflag(lastot->flags, F_CHARGELOWMSG, B_TRUE, NA, NA, "flickers"); addflag(lastot->flags, F_CHARGEOUTMSG, B_TRUE, NA, NA, "goes out"); + addot(OT_COFFIN, "coffin", "A wooden coffin, made for holding the dead.", MT_WOOD, 100, OC_FURNITURE, SZ_HUMAN); + addflag(lastot->flags, F_RARITY, H_ALL, 75, RR_RARE, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "|"); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_STARTOB, 80, NA, NA, "3-4 piles of ash"); + addflag(lastot->flags, F_STARTOB, 80, NA, NA, "5-10 bones"); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL); + addot(OT_FIREPLACE, "fireplace", "A roaring fireplace.", MT_STONE, 200, OC_FURNITURE, SZ_LARGE); addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "\\"); addflag(lastot->flags, F_PRODUCESLIGHT, 3, NA, IFACTIVE, NULL); @@ -8691,7 +8712,7 @@ void initobjects(void) { addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL); addflag(lastot->flags, F_ACCURACYMOD, -10, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL); - addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -2, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -4, NA, NULL); addot(OT_GOLDCROWN, "golden crown", "A heavy gold crown, encrusted with jewels.", MT_GOLD, 5, OC_ARMOUR, SZ_SMALL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 25, RR_RARE, NULL); addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL); @@ -8712,7 +8733,8 @@ void initobjects(void) { addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL); addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL); - addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -4, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -2, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_NIGHTVISRANGEMOD, -2, NA, NULL); addflag(lastot->flags, F_TINTED, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOQUALITY, B_TRUE, NA, NA, NULL); addot(OT_EYEPATCH, "eyepatch", "A small patch of black material which covers one eye. Scary looking.", MT_CLOTH, 0.01, OC_ARMOUR, SZ_SMALL); @@ -8721,6 +8743,7 @@ void initobjects(void) { addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL); addflag(lastot->flags, F_NOQUALITY, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_SCARY, 2, NA, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -2, NA, NULL); // armour - shields @@ -8765,7 +8788,7 @@ void initobjects(void) { addot(OT_RING_SIGHT, "ring of sight", "Allows the caster to see the invisible, and in the dark.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, ""); addflag(lastot->flags, F_EQUIPCONFER, F_SEEINVIS, NA, NA, NULL); - addflag(lastot->flags, F_EQUIPCONFER, F_SEEINDARK, 3, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_SEEINDARK, 2, NA, NULL); addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, 1, NA, NULL); addot(OT_RING_MANA, "ring of mana", "Increases the wearer's MP pool.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, ""); @@ -8825,7 +8848,7 @@ void initobjects(void) { addflag(lastot->flags, F_EQUIPCONFER, F_RESISTMAG, 5, NA, NULL); addot(OT_RING_MIRACLES, "ring of miracles", "Grants a limited number of miracles to the wearer.", MT_METAL, 0.1, OC_RING, SZ_MINI); addflag(lastot->flags, F_RARITY, H_ALL, 40, NA, ""); - addflag(lastot->flags, F_CHARGES, 3, 3, NA, NULL); + addflag(lastot->flags, F_CHARGES, 1, 3, NA, NULL); // unarmed weapons - note these damage/accuracys can be // overridded with the lifeform flag F_HASATTACK @@ -8919,7 +8942,8 @@ void initobjects(void) { addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL); addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL); addot(OT_TOUCHPARALYZE, "paralyzing touch", "paralyzing touch object", MT_BONE, 0, OC_WEAPON, SZ_TINY); - addflag(lastot->flags, F_DAM, DT_TOUCH, NA, NA, "0d1"); + addflag(lastot->flags, F_ARMOURPIERCE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_DAM, DT_TOUCH, NA, NA, "1d1"); addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); addflag(lastot->flags, F_HITCONFER, F_PARALYZED, SC_CON, 22, "2-4"); addflag(lastot->flags, F_HITCONFERVALS, B_TRUE, NA, NA, NULL); @@ -8927,7 +8951,8 @@ void initobjects(void) { addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL); addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL); addot(OT_TOUCHPARALYZE2, "strong paralyzing touch", "strong paralyzing touch object", MT_BONE, 0, OC_WEAPON, SZ_TINY); - addflag(lastot->flags, F_DAM, DT_TOUCH, NA, NA, "0d1"); + addflag(lastot->flags, F_ARMOURPIERCE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_DAM, DT_TOUCH, NA, NA, "1d1"); addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); addflag(lastot->flags, F_HITCONFER, F_PARALYZED, SC_CON, 30, "5-10"); addflag(lastot->flags, F_HITCONFERVALS, B_TRUE, NA, NA, NULL); @@ -11288,7 +11313,7 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { case 0: // butterflies around user willid = B_TRUE; where = getrandomadjcell(lf->cell, WE_WALKABLE, B_ALLOWEXPAND); - addmonster(where, R_BUTTERFLY, B_FALSE, rnd(10,20), B_FALSE, NULL); + addmonster(where, R_BUTTERFLY, NULL, B_FALSE, rnd(10,20), B_FALSE, NULL); if (haslos(player, where)) { msg("A swarm of butterflies appears!"); } @@ -11964,6 +11989,15 @@ int pour(lifeform_t *lf, object_t *o) { blessob(dst); // we now know that this is holy water if (!isknown(o)) makeknown(o->type->id); + } else if ((o->type->id == OT_POT_WATER) && (o->blessed == B_CURSED)) { // unholy water + if (isplayer(lf)) { + msg("You pour %s onto %s.", obname,dstname); + } + o->blessknown = B_TRUE; + // bless whatever we poured onto + curseob(dst); + // we now know that this is holy water + if (!isknown(o)) makeknown(o->type->id); } else if (o->type->id == OT_POT_INVULN) { flag_t *f; f = hasflag(dst->flags, F_DAMAGABLE); @@ -12582,14 +12616,14 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE } losehp(lf, rnd(5,15), DT_HOLY, NULL, "drinking holy water"); } else { - msg("Mmm, holy water."); + if (isplayer(lf)) msg("Mmm, holy water."); } break; case B_UNCURSED: - msg("Mmm, water."); + if (isplayer(lf)) msg("Mmm, water."); break; case B_CURSED: - msg("Yuck! Something is wrong with this water."); + if (isplayer(lf)) msg("Yuck! Something is wrong with this water."); break; } if (seen) { @@ -12600,14 +12634,25 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE modhunger(lf, -pctof(0.05, (float)HUNGERCONST)); break; case OT_POT_BLOOD: - msg("Yuck, this tastes like blood!"); + if (lf->race->id == R_VAMPIRE) { + int b = B_UNCURSED; + if (isplayer(lf)) msg("This blood is delicious!"); + if (potblessed == B_BLESSED) { + b = B_CURSED; + } else if (potblessed == B_CURSED) { + b = B_BLESSED; + } + dospelleffects(lf, OT_S_HEALINGMAJ,b ? 5 : 1, lf, NULL, lf->cell, b, seen, B_TRUE); + } else { + if (isplayer(lf)) msg("Yuck, this tastes like blood!"); + } break; case OT_POT_BLOODC: f = lfhasflag(lf, F_BEINGSTONED); if (f) { killflag(f); } else { - msg("Yuck, this tastes like oddly-flavoured blood!"); + if (isplayer(lf)) msg("Yuck, this tastes like oddly-flavoured blood!"); } break; case OT_POT_JUICE: @@ -13517,6 +13562,19 @@ int shatter(object_t *o, int hitlf, char *damstring, lifeform_t *fromlf) { addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); + if (fromlf && isplayer(fromlf)) { + switch (o->type->id) { + case OT_POT_HEALING: + case OT_POT_HEALINGMIN: + case OT_POT_HEALINGMAJ: + case OT_POT_AMBROSIA: + angergodmaybe(R_GODMERCY, 25); + break; + default: + break; + } + } + return B_TRUE; } @@ -15017,7 +15075,7 @@ void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf) { // can't be dodged c = getrandomadjcell(lf->cell, WE_WALKABLE, B_ALLOWEXPAND); if (c) { - summonmonster(NULL, c, R_NONE, B_TRUE, NULL, PERMENANT, B_FALSE); + summonmonster(NULL, c, R_SPECIFIED, "random", PERMENANT, B_FALSE); } if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards diff --git a/save.c b/save.c index e0bebd0..3e19b61 100644 --- a/save.c +++ b/save.c @@ -1035,12 +1035,12 @@ int showhiscores(lifeform_t *lf, int min, int max) { sprintf(hilitescoretext, "%ld",hilitescore); rc = sqlite3_exec(db, cmd, showhiscoreline, hilitescoretext, &errmsg); - free(cmd); if (rc != SQLITE_OK) { msg("error writing hiscores: '%s'", errmsg); - dblog("error writing hiscores: '%s'", errmsg); + dblog("error writing hiscores: '%s'\n sql command: [%s]", errmsg, cmd); sqlite3_free(errmsg); } + free(cmd); sqlite3_close(db); return B_FALSE; diff --git a/spell.c b/spell.c index a596594..ee8fc90 100644 --- a/spell.c +++ b/spell.c @@ -7,6 +7,7 @@ #include "attack.h" #include "defs.h" #include "flag.h" +#include "god.h" #include "io.h" #include "lf.h" #include "map.h" @@ -19,6 +20,8 @@ extern lifeform_t *player; extern skill_t *firstskill, *lastskill; extern race_t *firstrace, *lastrace; +extern map_t *heaven; +extern region_t *firstregion; extern knowledge_t *knowledge; extern int needredraw; @@ -36,6 +39,9 @@ extern object_t *retobs[MAXPILEOBS+1]; extern int retobscount[MAXPILEOBS+1]; extern int nretobs; +extern lifeform_t *godlf[]; +extern int ngodlfs; + extern enum ERROR reason; extern int needredraw, statdirty; @@ -1250,7 +1256,34 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // this call will also remove this ability... setrace(user, f->val[0], B_TRUE); - + } else if (abilid == OT_A_PRAY) { + lifeform_t *lf; + char buf[BUFLEN]; + int i; + if (!isplayer(user)) return B_FALSE; + // ask for which god + initprompt(&prompt, "To whom will you pray?"); + prompt.maycancel = B_TRUE; + + for (i = 0 ; i < ngodlfs; i++) { + flag_t *f; + char godof[BUFLEN]; + lf = godlf[i]; + real_getlfname(lf, buf, B_FALSE); + f = hasflag(lf->flags, F_GODOF); + sprintf(godof, " (%s of %s)", (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); + strcat(buf, godof); + addchoice(&prompt, 'a', buf, NULL, lf); + } + + getchoicestr(&prompt, B_FALSE, B_TRUE); + lf = (lifeform_t *)prompt.result; + if (!lf) { + msg("Cancelled."); + return B_TRUE; + } + + prayto(user, lf); } else if (abilid == OT_A_LEARN) { skill_t *sk; char ch = 'a'; @@ -1995,9 +2028,12 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int i; object_t *o,*nexto; int donesomething = B_FALSE; - // animate corpses within los of caster - for (i = 0; i < caster->nlos; i++) { - targcell = caster->los[i]; + if (isplayer(caster)) { + target = caster; + } + // animate corpses within lof of caster + for (i = 0; i < target->nlos; i++) { + targcell = target->los[i]; for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; if (o->type->id == OT_CORPSE) { @@ -2140,30 +2176,42 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // can't see anywhere fizzle(caster); } else { - int tries = 0,maxtries = 10; if (lfhasflag(caster, F_CONTROL) && (power < 6)) { power = 6; } if (power >= 6) { // controlled + // must be within line of sight. if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; if (!targcell) { fizzle(caster); return B_TRUE; } } else { + cell_t *poss[MAXCANDIDATES]; + int nposs = 0,x,y; // pick a random location - targcell = NULL; - while (!targcell || !cellwalkable(caster, targcell, NULL) || celldangerous(caster, targcell, B_FALSE, NULL)) { - int i; - i = rnd(0,caster->nlos-1); - targcell = caster->los[i]; - tries++; - if (tries >= maxtries) { - fizzle(caster); - return B_FALSE; + // only needs to be in line-of-FIRE, not neccesarily sight. + for (y = 0 ; y <= caster->cell->map->h; y++) { + for (x = 0 ; x <= caster->cell->map->w; x++) { + cell_t *c; + c = getcellat(caster->cell->map, x, y); + if (c && haslof(caster->cell, c, LOF_WALLSTOP, NULL)) { + if (cellwalkable(caster, targcell, NULL) && + !celldangerous(caster, targcell, B_FALSE, NULL)) { + poss[nposs++] = c; + } + } + if (nposs >= MAXCANDIDATES) break; } + if (nposs >= MAXCANDIDATES) break; } + + if (!nposs) { + fizzle(caster); + return B_TRUE; + } + targcell = poss[rnd(0,nposs-1)]; } teleportto(caster, targcell, B_TRUE); } @@ -2231,58 +2279,65 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ needredraw = B_TRUE; } } else if (spellid == OT_S_CONFISCATE) { - object_t *o; char ch = 'a'; char obname[BUFLEN]; - // ask for a target cell - if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; - - target = targcell->lf; - if (!target) { - fizzle(caster); - return B_FALSE; - } - // take an object - initprompt(&prompt, "Confiscate which object?"); - addchoice(&prompt, '-', "(Cancel)", NULL, NULL); - for (o = target->pack->first ; o ; o = o->next) { - getobname(o, obname, o->amt); - addchoice(&prompt, ch, obname, NULL, o); - if (ch == 'z') { - ch = 'A'; - } else { - ch++; - } - } - if (isplayer(caster)) { - // select one - getchoice(&prompt); - o = (object_t *)prompt.result; + if (targob) { + target = targob->pile->owner; } else { - // random one - o = (object_t *) prompt.choice[rnd(0,prompt.nchoices-1)].data; - } - if (o) { - if (isplayer(target)) { - getobname(o, obname, o->amt); - msg("^%cYour %s vanish%s!", getlfcol(target, CC_VBAD), noprefix(obname), (o->amt == 1) ? "es" : ""); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else if (isplayer(caster)) { - char targname[BUFLEN]; - getlfname(target, targname); - getobname(o, obname, o->amt); - msg("%s%s %s appear%s in your pack!", targname, getpossessive(targname), - noprefix(obname), (o->amt == 1) ? "es" : ""); - if (seenbyplayer) *seenbyplayer = B_TRUE; - } else if (cansee(player, target)) { - char targname[BUFLEN]; - getlfname(target, targname); - getobname(o, obname, o->amt); - msg("^%c%s%s %s vanish%s!", getlfcol(target, CC_VBAD), targname, getpossessive(targname), noprefix(obname), - (o->amt == 1) ? "es" : ""); - if (seenbyplayer) *seenbyplayer = B_TRUE; + object_t *o; + // ask for a target cell + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; + + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_FALSE; } - moveob(o, caster->pack, ALL); + + // which object to take? + initprompt(&prompt, "Confiscate which object?"); + addchoice(&prompt, '-', "(Cancel)", NULL, NULL); + for (o = target->pack->first ; o ; o = o->next) { + getobname(o, obname, o->amt); + addchoice(&prompt, ch, obname, NULL, o); + if (ch == 'z') { + ch = 'A'; + } else { + ch++; + } + } + if (isplayer(caster)) { + // select one + getchoice(&prompt); + targob = (object_t *)prompt.result; + } else { + // random one + targob = (object_t *) prompt.choice[rnd(0,prompt.nchoices-1)].data; + } + } + if (targob) { + if (target) { + if (isplayer(target)) { + getobname(targob, obname, targob->amt); + msg("^%cYour %s vanish%s!", getlfcol(target, CC_VBAD), noprefix(obname), (targob->amt == 1) ? "es" : ""); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (isplayer(caster)) { + char targname[BUFLEN]; + getlfname(target, targname); + getobname(targob, obname, targob->amt); + msg("%s%s %s appear%s in your pack!", targname, getpossessive(targname), + noprefix(obname), (targob->amt == 1) ? "es" : ""); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + char targname[BUFLEN]; + getlfname(target, targname); + getobname(targob, obname, targob->amt); + msg("^%c%s%s %s vanish%s!", getlfcol(target, CC_VBAD), targname, getpossessive(targname), noprefix(obname), + (targob->amt == 1) ? "es" : ""); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + } + moveob(targob, caster->pack, ALL); } else { fizzle(caster); } @@ -2809,9 +2864,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_CREATEMONSTER) { lifeform_t *newlf; - job_t *forcejob = NULL; race_t *r = NULL; - int randomjobsok = B_TRUE; if (!targcell) { if ((power >= 5) && isplayer(caster)) { @@ -2836,52 +2889,13 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if ((power >= 7) && isplayer(caster)) { // ask what kind of monster askstring("Create what kind of monster", '?', buf, BUFLEN, NULL); - r = findracebyname(buf); - // not found - are we asking for a job with the monster? - if (!r) { - job_t *j; - // try removing suffixes - for (j = firstjob ; j ; j = j->next) { - char *p; - char jobname[BUFLEN]; - strcpy(jobname, j->name); - jobname[0] = tolower(jobname[0]); - p = strstr(buf, jobname); - if (p) { - if (p == buf) { - // just asked for the job name - // fail. - break; - } else { - char newbuf[BUFLEN]; - strncpy(newbuf, buf, (p - buf) - 1); - r = findracebyname(newbuf); - if (r) { - forcejob = j; - break; - } - } - } - } - } - if (!r) { - fizzle(caster); - return B_TRUE; - } } else { - // random one r = getreallyrandomrace(RC_ANY); - } - - - if (forcejob) { - randomjobsok = B_FALSE; - } else { - randomjobsok = B_TRUE; + sprintf(buf, "%s", r->name); } // add the monster - newlf = summonmonster(caster, targcell, r->id, randomjobsok, forcejob, PERMENANT, B_FALSE); + newlf = summonmonster(caster, targcell, R_SPECIFIED, buf, PERMENANT, B_FALSE); if (newlf) { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -2952,6 +2966,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } + if (isplayer(caster) && !frompot) { + pleasegodmaybe(R_GODMERCY, 3); + } } else if (spellid == OT_S_DARKNESS) { if (!targcell) targcell = caster->cell; // centre on the caster @@ -3008,7 +3025,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ o->blessknown = B_TRUE; } } else { - nothinghappens(); + if (isplayer(caster)) nothinghappens(); } } else { // monsters can't use this @@ -3363,6 +3380,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("%s suck%s life from %s!",castername,isplayer(caster) ? "" : "s", lfname); } if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (isplayer(target)) { + msg("You feel your life force draining away!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; } if (target) { @@ -3498,7 +3518,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ return B_TRUE; } // add it - newlf = summonmonster(caster, targcell, R_FLOATINGDISC, B_FALSE, NULL, PERMENANT, B_TRUE); + newlf = summonmonster(caster, targcell, R_FLOATINGDISC, NULL, PERMENANT, B_TRUE); if (newlf) { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -3790,7 +3810,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (isplayer(caster)) { if (!hasflag(o->flags, F_ENCHANTABLE)) { - nothinghappens(); + if (isplayer(caster)) nothinghappens(); return B_TRUE; } else { int amt; @@ -4010,8 +4030,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ losehp(target, roll("1d3"), DT_COLD, caster, "a frostbite spell"); } } else if (spellid == OT_S_GASEOUSFORM) { - target = caster; - if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) { + if (!target) target = caster; + if ((target->race->id != R_VAMPIRE) && skillcheck(target, SC_RESISTMAG, 20 + power, 0)) { if (isplayer(target)) { msg("You feel momentarily insubstantial."); if (seenbyplayer) *seenbyplayer = B_TRUE; @@ -4022,8 +4042,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } // remember the original race addflag(target->flags, F_ORIGRACE, target->race->id, NA, NA, NULL); - // polymorph is always will be temporary - addtempflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, 10); + if (target->race->id != R_VAMPIRE) { + // polymorph is temporary + addtempflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, 10); + } setrace(target, R_GASCLOUD, B_TRUE); } } else if (spellid == OT_S_GREASE) { @@ -4208,11 +4230,14 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ donesomething = B_TRUE; } - if (!donesomething) { - if (isplayer(target)) { + if (isplayer(target)) { + if (!donesomething) { nothinghappens(); } } + if (donesomething && isplayer(caster) && !frompot) { + pleasegodmaybe(R_GODMERCY, 3); + } } else if (spellid == OT_S_HOLDPORTAL) { object_t *o; if (!validatespellcell(caster, &targcell,TT_DOOR, spellid, power, frompot)) return B_TRUE; @@ -4246,11 +4271,11 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } else { if (cansee(player, target)) { - char lfname[BUFLEN]; - getlfname(target, lfname); - msg("%s looks ravenously hungry!", lfname); - if (!lfhasflagval(target, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL)) { - addflag(target->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL); + if (lfhasflag(target, F_HUNGER)) { + modhunger(target, HUNGERCONST); // make more hungry + } else { + // ie. "hungry" + addflag(target->flags, F_HUNGER, HUNGERCONST*2, NA, NA, NULL); } if (seenbyplayer) *seenbyplayer = B_TRUE; } @@ -4635,15 +4660,49 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // 10 = gun speed fireat(caster, targob, 1, targcell, 8 + (power / 2) , NULL); } else if (spellid == OT_S_PLANESHIFT) { - if (isplayer(caster)) { - msg("^B__not yet implemented__"); + map_t *m; + target = caster; + if (isplayer(target)) { + region_t *r; + char ch = 'a'; if (seenbyplayer) *seenbyplayer = B_TRUE; + // ask for region + initprompt(&prompt, "Where do you wish to travel?"); + prompt.maycancel = B_TRUE; + + for (r = firstregion ; r ; r = r->next) { + regiontype_t *rt; + rt = r->rtype; + addchoice(&prompt, ch++, rt->name, NULL, r); + } + + ch = getchoice(&prompt); + r = (region_t *)prompt.result; + // find first map in region + m = findregionmap(r->id, 1); } else { - if (cansee(player, caster)) { + if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } - // for now, just disappear. - unsummon(caster, B_TRUE); + + m = findregionmap(RG_HEAVEN, 1); + } + if (m) { + int ntries = 0; + // find random free cell in map + targcell = getrandomcell(m); + while (!cellwalkable(target, targcell, NULL)) { + targcell = getrandomcell(m); + ntries++; + if (ntries >= 5) { + fizzle(caster); + return B_TRUE; + } + } + teleportto(target, targcell, B_TRUE); + } else { + fizzle(caster); + return B_TRUE; } } else if (spellid == OT_S_PARALYZE) { int howlong; @@ -5156,7 +5215,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ getlfname(target, lfname); msg("A pulse of electricity shocks %s!",lfname); } - dam = rolldie(1, power); + dam = power*2; losehp(target, dam, DT_ELECTRIC, caster, "a jolt of electricity"); } else { fizzle(caster); @@ -5604,7 +5663,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ getobname(blowob[i], obname, 1); c = getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND); - if (c && ((rnd(1,100)+power) <= 33)) { + if (c && ((rnd(1,100)-power) <= 33)) { // move it fireat(NULL, blowob[i], 1, c, 4, NULL); } @@ -5702,7 +5761,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { - nothinghappens(); + if (isplayer(caster)) nothinghappens(); return B_TRUE; } } else if (spellid == OT_S_PACIFY) { @@ -6009,6 +6068,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else { fizzle(caster); } + if (ndone && isplayer(caster) && !frompot) { + pleasegodmaybe(R_GODMERCY, 3); + } } else if (spellid == OT_S_REPELINSECTS) { // just announce if (isplayer(caster)) { @@ -6075,6 +6137,29 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ nothinghappens(); } } + } else if (spellid == OT_S_SATEHUNGER) { + int hunger; + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power, frompot)) return B_TRUE; + + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_TRUE; + } + + hunger = gethungerval(target); + if (hunger > 0) { + int modamt; + modamt = hunger; // take away all current hunger + modamt += HUNGERCONST * 2; // ... then make them stuffed + modhunger(target, -modamt); + if (isplayer(target)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + } else { + fizzle(caster); + return B_TRUE; + } } else if (spellid == OT_S_SEEINVIS) { flag_t *f; // always targetted at caster @@ -6862,7 +6947,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ wantsize = SZ_ANY; break; } - ngot = summonlfs(caster, wantrc, wantsize, nwant, lifetime); + ngot = summonlfs(caster, caster->cell, wantrc, wantsize, nwant, lifetime); if (!ngot) { fizzle(caster); return B_TRUE; @@ -7414,12 +7499,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // now age the caster if (!isgod(caster)) { - caster->maxhp -= pctof(25,caster->maxhp); - limit(&caster->hp, NA, caster->maxhp); - if (isplayer(caster)) { - msg("^BYour body ages unnaturally!"); - statdirty = B_TRUE; - } + age(caster, 25); } if (isplayer(target)) { @@ -7491,12 +7571,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // now age the caster if (!isgod(caster)) { - caster->maxhp -= pctof(50,caster->maxhp); - limit(&caster->hp, NA, caster->maxhp); - if (isplayer(caster)) { - msg("^BYour body ages unnaturally!"); - statdirty = B_TRUE; - } + age(caster, 50); } } else { // monsters can't wish @@ -8281,7 +8356,7 @@ void stopspell(lifeform_t *caster, enum OBTYPE spellid) { // returns # created -int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, int howmany, int lifetime) { +int summonlfs(lifeform_t *caster, cell_t *where, enum RACECLASS wantrc, enum LFSIZE wantsize, int howmany, int lifetime) { lifeform_t *newlf; race_t *r = NULL; enum RACE poss[MAXCANDIDATES]; @@ -8316,7 +8391,7 @@ int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, i ncreated = 0; for (i = 0; i < howmany; i++) { // get random adjacent cell - c = getrandomadjcell(caster->cell, WE_EMPTY, B_ALLOWEXPAND); + c = getrandomadjcell(where, WE_EMPTY, B_ALLOWEXPAND); if (!c) { return ncreated; } @@ -8324,7 +8399,7 @@ int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, i r = findrace(poss[rnd(0,nposs-1)]); if (r) { // add it! - newlf = addmonster(c, r->id, B_FALSE, 1, B_FALSE, NULL); + newlf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL); // not worth any xp killflagsofid(newlf->flags, F_XPVAL); addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL); @@ -8396,6 +8471,8 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, e if (!caster) { return *targcell; + } else if ((caster->race->raceclass->id == RC_GOD) && targcell) { + return *targcell; } sp = findot(spellid); @@ -8408,6 +8485,10 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, e } } + if (caster && (caster->race->raceclass->id == RC_GOD)) { + needlos = B_FALSE; + } + maxrange = getspellrange(spellid, power); diff --git a/spell.h b/spell.h index 6e9ebc0..2bbf47b 100644 --- a/spell.h +++ b/spell.h @@ -26,7 +26,7 @@ void spellcloud(cell_t *srcloc, int radius, char ch, enum COLOUR col, enum OBTYP void stopspell(lifeform_t *caster, enum OBTYPE spellid); void stopallspells(lifeform_t *lf); void stopallspellsexcept(lifeform_t *lf, ...); -int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, int howmany, int lifetime); +int summonlfs(lifeform_t *caster, cell_t *where, enum RACECLASS wantrc, enum LFSIZE wantsize, int howmany, int lifetime); lifeform_t *validateabillf(lifeform_t *user, enum OBTYPE aid, lifeform_t **target); cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, enum OBTYPE spellid, int power, int frompot); //lifeform_t *validatespelllf(lifeform_t *caster, lifeform_t **lf); diff --git a/text.c b/text.c index f7d2d19..947fccb 100644 --- a/text.c +++ b/text.c @@ -10,7 +10,6 @@ extern long curtime; - int needan(char *text) { if (isvowel(tolower(text[0]))) { return B_TRUE; @@ -58,12 +57,18 @@ enum COLOUR chartocol(char ch) { return C_BROWN; case 'B': // v.bad return C_RED; + case 'T': // terrible + return C_ORANGE; case 'g': // good return C_GREEN; case 'G': // v.good return C_CYAN; + case 'E': // excllent + return C_BOLDCYAN; case 'h': // 'hilite' return C_WHITE; + case 'l': // 'bLue' + return C_BLUE; case 'n': // normal default: break; @@ -678,6 +683,20 @@ int streq(char *a, char *b) { return !strcmp(a,b); } +char *strends(char *a, char *suffix) { + char *ep; + if (!a || !suffix) return NULL; + + ep = strstr(a, suffix); + if (ep) { + if ((ep - a) + strlen(ep) == strlen(a)) { + return ep; + } + } + + return NULL; +} + char *strstarts(char *a, char *prefix) { if (!a || !prefix) return NULL; diff --git a/text.h b/text.h index bbd6ec7..78bd963 100644 --- a/text.h +++ b/text.h @@ -32,6 +32,7 @@ void splittime(int *hours, int *mins, int *secs); char *strrep(char *text, char *oldtok, char *newtok, int *rv); char *dostrrep(char* in, char** out, char* oldtok, char* newtok, int *rv); int streq(char *a, char *b); +char *strends(char *a, char *suffix); char *strstarts(char *a, char *prefix); int strpixmatch(char *haystack, char *needle); int texttodice(char *text, int *ndice, int *nsides, int *bonus); diff --git a/vaults/cavein.vlt b/vaults/cavein.vlt index 1017e98..e8cd89f 100644 --- a/vaults/cavein.vlt +++ b/vaults/cavein.vlt @@ -13,5 +13,6 @@ autodoors:25 autopop scatter(1,1,-2,-2) ob:boulder:25% scatter(1,1,-2,-2) ob:25-75 stones:50% +rarity:uncommon @end diff --git a/vaults/circle.vlt b/vaults/circle.vlt index 8efaaf3..09cae92 100644 --- a/vaults/circle.vlt +++ b/vaults/circle.vlt @@ -14,11 +14,13 @@ @legend #:cell:rock wall +:ob:wooden door ++:exit @end @flags goesin:dungeon autodoors:50 autopop +rarity:uncommon @end diff --git a/vaults/crosshatch.vlt b/vaults/crosshatch.vlt index 213c0bc..f455bc2 100644 --- a/vaults/crosshatch.vlt +++ b/vaults/crosshatch.vlt @@ -21,5 +21,6 @@ goesin:dungeon autodoors:50 autopop +rarity:uncommon @end diff --git a/vaults/diagcross.vlt b/vaults/diagcross.vlt index 0c2c3b0..31b376f 100644 --- a/vaults/diagcross.vlt +++ b/vaults/diagcross.vlt @@ -20,5 +20,6 @@ X:exit ! autoscale, lock xy goesin:dungeon mayrotate +rarity:common @end diff --git a/vaults/diningroom.vlt b/vaults/diningroom.vlt index 72f3594..47b6633 100644 --- a/vaults/diningroom.vlt +++ b/vaults/diningroom.vlt @@ -18,5 +18,6 @@ scatter(1,1,-2,-2) ob:random food:5-10 scatter(1,1,-2,-2) ob:steak knife:1-5 ! mayrotate ! mayscale +rarity:uncommon @end diff --git a/vaults/floodroom1.vlt b/vaults/floodroom1.vlt index 3ec2586..317867a 100644 --- a/vaults/floodroom1.vlt +++ b/vaults/floodroom1.vlt @@ -15,5 +15,6 @@ autodoors:100 autopop fill(1,1,-2,-2) ob:very deep water:100 scatter(1,1,-2,-2) mon:piranha:3:100 +rarity:uncommon @end diff --git a/vaults/floodroom2.vlt b/vaults/floodroom2.vlt index 5ca9a7c..c002ea7 100644 --- a/vaults/floodroom2.vlt +++ b/vaults/floodroom2.vlt @@ -14,5 +14,6 @@ autopop fill(2,2,-3,-3) cell:low rock floor:100 fill(2,2,-3,-3) ob:waist-deep water:100 scatter(2,2,-3,-3) mon:piranha:50%:100 +rarity:uncommon @end diff --git a/vaults/jimbo.vlt b/vaults/jimbo.vlt index 1b6f964..f0e8ea5 100644 --- a/vaults/jimbo.vlt +++ b/vaults/jimbo.vlt @@ -17,6 +17,7 @@ ,:ob:1-4 bones:50 ,:mon:prisoner:50 +:ob:wooden door ++:exit /:ob:wooden table -:ob:wooden footstool c:ob:lit candelabrum diff --git a/vaults/labyrinth.vlt b/vaults/labyrinth.vlt index 00274b1..bde8222 100644 --- a/vaults/labyrinth.vlt +++ b/vaults/labyrinth.vlt @@ -21,6 +21,8 @@ @legend #:cell:metal wall m:mon:minotaur ++:ob:iron door ++:exit @end @flags @@ -38,5 +40,6 @@ scatter(0,0,-1,-1) ob:common weapon:4-5 ! TODO: scattered minions around ?? mayrotate ! mayscale +rarity:rare @end diff --git a/vaults/lair_cockatrice.vlt b/vaults/lair_cockatrice.vlt index 69e5822..4e1a9c5 100644 --- a/vaults/lair_cockatrice.vlt +++ b/vaults/lair_cockatrice.vlt @@ -14,5 +14,6 @@ autodoors:100 scatter(1,1,-2,-2) ob:statue:25% scatter(1,1,-2,-2) mon:cockatrice:1 scatter(1,1,-2,-2) ob:1-10 stones:1-5 +rarity:rare @end diff --git a/vaults/monsterzoo.vlt b/vaults/monsterzoo.vlt index 3ea9733..e4c47bb 100644 --- a/vaults/monsterzoo.vlt +++ b/vaults/monsterzoo.vlt @@ -16,5 +16,6 @@ scatter(1,1,-2,-2) ob:random:100% scatter(1,1,-2,-2) ob:random:100% scatter(1,1,-2,-2) ob:random:100%:50 scatter(1,1,-2,-2) ob:random:100%:50 +rarity:rare @end diff --git a/vaults/mudroom.vlt b/vaults/mudroom.vlt index 0a4f01c..f8b0311 100644 --- a/vaults/mudroom.vlt +++ b/vaults/mudroom.vlt @@ -13,5 +13,6 @@ autopop goesin:dungeon ! add mud to 50% of room cells scatter(1,1,-2,-2) ob:pool of mud:50% +rarity:uncommon @end diff --git a/vaults/pillar.vlt b/vaults/pillar.vlt index 5b7d9b8..975c668 100644 --- a/vaults/pillar.vlt +++ b/vaults/pillar.vlt @@ -11,5 +11,6 @@ goesin:dungeon autodoors:50 autopop scatter(1,1,-2,-2) cell:rock wall:25% +rarity:uncommon @end diff --git a/vaults/pillarglass.vlt b/vaults/pillarglass.vlt index b338c9f..f98bee4 100644 --- a/vaults/pillarglass.vlt +++ b/vaults/pillarglass.vlt @@ -12,5 +12,6 @@ goesin:dungeon autodoors:50 autopop scatter(1,1,-2,-2) cell:glass wall:25% +rarity:uncommon @end diff --git a/vaults/riverroom.vlt b/vaults/riverroom.vlt index c4da369..8a48776 100644 --- a/vaults/riverroom.vlt +++ b/vaults/riverroom.vlt @@ -20,5 +20,6 @@ goesin:dungeon autodoors:50 mayrotate +rarity:uncommon @end diff --git a/vaults/traproom.vlt b/vaults/traproom.vlt index 9e51553..61bcce3 100644 --- a/vaults/traproom.vlt +++ b/vaults/traproom.vlt @@ -17,5 +17,6 @@ scatter(1,1,-2,-2) ob:corpse:1-5 ! chance of great items... scatter(1,1,-2,-2) ob:great armour:1:30 scatter(1,1,-2,-2) ob:great weapon:1:30 +rarity:uncommon @end diff --git a/vaults/uturn.vlt b/vaults/uturn.vlt index 6b3e20e..389401d 100644 --- a/vaults/uturn.vlt +++ b/vaults/uturn.vlt @@ -15,5 +15,6 @@ X:exit ! autoscale goesin:dungeon mayrotate +rarity:common @end diff --git a/vaults/vault.vlt b/vaults/vault.vlt index 60e6018..1f00997 100644 --- a/vaults/vault.vlt +++ b/vaults/vault.vlt @@ -18,5 +18,6 @@ $:ob:25-200 gold goesin:dungeon mayrotate ! no auto doors. ie this can be in the middle of nowhere. +rarity:uncommon @end