From 858a264b075a730cf7a0cd98cdde24e50c16bcfa Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Thu, 12 May 2011 01:49:35 +0000 Subject: [PATCH] * [+] need to set "needredraw" every time we exit: - [+] make eating take longer - depends on lf size and food sizes * [+] stop eating if something attacks you! - [+] change spell code to cope with caster = NULL!! - [+] why are rooms never more than 2 high * [+] traps * [+] eating bug again * [+] disarm trap skill? - [+] lots of needredraw bugs - [+] bug with cursor jumping around lots - [+] draw darkened visible cells in blue * [+] shadow cloak - [+] tree shouldn't prevent resting! - [+] make plants not attack druids * [+] cooking - [+] stop eating if your eating object is no longer with you * [+] FLAG CORRUPTION BUG - [+] pet walking back and forth on rotted objects - [+] purified food shouldn't decay anymore. - [+] plants shouldn't sleep - [+] RESTING on statbar not being cleared. the add of f_interrupted was clearing statdirty before f_asleep got removed in killflag(). - [+] AI: don't eat if in battle - [+] reduce projectile damage - [+] show raceclass in statbars - [+] smoke should make you cough. - [+] when going up levle, only prompt for spells you can cast?? (don't show "NOTCASTABLE") - [+] Your young hawk dies. The stirge releases something! - [+] sleeping thigns shoudn't follow you up/down stairs. - [+] when throw'ng an object, don't let it stack (otherwise we might destroy too much) - [+] don't draw "c - " for nopickup objects. - [+] saving throw for traps if you know about it. - [+] druid - get xp for calming animals - [+] rogue- get xp for picking locks, disarming traps. - [+] metal should be immune to most damage types * [+] make heavy blow need HEAVY weapon, not bashing. - [+] can't rest/train while levitating! - [+] gas traps only go off once. - [+] bug: The goblin throws a boulder at you. A boulder misses you. - [+] don't give short sword skill to wizard. - [+] hearing range based on listen skill * [+] coldness disease: - [+] CRASH when swapping places - [+] bug: i can teleport into an impassable object! - [+] add: "really target (your ally)?" - [+] give wizards school-based skill instead of manaspike + wildmagic - [+] LevUp still not being cleared!!! * [+] why is air wizard being prompted for call lightning at level 2??? * [+] summon weapon (summoning) - [+] hold portal (mod) - [+] reveal hidden - [+] stench (death) - [+] frostbite (minor but direct cold damage. 1dpower. maxpower 3) - [+] grease (modific) creates oil in a circle - [+] fear (death) - [+] seeinvis (div) - [+] locate obejct (div) tells you where a seen objcet is. - [+] swap places (transl) "twiddle" - [+] fire brand (fire, melee attaks deal fire damage) - [+] iceedge - [+] lore (div, temporary knowledge from a particular school?) - [+] icicle (cold, deals cold dam and knocks enemies away) * [+] chill (ice, 1d3 damage per exposed body part) - [+] hail storm (ice, big damage in area) - [+] wall of ice (creates icy wall, hp based on power) --- ai.c | 30 +- ai.h | 1 + attack.c | 47 ++- defs.h | 105 ++++- doc/glyphs.txt | 1 + flag.c | 107 ++++- flag.h | 1 + io.c | 317 +++++++++++---- io.h | 10 +- lf.c | 1032 +++++++++++++++++++++++++++++++++++------------- lf.h | 10 +- log.txt | 59 +-- map.c | 100 +++-- map.h | 1 + move.c | 125 ++++-- move.h | 1 + nexus.c | 52 ++- objects.c | 628 +++++++++++++++++++++++++---- objects.h | 5 +- spell.c | 938 ++++++++++++++++++++++++++++++++++++++++--- spell.h | 1 + text.c | 4 +- 22 files changed, 2942 insertions(+), 633 deletions(-) diff --git a/ai.c b/ai.c index 8874cc8..01c2852 100644 --- a/ai.c +++ b/ai.c @@ -364,6 +364,19 @@ int aipickup(lifeform_t *lf, object_t *o) { return B_FALSE; } + +int aipickupok(lifeform_t *lf, object_t *o) { + int ok = B_FALSE; + if (isedible(o)) { + if (caneat(lf, o)) { + ok = B_TRUE; + } + } else if (canpickup(lf, o, 1)) { + ok = B_TRUE; + } + return ok; +} + int aiobok(lifeform_t *lf, object_t *o, lifeform_t *target) { switch (o->type->id) { case OT_POT_INVIS: @@ -863,10 +876,12 @@ void aiturn(lifeform_t *lf) { /////////////////////////////////////////////// // look for any object which we want - if (db) dblog(".oO { looking for any ob which i want. }"); - if (lookforobs(lf, B_ANY)) { - if (db) dblog(".oO { found ob that i want. returning. }"); - return; + if (!isinbattle(lf)) { + if (db) dblog(".oO { looking for any ob which i want. }"); + if (lookforobs(lf, B_ANY)) { + if (db) dblog(".oO { found ob that i want. returning. }"); + return; + } } @@ -1456,7 +1471,7 @@ int lookforobs(lifeform_t *lf, int covetsonly) { // current cell has an object we want? o = hasobmulti(lf->cell->obpile, oid, noids); - if (o && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || caneat(lf,o)) ) { + if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o)) { if (db) dblog(".oO { current cell has ob i want (%s) }",o->type->name); // try to pick it up if (!aipickup(lf, o)) return B_TRUE; @@ -1518,7 +1533,7 @@ int lookforobs(lifeform_t *lf, int covetsonly) { c = lf->los[i]; if (!c->lf && !lfhasflagval(lf, F_IGNORECELL, c->x, c->y, NA, NULL)) { o = hasobmulti(c->obpile, oid, noids); - if (o && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || caneat(lf,o)) ) { + if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o)) { if (db) dblog(".oO { remote cell has ob i want (%s). setting f_targetcell. }",o->type->name); gothere = B_TRUE; } @@ -1526,7 +1541,7 @@ int lookforobs(lifeform_t *lf, int covetsonly) { // has an object with a flag we want? for (n = 0; n < nwantflags; n++) { o = hasobwithflag(c->obpile, wantflag[n]); - if (o && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || caneat(lf, o)) ) { + if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o)) { if (db) dblog(".oO { remote cell has ob with flag i want (%s) }", o->type->name); gothere = B_TRUE; } @@ -1538,7 +1553,6 @@ int lookforobs(lifeform_t *lf, int covetsonly) { f = hasflag(lf->flags, F_WANTSBETTERWEP); if (f) { if (!covetsonly || (f->val[1] == B_COVETS)) { - o = hasbetterweapon(lf, c->obpile); if (o && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) { if (db) dblog(".oO { remote cell has better weapon (%s). setting f_targetcell }",o->type->name); diff --git a/ai.h b/ai.h index 71373de..9b23b35 100644 --- a/ai.h +++ b/ai.h @@ -9,6 +9,7 @@ object_t *aigetwand(lifeform_t *lf, enum FLAG purpose); flag_t *aigoto(lifeform_t *lf, cell_t *c, enum MOVEREASON why, void *data, int timelimit); void aimovetotargetcell(lifeform_t *lf, flag_t *f); int aipickup(lifeform_t *lf, object_t *o); +int aipickupok(lifeform_t *lf, object_t *o); int aiobok(lifeform_t *lf, object_t *o, lifeform_t *target); int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG purpose); void aiturn(lifeform_t *lf); diff --git a/attack.c b/attack.c index 3de6301..9f4bee6 100644 --- a/attack.c +++ b/attack.c @@ -60,9 +60,7 @@ int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damty case PR_EXPERT: actualdam = pctof(50, dam); break; case PR_MASTER: actualdam = pctof(40, dam); break; } - if (actualdam > 0) { - limit(&dam, 1, NA); - } + limit(&actualdam, 1, NA); } // modify for rust @@ -130,7 +128,7 @@ int attackcell(lifeform_t *lf, cell_t *c) { // anyone there? if so just attack. if (c->lf) { - if (isplayer(lf) && !areenemies(lf,c->lf)) { + if (isplayer(lf) && !areenemies(lf,c->lf) && (getraceclass(c->lf) != RC_PLANT)) { char ch; char victimname[BUFLEN]; char buf[BUFLEN]; @@ -184,11 +182,15 @@ int attackcell(lifeform_t *lf, cell_t *c) { if (getskill(lf, SK_TWOWEAPON)) { wep[nweps] = getsecmeleeweapon(lf); if (wep[nweps]) { - damflag[nweps] = hasflag(wep[nweps]->flags, F_DAM); - validwep[nweps] = B_TRUE; - lastweaponidx = nweps; - nweps++; - gotweapon = B_TRUE; + if ((nweps >= 1) && (wep[nweps] == wep[nweps-1])) { + // can't be the same as first one + } else { + damflag[nweps] = hasflag(wep[nweps]->flags, F_DAM); + validwep[nweps] = B_TRUE; + lastweaponidx = nweps; + nweps++; + gotweapon = B_TRUE; + } } } @@ -691,6 +693,8 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) int dir; cell_t *c; int nmatched = 0; + char lfname[BUFLEN]; + getlfname(lf, lfname); // count adjacent allies of name xx for (dir = DC_N; dir <= DC_NW; dir++) { c = getcellindir(victim->cell, dir); @@ -702,10 +706,10 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } if (nmatched >= f->val[2]) { char damstring[BUFLEN]; - sprintf(damstring, "a %s pack", f->text); + sprintf(damstring, "%s pack", lfname); losehp(victim, f->val[0], f->val[1], lf, damstring); if (isplayer(victim) || cansee(player, victim)) { - msg("The %s pack attacks %s!", f->text, victimname); + msg("%s pack attacks %s!", lfname, victimname); } } } @@ -1232,7 +1236,11 @@ int getextradamwep(object_t *wep, int *dam, enum DAMTYPE *damtype, int *ndam) { flag_t *f; for (f = wep->flags->first ; f ; f = f->next) { if (f->id == F_ONFIRE) { - *(dam + *ndam) = rolldie(2,6); + if (f->text) { + *(dam + *ndam) = roll(f->text); + } else { + *(dam + *ndam) = rolldie(2,6); + } *(damtype + *ndam) = DT_FIRE; (*ndam)++; } else if (f->id == F_FROZEN) { @@ -1252,6 +1260,10 @@ char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int d return "defeat"; } + if (getraceclass(victim) == RC_PLANT) { + return "destroy"; + } + if (wep) { flag_t *f; for (f = wep->flags->first ; f ; f = f->next) { @@ -1304,12 +1316,13 @@ char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int d } } + if (getraceclass(victim) == RC_UNDEAD) { + // can't "kill" the undead + return "destroy"; + } return "kill"; } - - - void getdamrange(flag_t *f, int *min, int *max) { int mindam,maxdam; @@ -1670,11 +1683,11 @@ int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical) void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam) { flag_t *f; lifeform_t *victim; - lifeform_t *owner; + lifeform_t *owner = NULL; object_t *wep; if (!where) return; - + wep = fp->ob; if (wep) { cell_t *c; diff --git a/defs.h b/defs.h index 05a0fd0..3740594 100644 --- a/defs.h +++ b/defs.h @@ -39,6 +39,7 @@ enum SKILL { SK_ARMOUR = 1, SK_ATHLETICS, SK_BACKSTAB, + SK_COOKING, SK_FIRSTAID, SK_LISTEN, SK_LOCKPICKING, @@ -48,6 +49,7 @@ enum SKILL { SK_SPOTHIDDEN, SK_STEALTH, SK_TECHUSAGE, + SK_TRAPS, SK_TWOWEAPON, // knowledge SK_LORE_ARCANA, @@ -79,7 +81,7 @@ enum SKILL { SK_SS_TRANSLOCATION, SK_SS_WILD, }; -#define MAXSKILLS 40 +#define MAXSKILLS 42 // proficiency levels enum SKILLLEVEL { @@ -131,6 +133,7 @@ enum CHECKTYPE { SC_IQ, SC_CON, ////////// + SC_DISARM, SC_DODGE, SC_SHIELDBLOCK, SC_FALL, @@ -229,7 +232,7 @@ enum LFCONDITION { #define MAXRETCELLS 80 -#define MAXCHOICES 150 +#define MAXCHOICES 200 #define MAXDEPTH 25 // max dungeon depth @@ -247,8 +250,8 @@ enum LFCONDITION { #define MIN_ROOMH 4 #define MIN_ROOMW 4 -#define MAX_ROOMW (MAX_MAPW / 5) -#define MAX_ROOMH (MAX_MAPH / 5) +#define MAX_ROOMW (MAX_MAPW / 3) +#define MAX_ROOMH (MAX_MAPH / 3) #define MAXDIR_ORTH 4 #define MAXDIR_COMPASS 8 @@ -553,6 +556,7 @@ enum RACECLASS { RC_SLIME, RC_MAGIC, RC_OTHER, + RC_PLANT, RC_UNDEAD, }; @@ -597,6 +601,11 @@ enum RACE { R_TROGLODYTE, R_TROLL, R_XAT, + // plants + R_CACTUS, + R_DREAMFUNGUS, + R_SAWGRASS, + R_TREE, // animals R_ANT, R_ANTS, @@ -693,6 +702,7 @@ enum MATERIAL { MT_ACID = 23, MT_SILK = 24, MT_OIL = 25, + MT_PLANT = 26, }; // Object Types @@ -700,6 +710,7 @@ enum OBTYPE { OT_NONE, // dungeon features OT_BOULDER, + OT_ICICLE, OT_STATUE, OT_DOORWOOD, OT_DOORIRON, @@ -710,12 +721,21 @@ enum OBTYPE { OT_STAIRSUP, OT_VENDINGMACHINE, OT_PORTAL, + // traps + OT_TRAPROCK, + OT_TRAPARROW, + OT_TRAPARROWP, + OT_TRAPGAS, + OT_TRAPFIRE, + OT_TRAPMINE, + OT_TRAPTRIP, // rocks OT_GOLD, OT_STONE, OT_ASH, OT_ASHEXPLODE, OT_ASHCONCEAL, + OT_ASHSLEEP, OT_GEMOFSEEING, // food OT_BERRY, @@ -726,6 +746,8 @@ enum OBTYPE { OT_MUSHROOM, OT_BREADSTALE, OT_CHEESE, + OT_STEW, + OT_JERKY, OT_ROASTMEAT, OT_BREADFRESH, OT_CHOCOLATE, @@ -786,6 +808,7 @@ enum OBTYPE { OT_MAN_ARMOUR, OT_MAN_ATHLETICS, OT_MAN_BACKSTAB, + OT_MAN_COOKING, OT_MAN_FIRSTAID, OT_MAN_LISTEN, OT_MAN_LOCKPICKING, @@ -795,6 +818,7 @@ enum OBTYPE { OT_MAN_SPOTHIDDEN, OT_MAN_STEALTH, OT_MAN_TECHUSAGE, + OT_MAN_TRAPS, OT_MAN_TWOWEAPON, // manuals of knowledge OT_MAN_LORE_ARCANA, @@ -838,13 +862,18 @@ enum OBTYPE { OT_SB_WEAKEN, OT_SB_FEEBLEMIND, OT_SB_BLINDNESS, + OT_SB_POSSESSION, + OT_SB_STENCH, // -- divination OT_SB_DETECTAURA, OT_SB_DETECTLIFE, OT_SB_DETECTOBS, + OT_SB_LOCATEOBJECT, + OT_SB_LORE, OT_SB_REVEALHIDDEN, OT_SB_IDENTIFY, OT_SB_MAPPING, + OT_SB_SEEINVIS, // -- elemental - air OT_SB_AIRBLAST, OT_SB_CALLLIGHTNING, @@ -853,16 +882,22 @@ enum OBTYPE { OT_SB_LIGHTNINGSTORM, OT_SB_WINDSHIELD, // -- elemental - fire - OT_SB_SPARK, + OT_SB_BLADEBURN, + OT_SB_BURNINGWAVE, OT_SB_FIREDART, OT_SB_FIREBALL, OT_SB_FLAMEPILLAR, OT_SB_FLAMEBURST, - OT_SB_BURNINGWAVE, + OT_SB_SPARK, // -- elemental - ice + OT_SB_CHILL, OT_SB_CONECOLD, - OT_SB_FREEZEOB, OT_SB_COLDBURST, + OT_SB_FREEZEOB, + OT_SB_FROSTBITE, + OT_SB_ICEEDGE, + OT_SB_ICICLE, + OT_SB_WALLOFICE, // -- elemental - earth OT_SB_DIG, // -- gravity @@ -886,7 +921,9 @@ enum OBTYPE { OT_SB_CHARM, // -- modification OT_SB_GASEOUSFORM, + OT_SB_GREASE, OT_SB_KNOCK, + OT_SB_HOLDPORTAL, OT_SB_INSCRIBE, OT_SB_INVISIBILITY, OT_SB_LIGHT, @@ -898,11 +935,13 @@ enum OBTYPE { OT_SB_STICKTOSNAKE, // -- summoning OT_SB_CREATEMONSTER, + OT_SB_SUMMONWEAPON, // -- translocation OT_SB_BLINK, OT_SB_DISPERSAL, OT_SB_GATE, OT_SB_TELEPORT, + OT_SB_TWIDDLE, // -- wild can't be learned from books // spells // -- allomancy @@ -925,30 +964,41 @@ enum OBTYPE { OT_S_BLINDNESS, OT_S_POISONBOLT, OT_S_POSSESSION, + OT_S_STENCH, // -- divination OT_S_DETECTAURA, OT_S_DETECTLIFE, OT_S_DETECTOBS, OT_S_DETECTMAGIC, + OT_S_LOCATEOBJECT, + OT_S_LORE, OT_S_REVEALHIDDEN, + OT_S_SEEINVIS, OT_S_IDENTIFY, OT_S_MAPPING, // -- elemental - air OT_S_AIRBLAST, OT_S_CLOUDKILL, OT_S_GUSTOFWIND, + OT_S_MIST, OT_S_WINDSHIELD, // -- elemental - fire - OT_S_SPARK, + OT_S_BLADEBURN, + OT_S_BURNINGWAVE, OT_S_FIREDART, OT_S_FIREBALL, OT_S_FLAMEPILLAR, OT_S_FLAMEBURST, - OT_S_BURNINGWAVE, + OT_S_SPARK, // -- elemental - ice + OT_S_CHILL, OT_S_COLDBURST, OT_S_CONECOLD, OT_S_FREEZEOB, + OT_S_FROSTBITE, + OT_S_ICEEDGE, + OT_S_ICICLE, + OT_S_WALLOFICE, // -- gravity OT_S_GRAVLOWER, OT_S_GRAVBOOST, @@ -972,6 +1022,8 @@ enum OBTYPE { OT_S_DARKNESS, OT_S_ENCHANT, OT_S_GASEOUSFORM, + OT_S_GREASE, + OT_S_HOLDPORTAL, OT_S_INSCRIBE, OT_S_INVISIBILITY, OT_S_KNOCK, @@ -1010,12 +1062,14 @@ enum OBTYPE { OT_S_WATERJET, // -- summoning OT_S_CREATEMONSTER, + OT_S_SUMMONWEAPON, // -- translocation OT_S_BLINK, OT_S_PULL, OT_S_DISPERSAL, OT_S_GATE, OT_S_TELEPORT, + OT_S_TWIDDLE, // -- wild OT_S_MANASPIKE, OT_S_DETONATE, @@ -1030,6 +1084,9 @@ enum OBTYPE { OT_A_LEARN, OT_A_LEVELUP, // abilities + OT_A_COOK, + OT_A_DARKWALK, + OT_A_DISARM, OT_A_FLURRY, OT_A_GRAB, OT_A_CHARGE, @@ -1125,10 +1182,12 @@ enum OBTYPE { OT_FIREMED, OT_FIRESMALL, OT_HAILSTORM, + OT_ICEWALL, OT_MAGICBARRIER, OT_STEAMCLOUD, OT_STEAMPUFF, OT_SLEETSTORM, + OT_MIST, OT_SMOKECLOUD, OT_SMOKEPUFF, OT_POISONCLOUD, @@ -1281,6 +1340,7 @@ enum OBTYPE { OT_REVOLVER, OT_SLING, // special weapons + OT_ENERGYBLADE, OT_HANDOFGOD, @@ -1338,6 +1398,7 @@ enum ALLEGIENCE { }; enum POISONTYPE { + P_COLD, P_FOOD, P_GAS, P_VENOM, @@ -1373,6 +1434,9 @@ enum FLAG { F_NOPICKUP, // cannot pick this up F_IMPASSABLE, // cannot walk past this if your size if v0 or smaller F_CRUSHABLE, // if you are bigger than size v0, walking on this crushes it + F_CAUSESCOUGH, // being in this ob's cell will make you cough unless + // immune to gas. + // v0 = con skillcheck difficulty. F_BLOCKSVIEW, // if v0 = true, cannot see past this // if v0 > 0, reduces your vision by v0. F_BLOCKSTHROW, // cannot throw past this @@ -1436,6 +1500,7 @@ enum FLAG { F_TAINTED, // will give food poisoning if you eat/drink it F_EDIBLE, // you can eat this. val1 = nutrition. 100 = a meal // -1 means "nutrition is weight x abs(val1)" + // v2 = nutrition left when partially eaten F_DRINKABLE, // you can drink this. val1 = nutrition. 100 = a meal // -1 means "nutrition is weight x abs(val1)" F_OPERABLE, // can operate? @@ -1444,12 +1509,18 @@ enum FLAG { F_PICKLOCKS, // can pick locks? val0=% change, // val1=b_false, f_dieonfail, f_bluntonfail F_LOCKABLE,// this object can be locked + F_TRAP, // this object is a trap. v0 is sc_disarm difficulty. + // (NA = impossible) + // if v1 = true, trap will go off if you fail your 2nd disarm + // check. + // v2 = sc_dodge difficulty // doors F_DOOR, // this object is a door - ie. can open it F_OPEN, // is this door open? F_LOCKED,// door is locked - F_JAMMED, // is this door jammed? - F_SECRETDOOR, // this door is secret. v0 is sc_search difficulty + // v1 is difficulty to disarm + F_JAMMED, // is this door jammed? v0 is # turns it'll take to open it. + F_SECRET, // this object is secret. v0 is sc_search difficulty // to find it. // stairs / teleporters / portals F_CLIMBABLE, // this is a stiarcase, v0 = up/down/in @@ -1487,6 +1558,7 @@ enum FLAG { // v0 = enum RUSTINESS. // object mods/effects F_ONFIRE, // burning, also deals extra fire damage + // option text = xdx amount of damage. F_HEADLESS, // for corpses. can go on LFs too. F_MASTERWORK, // weps do higher damager, armour protects better F_SHODDY, // weps do less damage, armour protects less. @@ -1548,6 +1620,7 @@ enum FLAG { F_NODIECONVERTTEXT, // don't anounce when this object changes // misc flags F_LINKOB, // val0 = linked object id + F_LINKRACE, // val0 = linked race id // scroll flags F_LINKSPELL, // val0 = spell this scroll will cast when read // v1 = spell power @@ -1664,6 +1737,7 @@ enum FLAG { // when at zero, lf vanishes. F_HATESRACE, // lf will attack lfs with race=v0 or baseid=v0 on // sight + F_HARMLESS, // it is safe to rest around this lf F_HOSTILE, // lf will attack the player if in sight F_FRIENDLY, // lf will attack all non-players if in sight F_WANTS, // lf will try to pick up object type val0. if @@ -1721,6 +1795,7 @@ enum FLAG { // v0=slot (0-9) // text=spell text // for monsters + F_DOESNTMOVE, // this race doesn't move (but can still attack) F_HUMANOID, // this race can wear armour / use weapons F_INSECT, // this race is classed as an insect F_UNDEAD, // this race is classed as undead @@ -1855,6 +1930,7 @@ enum FLAG { F_STATGAINREADY, // ready to increase str/int etc. v2 is how many times // we can do it. F_INTERRUPTED, // somethign interrupted our rest. stop! + F_EATING, // lf is eating obid v0 F_TRAINING, // are we training? cleared on any action other than rest. // v2 = if not NA, it is the training counter. // when it hits 0, you finish trainign. @@ -2045,6 +2121,7 @@ enum ERROR { E_PARTVEGETARIAN = 50, E_CARNIVORE = 51, E_NOOB = 52, + E_LEVITATING = 53, }; @@ -2118,7 +2195,8 @@ typedef struct cell_s { struct celltype_s *type; struct obpile_s *obpile; enum LIGHTLEV lit; - enum LIGHTLEV origlit; + enum LIGHTLEV origlit; // for timed light + enum LIGHTLEV lastlit; int origlittimer; int littimer; @@ -2208,6 +2286,8 @@ typedef struct lifeform_s { int polyrevert; // about to revert form a polymorph? + int turnsskipped; // don't need to save this + // for loading long oblist[MAXPILEOBS]; int x,y; @@ -2383,6 +2463,7 @@ enum BRAND { BR_SHARPNESS, BR_PYROMANIA, BR_REVENGE, + BR_SHADOWS, BR_SLOTH, BR_SPEED, BR_POWER, diff --git a/doc/glyphs.txt b/doc/glyphs.txt index 714e004..252b8f7 100644 --- a/doc/glyphs.txt +++ b/doc/glyphs.txt @@ -9,6 +9,7 @@ B = bat d = canine/dog e = eye f = feline/cat +F = flora (flowers, plants, etc) g = goblin i = insect j = jelly/ooze/leech diff --git a/flag.c b/flag.c index 81fb373..aa69ded 100644 --- a/flag.c +++ b/flag.c @@ -5,6 +5,7 @@ #include "flag.h" #include "io.h" #include "lf.h" +#include "map.h" #include "objects.h" #include "spell.h" #include "text.h" @@ -24,8 +25,8 @@ flag_t *addtempflag(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text, int lifetime, int known, long obfromid) { flag_t *f; + map_t *redolight = NULL; int i; - int doredraw = B_FALSE; // identified things mean all new flags are autmaticlaly known. if (hasflag(fp, F_IDENTIFIED)) { @@ -36,6 +37,22 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, return NULL; } + if (gamemode == GM_GAMESTARTED) { + if ((id == F_PRODUCESLIGHT) || (id == F_ASLEEP) ) { + if (fp->owner && fp->owner->cell) { + if (fp->owner->cell->map == player->cell->map) { + redolight = fp->owner->cell->map; + } + } else if (fp->ob) { + cell_t *obloc; + obloc = getoblocation(fp->ob); + if (obloc && (obloc->map == player->cell->map)) { + redolight = obloc->map; + } + } + } + } + // certain flags stack... if (flagstacks(id)) { f = hasflag(fp, id); @@ -146,7 +163,8 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, } } // player flags which cause a redraw - doredraw = flagcausesredraw(f->pile->owner, f->id); + if (flagcausesredraw(f->pile->owner, f->id)) needredraw = B_TRUE; + if (flagcausesstatredraw(f->pile->owner, f->id)) statdirty = B_TRUE; } else if (f->pile->ob) { if (announceobflaggain(f->pile->ob, f)) { f->known = B_TRUE; @@ -176,9 +194,12 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, } } - if ((gamemode == GM_GAMESTARTED) && doredraw) { - statdirty = B_TRUE; - needredraw = B_TRUE; + if ((gamemode == GM_GAMESTARTED) && (needredraw || statdirty || redolight)) { + if (redolight) { + needredraw = B_TRUE; + calclight(redolight); + precalclos(player); + } drawscreen(); } return f; @@ -228,12 +249,15 @@ int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { if (isplayer(lf)) { // player switch (fid) { + case F_ASLEEP: case F_BLIND: case F_DETECTLIFE: case F_DETECTOBS: case F_FASTMOVE: case F_HIDING: case F_INVISIBLE: + case F_PRODUCESLIGHT: + case F_PRONE: case F_RAGE: case F_SEEINDARK: case F_SEEINVIS: @@ -246,7 +270,11 @@ int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { } } else if (haslos(player, lf->cell)) { switch (fid) { + case F_ASLEEP: + case F_HIDING: case F_INVISIBLE: + case F_PRODUCESLIGHT: + case F_PRONE: return B_TRUE; default: break; @@ -256,6 +284,33 @@ int flagcausesredraw(lifeform_t *lf, enum FLAG fid) { return B_FALSE; } +int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid) { + if (!lf || !isplayer(lf)) return B_FALSE; + + switch (fid) { + case F_ASLEEP: + case F_BLIND: + case F_EATING: + case F_FASTMOVE: + case F_HASNEWLEVEL: + case F_HIDING: + case F_INVISIBLE: + case F_PARALYZED: + case F_POISONED: + case F_PRODUCESLIGHT: + case F_PRONE: + case F_RAGE: + case F_SPRINTING: + case F_SLOWMOVE: + case F_TIRED: + case F_TRAINING: + return B_TRUE; + default: + break; + } + return B_FALSE; +} + int flagstacks(enum FLAG fid) { int res = B_FALSE; switch (fid) { @@ -356,13 +411,31 @@ int killflagsofid(flagpile_t *fp, enum FLAG fid) { void killflag(flag_t *f) { flag_t *nextone, *lastone; lifeform_t *lf; - int doredraw = B_FALSE; - + map_t *redolight = NULL; + int redostat = B_FALSE; + int redoscreen = B_FALSE; lf = f->pile->owner; + if (gamemode == GM_GAMESTARTED) { + if ((f->id == F_PRODUCESLIGHT) || (f->id == F_ASLEEP) ) { + if (lf && lf->cell) { + if (lf->cell->map == player->cell->map) { + redolight = lf->cell->map; + } + } else if (f->pile->ob) { + cell_t *obloc; + obloc = getoblocation(f->pile->ob); + if (obloc && (obloc->map == player->cell->map)) { + redolight = obloc->map; + } + } + } + } + // flags which cause a redraw - doredraw = flagcausesredraw(f->pile->owner, f->id); + if (flagcausesredraw(f->pile->owner, f->id)) redoscreen = B_TRUE; + if (flagcausesstatredraw(f->pile->owner, f->id)) redostat = B_TRUE; // notify if ((gamemode == GM_GAMESTARTED)) { @@ -436,10 +509,20 @@ void killflag(flag_t *f) { lastone->next = nextone; } - if ((gamemode == GM_GAMESTARTED) && doredraw) { - statdirty = B_TRUE; - needredraw = B_TRUE; - drawscreen(); + if (gamemode == GM_GAMESTARTED) { + if (redolight) { + calclight(redolight); + precalclos(player); + } + if (redoscreen || redolight) { + needredraw = B_TRUE; + } + if (redostat) { + statdirty = B_TRUE; + } + if (statdirty || needredraw || redolight) { + drawscreen(); + } } } diff --git a/flag.h b/flag.h index 386529f..2b3e333 100644 --- a/flag.h +++ b/flag.h @@ -10,6 +10,7 @@ void copyflag(flagpile_t *dst, flagpile_t *src, enum FLAG id); void copyflags(flagpile_t *dst, flagpile_t *src, int lifetime); int countflags(flagpile_t *fp); int flagcausesredraw(lifeform_t *lf, enum FLAG fid); +int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid); int flagstacks(enum FLAG fid); int modcounter(flagpile_t *fp, int amt); flag_t *hasflag(flagpile_t *fp, int id); diff --git a/io.c b/io.c index 86a1aef..39c3297 100644 --- a/io.c +++ b/io.c @@ -72,6 +72,7 @@ int msgmod = B_FALSE; void addchoice(prompt_t *p, char ch, char *text, char *desc, void *data) { + assert(p->choice[p->nchoices].text == NULL); p->choice[p->nchoices].ch = ch; p->choice[p->nchoices].text = strdup(text); if (desc) { @@ -230,6 +231,7 @@ void anim(cell_t *src, cell_t *dst, char ch, int colour) { } // show cursor curs_set(1); + needredraw = B_TRUE; } void animradial(cell_t *src, int radius, char ch, int colour) { @@ -269,6 +271,7 @@ void animradial(cell_t *src, int radius, char ch, int colour) { // show cursor curs_set(1); + needredraw = B_TRUE; } void animradialorth(cell_t *src, int radius, char ch,int colour) { @@ -307,6 +310,7 @@ void animradialorth(cell_t *src, int radius, char ch,int colour) { } // show cursor curs_set(1); + needredraw = B_TRUE; } @@ -339,6 +343,7 @@ void animsky(cell_t *src, char ch, int colour) { // show cursor curs_set(1); + needredraw = B_TRUE; } char askchar(char *prompt, char *validchars, char *def, int showchars) { @@ -374,6 +379,7 @@ char askchar(char *prompt, char *validchars, char *def, int showchars) { mvwprintw(msgwin, 0, 0, buf); wrefresh(msgwin); + curs_set(1); valid = B_FALSE; while (!valid) { ch = getkey(); @@ -385,6 +391,7 @@ char askchar(char *prompt, char *validchars, char *def, int showchars) { valid = B_FALSE; } } + curs_set(0); clearmsg(); if ((ch == 13) && def) { return def[0]; @@ -422,13 +429,14 @@ cell_t *askcoords(char *prompt, int targettype) { valid = B_TRUE; } else if ((targettype & TT_PLAYER) && haslf(c) && cansee(player, c->lf) && isplayer(c->lf)) { valid = B_TRUE; - } else if ((targettype & TT_OBJECT) && hasobject(c)) { + } else if ((targettype & TT_OBJECT) && hasknownobject(c)) { valid = B_TRUE; } + if (targettype & TT_DOOR) { object_t *o; o = hasobwithflag(c->obpile, F_DOOR); - if (o && !hasflag(o->flags, F_SECRETDOOR)) { + if (o && !hasflag(o->flags, F_SECRET)) { valid = B_TRUE; } } @@ -772,6 +780,8 @@ char *askstring(char *prompt, char punc, char *retbuf, int retbuflen, char *def) strcat(buf, "]"); } + curs_set(1); + asprintf(&ending, "%c ",punc); strcat(buf, ending); free(ending); @@ -784,6 +794,7 @@ char *askstring(char *prompt, char punc, char *retbuf, int retbuflen, char *def) if (def && (strlen(retbuf) == 0)) { strcpy(retbuf, def); } + drawcursor(); return retbuf; } @@ -1137,6 +1148,9 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { if (isplayer(lf)) { msg("You are nauseated by a disgusting stench!"); donesomething = B_TRUE; + } else { + msg("%s looks very unwell.",lfname); + donesomething = B_TRUE; } break; case F_NONCORPOREAL: @@ -1231,7 +1245,7 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { break; case F_TREMORSENSE: if (isplayer(lf)) { // don't know if monsters get it - msg("You can 'see' by sensing vibrations around you."); + msg("You can now 'see' by sensing vibrations around you."); donesomething = B_TRUE; } break; @@ -1282,6 +1296,10 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { if (donesomething) statdirty = B_TRUE; + if (statdirty || needredraw) { + drawscreen(); + } + return donesomething; } @@ -1300,8 +1318,8 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { if (isdead(lf) || isdead(player)) return B_FALSE; getlfname(lf, lfname); - // player can't see? - if (!cansee(player, lf)) { + // player can't see this? + if (!cansee(player, lf) && !isplayer(lf)) { return B_FALSE; } switch (f->id) { @@ -1349,7 +1367,7 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { break; case F_ATTACHEDTO: lf2 = findlf(NULL, f->val[0]); - if (lf2) { + if (lf2 && !isdead(lf2)) { char buf[BUFLEN]; getlfname(lf2, buf); msg("%s %s %s!",lfname, isplayer(lf) ? "release" : "releases", buf); @@ -1857,11 +1875,11 @@ void announceobflagloss(object_t *o, flag_t *f) { object_t *askobject(obpile_t *op, char *prompt, int *count, long opts) { - return doaskobject(op, prompt, count, opts, F_NONE); + return doaskobject(op, prompt, count, B_TRUE, opts, F_NONE); } object_t *askobjectwithflag(obpile_t *op, char *prompt, int *count, long opts, enum FLAG withflag) { - return doaskobject(op, prompt, count, opts, withflag, F_NONE); + return doaskobject(op, prompt, count, B_TRUE, opts, withflag, F_NONE); } /* @@ -1881,7 +1899,7 @@ int contains(enum OBCLASS *array, int nargs, enum OBCLASS want) { return B_FALSE; } -void listobs(WINDOW *win, object_t **mylist, int *sellist, int *selcount, int firstob, int *counter, int lastline, int *y, char *myletters) { +void listobs(WINDOW *win, object_t **mylist, int *sellist, int *selcount, int firstob, int *counter, int lastline, int *y, char *myletters, int forpickup) { int lastclass = OC_NULL; int i; int useobletters = B_TRUE; @@ -1917,17 +1935,21 @@ void listobs(WINDOW *win, object_t **mylist, int *sellist, int *selcount, int fi selchar = ' '; } - sprintf(buf, "%c %c - %s", selchar, useobletters ? mylist[i]->letter : myletters[i], - obname); + if (forpickup && hasflag(mylist[i]->flags, F_NOPICKUP)) { + sprintf(buf, "%c %s", selchar, obname); + } else { + sprintf(buf, "%c %c - %s", selchar, useobletters ? mylist[i]->letter : myletters[i], + obname); + } - setobcolour(win, mylist[i]); + setobcolour(win, mylist[i], B_TRUE); getobextrainfo(mylist[i], infobuf); getobequipinfo(mylist[i], equipbuf); mvwprintw(win, *y, 0, "%s%s", buf, infobuf); - unsetobcolour(win, mylist[i]); + setobcolour(win, mylist[i], B_FALSE); if (strlen(equipbuf)) { setcol(win, C_BROWN); @@ -1940,7 +1962,7 @@ void listobs(WINDOW *win, object_t **mylist, int *sellist, int *selcount, int fi *counter = i; } -object_t *doaskobject(obpile_t *op, char *prompt, int *count, long opts, ...) { +object_t *doaskobject(obpile_t *op, char *prompt, int *count, int forpickup, long opts, ...) { int c,i; object_t *mylist[MAXPILEOBS+1]; char myletters[MAXPILEOBS+1]; @@ -1981,6 +2003,7 @@ object_t *doaskobject(obpile_t *op, char *prompt, int *count, long opts, ...) { centre(mainwin, getmaxy(mainwin)-1, "[Press any key]"); getch(); + needredraw = B_TRUE; clearmsg(); drawscreen(); return NULL; @@ -2065,7 +2088,7 @@ object_t *doaskobject(obpile_t *op, char *prompt, int *count, long opts, ...) { // list the objects y = 2; - listobs(mainwin, mylist, NULL, NULL, firstob, &i, lastline, &y, useobletters ? NULL : myletters ); + listobs(mainwin, mylist, NULL, NULL, firstob, &i, lastline, &y, useobletters ? NULL : myletters , forpickup); if (mylist[i] == NULL) { nextpage = -1; @@ -2124,6 +2147,7 @@ object_t *doaskobject(obpile_t *op, char *prompt, int *count, long opts, ...) { *count = o->amt; } // display game windows again + needredraw = B_TRUE; clearmsg(); drawscreen(); return o; @@ -2131,6 +2155,7 @@ object_t *doaskobject(obpile_t *op, char *prompt, int *count, long opts, ...) { } else if ((ch == '-') && (opts & AO_INCLUDENOTHING)) { // select nothing reason = E_SELNOTHING; // display game windows again + needredraw = B_TRUE; clearmsg(); drawscreen(); return NULL; @@ -2154,10 +2179,9 @@ object_t *doaskobject(obpile_t *op, char *prompt, int *count, long opts, ...) { } } - // clear msg bar - clearmsg(); - // display game windows again + needredraw = B_TRUE; + clearmsg(); drawscreen(); return NULL; } @@ -2194,6 +2218,7 @@ int askobjectmulti(obpile_t *op, char *prompt, long opts) { centre(mainwin, getmaxy(mainwin)-1, "[Press any key]"); getch(); + needredraw = B_TRUE; clearmsg(); drawscreen(); return B_TRUE; @@ -2253,7 +2278,7 @@ int askobjectmulti(obpile_t *op, char *prompt, long opts) { // list the objects y = 2; - listobs(mainwin, mylist, selected, selcount, firstob, &i, lastline, &y, useobletters ? NULL : myletters ); + listobs(mainwin, mylist, selected, selcount, firstob, &i, lastline, &y, useobletters ? NULL : myletters , B_TRUE); if (mylist[i] == NULL) { nextpage = -1; @@ -2330,6 +2355,7 @@ int askobjectmulti(obpile_t *op, char *prompt, long opts) { reason = E_SELNOTHING; nretobs = 0; // display game windows again + needredraw = B_TRUE; clearmsg(); drawscreen(); return B_TRUE; @@ -2381,6 +2407,7 @@ int askobjectmulti(obpile_t *op, char *prompt, long opts) { clearmsg(); // display game windows again + needredraw = B_TRUE; drawscreen(); if (nretobs <= 0) { return B_TRUE; @@ -2425,6 +2452,7 @@ char checkforkey(void) { ch = getch(); nodelay(mainwin, FALSE); if (ch == ERR) ch = '\0'; + if (ch == ' ') ch = '\0'; return ch; } @@ -2491,6 +2519,7 @@ void updateviewfor(cell_t *cell) { void drawscreen(void) { + int didstatus = B_FALSE; if (gamemode < GM_GAMESTARTED) { return; } @@ -2499,12 +2528,17 @@ void drawscreen(void) { drawstatus(); wrefresh(statwin); statdirty = B_FALSE; + didstatus = B_TRUE; } if (needredraw) { updateviewfor(player->cell); drawlevelfor(player); - drawcursor(); // this will call redraw gamewin + //drawcursor(); // this will call redraw gamewin + } + + if (didstatus && !needredraw) { + drawcursor(); } } @@ -2610,7 +2644,11 @@ void describeob(object_t *o) { // other extra damage or effects? f = hasflag(o->flags, F_ONFIRE); if (f) { - mvwprintw(mainwin, y, 0, " It also inflicts extra burning damage."); + if (f->text) { + mvwprintw(mainwin, y, 0, " It also inflicts %s extra burning damage.", f->text); + } else { + mvwprintw(mainwin, y, 0, " It also inflicts extra burning damage."); + } y++; } @@ -2699,21 +2737,90 @@ void describeob(object_t *o) { y++; } + // skip line + y++; + // immunities - for (i = 0; i < MAXDAMTYPE; i++) { - if (isimmuneto(o->flags, i)) { - mvwprintw(mainwin, y, 0, "It is immune to %s.",getdamnamenoun(i)); - y++; - } - if (isresistantto(o->flags, i)) { - mvwprintw(mainwin, y, 0, "It is resistant to %s.",getdamnamenoun(i)); - y++; - } - if (isvulnto(o->flags, i)) { - mvwprintw(mainwin, y, 0, "It is vulnerable to %s.",getdamnamenoun(i)); - y++; + strcpy(buf, ""); + f = hasflagval(o->flags, F_DTIMMUNE, DT_ALL, NA, NA, NULL); + if (f) { + sprintf(buf, "It is immune to %s", getdamname(DT_ALL)); + } else { + int first = B_TRUE; + for (i = 0; i < MAXDAMTYPE; i++) { + f = isimmuneto(o->flags, i); + if (f) { + char buf2[BUFLEN]; + if (first) { + sprintf(buf2, "It is immune to: %s", getdamname(i)); + first = B_FALSE; + } else { + sprintf(buf2, ", %s", getdamname(i)); + } + strcat(buf, buf2); + } } } + if (strlen(buf) > 0) { + strcat(buf, "."); + mvwprintw(mainwin, y, 0, buf); + y++; + } + + // resistances + strcpy(buf, ""); + f = hasflagval(o->flags, F_DTRESIST, DT_ALL, NA, NA, NULL); + if (f) { + sprintf(buf, "It is resistant to %s", getdamname(DT_ALL)); + } else { + int first = B_TRUE; + for (i = 0; i < MAXDAMTYPE; i++) { + f = isresistantto(o->flags, i); + if (f) { + char buf2[BUFLEN]; + if (first) { + sprintf(buf2, "It is resistant to: %s", getdamname(i)); + first = B_FALSE; + } else { + sprintf(buf2, ", %s", getdamname(i)); + } + strcat(buf, buf2); + } + } + } + if (strlen(buf) > 0) { + strcat(buf, "."); + mvwprintw(mainwin, y, 0, buf); + y++; + } + + // vulnerabilities + strcpy(buf, ""); + f = hasflagval(o->flags, F_DTVULN, DT_ALL, NA, NA, NULL); + if (f) { + sprintf(buf, "It is vulnerable to %s", getdamname(DT_ALL)); + } else { + int first = B_TRUE; + for (i = 0; i < MAXDAMTYPE; i++) { + f = isvulnto(o->flags, i); + if (f) { + char buf2[BUFLEN]; + if (first) { + sprintf(buf2, "It is vulnerable to: %s", getdamname(i)); + first = B_FALSE; + } else { + sprintf(buf2, ", %s", getdamname(i)); + } + strcat(buf, buf2); + } + } + } + if (strlen(buf) > 0) { + strcat(buf, "."); + mvwprintw(mainwin, y, 0, buf); + y++; + } + // been made invulnerable ? if (hasflag(o->type->flags, F_DAMAGABLE) && !hasflag(o->flags, F_DAMAGABLE)) { @@ -3020,7 +3127,7 @@ void describeob(object_t *o) { // skill type? f = hasflag(o->flags, F_USESSKILL); - if (f) { + if (f && (f->val[0] != SK_NONE)) { enum SKILLLEVEL slev; enum COLOUR col; slev = getskill(player, f->val[0]); @@ -3061,6 +3168,7 @@ void describeob(object_t *o) { // wait for key getch(); real_clearmsg(B_TRUE); + needredraw = B_TRUE; drawscreen(); } @@ -3114,6 +3222,7 @@ void describespell(objecttype_t *ot) { // wait for key getch(); real_clearmsg(B_TRUE); + needredraw = B_TRUE; drawscreen(); } @@ -3629,6 +3738,7 @@ void dovendingmachine(lifeform_t *lf, object_t *vm) { killobpile(op); + needredraw = B_TRUE; drawscreen(); real_clearmsg(B_TRUE); } @@ -3713,6 +3823,7 @@ void doknowledgelist(void) { wrefresh(mainwin); getch(); + needredraw = B_TRUE; clearmsg(); drawscreen(); } @@ -3729,6 +3840,9 @@ void dolook(cell_t *where, int onpurpose) { // (also count objects without this flag) numobs = 0; for (o = where->obpile->first ; o ; o = o->next) { + if (hasflag(o->flags, F_SECRET)) continue; + if (hasflag(o->flags, F_COSMETIC)) continue; + f = hasflag(o->flags, F_THEREISHERE); if (f) { // doens't matter if you're blind @@ -3737,10 +3851,10 @@ void dolook(cell_t *where, int onpurpose) { interrupt(player); seensomething = B_TRUE; } else { - if (onpurpose || !hasflag(o->flags, F_COSMETIC)) { - if (!numobs) firstob = o; - numobs++; - } + //if (onpurpose) { + if (!numobs) firstob = o; + numobs++; + // } } } @@ -3787,7 +3901,7 @@ void dolook(cell_t *where, int onpurpose) { // if wantunknown is set, lsit spells we DONT know. // otherwise list spells we DO know. -void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2, enum SPELLSCHOOL wantschool, int wantunknown) { +void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2, enum SPELLSCHOOL wantschool, int wantunknown, int wantinvalid) { char ch; flag_t *f; char buf[BUFLEN]; @@ -3947,8 +4061,10 @@ void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2, strcat(costbuf, mpdesc[i]); sprintf(buf, "%-30s%s", buf2, costbuf); - // letter doesn't matter - addchoice(pr, 'a', buf2, buf, ot); + if (wantinvalid || validspell[i]) { + // letter doesn't matter + addchoice(pr, 'a', buf2, buf, ot); + } } } @@ -3959,7 +4075,7 @@ void domagic(enum OBTYPE spellid, int cellx, int celly) { // init the prompt if required. if (spellid == OT_NONE) { - makespellchoicelist(&prompt, player, "Use which spell/ability:","Describe which spell/ability:", SS_NONE, B_FALSE); + makespellchoicelist(&prompt, player, "Use which spell/ability:","Describe which spell/ability:", SS_NONE, B_FALSE, B_TRUE); } finished = B_FALSE; @@ -4027,7 +4143,7 @@ void domemmagic(void) { char ch; int slot; objecttype_t *ot; - makespellchoicelist(&prompt, player, "Memorise which spell/ability:","Describe which spell/ability",SS_NONE, B_FALSE); + makespellchoicelist(&prompt, player, "Memorise which spell/ability:","Describe which spell/ability",SS_NONE, B_FALSE, B_TRUE); if (prompt.nchoices <= 0) { msg("You don't have any spells or abilities!"); return; @@ -4063,6 +4179,7 @@ void domsghist(void) { getch(); // restore screen //cls(); + needredraw = B_TRUE; drawscreen(); real_clearmsg(B_TRUE); } @@ -4173,7 +4290,7 @@ void doexplain(char *question) { where = askcoords(question, TT_NONE); if (!where) { clearmsg(); - return; + break; } // explain it @@ -4249,6 +4366,7 @@ void dohelp(void) { centre(mainwin, h-1, "[Press any key]"); getch(); + needredraw = B_TRUE; drawscreen(); real_clearmsg(B_TRUE); } @@ -4524,6 +4642,9 @@ void dorest(void) { rest(player, B_TRUE); } else { switch (reason) { + case E_LEVITATING: + msg("You cannot rest while levitating in mid-air!"); + break; case E_MONSTERNEARBY: msg("You cannot rest - there are monsters nearby!"); break; @@ -4762,6 +4883,8 @@ void drawcursor(void) { // move cursor to player position wmove(gamewin, player->cell->y - viewy, player->cell->x - viewx); wrefresh(gamewin); + // turn on cursor + curs_set(1); } void drawlevelfor(lifeform_t *lf) { @@ -4770,16 +4893,18 @@ void drawlevelfor(lifeform_t *lf) { map_t *map; map = lf->cell->map; + needredraw = B_FALSE; numdraws++; // turn off cursor curs_set(0); - // TODO: draw to buffer here... - // buffer is an array of 'glyph_t's wclear(gamewin); for (y = viewy; y < viewy + viewh; y++) { for (x = viewx; x < viewx + vieww; x++) { + if ((x == lf->cell->x + 1) && (y == lf->cell->y)) { + dblog("x"); + } cell = getcellat(map, x, y); if (cell) { glyph_t glyph,screenglyph; @@ -4800,10 +4925,8 @@ void drawlevelfor(lifeform_t *lf) { } } } - // turn on cursor - curs_set(1); - // move it to the player's position - wmove(gamewin, lf->y - viewy, lf->x - viewx); + // move cursor to the player's position and blit + drawcursor(); } void doheading(WINDOW *win, int *y, int x, char *what) { @@ -4928,6 +5051,29 @@ int drop(object_t *o, int count) { return B_FALSE; } +void dumpspells(void) { + objecttype_t *ot; + enum SPELLSCHOOL ss; + int lev; + dblog("Spell dump:"); + for (ss = 0; ss < SS_LAST; ss++) { + dblog("%s", getschoolname(ss)); + for (lev = 1; lev <= MAXSPELLLEV; lev++) { + dblog("\tLevel %d:", lev); + // get list of spells/abilities we can cast at will + for (ot = objecttype ; ot ; ot = ot->next) { + if (ot->obclass->id == OC_SPELL) { + // matches the current school & level? + if ((getspellschool(ot->id) == ss) && (getspelllevel(ot->id) == lev)) { + dblog("\t\t%s", ot->name); + } + } + } + } + } +} + + char getchoice(prompt_t *prompt) { int i; int y; @@ -4938,6 +5084,7 @@ char getchoice(prompt_t *prompt) { int nextpage = -1; int lastline = SCREENH - 4; + gotheadings = B_FALSE; for (i = 0; i < prompt->nchoices; i++) { if (prompt->choice[i].heading) { @@ -4946,6 +5093,7 @@ char getchoice(prompt_t *prompt) { } } + curs_set(1); // loop until result is valid sel = -1; while (sel == -1) { @@ -5013,11 +5161,13 @@ char getchoice(prompt_t *prompt) { } if ((gamemode == GM_GAMESTARTED)) { + needredraw = B_TRUE; drawscreen(); } else { cls(); wrefresh(mainwin); } + curs_set(0); // return NUL or result char if (ch == 27) { return '\0'; @@ -5062,6 +5212,7 @@ char getchoicestr(prompt_t *prompt, int useshortcuts, int showallatstart) { doneheading = B_FALSE; // loop until result is valid + curs_set(1); sel = -1; while (sel == -1) { int atbottom; @@ -5268,11 +5419,13 @@ char getchoicestr(prompt_t *prompt, int useshortcuts, int showallatstart) { } if ((gamemode == GM_GAMESTARTED)) { + needredraw = B_TRUE; drawscreen(); } else { cls(); wrefresh(mainwin); } + curs_set(0); // return NUL or result char if (ch == 27) { return '\0'; @@ -5725,8 +5878,10 @@ void more(void) { strcat(msgbuf, MORESTRING); //mvwprintw(msgwin, 0, 0, msgbuf); drawmsg(); + curs_set(1); // wait for space while (getch() != ' '); + curs_set(0); // clear msg clearmsg(); } @@ -5847,6 +6002,7 @@ void drawstatus(void) { getplayernamefull(pname); + curs_set(0); wclear(statwin); xpleft = getxpforlev(player->level + 1) - player->xp; @@ -5989,16 +6145,16 @@ void drawstatus(void) { if (getskill(player, SK_FIRSTAID) >= PR_ADEPT) { if (poisonthreatenslife(player, f)) { setcol(statwin, C_GREEN); - wprintw(statwin, " Poison(bad)"); + wprintw(statwin, " %s(bad)", getpoisondesc(f->val[0])); unsetcol(statwin, C_GREEN); } else { setcol(statwin, C_BROWN); - wprintw(statwin, " Poisoned(mild)"); + wprintw(statwin, " %s(mild)", getpoisondesc(f->val[0])); unsetcol(statwin, C_BROWN); } } else { setcol(statwin, C_GREEN); - wprintw(statwin, " Poisoned"); + wprintw(statwin, " %s", getpoisondesc(f->val[0])); unsetcol(statwin, C_GREEN); } } @@ -6232,26 +6388,35 @@ void unsetcol(WINDOW *win, enum COLOUR col) { } } -void setobcolour(WINDOW *win, object_t *o) { - if (!o) return; - if (o->blessknown) { - if (iscursed(o)) { - setcol(win, C_RED); - } else if (isblessed(o)) { - setcol(win, C_CYAN); - } +void setobcolour(WINDOW *win, object_t *o, int set) { + void (*funcptr)(WINDOW *, enum COLOUR); + // which function? + if (set) { + funcptr = setcol; + } else { + funcptr = unsetcol; } -} + if (!o) return; -void unsetobcolour(WINDOW *win, object_t *o) { - if (!o) return; if (o->blessknown) { if (iscursed(o)) { - unsetcol(win, C_RED); + funcptr(win, C_RED); + return; } else if (isblessed(o)) { - unsetcol(win, C_CYAN); + funcptr(win, C_CYAN); + return; } } + + if ((getskill(player, SK_COOKING) >= PR_BEGINNER) && isbadfood(o)) { + funcptr(win, C_GREEN); + return; + } + if (lfhasflag(player, F_DETECTMAGIC) && ismagical(o)) { + funcptr(win, C_BOLDGREEN); + return; + } + } void showlfarmour(lifeform_t *lf) { @@ -6296,9 +6461,9 @@ void showlfarmour(lifeform_t *lf) { } mvwprintw(mainwin, y, 0, "%s", buf); - setobcolour(mainwin, o); + setobcolour(mainwin, o, B_TRUE); wprintw(mainwin, "%s", rhs); - unsetobcolour(mainwin, o); + setobcolour(mainwin, o, B_FALSE); y++; } } @@ -6328,6 +6493,7 @@ void showlfarmour(lifeform_t *lf) { */ getch(); + needredraw = B_TRUE; drawscreen(); real_clearmsg(B_TRUE); } @@ -6424,6 +6590,9 @@ void showlfstats(lifeform_t *lf, int showall) { if (isplayer(lf)) { mvwprintw(mainwin, y, 0, ftext, "Race"); wprintw(mainwin, "%-20s", lf->race->name); y++; + } else { + mvwprintw(mainwin, y, 0, ftext, "Type"); + wprintw(mainwin, "%-20s", lf->race->raceclass->name); y++; } j = getjob(lf); @@ -7274,11 +7443,12 @@ void showlfstats(lifeform_t *lf, int showall) { getskillname(available[n]->val[0]) ); } if (n < numknown) { - mvwprintw(mainwin, y, 40, "%c %s (%s%s)", + 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" : ""); + //ismaxedskill(lf, known[n]->val[0]) ? "/MAX" : "", + (known[n]->lifetime == FROMSPELL) ? "[spell]" : ""); } if (downline(&y, h, "SKILLS", skilltitle, prompt, cmdchars, &ch)) { exitnow = B_TRUE; @@ -7625,8 +7795,7 @@ void showlfstats(lifeform_t *lf, int showall) { } f = lfhasknownflag(lf, F_PACKATTACK); if (f && (f->known)) { - sprintf(buf,"%s deal%s extra damage when in a %s pack.", you(lf), isplayer(lf) ? "" : "s", - f->text); + sprintf(buf,"%s deal%s extra damage when in a pack.", you(lf), isplayer(lf) ? "" : "s"); mvwprintw(mainwin, y, 0, buf); y++; } @@ -7742,7 +7911,7 @@ void showlfstats(lifeform_t *lf, int showall) { } f = lfhasknownflag(lf, F_TREMORSENSE); if (f) { - mvwprintw(mainwin, y, 0, "%s can 'see' by sensing vibrations around you.", you(lf)); + mvwprintw(mainwin, y, 0, "%s can 'see' by sensing vibrations around %s.", you(lf), you(lf)); y++; } f = lfhasknownflag(lf, F_WINDSHIELD); @@ -7858,6 +8027,7 @@ void showlfstats(lifeform_t *lf, int showall) { needredraw = B_TRUE; cls(); wrefresh(mainwin); + needredraw = B_TRUE; drawscreen(); wrefresh(gamewin); wrefresh(statwin); @@ -7883,6 +8053,7 @@ void tombstone(lifeform_t *lf) { centre(mainwin, y, "R.I.P."); y++; //printf("%s\n",lf->name); centre(mainwin, y, "%s",pname); y++; + centre(mainwin, y, "Died on level %d of the dungeon.", lf->cell->map->depth); y++; p = strtok_r(lf->lastdam,"^", &dummy); if (p) { diff --git a/io.h b/io.h index 624aa1f..5896550 100644 --- a/io.h +++ b/io.h @@ -15,7 +15,7 @@ int announceobflaggain(object_t *o, flag_t *f); void announceobflagloss(object_t *o, flag_t *f); object_t *askobject(obpile_t *op, char *title, int *count, long opts); object_t *askobjectwithflag(obpile_t *op, char *title, int *count, long opts, enum FLAG withflag); -object_t *doaskobject(obpile_t *op, char *title, int *count, long opts, ...); +object_t *doaskobject(obpile_t *op, char *title, int *count, int forpickup, long opts, ...); int askobjectmulti(obpile_t *op, char *prompt, long opts); char askchar(char *prompt, char *validchars, char *def, int showchars); cell_t *askcoords(char *prompt, int targettype); @@ -72,6 +72,7 @@ void drawmsg(void); void drawscreen(void); void drawstatus(void); int drop(object_t *o, int count); +void dumpspells(void); char getchoice(prompt_t *prompt); char getchoicestr(prompt_t *prompt, int useshortcuts, int showlallatstart); int getkey(void); @@ -81,8 +82,8 @@ void doheading(WINDOW *win, int *y, int x, char *what); void initgfx(void); void initprompt(prompt_t *p, char *q1); int keycodetokey(int keycode); -void listobs(WINDOW *win, object_t **mylist, int *sellist, int *selcount, int firstob, int *counter, int lastline, int *y, char *myletters); -void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2, enum SPELLSCHOOL wantschool, int wantunknown); +void listobs(WINDOW *win, object_t **mylist, int *sellist, int *selcount, int firstob, int *counter, int lastline, int *y, char *myletters, int forpickup); +void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2, enum SPELLSCHOOL wantschool, int wantunknown, int wantinvalid); void more(void); void warn(char *format, ... ); void msg(char *format, ... ); @@ -95,8 +96,7 @@ void redraw(void); int savequit(void); void setcol(WINDOW *win, enum COLOUR col); void unsetcol(WINDOW *win, enum COLOUR col); -void setobcolour(WINDOW *win, object_t *o); -void unsetobcolour(WINDOW *win, object_t *o); +void setobcolour(WINDOW *win, object_t *o, int set); void showlfarmour(lifeform_t *lf); void showlfstats(lifeform_t *lf, int showall); void tombstone(lifeform_t *lf); diff --git a/lf.c b/lf.c index 82d683d..970cab3 100644 --- a/lf.c +++ b/lf.c @@ -110,6 +110,31 @@ int appearsrandomly(enum RACE rid) { return B_TRUE; } +void awardxpfor(lifeform_t *killed, float pct) { + int xpval,xpeach,i,nposs; + lifeform_t *poss[MAXCANDIDATES], *l; + int n; + + xpval = (int) ((float)calcxp(killed) * (pct/100)); + + // find all allies on the map + nposs = 0; + n = 0; + for (l = killed->cell->map->lf ; l ; l = l->next) { + if ((l != killed) && (getallegiance(l) == AL_FRIENDLY)) { + poss[nposs] = l; + nposs++; + } + } + if (nposs == 0) return; + + xpeach = xpval / nposs; + + for (i = 0; i < nposs; i++) { + gainxp(poss[i], xpeach); + } +} + void bleed(lifeform_t *lf) { flag_t *f; char obname[BUFLEN]; @@ -495,7 +520,7 @@ int caneat(lifeform_t *lf, object_t *o) { } if (lfhasflag(lf, F_PARTVEGETARIAN) && (o->material->id == MT_FLESH)) { - if (gethungerlevel(gethungerval(player)) < H_HUNGRY) { + if (gethungerlevel(gethungerval(player)) < H_PECKISH) { reason = E_PARTVEGETARIAN; return B_FALSE; } @@ -540,22 +565,9 @@ int canhear(lifeform_t *lf, cell_t *dest) { // can't hear if you have a hostile mosnter next to you // and you're not blind. // (you're too engrossed in the battle) - if (isplayer(lf) && !isblind(lf)) { - int dir; - int inbattle = B_FALSE; - for (dir = DC_N; dir <= DC_NW; dir++) { - cell_t *c; - c = getcellindir(lf->cell, dir); - if (c && c->lf && areenemies(lf, c->lf)) { - inbattle = B_TRUE; - break; - } - } - if (inbattle) { - return B_FALSE; - } + if (isplayer(lf) && isinbattle(lf)) { + return B_FALSE; } - map = dest->map; @@ -720,9 +732,26 @@ int canquaff(lifeform_t *lf, object_t *o) { } int cansee(lifeform_t *viewer, lifeform_t *viewee) { + object_t *o; + flag_t *f; + int xray = 0; + int dist; + if (gamemode < GM_GAMESTARTED) { return B_TRUE; } + if (!viewee->cell || !viewer->cell) { + return B_FALSE; + } + + f = hasflag(viewer->flags, F_XRAYVIS); + if (f) { + xray = f->val[0]; + } else { + xray = 0; + } + dist = getcelldist(viewer->cell, viewee->cell); + // viewer asleep? if (lfhasflag(viewer, F_ASLEEP)) { // can only 'see' yourself @@ -743,14 +772,39 @@ int cansee(lifeform_t *viewer, lifeform_t *viewee) { } } + // viewee hiding? if (lfhasflag(viewee, F_HIDING) && (viewee != viewer)) { if (!lfhasflagval(viewer, F_SPOTTED, viewee->id, NA, NA, NULL)) { return B_FALSE; } } + + // something obscuring them? + for (o = viewee->cell->obpile->first ; o ; o = o->next) { + f = hasflag(o->flags, F_BLOCKSVIEW); + if (f) { + if (!lfhasflagval(viewer, F_CANSEETHROUGHMAT, o->material->id, NA, NA, NULL)) { + if (!xray || (xray < dist)) { + return B_FALSE; + } + } + } + } + + return B_TRUE; } +int cansleep(lifeform_t *lf) { + enum RACECLASS rc; + rc = getraceclass(lf); + if (rc == RC_PLANT) { + return B_FALSE; + } else if (rc == RC_UNDEAD) { + return B_FALSE; + } + return B_TRUE; +} // where == BP_NONE means "can i wear it anywhere?' int canwear(lifeform_t *lf, object_t *o, enum BODYPART where) { @@ -837,7 +891,7 @@ int canweild(lifeform_t *lf, object_t *o) { weildloc = getweildloc(o, &otherloc, NULL); // already weilding it? - if (isequipped(o)) { + if (o && isequipped(o)) { reason = E_ALREADYUSING; return B_FALSE; } @@ -852,7 +906,7 @@ int canweild(lifeform_t *lf, object_t *o) { return B_FALSE; } if (!hasbp(lf, weildloc)) { - if (isfirearm(o) && !getequippedob(lf->pack, otherloc)) { + if (o && isfirearm(o) && !getequippedob(lf->pack, otherloc)) { int temp; // firearm can go in other hand. // swap locations. @@ -1190,6 +1244,10 @@ void die(lifeform_t *lf) { int willbecomeghost = B_FALSE; object_t *corpse = NULL; + if (cansee(player, lf)) { + needredraw = B_TRUE; + } + if (lfhasflag(lf, F_RISEASGHOST)) { willbecomeghost = B_TRUE; } @@ -1205,6 +1263,9 @@ void die(lifeform_t *lf) { if (hunger > 0) { modhunger(lf, -hunger); } + if (isplayer(lf)) { + statdirty = B_TRUE; + } return; } @@ -1275,29 +1336,7 @@ void die(lifeform_t *lf) { // award xp (but not if another monster killed it) if (hasflag(lf->flags, F_KILLEDBYPLAYER)) { - int xpval,xpeach,i,nposs; - lifeform_t *poss[MAXCANDIDATES], *l; - int n; - - xpval = calcxp(lf); - - // find all allies on the map - nposs = 0; - n = 0; - for (l = lf->cell->map->lf ; l ; l = l->next) { - if (l != lf) { - if (getallegiance(l) == AL_FRIENDLY) { - poss[nposs] = l; - nposs++; - } - } - } - //assert(xpval > 0); - xpeach = xpval / nposs; - - for (i = 0; i < nposs; i++) { - gainxp(poss[i], xpeach); - } + awardxpfor(lf,100); } } @@ -1452,6 +1491,9 @@ void die(lifeform_t *lf) { } } + if (needredraw) { + drawscreen(); + } } void dumplf(void) { @@ -1624,12 +1666,18 @@ void dumplev(void) { int eat(lifeform_t *lf, object_t *o) { char lfname[BUFLEN]; char obname[BUFLEN]; + char buf[BUFLEN]; flag_t *f; - int nutrition; - double amt; - double mod; + double nutrition; + double turnstoeat; + double eateachturn; + double startpcteaten = 0; + double pcteaten; int drinking = B_FALSE; - enum LFSIZE sz; + int amt; + int fullyeaten = B_FALSE; + flag_t *alreadyeating; + enum HUNGER hlev,posthlev; if (hasflag(o->flags, F_DRINKABLE)) { drinking = B_TRUE; @@ -1683,10 +1731,13 @@ int eat(lifeform_t *lf, object_t *o) { getobname(o, obname, 1); getlfname(lf, lfname); + // get total nutrition nutrition = getnutrition(o); if (nutrition == 0) { - // should never happen! + // this might happen if you purposely try to eat a potion. + // technically a potion will pass above checks because you can + // drink it. if (isplayer(lf)) { msg("That doesn't seem very nutritious..."); } @@ -1705,18 +1756,93 @@ int eat(lifeform_t *lf, object_t *o) { } } - taketime(lf, getactspeed(lf)); - if (touch(lf, o)) { + taketime(lf, getactspeed(lf)); return B_TRUE; } + // how many turns to eat whole thing? + f = hasflag(o->flags, F_EDIBLE); + if (f && (f->val[2] != NA)) { + startpcteaten = f->val[2]; + } else { + startpcteaten = 0; + } + + if (drinking) { + // when drinking you can drink all of it. + turnstoeat = 1; + } else { + // time to eat entire food: + turnstoeat = getobunitweight(o) / (getlfweight(lf, B_NOOBS) / 10); + } + + if (startpcteaten > 0) { + turnstoeat -= ((startpcteaten/100) * turnstoeat); + } + + // now find out how much we'll eat in one turn. + if (turnstoeat <= 1) { + eateachturn = ((100 - startpcteaten)/100) * nutrition; + fullyeaten = B_TRUE; + } else { + eateachturn = nutrition / turnstoeat; + } + + pcteaten = (eateachturn / nutrition) * 100; + // announce - if (isplayer(lf)) { - msg("You %s %s.%s", drinking ? "drink" : "eat", obname, - (f->val[1] >= 20) ? " Yum!" : ""); - } else if (cansee(player, lf)) { - msg("%s %s %s.", lfname, drinking ? "drinks" : "eats", obname); + /* + msg("totnutr:%0.0lf,startpcteaten:%0.0lf,endpcteaten:%0.0f,eatperturn:%0.0f,turnstoeat:%0.0lf,fullyeaten:%s", + nutrition, + startpcteaten,pcteaten, + eateachturn,turnstoeat,fullyeaten ? "YES" : "NO"); + */ + sprintf(buf, "%ld",o->id); + alreadyeating = lfhasflagval(lf, F_EATING, NA, NA, NA, buf); + + // announce + if (turnstoeat <= 1) { + if (alreadyeating) { + if (isplayer(lf)) { + msg("You finish %s.%s", drinking ? "drinking" : "eating", + (f->val[1] >= 20) ? " Yum!" : ""); + } else if (cansee(player, lf)) { + msg("%s finishes %s.", lfname, drinking ? "drinking" : "eating"); + } + } else { + if (isplayer(lf)) { + msg("You %s %s.%s", drinking ? "drink" : "eat", obname, + (f->val[1] >= 20) ? " Yum!" : ""); + + } else if (cansee(player, lf)) { + msg("%s %s %s.", lfname, drinking ? "drinks" : "eats", obname); + } + } + } else { + if (alreadyeating) { + if (isplayer(lf)) { + msg("You continue %s.", drinking ? "drinking" : "eating"); + } else if (cansee(player, lf)) { + msg("%s continues %s.", lfname, drinking ? "drinking" : "eating"); + } + } else { + if (isplayer(lf)) { + msg("You start %s %s.", drinking ? "drinking" : "eating", obname); + + } else if (cansee(player, lf)) { + msg("%s starts %s %s.", lfname, drinking ? "drinking" : "eating", obname); + } + } + } + + hlev = gethungerlevel(gethungerval(player)); + if (alreadyeating) { + if (turnstoeat <= 1) { + killflag(alreadyeating); + } + } else if (!alreadyeating && (turnstoeat > 1)) { + addflag(lf->flags, F_EATING, NA, NA, NA, buf); } if (isrotting(o)) { @@ -1736,87 +1862,83 @@ int eat(lifeform_t *lf, object_t *o) { } } - // change hunger - modhunger(lf, -nutrition); + // get less hungry + modhunger(lf, -eateachturn); + posthlev = gethungerlevel(gethungerval(player)); - // special cases - if (hasflagval(o->flags, F_CORPSEOF, R_GLOWBUG, NA, NA, NULL)) { - addtempflag(lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, 30); - } - if (hasflagval(o->flags, F_CORPSEOF, R_NEWT, NA, NA, NULL)) { - // think "eye of newt" - gainmp(lf, 2); - } - + if (fullyeaten) { + // special cases + if (hasflagval(o->flags, F_CORPSEOF, R_GLOWBUG, NA, NA, NULL)) { + addtempflag(lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, 30); + } + if (hasflagval(o->flags, F_CORPSEOF, R_NEWT, NA, NA, NULL)) { + // think "eye of newt" + gainmp(lf, 2); + } + + // special case for bananas + if (o->type->id == OT_BANANA) { + object_t *skin; + skin = addob(lf->pack, "banana skin"); + if (skin) { + if (isplayer(lf)) { + char skinname[BUFLEN]; + getobname(skin, skinname, 1); + msgnocap("%c - %s", skin->letter, skinname); + } + } else { + skin = addob(lf->cell->obpile, "banana skin"); + if (skin && cansee(player, lf)) { + char skinname[BUFLEN]; + getobname(skin, skinname, 1); + msg("%s drop%s %s on the ground.",lfname, isplayer(lf) ? "" : "s", + skinname); + } + } + } + } // end if fullyeaten - // special case for bananas - if (o->type->id == OT_BANANA) { - object_t *skin; - skin = addob(lf->pack, "banana skin"); - if (skin) { - if (isplayer(lf)) { - char skinname[BUFLEN]; - getobname(skin, skinname, 1); - msgnocap("%c - %s", skin->letter, skinname); - } - } else { - skin = addob(lf->cell->obpile, "banana skin"); - if (skin && cansee(player, lf)) { - char skinname[BUFLEN]; - getobname(skin, skinname, 1); - msg("%s drop%s %s on the ground.",lfname, isplayer(lf) ? "" : "s", - skinname); - } + // take time + amt = getactspeed(lf); + if (o->pile->owner != lf) { + amt += SPEED_PICKUP; + } + taketime(lf, amt); + + if (fullyeaten) { + // remove object + removeob(o, 1); + } else { + // mark how much we ate + f = hasflag(o->flags, F_EDIBLE); + if (f) { + f->val[2] = (int)(startpcteaten + pcteaten); } } - // remove object - removeob(o, 1); - - // how long will it take? - // ie. picking up off ground - if (o->pile->owner != lf) { - amt = SPEED_PICKUP; - } else { - amt = 0; + // stop eating if we are full + if (!fullyeaten && (posthlev != hlev) && (posthlev <= H_FULL)) { + int stopeating = B_FALSE; + if (isplayer(lf)) { + int ch; + more(); + ch = askchar("Stop eating?","yn","y", B_TRUE); + if (ch == 'y') { + stopeating = B_TRUE; + } + } else { + stopeating = B_TRUE; + } + if (stopeating) { + killflagsofid(lf->flags, F_EATING); + } } - // select modifier based on creature size - sz = getlfsize(lf); - switch (sz) { - case SZ_MINI: - mod = 3; break; - case SZ_TINY: - mod = 2.5; break; - case SZ_SMALL: - mod = 2; break; - case SZ_MEDIUM: - mod = 1.5; break; - case SZ_HUMAN: - mod = 1; break; - case SZ_LARGE: - mod = 0.5; break; - case SZ_HUGE: - mod = 0.25; break; - case SZ_ENORMOUS: - mod = 0.125; break; - default: - mod = 1; break; - } - - amt += ((double)getactspeed(lf) * mod); - - amt -= getactspeed(lf); // because we already used 1 time before. - - if (amt < 1) amt = 1; - taketime(lf, amt); - if (isplayer(lf)) { drawstatus(); wrefresh(statwin); } - return B_FALSE; } @@ -2019,7 +2141,7 @@ void enhanceskills(lifeform_t *lf) { addflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL); } else if (f->id == F_LEVSPELLSCHOOL) { // select a spell from school if (isplayer(lf)) { - makespellchoicelist(&prompt, player, "Learn which new spell:","Describe which spell:", f->val[1], B_TRUE); + makespellchoicelist(&prompt, player, "Learn which new spell:","Describe which spell:", f->val[1], B_TRUE, B_FALSE); if (prompt.nchoices > 0) { objecttype_t *ot; getchoicestr(&prompt, B_TRUE, B_TRUE); @@ -2031,7 +2153,7 @@ void enhanceskills(lifeform_t *lf) { msg("There are no new spells for you to learn at this time."); } } else { - makespellchoicelist(&prompt, player, "xx","xx:", f->val[1], B_FALSE); + makespellchoicelist(&prompt, player, "xx","xx:", f->val[1], B_FALSE, B_FALSE); if (prompt.nchoices > 0) { objecttype_t *ot; // pick one randomly @@ -2102,7 +2224,10 @@ int fallasleep(lifeform_t *lf, int howlong) { if (lfhasflag(lf, F_ASLEEP)) { return B_TRUE; } + loseconcentration(lf); + interrupt(lf); + killflagsofid(lf->flags, F_RAGE); killflagsofid(lf->flags, F_TRAINING); addtempflag(lf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL, howlong); @@ -2119,9 +2244,9 @@ void fightback(lifeform_t *lf, lifeform_t *attacker) { // rest our sated counter killflagsofid(lf->flags, F_COUNTER); } - } + } - if (attacker && !isdead(lf)) { + if (attacker) { // wake up killflagsofid(lf->flags, F_ASLEEP); // monsters might flee, fight back, etc @@ -2158,6 +2283,7 @@ void fightback(lifeform_t *lf, lifeform_t *attacker) { } if (nposs) { teleportto(lf, poss[rnd(0,nposs-1)], B_FALSE); // no smoke + abilityeffects(lf, OT_A_DARKWALK, poss[rnd(0,nposs-1)], NULL, NULL); } } } @@ -2511,6 +2637,12 @@ void gainlevel(lifeform_t *lf) { // promotion! setrace(lf, f->val[1], B_FALSE); } + + if (isplayer(lf)) { + needredraw = B_TRUE; + statdirty = B_TRUE; + drawscreen(); + } } @@ -2519,10 +2651,6 @@ void gainmp(lifeform_t *lf, int amt) { int maxed = B_FALSE; int max; - if (isplayer(lf)) { - statdirty = B_TRUE; - } - max = getmaxmp(lf); // magic resistance means you can't regenerate mana! @@ -2544,14 +2672,15 @@ void gainmp(lifeform_t *lf, int amt) { if (maxed) { msg("Your mana is now fully restored."); } - // update screen - drawstatus(); - updatestatus(); + if (gained) { + statdirty = B_TRUE; + drawstatus(); + updatestatus(); + } } } void gainxp(lifeform_t *lf, long amt) { - if (lfhasflag(lf, F_HASNEWLEVEL)) { // can't gain any more xp until you do training return; @@ -3044,7 +3173,7 @@ object_t *getbestfirearm(lifeform_t *lf) { bestgun = NULL; for (o = lf->pack->first ; o ; o = o->next) { // if it is a gun and we can weild it... - if (isfirearm(o) && canweild(lf, o)) { + if (isfirearm(o) && (isequipped(o) || canweild(lf, o))) { int thisfirespeed; thisfirespeed = getfirearmspeed(o); if (thisfirespeed > bestfirespeed) { @@ -3217,6 +3346,7 @@ int gethealtime(lifeform_t *lf) { int gethearingrange(lifeform_t *lf) { int range = 8; // deafult + range = 2 + (getskill(lf, SK_LISTEN)*2); return range; } @@ -4027,12 +4157,9 @@ int getpoisondamchance(enum POISONTYPE ptype) { int chance = 0; switch (ptype) { case P_FOOD: - chance = 33; - break; case P_GAS: - chance = 33; - break; case P_VENOM: + case P_COLD: chance = 33; break; default: @@ -4048,22 +4175,39 @@ char *getpoisondamverb(enum POISONTYPE ptype) { case P_VENOM: return "vomit"; case P_GAS: + case P_COLD: return "cough"; - break; default: break; } return ""; } -char *getpoisonname(enum POISONTYPE ptype) { +char *getpoisondesc(enum POISONTYPE ptype) { switch (ptype) { case P_FOOD: - return "food poisoning"; case P_VENOM: - return "venom poisoning"; + case P_GAS: + case P_WEAKNESS: + return "Poisoned"; + case P_COLD: + return "Sick"; + default: + break; + } + return "Poisoned"; +} + +char *getpoisonname(enum POISONTYPE ptype) { + switch (ptype) { + case P_COLD: + return "hypothermia"; + case P_FOOD: + return "food poisoning"; case P_GAS: return "gas inhalation"; + case P_VENOM: + return "venom poisoning"; case P_WEAKNESS: return "weakening poison"; default: @@ -4574,24 +4718,23 @@ int getthrowspeed(int str) { object_t *getweapon(lifeform_t *lf) { object_t *o; - flag_t *f; - for (o = lf->pack->first ; o ; o = o->next) { - f = hasflagval(o->flags, F_EQUIPPED, BP_WEAPON, NA, NA, NULL); - if (f) { - return o; - } + o = getequippedob(lf->pack, BP_WEAPON); + if (o) { + return o; } - - // no primary weapon. do we have a secondary one? - return getsecmeleeweapon(lf); + o = getequippedob(lf->pack, BP_SECWEAPON); + if (o && ismeleeweapon(o)) { + return o; + } + return NULL; } long getxpforlev(int level) { long needxp = 0; // 2.8 float multiplier = 10; - float constant = 3.5; + float constant = 3.1; // no xp needed for level 1 /* @@ -4866,9 +5009,14 @@ int giveskill(lifeform_t *lf, enum SKILL id) { if (id == SK_ATHLETICS) { newf = addflag(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL); newf->lifetime = FROMJOB; + } else if (id == SK_ATHLETICS) { + makeknown(OT_POT_WATER); } else if (id == SK_LORE_ARCANA) { newf = addflag(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL); newf->lifetime = FROMJOB; + } else if (id == SK_TRAPS) { + newf = addflag(lf->flags, F_CANWILL, OT_A_DISARM, NA, NA, NULL); + newf->lifetime = FROMJOB; } statdirty = B_TRUE; // in case skill changes your stats @@ -4881,6 +5029,17 @@ int giveskill(lifeform_t *lf, enum SKILL id) { newf = addflag(lf->flags, F_CANWILL, OT_A_JUMP, 3, 3, NULL); newf->lifetime = FROMJOB; } + } else if (id == SK_COOKING) { + if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { + if (f->val[1] == PR_NOVICE) { + msg("You can now recognise rotting food."); + } else if (f->val[1] == PR_BEGINNER) { + msg("You can now recognise all kinds of bad food."); + } else if (f->val[2] == PR_BEGINNER) { + newf = addflag(lf->flags, F_CANWILL, OT_A_COOK, NA, NA, NULL); + newf->lifetime = FROMJOB; + } + } } else if (id == SK_FIRSTAID) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (f->val[1] == PR_ADEPT) { @@ -5327,12 +5486,46 @@ flag_t *lfhasknownflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int return NULL; } +// returns radius of light produces +int lfproduceslight(lifeform_t *lf) { + int temp = 0; + int radius = 0; + object_t *o; + + // lf producing light itself? + sumflags(lf->flags, F_PRODUCESLIGHT, &temp, NULL, NULL); + if (temp) radius = temp; + + // objects in hands or on body... + for (o = lf->pack->first ; o ; o = o->next) { + if (isequipped(o)) { + temp = obproduceslight(o); + if (temp > radius) { + radius = temp; + } + } + } + return radius; +} + // return true on failure int lockpick(lifeform_t *lf, object_t *target, object_t *device) { - flag_t *f; + flag_t *f,*lockflag; char lfname[BUFLEN]; char obname[BUFLEN]; int faileffect; + int difficulty = 20; // default, never used though + + lockflag = hasflag(target->flags, F_LOCKED); + if (lockflag) { + difficulty = lockflag->val[1]; + } else { + // should never happen + if (isplayer(lf)) { + msg("That isn't locked!"); + } + return B_TRUE; + } getlfname(lf,lfname); getobname(target,obname, 1); @@ -5345,16 +5538,22 @@ int lockpick(lifeform_t *lf, object_t *target, object_t *device) { taketime(lf, getactspeed(lf) ); // TODO: different difficulty based on doors (maybe part of F_LOCKED flag) - if (skillcheck(lf, SC_OPENLOCKS, 20, f->val[0])) { + if (skillcheck(lf, SC_OPENLOCKS, difficulty, f->val[0])) { // success! // announce if (isplayer(lf) || cansee(player, lf)) { msg("%s unlock%s %s.",lfname, isplayer(lf) ? "" : "s", obname); } - // xp - gainxp(lf, 1); // unlock it killflagsofid(target->flags, F_LOCKED); + // xp + if (isplayer(lf)) { + if (hasjob(lf, J_ROGUE)) { + gainxp(lf, difficulty); + } else { + gainxp(lf, difficulty/3); + } + } } else { // failed! if (faileffect == B_DIEONFAIL) { @@ -5712,7 +5911,7 @@ void initjobs(void) { if ((i == SS_ABILITY) || (i == SS_DIVINE)) { mayusespellschool(lastjob->flags, i, F_CANWILL); } else { - // mayusespellschool(lastjob->flags, i, F_CANCAST); + mayusespellschool(lastjob->flags, i, F_CANCAST); } } @@ -5767,6 +5966,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL); addflag(lastjob->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL); addflag(lastjob->flags, F_STARTATT, A_CON, CN_FIT, NA, NULL); + addflag(lastjob->flags, F_HITDICE, 1, 6, NA, NULL); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "hand axe"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "buckler"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather armour"); @@ -5779,6 +5979,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_ARMOUR, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SHIELDS, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LORE_HUMANOID, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LOCKPICKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_STEALTH, NA, NA, NULL); @@ -5817,6 +6018,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_ARMOUR, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_TECHUSAGE, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_BACKSTAB, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_FIRSTAID, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_STEALTH, NA, NA, NULL); @@ -5834,6 +6036,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LORE_NATURE, PR_ADEPT, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_STAVES, PR_NOVICE, NA, NULL); // learnable skills + addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_FIRSTAID, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, NA, NA, NULL); @@ -5911,9 +6114,6 @@ 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_LORE_ARCANA, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_POLEARMS, NA, NA, NULL); - // TODO: in-built "hook" weapon ? but have to replace 'fists' somehow. - // maybe hardcode after character creation. addjob(J_ROGUE, "Rogue"); addflag(lastjob->flags, F_STARTATT, A_STR, ST_WEAK, NA, NULL); @@ -5932,6 +6132,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_SHORTBLADES, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_ATHLETICS, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_STAVES, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_UNARMED, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_TWOWEAPON, NA, NA, NULL); @@ -5946,7 +6147,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "knife"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "robe"); addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "2 potions of magic"); - addflag(lastjob->flags, F_CANCAST, OT_S_MANASPIKE, NA, NA, NULL); + addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "2 potions of experience"); /* addflag(lastjob->flags, F_IFPLAYER, NA, NA, NA, NULL); addflag(lastjob->flags, F_IFPCT, 50, NA, NA, NULL); @@ -6016,13 +6217,14 @@ void initjobs(void) { void initrace(void) { // race classes - addraceclass(RC_OTHER, "miscellaneous creature", "miscellaneous creatures", SK_NONE); + addraceclass(RC_OTHER, "misc. creature", "miscellaneous creatures", SK_NONE); addraceclass(RC_ANIMAL, "animal", "animals and insects", SK_LORE_NATURE); addraceclass(RC_DEMON, "demon", "demons", SK_LORE_DEMONS); addraceclass(RC_HUMANOID, "humanoid", "humanoid creatures", SK_LORE_HUMANOID); addraceclass(RC_INSECT, "insect", "insects and animals", SK_LORE_NATURE); - addraceclass(RC_SLIME, "slime", "slimes", SK_NONE); addraceclass(RC_MAGIC, "magical creature", "magical creatures", SK_LORE_ARCANA); + addraceclass(RC_PLANT, "plant", "plants", SK_LORE_NATURE); + addraceclass(RC_SLIME, "slime", "slimes", SK_NONE); addraceclass(RC_UNDEAD, "undead", "the undead", SK_LORE_UNDEAD); // races / monsters @@ -6188,7 +6390,6 @@ void initrace(void) { addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, ""); addflag(lastrace->flags, F_MPDICE, 0, 25, NA, NULL); addflag(lastrace->flags, F_MPREGEN, 12, NA, NA, NULL); - addflag(lastrace->flags, F_CANCAST, OT_S_SLOW, NA, NA, NULL); addflag(lastrace->flags, F_CANCAST, OT_S_DISPERSAL, NA, NA, NULL); addflag(lastrace->flags, F_CANCAST, OT_S_GRAVBOOST, NA, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "gazes at you"); @@ -6583,8 +6784,8 @@ void initrace(void) { addflag(lastrace->flags, F_STARTATT, A_DEX, DX_NIMBLE, NA, NULL); addflag(lastrace->flags, F_STARTOBDT, 20, DT_PIERCE, NA, NULL); addflag(lastrace->flags, F_STARTOBCLASS, 40, OC_POTION, NA, NULL); - addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-5 darts"); - addflag(lastrace->flags, F_STARTOB, 33, NA, NA, "1-3 javelins"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-3 darts"); + addflag(lastrace->flags, F_STARTOB, 33, NA, NA, "javelin"); addflag(lastrace->flags, F_STARTOB, 33, NA, NA, "leather armour"); addflag(lastrace->flags, F_STARTOB, 33, NA, NA, "buckler"); addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-10 gold coins"); @@ -7016,6 +7217,104 @@ void initrace(void) { // end monsters + // plants + addrace(R_CACTUS, "cactus", 30, 'F', C_YELLOW, MT_PLANT, RC_PLANT); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, 76, NA, ""); + addflag(lastrace->flags, F_HARMLESS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL); + addflag(lastrace->flags, F_RETALIATE, 1, 4, DT_PIERCE, "sharp spines"); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_DOESNTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_HITDICE, 4, NA, NA, ""); + addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_WAIST, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); + addrace(R_DREAMFUNGUS, "dreamfungus", 0.5, 'F', C_MAGENTA, MT_METAL, RC_PLANT); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, ""); + addflag(lastrace->flags, F_HARMLESS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_VEGETABLE, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); + addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_DOESNTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_HITDICE, 1, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_WAIST, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); + addrace(R_SAWGRASS, "sawgrass", 1, 'F', C_GREY, MT_METAL, RC_PLANT); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, 80, NA, ""); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_VEGETABLE, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); + addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_DOESNTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_HITDICE, 2, NA, NA, ""); + addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, NA, NA, "1d6"); + addflag(lastrace->flags, F_MAXATTACKS, 1, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_WAIST, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_TREMORSENSE, 10, NA, NA, NULL); + addrace(R_TREE, "tree", 140, 'F', C_BROWN, MT_WOOD, RC_PLANT); + addflag(lastrace->flags, F_HARMLESS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL); + addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_DOESNTMOVE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, ""); + addflag(lastrace->flags, F_HITDICE, 8, NA, NA, ""); + addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_WAIST, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL); + addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_DTVULN, DT_CHOP, NA, NA, NULL); + // end plants + // animals addrace(R_BAT, "giant bat", 3, 'B', C_BROWN, MT_FLESH, RC_ANIMAL); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 95, NA, ""); @@ -7351,8 +7650,8 @@ void initrace(void) { addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); + addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL); addflag(lastrace->flags, F_RETALIATE, 1, 4, DT_PIERCE, "sharp spines"); addflag(lastrace->flags, F_CORPSEFLAG, F_SHARP, 1, 4, NULL); addrace(R_RAT, "giant rat", 3, 'r', C_BROWN, MT_FLESH, RC_ANIMAL); @@ -7528,7 +7827,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, NULL); // don't announce spellcasting - addflag(lastrace->flags, F_CANWILL, OT_S_WEB, 3, 3, "pw:2;"); + addflag(lastrace->flags, F_CANWILL, OT_S_WEB, 3, 3, "pw:5;"); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_LIGHT, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); @@ -7553,7 +7852,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL); addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, NULL); // don't announce spellcasting - addflag(lastrace->flags, F_CANWILL, OT_S_WEB, 3, 3, "pw:3;"); + addflag(lastrace->flags, F_CANWILL, OT_S_WEB, 3, 3, "pw:7;"); addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_DTVULN, DT_LIGHT, NA, NA, NULL); addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); @@ -7561,7 +7860,7 @@ void initrace(void) { addflag(lastrace->flags, F_POISONOUS, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HOMEOB, NA, NA, NA, "web"); addflag(lastrace->flags, F_HOMELEVOB, NA, NA, NA, "10-20 webs"); - addrace(R_WOLFYOUNG, "young wolf", 5, 'd', C_GREY, MT_FLESH, RC_ANIMAL); + addrace(R_WOLFYOUNG, "young wolf", 10, 'd', C_GREY, MT_FLESH, RC_ANIMAL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_CON, CN_HARDY, NA, NULL); @@ -7582,7 +7881,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_A_SPRINT, 8, 8, NULL); addflag(lastrace->flags, F_LEVRACE, 5, R_WOLF, NA, NULL); addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, NA, NA, "whines in pain^whining"); - addrace(R_WOLF, "wolf", 10, 'd', C_GREY, MT_FLESH, RC_ANIMAL); + addrace(R_WOLF, "wolf", 25, 'd', C_GREY, MT_FLESH, RC_ANIMAL); addflag(lastrace->flags, F_STARTATT, A_CON, CN_HARDY, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL); @@ -7673,7 +7972,6 @@ void initrace(void) { addflag(lastrace->flags, F_INSECT, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL); - addflag(lastrace->flags, F_NUMAPPEAR, 1, 3, NA, NULL); addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL); addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, ""); @@ -7973,6 +8271,12 @@ void killlf(lifeform_t *lf) { map_t *m; flag_t *f; + if ((gamemode == GM_GAMESTARTED)) { + if (cansee(player, lf)) { + needredraw = B_TRUE; + } + } + m = lf->cell->map; // remove references @@ -8109,6 +8413,24 @@ flag_t *isimmuneto(flagpile_t *fp, enum DAMTYPE dt) { return NULL; } + +int isinbattle(lifeform_t *lf) { + int dir; + if (isblind(lf)) { + return B_FALSE; + } + + for (dir = DC_N; dir <= DC_NW; dir++) { + cell_t *c; + c = getcellindir(lf->cell, dir); + if (c && c->lf && areenemies(lf, c->lf)) { + return B_TRUE; + } + } + return B_FALSE; +} + + int isingunrange(lifeform_t *lf, cell_t *where) { object_t *gun; int range; @@ -8200,6 +8522,10 @@ flag_t *ispoisoned(lifeform_t *lf) { return max; } +flag_t *ispoisonedwith(lifeform_t *lf, enum POISONTYPE pt) { + return lfhasflagval(lf, F_POISONED, pt, NA, NA, NULL); +} + int ispolymorphed(lifeform_t *lf) { if (lfhasflag(lf, F_POLYMORPHED)) { return B_TRUE; @@ -8356,6 +8682,8 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { a->pack = addobpile(a, NOLOC); + a->turnsskipped = 0; + // clear laoding variables for (i = 0; i < MAXPILEOBS; i++) { a->oblist[i] = -1; @@ -8372,6 +8700,8 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { // init flags a->flags = addflagpile(a, NULL); + //addflag(a->flags, F_DEBUG, B_TRUE, NA, NA, NULL); // ooooooooooo + // set race - this will inherit race flags a->race = NULL; setrace(a, rid, B_FALSE); @@ -8385,6 +8715,9 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { } a->created = B_TRUE; a->born = B_TRUE; // now finished creating it. + if ((gamemode == GM_GAMESTARTED) && cansee(player, a)) { + needredraw = B_TRUE; + } return a; } @@ -8646,6 +8979,13 @@ lifeform_t *makezombie(object_t *o) { int areenemies(lifeform_t *lf1, lifeform_t *lf2) { reason = E_OK; + + if (hasjob(lf1, J_DRUID) && (getraceclass(lf2) == RC_PLANT)) { + return B_FALSE; + } else if (hasjob(lf2, J_DRUID) && (getraceclass(lf1) == RC_PLANT)) { + return B_FALSE; + } + switch (getallegiance(lf1)) { case AL_HOSTILE: switch (getallegiance(lf2)) { @@ -8734,24 +9074,26 @@ void autoskill(lifeform_t *lf) { slev = PR_ADEPT; } - for (o = lf->pack->first ; o ; o = o->next) { - if (isweapon(o) && canweild(lf, o)) { - sk = getobskill(o); - if (sk && !getskill(lf, sk->id)) { - giveskilllev(lf, sk->id, slev); + if (!hasjob(lf, J_WIZARD)) { + for (o = lf->pack->first ; o ; o = o->next) { + if (isweapon(o) && canweild(lf, o)) { + sk = getobskill(o); + if (sk && !getskill(lf, sk->id)) { + giveskilllev(lf, sk->id, slev); + } + nweps++; } - nweps++; - } - if (isarmour(o) && canwear(lf, o, BP_NONE)) { - flag_t *f; - // evasion penalty? - f = hasflag(o->flags, F_EVASION); - if (f && (f->val[0] < 0)) { - giveskilllev(lf, SK_ARMOUR, PR_NOVICE); + if (isarmour(o) && canwear(lf, o, BP_NONE)) { + flag_t *f; + // evasion penalty? + f = hasflag(o->flags, F_EVASION); + if (f && (f->val[0] < 0)) { + giveskilllev(lf, SK_ARMOUR, PR_NOVICE); + } + } + if (isshield(o) && canwear(lf, o, BP_NONE)) { + giveskilllev(lf, SK_SHIELDS, PR_NOVICE); } - } - if (isshield(o) && canwear(lf, o, BP_NONE)) { - giveskilllev(lf, SK_SHIELDS, PR_NOVICE); } } @@ -8939,6 +9281,8 @@ void loseconcentration(lifeform_t *lf) { // stop hiding killflagsofid(lf->flags, F_HIDING); + + interrupt(lf); } // lose hp, and adjust damage based on resistances @@ -9071,6 +9415,19 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml setlastdam(lf, buf); + + // special case + if (lf->race->id == R_DREAMFUNGUS) { + if (cansee(player, lf)) { + animradialorth(lf->cell, 4, '}', C_MAGENTA); + msg("%s releases a cloud of purple spores!", lfname); + drawscreen(); + } + spellcloud(lf->cell, 3, '\0', C_MAGENTA, OT_S_SLEEP, 8); + } + + + // further effects if not dead... if (!isdead(lf)) { // fight back if required @@ -9089,7 +9446,9 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml } } else if (prebleed != postbleed) { if (ispetof(lf, player) && !canhear(player, lf->cell)) { - warn("You feel worried about your %s.", noprefix(lfname)); + char realname[BUFLEN]; + real_getlfname(lf, realname, B_FALSE); + warn("You feel worried about your %s.", noprefix(realname)); } else { makenoise(lf, N_LOWHP); } @@ -9151,14 +9510,14 @@ void makefriendly(lifeform_t *who, int howlong) { } -void makenauseated(lifeform_t *lf, int amt, int howlong) { +int makenauseated(lifeform_t *lf, int amt, int howlong) { flag_t *f; - if (lfhasflag(lf, F_UNDEAD)) return; - if (!hasbp(lf, BP_HEAD)) return; // can't smell with no head - if (lfhasflag(lf, F_UNDEAD)) return; + if (lfhasflag(lf, F_UNDEAD)) return B_TRUE; + if (!hasbp(lf, BP_HEAD)) return B_TRUE; // can't smell with no head + if (lfhasflag(lf, F_UNDEAD)) return B_TRUE; - if (!lfhasflag(lf, F_HUMANOID)) return; + if (!lfhasflag(lf, F_HUMANOID)) return B_TRUE; f = lfhasflag(lf, F_NAUSEATED); if (f) { @@ -9169,6 +9528,7 @@ void makenauseated(lifeform_t *lf, int amt, int howlong) { } else { addtempflag(lf->flags, F_NAUSEATED, amt, NA, NA, NULL, howlong); } + return B_FALSE; } void makenoise(lifeform_t *lf, enum NOISETYPE nid) { @@ -9386,41 +9746,39 @@ void modhunger(lifeform_t *lf, int amt) { f->val[0] += amt; posthlev = gethungerlevel(f->val[0]); - - // announce - if (isplayer(lf)) { - if (posthlev == H_STARVED) { + if (posthlev == H_STARVED) { + if (isplayer(lf)) { msg("You collapse from starvation."); - setlastdam(lf, "starvation"); - lf->hp = 0; - } else if (prehlev != posthlev) { - if (posthlev != H_NONE) { - char buf[BUFLEN]; - int needfeeling = B_FALSE; - int needexclam = B_FALSE; - switch (posthlev) { - case H_PECKISH: - case H_HUNGRY: - case H_VHUNGRY: - needfeeling = B_TRUE; - break; - default: - needfeeling = B_FALSE; - break; - } - switch (posthlev) { - case H_STUFFED: - case H_STARVING: - needexclam = B_TRUE; - break; - default: - needexclam = B_FALSE; - break; - } - - - + } + setlastdam(lf, "starvation"); + lf->hp = 0; + } else if (prehlev != posthlev) { + if (posthlev != H_NONE) { + char buf[BUFLEN]; + int needfeeling = B_FALSE; + int needexclam = B_FALSE; + switch (posthlev) { + case H_PECKISH: + case H_HUNGRY: + case H_VHUNGRY: + needfeeling = B_TRUE; + break; + default: + needfeeling = B_FALSE; + break; + } + switch (posthlev) { + case H_STUFFED: + case H_STARVING: + needexclam = B_TRUE; + break; + default: + needexclam = B_FALSE; + break; + } + + if (isplayer(lf)) { gethungername(posthlev, buf); msg("You are %s%s%s%c", ((amt < 0) && (posthlev > H_NONE)) ? "still " : "", @@ -9429,20 +9787,18 @@ void modhunger(lifeform_t *lf, int amt) { (needexclam) ? '!' : '.'); statdirty = B_TRUE; + } - if ((posthlev >= H_VHUNGRY) && (amt > 0)) { - stopresting(lf); - } - if (posthlev > H_STARVING) { - useringofmiracles(lf, 2); - // reset hunger - f->val[0] = 0; - } - + if ((posthlev >= H_VHUNGRY) && (amt > 0)) { + stopresting(lf); + } + if (posthlev > H_STARVING) { + useringofmiracles(lf, 2); + // reset hunger + f->val[0] = 0; } } } - } float modifybystat(float num, lifeform_t *lf, enum ATTRIB att) { @@ -9785,6 +10141,17 @@ void poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char } } +int poisoncausesvomit(enum POISONTYPE ptype) { + switch (ptype) { + case P_FOOD: + case P_VENOM: + return B_TRUE; + default: + break; + } + return B_FALSE; +} + int poisonthreatenslife(lifeform_t *lf, flag_t *f) { float time,dam,totaldam; @@ -9995,6 +10362,10 @@ void startresting(lifeform_t *lf, int willtrain) { traincounter = NA; } + if (isplayer(lf)) { + lf->turnsskipped = 0; + } + // stop hiding killflagsofid(lf->flags, F_HIDING); @@ -10220,8 +10591,10 @@ int safetorest(lifeform_t *lf) { for (l = lf->cell->map->lf ; l ; l = l->next) { if ((l != lf) && cansee(lf, l) && areenemies(lf, l) ) { - reason = E_MONSTERNEARBY; - return B_FALSE; + if (!lfhasflag(l, F_HARMLESS)) { + reason = E_MONSTERNEARBY; + return B_FALSE; + } } } return B_TRUE; @@ -10572,16 +10945,18 @@ void initskills(void) { addskill(SK_ARMOUR, "Armour", "Helps maintain armour, reducing evasion penalties and armour damage."); addskill(SK_ATHLETICS, "Athletics", "Assists with sprinting and exhaustion recovery."); addskill(SK_BACKSTAB, "Backstab", "Lets you inflict massive damage with stabs when unseen."); + addskill(SK_COOKING, "Cooking", "Your ability to combine foods into nutritious meals."); addskill(SK_FIRSTAID, "First Aid", "Increases your healing rate and reduces duration of poison."); addskill(SK_LISTEN, "Listen", "How good you are at hearing and interpreting sounds."); addskill(SK_LOCKPICKING, "Lockpicking", "Enhances your ability to pick locks."); addskill(SK_CHANNELING, "Channeling", "Lets you make better use of magical items."); addskill(SK_TWOWEAPON, "Dual Weilding", "Allows you to weild two melee weapons at once."); addskill(SK_SHIELDS, "Shields", "Reduces shield accuracy penalty, and raises chance to block projectiles."); - addskill(SK_SPELLCASTING, "Spellcasting", "Determines your ability to cast spells from all schools."); + addskill(SK_SPELLCASTING, "Sorcery", "Determines your ability to cast spells from all schools."); addskill(SK_SPOTHIDDEN, "Searching", "Helps you to spot hidden traps or creatures."); addskill(SK_STEALTH, "Stealth", "Affects your ability to move silently."); addskill(SK_TECHUSAGE, "Technology", "Determines your comprehension of modern technological items."); + addskill(SK_TRAPS, "Traps", "Affects your ability to locate and disarm traps."); // knowledge addskill(SK_LORE_ARCANA, "Lore:Arcana", "Allows you a chance of recognising magical objects and creatures."); addskill(SK_LORE_DEMONS, "Lore:Demonology", "Determines your knowledge about demons."); @@ -10614,6 +10989,7 @@ void initskills(void) { } void interrupt(lifeform_t *lf) { + stopeating(lf); stopresting(lf); stoprunning(lf); killflagsofid(lf->flags, F_AUTOCMD); @@ -10726,6 +11102,12 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r case SC_POISON: attrib = getattr(lf, A_CON); break; + case SC_DISARM: + attrib = (getskill(lf, SK_TRAPS)*3); + if (attrib) { + attrib += (getattr(lf, A_DEX)/4); + } + break; case SC_DODGE: // getevasion returns 0-100 (ie. pct chance) // convert this to 0-20 @@ -10907,7 +11289,8 @@ int slipon(lifeform_t *lf, object_t *o) { // slip! if (isplayer(lf) || cansee(player, lf)) { char damstring[BUFLEN]; - msg("%s slip%s on %s and falls to the ground.",lfname, isplayer(lf) ? "" : "s", obname); + msg("%s slip%s on %s and fall%s to the ground.",lfname, isplayer(lf) ? "" : "s", obname, + isplayer(lf) ? "" : "s"); sprintf(damstring, "slipping on %s",obname); losehp(lf, 1, DT_FALL, NULL, damstring); } @@ -11034,15 +11417,21 @@ int stone(lifeform_t *lf) { return B_FALSE; } +void stopeating(lifeform_t *lf) { + flag_t *f; + f = lfhasflag(lf, F_EATING); + if (f) { + if (isplayer(lf)){ + msg("You stop eating."); + } + killflagsofid(lf->flags, F_EATING); + } + +} void stopresting(lifeform_t *lf) { flag_t *f; - if (isplayer(lf)) { - statdirty = B_TRUE; - needredraw = B_TRUE; - } - // stop training f = hasflag(lf->flags, F_TRAINING); if (f) { @@ -11055,6 +11444,7 @@ void stopresting(lifeform_t *lf) { capitalise(buf); msg("%s stops training.",buf); } + statdirty = B_TRUE; } // stop resting! f = isresting(lf); @@ -11067,6 +11457,7 @@ void stopresting(lifeform_t *lf) { getlfname(lf, buf); msg("%s stops resting.",buf); } + statdirty = B_TRUE; } } @@ -11154,6 +11545,7 @@ int takeoff(lifeform_t *lf, object_t *o) { if (obproduceslight(o)) { calclight((getoblocation(o))->map); + precalclos(lf); drawscreen(); } @@ -11276,6 +11668,11 @@ void timeeffectslf(lifeform_t *lf) { // time effects on lifeform's objects for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; + + if ((o->type->id == OT_ENERGYBLADE) && !hasactivespell(lf, OT_S_SUMMONWEAPON)) { + killob(o); + continue; + } timeeffectsob(o); } @@ -11344,6 +11741,11 @@ void turneffectslf(lifeform_t *lf) { } + if (hasactivespell(lf, OT_S_SUMMONWEAPON)) { + if (!hasob(lf->pack, OT_ENERGYBLADE)) { + stopspell(lf, OT_S_SUMMONWEAPON); + } + } if (lfhasflag(lf, F_MAGSHIELD)) { object_t *nexto; @@ -11472,7 +11874,7 @@ void turneffectslf(lifeform_t *lf) { // they are hiding, and you haven't spotted them yet f = lfhasflag(l, F_HIDING); - if (f && !lfhasflagval(lf, F_SPOTTED, l->id, NA, NA, NULL)) { + if (f && !lfhasflagval(lf, F_SPOTTED, l->id, NA, NA, NULL) && !isinbattle(lf)) { // can you see their cell? if (haslos(lf, l->cell)) { int bonus = 0; @@ -11506,20 +11908,30 @@ void turneffectslf(lifeform_t *lf) { } } - // secret doors? - if (isplayer(lf)) { + // secret doors, traps, etc? + if (isplayer(lf) && !isinbattle(lf) && !isblind(lf)) { for (i = 0; i < lf->nlos; i++) { - object_t *o; - o = hasobwithflag(lf->los[i]->obpile, F_SECRETDOOR); - if (o) { - flag_t *f; - f = hasflag(o->flags, F_SECRETDOOR); - if (skillcheck(lf, SC_SEARCH, f->val[0], 0)) { - char obname[BUFLEN]; - // reveal it - getobname(o, obname, o->amt); - msg("You notice %s!",obname); - killflag(f); + if (!lf->los[i]->lf) { + object_t *o; + o = hasobwithflag(lf->los[i]->obpile, F_SECRET); + if (o) { + flag_t *f; + int mod = 0; + + if (hasflag(o->flags, F_TRAP)) { + mod += getskill(lf, SK_TRAPS); + } + + f = hasflag(o->flags, F_SECRET); + if (skillcheck(lf, SC_SEARCH, f->val[0], mod)) { + char obname[BUFLEN]; + // reveal it + getobname(o, obname, o->amt); + msg("You notice %s!",obname); + killflag(f); + needredraw = B_TRUE; + drawscreen(); + } } } } @@ -11569,7 +11981,11 @@ void turneffectslf(lifeform_t *lf) { if (isdead(lf)) return; + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// // IMPORTANT: any potentially damaging effects have to go after here... + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// f = hasflag(lf->flags, F_POISONED); if (f) { // chance of fighting it off - gets easier over time. @@ -11580,22 +11996,50 @@ void turneffectslf(lifeform_t *lf) { // chance of losing hp if (rnd(1,100) <= getpoisondamchance(f->val[0])) { char buf[BUFLEN]; - if (isplayer(lf)) { - msg("You %s violently.", getpoisondamverb(f->val[0])); - } else if (cansee(player, lf)) { - char lfname[BUFLEN]; - getlfname(lf, lfname); - msg("%s %ss violently.", lfname, getpoisondamverb(f->val[0])); + if (!hasflag(lf->flags, F_ASLEEP)) { + if (isplayer(lf)) { + msg("You %s violently.", getpoisondamverb(f->val[0])); + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s %ss violently.", lfname, getpoisondamverb(f->val[0])); + } + taketime(lf, getactspeed(lf)); } + sprintf(buf, "poisoning^from %s",f->text); - losehp(lf, f->val[1], DT_DIRECT, NULL, buf); - taketime(lf,5); - addob(lf->cell->obpile, "pool of vomit"); + if (!hasflag(lf->flags, F_ASLEEP)) { + if (poisoncausesvomit(f->val[0])) { + addob(lf->cell->obpile, "pool of vomit"); + } + } loseconcentration(lf); } + + // extra effects + if (f->val[0] == P_COLD) { + if (rnd(1,100) <= 10) { + object_t *wep; + if (isplayer(lf)) { + msg("You shiver uncontrollably."); + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s shivers.", lfname); + } + wep = getweapon(lf); + if (wep) { + char wname[BUFLEN]; + getobname(wep, wname, 1); + drop(wep, wep->amt); + } + + loseconcentration(lf); + } + } } } @@ -11610,7 +12054,7 @@ void turneffectslf(lifeform_t *lf) { getlfname(lf, lfname); msg("%s %s.", lfname, rnd(0,1) ? "retches" : "gags"); } - taketime(lf,5); + taketime(lf,getactspeed(lf)); loseconcentration(lf); } @@ -11638,6 +12082,25 @@ void turneffectslf(lifeform_t *lf) { if (f) { applywalkdam(lf, roll(f->text), f->val[0], o); } + + f = hasflag(o->flags, F_CAUSESCOUGH); + if (f && !isimmuneto(lf->flags, DT_POISONGAS)) { + char obname[BUFLEN]; + getobname(o, obname, o->amt); + if (!skillcheck(lf, SC_CON, f->val[0] * o->amt, 0)) { + if (isplayer(lf)) { + msg("You cough on %s.", obname); + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s coughs on %s.",lfname, obname); + } + taketime(lf, 5); + loseconcentration(lf); + } + + } + // for flags which can occur multiple times for (f = o->flags->first ; f ; f = f->next) { @@ -11944,9 +12407,14 @@ int unweild(lifeform_t *lf, object_t *o) { if (obproduceslight(o)) { calclight((getoblocation(o))->map); + precalclos(lf); drawscreen(); } + if (o->type->id == OT_ENERGYBLADE) { + stopspell(lf, OT_S_SUMMONWEAPON); + } + return B_FALSE; } @@ -12035,6 +12503,7 @@ int usestairs(lifeform_t *lf, object_t *o) { } // announce + curs_set(1); if (isportal) { if (isplayer(lf)) { msg("You enter %s...", obname); @@ -12060,8 +12529,10 @@ int usestairs(lifeform_t *lf, object_t *o) { c = getcellindir(lf->cell, dir); if (c && c->lf) { if (areallies(lf, c->lf) || areenemies(lf, c->lf)) { - adjally[nadjallies] = c->lf; - nadjallies++; + if (!isimmobile(c->lf)) { + adjally[nadjallies] = c->lf; + nadjallies++; + } } } } @@ -12090,6 +12561,7 @@ int usestairs(lifeform_t *lf, object_t *o) { } } + curs_set(0); if (newcell) { int n; // check noone is in the way @@ -12194,7 +12666,7 @@ int validateraces(void) { cell_t fakecell; map_t fakemap; - // add undead flags + // add flags based on raceclass for (r = firstrace ; r ; r = r->next) { if (r->raceclass->id == RC_UNDEAD) { addflag(r->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL); @@ -12203,6 +12675,12 @@ int validateraces(void) { addflag(r->flags, F_DTIMMUNE, DT_DECAY, NA, NA, NULL); addflag(r->flags, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL); addflag(r->flags, F_DTVULN, DT_HOLY, NA, NA, NULL); + } else if (r->raceclass->id == RC_PLANT) { + addflag(r->flags, F_DTRESIST, DT_BASH, NA, NA, NULL); + addflag(r->flags, F_DTVULN, DT_FIRE, NA, NA, NULL); + addflag(r->flags, F_DTVULN, DT_COLD, NA, NA, NULL); + addflag(r->flags, F_DTVULN, DT_DECAY, NA, NA, NULL); + addflag(r->flags, F_FLAMMABLE, PERMENANT, NA, NA, NULL); } } @@ -12524,6 +13002,10 @@ int rest(lifeform_t *lf, int onpurpose) { } } // end if !poisoned + if (statdirty || needredraw) { + drawscreen(); + } + if (onpurpose && wantclearmsg) { if (isplayer(lf)) { // clear msg bar @@ -12761,6 +13243,8 @@ int weild(lifeform_t *lf, object_t *o) { if (o) { getobname(o, buf, o->amt); + } else { + sprintf(buf, "nothing"); } if (!canweild(lf, o)) { diff --git a/lf.h b/lf.h index d866f13..72e67f3 100644 --- a/lf.h +++ b/lf.h @@ -14,6 +14,7 @@ void autoskill(lifeform_t *lf); void autotarget(lifeform_t *lf); void autoweild(lifeform_t *lf); int appearsrandomly(enum RACE rid); +void awardxpfor(lifeform_t *killed, float pct); void bleed(lifeform_t *lf); void breakallgrabs(lifeform_t *lf); int calcxp(lifeform_t *lf); @@ -28,6 +29,7 @@ int canpolymorphto(enum RACE rid); int canpush(lifeform_t *lf, object_t *o, int dir); int canquaff(lifeform_t *lf, object_t *o); int cansee(lifeform_t *viewer, lifeform_t *viewee); +int cansleep(lifeform_t *lf); int canwear(lifeform_t *lf, object_t *o, enum BODYPART where); int canweild(lifeform_t *lf, object_t *o); int cantakeoff(lifeform_t *lf, object_t *o); @@ -132,6 +134,7 @@ char *getplayername(char *buf); char *getplayernamefull(char *buf); int getpoisondamchance(enum POISONTYPE ptype); char *getpoisondamverb(enum POISONTYPE ptype); +char *getpoisondesc(enum POISONTYPE ptype); char *getpoisonname(enum POISONTYPE ptype); int getraceclass(lifeform_t *lf); int getracerarity(map_t *map, enum RACE rid); @@ -167,6 +170,7 @@ flag_t *lfhasflag(lifeform_t *lf, enum FLAG fid); flag_t *lfhasflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, char *text); flag_t *lfhasknownflag(lifeform_t *lf, enum FLAG fid); flag_t *lfhasknownflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, char *text); +int lfproduceslight(lifeform_t *lf); int lockpick(lifeform_t *lf, object_t *target, object_t *device); void loseobflags(lifeform_t *lf, object_t *o, int kind); int hasbp(lifeform_t *lf, enum BODYPART bp); @@ -191,6 +195,7 @@ int isfriendly(lifeform_t *lf); int isgenius(lifeform_t *lf); int isimmobile(lifeform_t *lf); flag_t *isimmuneto(flagpile_t *fp, enum DAMTYPE dt); +int isinbattle(lifeform_t *lf); int isingunrange(lifeform_t *lf, cell_t *where); int isloreskill(enum SKILL skid); int ismaxedskill(lifeform_t *lf, enum SKILL skid); @@ -198,6 +203,7 @@ int ispeaceful(lifeform_t *lf); int ispetof(lifeform_t *lf, lifeform_t *owner); int isplayer(lifeform_t *lf); flag_t *ispoisoned(lifeform_t *lf); +flag_t *ispoisonedwith(lifeform_t *lf, enum POISONTYPE pt); int ispolymorphed(lifeform_t *lf); int isprone(lifeform_t *lf); flag_t *isresistantto(flagpile_t *fp, enum DAMTYPE dt); @@ -213,7 +219,7 @@ int losehp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, ch int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam); void losemp(lifeform_t *lf, int amt); void makefriendly(lifeform_t *lf, int howlong); -void makenauseated(lifeform_t *lf, int amt, int howlong); +int makenauseated(lifeform_t *lf, int amt, int howlong); void makenoise(lifeform_t *lf, enum NOISETYPE nid); void makepeaceful(lifeform_t *lf); lifeform_t *makezombie(object_t *o); @@ -227,6 +233,7 @@ void noise(cell_t *c, lifeform_t *noisemaker, char *text, char *seetext); void outfitlf(lifeform_t *lf); int pickup(lifeform_t *lf, object_t *what, int howmany, int fromground); void poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat); +int poisoncausesvomit(enum POISONTYPE ptype); int poisonthreatenslife(lifeform_t *lf, flag_t *f); void practice(lifeform_t *lf, enum SKILL skid); void precalclos(lifeform_t *lf); @@ -256,6 +263,7 @@ int skillcheckvs(lifeform_t *lf1, enum CHECKTYPE ct1, int mod1, lifeform_t *lf2, int slipon(lifeform_t *lf, object_t *o); void sortlf(map_t *map, lifeform_t *lf); int stone(lifeform_t *lf); +void stopeating(lifeform_t *lf); void stopresting(lifeform_t *lf); void stoprunning(lifeform_t *lf); int testammo(lifeform_t *lf, object_t *o); diff --git a/log.txt b/log.txt index b7ea667..7d606d5 100644 --- a/log.txt +++ b/log.txt @@ -4,50 +4,15 @@ ====== NEW LOGFILE ==== givejob() starting. -fireat(): dam = throwdam(2) * speed(3) = 6 -fireat(): dam = throwdam(2) * speed(3) = 6 -fireat(): dam = throwdam(3) * speed(3) = 9 -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -fireat(): dam = throwdam(2) * speed(2) = 4 -xxx -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -fireat(): dam = throwdam(3) * speed(2) = 6 -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast charge - specific spell check failed } -.oO { cant cast charge - specific spell check failed } -fireat(): dam = throwdam(2) * speed(2) = 4 -fireat(): dam = throwdam(2) * speed(3) = 6 -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -.oO { cant cast suck blood - targetting conditions cannot be met } -xxx -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -fireat(): dam = throwdam(2) * speed(3) = 6 -fireat(): dam = throwdam(3) * speed(3) = 9 -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -.oO { cant cast grab - targetting conditions cannot be met } -xxx +x +x +x +x +x +x +x +x +x +x +x +x diff --git a/map.c b/map.c index 6e15582..04115b2 100644 --- a/map.c +++ b/map.c @@ -24,6 +24,10 @@ extern enum OBCLASS sortorder[]; extern enum ERROR reason; +extern enum GAMEMODE gamemode; + +extern int needredraw; + cell_t *addcell(map_t *m, int x, int y) { cell_t *cell; m->cell[(y*m->w)+x] = malloc(sizeof(cell_t)); @@ -143,7 +147,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto //if (lf->cell->map->beingcreated) { if (autogen) { // sometimes start off asleep in new maps - if (!lfhasflag(lf, F_DEAF)) { + if (!lfhasflag(lf, F_DEAF) && cansleep(lf)) { // TODO: base this on the time, and whether monster is nocturnal if (rnd(1,2) == 1) { addflag(lf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL); @@ -232,7 +236,13 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto char buf[BUFLEN]; int i; for (i = 0; i < nobs; i++) { - if (getrandomob(c->map, buf)) addob(lf->pack, buf); + if (getrandomob(c->map, buf)) { + object_t *o; + o = addob(lf->pack, buf); + if (o && !canpickup(lf, o, o->amt)) { + killob(o); + } + } } } } @@ -414,7 +424,7 @@ int getcelldistorth(cell_t *src, cell_t *dst) { // add x/y } //populates 'g' with the contects of cell 'c', as seen by 'viewer' -// if we can't see anything there, set g->ch to NUL. +// if we don't have LOS to there, set g->ch to NUL. void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { glyph_t tempgl; @@ -424,7 +434,6 @@ void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { if (haslos(viewer, c)) { // show cell contents //drawcellwithcontents(cell, x-viewx, y-viewy); - if (c->lf && cansee(viewer, c->lf)) { // lifeform here which we can see // draw the lf's race glyph *g = *(getlfglyph(c->lf)); @@ -449,25 +458,25 @@ void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { // draw highest object in sort order o = gettopobject(c); - if (hasobwithflag(c->obpile, F_SECRETDOOR)) { - celltype_t *ct; - ct = findcelltype(getwallcelltype(c->map->habitat)); - *g = ct->glyph; - } else if (o) { + if (o) { // return the object's glyph *g = *(getglyph(o)); } else { - // should never happen. if it does, just show the - // first object - dblog("Warn: sorted object glyph drawing matching nothing!"); - //mvwprintw(gamewin, y, x, "%c", cell->obpile->first->type->obclass->glyph); - //drawglyph(&cell->obpile->first->type->obclass->glyph, x, y); - *g = c->obpile->first->type->obclass->glyph; + // objects here, but we can't see them. draw the cell. + // otherwise just draw the cell + //*g = c->obpile->first->type->obclass->glyph; + *g = c->type->glyph; + if (!islit(c)) { + g->colour = C_BLUE; + } } } else { // draw cell normally //drawcell(cell, x, y); *g = c->type->glyph; + if (!islit(c)) { + g->colour = C_BLUE; + } } } else { // can't see the cell void *thing; @@ -533,9 +542,8 @@ object_t *gettopobject(cell_t *where) { // draw impassable objects first... o = hasobwithflag(where->obpile, F_IMPASSABLE); if (o) { - // ignore secret doors - if (issecretdoor(o)) { - return NULL; + // ignore hidden traps, but not secret doors + if (hasflag(o->flags, F_SECRET) && !isdoor(o, NULL)) { } else { return o; } @@ -549,9 +557,7 @@ object_t *gettopobject(cell_t *where) { // appear first. for (o = where->obpile->last ; o ; o = o->prev) { if (o->type->obclass->id == sortorder[c]) { - if (issecretdoor(o)) { - return NULL; - } else { + if (!hasflag(o->flags, F_SECRET)){ return o; } } @@ -564,10 +570,20 @@ object_t *gettopobject(cell_t *where) { void calclight(map_t *map) { int x,y; cell_t *c; + int i; + + // remember lit values for cells in player's los + if (gamemode == GM_GAMESTARTED) { + for (i = 0; i < player->nlos; i++) { + player->los[i]->lastlit = player->los[i]->lit; + } + } + for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x,y); //if (c && (c->lit == L_PERMLIGHT) && (c->lit != L_PERMDARK)) c->lit = B_FALSE; + c->lastlit = c->lit; if (c && (c->lit != L_PERMLIGHT) && (c->lit != L_PERMDARK)) c->lit = B_FALSE; } } @@ -580,7 +596,7 @@ void calclight(map_t *map) { // lit based on depth if ((map->depth <= 5) && (c->lit != L_PERMDARK)) { - c->lit = L_PERMLIGHT; + makelit(c, L_PERMLIGHT, -1); } // TODO: has dark producing lf? @@ -588,20 +604,10 @@ void calclight(map_t *map) { // has lightproducing lf? (ie.hasflag f_produceslight) if (c->lf) { - if (lfhasflag(c->lf, F_PRODUCESLIGHT)) { - sumflags(c->lf->flags, F_PRODUCESLIGHT, &radius, NULL, NULL); - + radius = lfproduceslight(c->lf); + if (radius) { makelitradius(c, radius, L_TEMP, -1); } - // objects in hands or on body... - for (o = c->lf->pack->first ; o ; o = o->next) { - if (isequipped(o)) { - radius = obproduceslight(o); - if (radius) { - makelitradius(c, radius, L_TEMP, -1); - } - } - } } // has light-producing object on ground? for (o = c->obpile->first ; o ; o = o->next) { @@ -614,6 +620,15 @@ void calclight(map_t *map) { } } } + // did any lit values within player's los change? + if (gamemode == GM_GAMESTARTED) { + for (i = 0; i < player->nlos; i++) { + if (player->los[i]->lastlit != player->los[i]->lit) { + needredraw = B_TRUE; + break; + } + } + } } int calcroompos(map_t *map, int w, int h, int *bx, int *by) { @@ -1516,7 +1531,7 @@ printf("dump of map '%s' (%d x %d):\n",map->name, map->w, map->h); } } -// dirtype of DT_COMPASS will give a square explosion +// dirtype of DT_ORTH will give a square explosion // dirtype of DT_COMPASS will give a circular explosion void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int dirtype, int wantannounce) { int x,y; @@ -1589,7 +1604,6 @@ void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *c } - damageallobs(o, c->obpile, dam, DT_EXPLOSIVE); if (killwalls) { @@ -2090,6 +2104,16 @@ int hasobject(cell_t *c) { return B_FALSE; } +int hasknownobject(cell_t *c) { + object_t *o; + for (o = c->obpile->first ; o ; o = o->next) { + if (o && !hasflag(o->flags, F_SECRET)) { + return B_TRUE; + } + } + return B_FALSE; +} + int isadjacent(cell_t *src, cell_t *dst) { if (getcelldist(src, dst) == 1) { return B_TRUE; @@ -2403,7 +2427,7 @@ void makedoor(cell_t *cell) { chance = rolldie(1,6) - (m->depth / 10); if (chance <= 1) { - addflag(o->flags, F_LOCKED, B_TRUE, NA, NA, NULL); + addflag(o->flags, F_LOCKED, B_TRUE, 19 + (m->depth), NA, NULL); } // make it secret? @@ -2414,7 +2438,7 @@ void makedoor(cell_t *cell) { // l10 = 25 // l20 = 30 if (chance <= 1) { - addflag(o->flags, F_SECRETDOOR, 20 + (m->depth / 2), NA, NA, NULL); + addflag(o->flags, F_SECRET, 20 + (m->depth / 2), NA, NA, NULL); } } } diff --git a/map.h b/map.h index 568a2ef..b947304 100644 --- a/map.h +++ b/map.h @@ -46,6 +46,7 @@ int getslipperyness(cell_t *c, object_t **slipob); cell_t *getstairdestination(object_t *o); object_t *hasenterableobject(cell_t *c); lifeform_t *haslf(cell_t *c); +int hasknownobject(cell_t *c); int hasobject(cell_t *c); int isadjacent(cell_t *src, cell_t *dst); int isdark(cell_t *c); diff --git a/move.c b/move.c index b714248..b097cdc 100644 --- a/move.c +++ b/move.c @@ -73,6 +73,12 @@ int canmove(lifeform_t *lf, int dir, enum ERROR *error) { } } + // not attacking + if (lfhasflag(lf, F_DOESNTMOVE) && !cell->lf) { + *error = E_CANTMOVE; + return B_FALSE; + } + return cellwalkable(lf, cell, error); } @@ -209,7 +215,7 @@ int cellwalkable(lifeform_t *lf, cell_t *cell, enum ERROR *error) { rdata = o; if (error) { if (isdoor(o, NULL)) { - if (hasflag(o->flags, F_SECRETDOOR)) { + if (hasflag(o->flags, F_SECRET)) { *error = E_WALLINWAY; } else { *error = E_DOORINWAY; @@ -658,6 +664,10 @@ int movelf(lifeform_t *lf, cell_t *newcell) { getlfname(lf, lfname); + if (isplayer(lf) || cansee(player, lf)) { + needredraw = B_TRUE; + } + if (newcell->map != lf->cell->map) { changedlev = B_TRUE; } @@ -690,8 +700,13 @@ int movelf(lifeform_t *lf, cell_t *newcell) { newcell->lf = lf; // update light - if (changedlev && isplayer(lf)) { + if (lfproduceslight(lf) || (changedlev && isplayer(lf))) { calclight(lf->cell->map); + precalclos(lf); + } + + if (isplayer(lf) || cansee(player, lf)) { + needredraw = B_TRUE; } didmsg = moveeffects(lf); @@ -806,7 +821,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) { // instead. // if you walked into a new fully lit room, reveal it if ((postroom > 0) && (postroom != preroom)) { - cell_t *c[MAXRETCELLS]; + cell_t *c[MAX_MAPW*MAX_MAPH]; int ncells; int i,alllit = B_TRUE,allknown = B_TRUE; @@ -831,7 +846,6 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } } - // does anyone else see you? if ((gamemode == GM_GAMESTARTED)) { for (l = newcell->map->lf ; l ; l = l->next) { @@ -917,7 +931,7 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { // just moved into a dark area - announce it. if (postdark && !predark && !lfhasflag(lf, F_DONEDARKMSG)) { - msg("It is pitch black!"); + msg("It is %s!", (lf->cell->lit == L_PERMDARK) ? "unnaturally dark" : "pitch black"); addflag(lf->flags, F_DONEDARKMSG, B_TRUE, NA, NA, NULL); dontclearmsg = B_TRUE; } @@ -985,11 +999,39 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { // slip on blood in new cell? if (!isairborne(lf)) { int slip; + object_t *o,*nexto; object_t *slipob; + slip = getslipperyness(newcell, &slipob); if (slip && !skillcheck(lf, SC_SLIP, slip, 0)) { slipon(lf, slipob); } + + // activate traps + for (o = newcell->obpile->first ; o ; o = nexto ) { + nexto = o->next; + if (hasflag(o->flags, F_TRAP)) { + char trapname[BUFLEN]; + getobname(o, trapname, 1); + if (haslos(player, newcell)) { + msg("%s trigger%s %s!", lfname, isplayer(lf) ? "" : "s", trapname); + if (isplayer(lf)) more(); + } + trapeffects(o, o->type->id, lf); + interrupt(lf); + if (haslos(player, newcell)) { + // no longer hidden + killflagsofid(o->flags, F_SECRET); + } + switch (o->type->id) { + case OT_TRAPGAS: + removeob(o, o->amt); + break; + default: + break; + } + } + } } return B_FALSE; @@ -1043,7 +1085,6 @@ int opendoor(lifeform_t *lf, object_t *o) { doorcell = o->pile->where; assert(doorcell); - getobname(o, obname, 1); if (!isdoor(o, NULL)) { @@ -1079,14 +1120,19 @@ int opendoor(lifeform_t *lf, object_t *o) { int openit = B_TRUE; f = hasflag(o->flags, F_JAMMED); if (f) { - - f->val[0] --; - // loosen a bit + int amt; + amt = getattr(lf, A_STR) - 10; + if (amt < 0) amt = 0; + if (lf && isplayer(lf)) { msg("You yank on the door but it holds fast."); } - if (f->val[0] <= 0) { - killflag(f); + // loosen a bit + if (amt) { + f->val[0] -= amt; + if (f->val[0] <= 0) { + killflag(f); + } } openit = B_FALSE; // don't open the door } @@ -1099,6 +1145,8 @@ int opendoor(lifeform_t *lf, object_t *o) { if (f) killflag(f); f = hasflag(o->flags, F_BLOCKSVIEW); if (f) killflag(f); + f = hasflag(o->flags, F_SECRET); + if (f) killflag(f); if (lf) { if (isplayer(lf)) { @@ -1223,6 +1271,10 @@ int closedoor(lifeform_t *lf, object_t *o) { taketime(lf, getactspeed(lf)); touch(lf, o); } + if (player && haslos(player, cell)) { + needredraw = B_TRUE; + drawscreen(); + } } return B_FALSE; @@ -1448,7 +1500,23 @@ int initiatemove(lifeform_t *lf, cell_t *cell, int *didmsg) { return B_FALSE; } +void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int onpurpose) { + cell_t *cell1,*cell2; + cell1 = lf1->cell; + cell2 = lf2->cell; + + // make lf who is there vanish temporarily... + cell2->lf = NULL; + lf2->cell = NULL; + + // move you.. + moveto(lf1, cell2, onpurpose, B_FALSE); + + // move them... + lf2->cell = cell1; + cell1->lf = lf2; +} // teleport somewhere, along with puffs of smoke etc int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) { @@ -1681,25 +1749,13 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { case E_LFINWAY: if (canswapwith(lf, cell->lf)) { lifeform_t *lfinway; - cell_t *oldcell; // otherwise swap locations. - - // make lf who is there vanish temporarily... lfinway = cell->lf; - cell->lf = NULL; - lfinway->cell = NULL; - // remember your cell - oldcell = lf->cell; + swapplaces(lf, lfinway, onpurpose); - // move you.. - moveto(lf, cell, onpurpose, B_FALSE); if (onpurpose) taketime(lf, getmovespeed(lf)); - // move them... - lfinway->cell = oldcell; - oldcell->lf = lfinway; - reason = E_OK; if (isplayer(lf)) { char lfname[BUFLEN]; getlfname(lfinway, lfname); @@ -1769,7 +1825,6 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { //object_t *o; iq = getiqname(getattr(lf, A_IQ), NULL); - // default if (error) { *error = E_OK; @@ -1808,7 +1863,7 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { } } - // for intelligent things... + // for at least average iq things... if (iq >= IQ_AVERAGE) { // don't move if in pain if (lfhasflag(lf, F_PAIN)) { @@ -1832,8 +1887,26 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { killflag(f); } } + + if (hasflag(o->flags, F_TRAP)) { + if (hasflag(o->flags, F_SECRET)) { + // hidden traps? + if (iq >= IQ_SMART) { + if (error) *error = E_WONT; + return B_FALSE; + } + } else { + // non-hidden traps? + if (iq >= IQ_AVERAGE) { + if (error) *error = E_WONT; + return B_FALSE; + } + } + } } return B_TRUE; } + + diff --git a/move.h b/move.h index a2cc4be..07f37ca 100644 --- a/move.h +++ b/move.h @@ -21,6 +21,7 @@ int opendoorat(lifeform_t *lf, cell_t *c); int opendoor(lifeform_t *lf, object_t *o); int pullnextto(lifeform_t *lf, cell_t *c); int initiatemove(lifeform_t *lf, cell_t *cell, int *didmsg); +void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int onpurpose); int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke); int trymove(lifeform_t *lf, int dir, int onpurpose); int tryrun(lifeform_t *lf, int dir); diff --git a/nexus.c b/nexus.c index 7d66db4..f1c4bba 100644 --- a/nexus.c +++ b/nexus.c @@ -197,6 +197,19 @@ int main(int argc, char **argv) { getchoice(&prompt); sk = (skill_t *) prompt.result; giveskill(player, sk->id); + switch (sk->id) { + case SK_SS_AIR: + addflag(player->flags, F_CANCAST, OT_S_MIST, NA, NA, NULL); + break; + case SK_SS_COLD: + addflag(player->flags, F_CANCAST, OT_S_FROSTBITE, NA, NA, NULL); + break; + case SK_SS_FIRE: + addflag(player->flags, F_CANCAST, OT_S_SPARK, NA, NA, NULL); + break; + default: + break; + } } @@ -271,6 +284,9 @@ int main(int argc, char **argv) { } } + needredraw = B_TRUE; + statdirty = B_TRUE; + // show level drawscreen(); @@ -471,10 +487,12 @@ void donextturn(map_t *map) { // pre-calculate line of sight for this lifeform precalclos(who); + /* if (isplayer(who) || cansee(player, who)) { needredraw = B_TRUE; drawscreen(); } + */ // update gun targets autotarget(who); @@ -491,6 +509,24 @@ void donextturn(map_t *map) { int donormalmove = B_TRUE; flag_t *f; + // eating? + if (donormalmove) { + f = lfhasflag(who, F_EATING); + if (f) { + object_t *o; + o = findobbyid(who->pack, atol(f->text)); + if (!o) { + o = findobidinmap(who->cell->map, atol(f->text)); + } + if (o && caneat(who, o) && (getoblocation(o) == who->cell)) { + eat(who,o); + donormalmove = B_FALSE; + } else { + killflag(f); + } + } + } + // resting? if (donormalmove) { f = isresting(who); @@ -503,6 +539,12 @@ void donextturn(map_t *map) { msg("Stopped %s.",(f->id == F_TRAINING) ? "training" : "resting"); killflag(f); } else { + if (isplayer(who)) { + if (++who->turnsskipped >= 10) { + msg("Time passes..."); + who->turnsskipped = 0; + } + } rest(who, B_TRUE); donormalmove = B_FALSE; } @@ -513,6 +555,12 @@ void donextturn(map_t *map) { if (donormalmove) { // paralyzed etc? if (isimmobile(who)) { + if (isplayer(who)) { + if (++who->turnsskipped >= 10) { + msg("Time passes..."); + who->turnsskipped = 0; + } + } rest(who, B_FALSE); donormalmove = B_FALSE; } @@ -535,8 +583,10 @@ void donextturn(map_t *map) { if (hasflag(player->flags, F_ASLEEP)) { // ooo is this right ? needredraw = B_FALSE; + /* } else if (isdead(who) || cansee(player, who)) { needredraw = B_TRUE; + */ } // check for death etc @@ -550,8 +600,6 @@ void donextturn(map_t *map) { // note: can't use 'who->' below since 'who' might have died // and been de-alloced during checkdeath() above. timeeffectsworld(player->cell->map); // in case the player changed levels! - - } char *getdirname(int dir) { diff --git a/objects.c b/objects.c index 33d1af7..59c78c3 100644 --- a/objects.c +++ b/objects.c @@ -704,7 +704,10 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { if (o->type->id == OT_CORPSE) { flag_t *rf, *cf; - assert(corpserace); + if (!corpserace) { + // random one. + corpserace = getrandomrace(NULL, 0); + } o->weight = corpserace->weight; o->material = corpserace->material; @@ -851,9 +854,11 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { } break; case OM_FROZEN: - // made of ice - // note: not using changemat() here to avoid adding f_frozen twice. - o->material = findmaterial(MT_ICE); + if (o->material->id != MT_FIRE) { // fire can't be frozen! + // made of ice + // note: not using changemat() here to avoid adding f_frozen twice. + o->material = findmaterial(MT_ICE); + } break; case OM_MASTERWORK: if (isweapon(o) || isarmour(o)) { @@ -1050,6 +1055,12 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { } } + if (gamemode == GM_GAMESTARTED) { + if (where->where && haslos(player, where->where)) { + needredraw = B_TRUE; + } + } + // return the last object given return o; } @@ -1057,7 +1068,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { // add objects in a circle // returns # added. -int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fromlf) { +int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fromlf, enum LOFTYPE needlof) { int (*distfunc)(cell_t *, cell_t *); int x,y; cell_t *c; @@ -1074,7 +1085,7 @@ int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fr for (y = where->y - range; y <= where->y + range; y++) { for (x = where->x - range; x <= where->x + range; x++) { c = getcellat(where->map, x,y); - if (distfunc(where, c) <= range) { + if (c && haslof(where, c, needlof, NULL) && (distfunc(where, c) <= range)) { if (!c->type->solid) { object_t *o; o = addob(c->obpile, name); @@ -1331,17 +1342,22 @@ void adjustdamob(object_t *o, unsigned int *dam, enum DAMTYPE damtype) { return; } } + // immune? if (isimmuneto(o->flags, damtype)) { *dam = 0; return; } if (isresistantto(o->flags, damtype)) { - *dam /= 2; + // no resistances etc if rusty... + if (!hasflag(o->flags, F_RUSTED)) { + *dam /= 2; + } } if (isvulnto(o->flags, damtype)) { *dam *= 2; } + // some damage types never hurts objects if (damtype == DT_POISONGAS) { *dam = 0; @@ -1665,12 +1681,18 @@ int canbepoisoned(enum OBTYPE oid) { } // does the pile "op" have an object we can -// stack "o" with +// stack "match" with object_t *canstackob(obpile_t *op, object_t *match) { object_t *o; + flag_t *f; if (!hasflag(match->flags, F_STACKABLE)) { return NULL; } + // can't stack damaged objects + f = hasflag(match->flags, F_OBHP); + if (f) { + if (f->val[0] != f->val[1]) return NULL; + } for (o = op->first ; o ; o = o->next) { if (o != match) { if (obpropsmatch(o, match)) return o; @@ -1852,7 +1874,7 @@ int countnoncosmeticobs(obpile_t *op) { int count = 0; for (o = op->first ; o ; o = o->next) { - if (!hasflag(o->flags, F_COSMETIC)) { + if (!hasflag(o->flags, F_COSMETIC) && !hasflag(o->flags, F_SECRET)) { count++; } } @@ -2192,13 +2214,20 @@ glyph_t *getglyph(object_t *o) { int isopen; char g = ' '; // default int col = C_GREY; + cell_t *obloc; + obloc = getoblocation(o); + if (isdoor(o, &isopen)) { - if (isopen) { - g = '-'; + if (issecretdoor(o)) { + return &(findcelltype(getwallcelltype(obloc->map->habitat))->glyph); } else { - g = '+'; + if (isopen) { + g = '-'; + } else { + g = '+'; + } + col = getmaterialcolour(o->material->id); } - col = getmaterialcolour(o->material->id); } else { f = hasflag(o->flags, F_GLYPH); if (f) { @@ -2242,12 +2271,28 @@ void fragments(cell_t *centre, char *what, int speed, int howfar) { if (maxdist == 0) { wantdist = 0; } else { - wantdist = rnd(1,(howfar > maxdist) ? maxdist : howfar); + int realmax; + if (howfar > maxdist) { + realmax = (maxdist-1); + } else { + realmax = howfar-1; + } + if (realmax < 1) { + wantdist = 0; + } else { + wantdist = rnd(1,realmax); + } } // go that far dst = centre; for (n = 0; n < wantdist; n++) { - dst = getcellindir(dst, dir); + cell_t *newdst; + newdst = getcellindir(dst, dir); + if (newdst) { + dst = newdst; + } else { + break; + } } // add object addob(dst->obpile, what); @@ -2638,7 +2683,7 @@ char *getdamname(enum DAMTYPE damtype) { case DT_ALL: return "all damage"; case DT_ACID: return "acid"; case DT_BASH: return "bludgeoning"; - case DT_BITE: return "bite"; + case DT_BITE: return "biting"; case DT_CHOP: return "chopping"; case DT_COLD: return "cold"; case DT_CRUSH: return "crushing"; @@ -2648,7 +2693,7 @@ char *getdamname(enum DAMTYPE damtype) { case DT_EXPLOSIVE: return "explosive"; case DT_FALL: return "falling"; case DT_FIRE: return "fire"; - case DT_HOLY: return "holy"; + case DT_HOLY: return "holy damage"; case DT_LIGHT: return "light"; case DT_MAGIC: return "magical"; case DT_MELT: return "melting"; @@ -2766,6 +2811,7 @@ int getmaterialvalue(enum MATERIAL mat) { case MT_ACID: return 0; case MT_FOOD: + case MT_PLANT: case MT_ICE: case MT_STONE: return 1; @@ -3151,6 +3197,13 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan corpserace = findrace(f->val[0]); sprintf(basename, "statue of a %s",corpserace->name); } + } else if ((o->type->id == OT_STEW) || (o->type->id == OT_JERKY)) { + f = hasflag(o->flags, F_LINKRACE); + if (f) { + race_t *r; + r = findrace(f->val[0]); + sprintf(basename, "%s %s",r->name, o->type->name); + } } // handle ALL @@ -3196,6 +3249,13 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } } + // eaten? + f = hasflag(o->flags, F_EDIBLE); + if (f && (f->val[2] != NA)) { + strcat(localbuf, "partially eaten "); + } + + // material changed? if (o->material != o->type->material) { switch (o->material->id) { @@ -3351,6 +3411,12 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan strcat(localbuf, " [magic]"); } } + + if (getskill(player, SK_COOKING) >= PR_BEGINNER) { + if (isbadfood(o)) { + strcat(localbuf, " [badfood]"); + } + } } // apply prefix now! @@ -3394,8 +3460,9 @@ char *getobconditionname(object_t *o, char *buf) { } if (iscorpse(o)) { - // you only know it's rotting if you are smart - if (isrotting(o) && (iqb >= IQ_SMART)) { + // you only know it's rotting if you are smart or a cook + if (isrotting(o) && + ( (iqb >= IQ_SMART) || getskill(player, SK_COOKING)) ) { sprintf(buf, "rotting"); } else { strcpy(buf, ""); @@ -3445,9 +3512,9 @@ char *getobhurtname(object_t *o, enum DAMTYPE damtype) { } case DT_MELT: if (o->amt == 1) { - return "is melting"; + return "melts a bit"; } else { - return "are melting"; + return "melt a bit"; } default: if (o->amt == 1) { @@ -4202,6 +4269,8 @@ void initobjects(void) { // brands modifiers - flags should be UNKNOWN! // also don't double up with names of scrolls etc. // ie. spellbook of flight, boots of flight. + + // weapons addbrand(BR_BALANCE, "of balance", BP_WEAPON); addflag_real(lastbrand->flags, F_BALANCE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_PYROMANIA, "of pyromania", BP_WEAPON); @@ -4210,25 +4279,28 @@ void initobjects(void) { addflag_real(lastbrand->flags, F_REVENGE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_SHARPNESS, "of sharpness", BP_WEAPON); addflag_real(lastbrand->flags, F_ARMOURPIERCE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); + addbrand(BR_IMPACT, "of impact", BP_WEAPON); // TODO: make thisonly go ont obashing weapons + addflag_real(lastbrand->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); + // feet addbrand(BR_LEVITATION, "of hovering", BP_FEET); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_LEVITATING, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_FEATHERFALL, "of featherfall", BP_FEET); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_DTIMMUNE, DT_FALL, NA, NULL, PERMENANT, B_UNKNOWN, -1); - addbrand(BR_SWIFTNESS, "of swiftness", BP_FEET); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_FASTMOVE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_SLOTH, "of sloth", BP_FEET); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_SLOWMOVE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1); + // hands addbrand(BR_POWER, "of power", BP_HANDS); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, 3, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_WEAKNESS, "of feebleness", BP_HANDS); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, -3, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_NIMBLENESS, "of nimbleness", BP_HANDS); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_DEX, 3, NULL, PERMENANT, B_UNKNOWN, -1); - addbrand(BR_IMPACT, "of impact", BP_WEAPON); // TODO: make thisonly go ont obashing weapons - addflag_real(lastbrand->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); + + // head addbrand(BR_THINKING, "of thinking", BP_HEAD); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_IQ, 3, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_KNOWLEDGE, "of knowledge", BP_HEAD); @@ -4238,6 +4310,8 @@ void initobjects(void) { addflag_real(lastbrand->flags, F_EQUIPCONFER, F_DETECTLIFE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_TELEKINESIS, "of the poltergeist", BP_HEAD); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_CANWILL, OT_S_TELEKINESIS, NA, NULL, PERMENANT, B_UNKNOWN, -1); + + // waist addbrand(BR_GIANTSTRENGTH, "of giant strength", BP_WAIST); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRSET, A_STR, 18, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_FEEBLENESS, "of feebleness", BP_WAIST); @@ -4246,18 +4320,31 @@ void initobjects(void) { addflag_real(lastbrand->flags, F_EQUIPCONFER, F_FLYING, B_TRUE, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_SPEED, "of swiftness", BP_WAIST); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_FASTACT, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1); + + // shoulders addbrand(BR_CONCEALMENT, "of concealment", BP_SHOULDERS); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_INVISIBLE, B_TRUE, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_ANTIMAG, "of antimagic", BP_SHOULDERS); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_RESISTMAG, 10, NA, NULL, PERMENANT, B_UNKNOWN, -1); + addbrand(BR_SHADOWS, "of shadows", BP_SHOULDERS); + addflag_real(lastbrand->flags, F_EQUIPCONFER, F_CANWILL, OT_A_DARKWALK, NA, NULL, PERMENANT, B_UNKNOWN, -1); + + // body addbrand(BR_HEALTH, "of health", BP_BODY); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_CON, 3, NULL, PERMENANT, B_UNKNOWN, -1); + + // materials addmaterial(MT_NOTHING, "nothing", 0); addmaterial(MT_MAGIC, "magical energy", 0); addmaterial(MT_FIRE, "fire", 0); addmaterial(MT_GAS, "gas", 0.5); + addmaterial(MT_PLANT, "plant matter", 1); + addflag(lastmaterial->flags, F_FLAMMABLE, PERMENANT, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTVULN, DT_FIRE, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTVULN, DT_ACID, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTIMMUNE, DT_WATER, NA, NA, NULL); addmaterial(MT_PAPER, "paper", 1); addflag(lastmaterial->flags, F_MATCONVERT, MT_WATER, NA, NA, "lump of soggy paper"); addflag(lastmaterial->flags, F_MATCONVERTTEXT, MT_WATER, NA, NA, "goes soggy"); @@ -4298,6 +4385,12 @@ void initobjects(void) { addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); addmaterial(MT_METAL, "metal", 13); addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTIMMUNE, DT_PIERCE, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTIMMUNE, DT_BITE, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTRESIST, DT_CHOP, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTRESIST, DT_SLASH, NA, NA, NULL); + addflag(lastmaterial->flags, F_DTRESIST, DT_PROJECTILE, NA, NA, NULL); addmaterial(MT_GLASS, "glass", 13); addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); addmaterial(MT_GOLD, "gold", 16); @@ -4432,6 +4525,15 @@ void initobjects(void) { addflag(lastot->flags, F_OBHP, 80, 80, NA, NULL); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "50-100 stones"); + addot(OT_ICICLE, "huge icicle", "A massive ice stalacmite.", MT_ICE, 200, OC_ROCK); + addflag(lastot->flags, F_GLYPH, C_CYAN, NA, NA, "'"); + addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 80, 80, NA, NULL); + addot(OT_STATUE, "statue", "A stone statue of a monster.", MT_STONE, 80, OC_ROCK); addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, ""); addflag(lastot->flags, F_GLYPH, NA, NA, NA, "'"); @@ -4469,6 +4571,69 @@ void initobjects(void) { addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + // traps + addot(OT_TRAPTRIP, "tripwire", "A thin wire at ankle height.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_TRAP, 10, B_FALSE, 20, NULL); + addflag(lastot->flags, F_RARITY, H_ALL, 90, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_SECRET, 25, NA, NA, NULL); + addot(OT_TRAPROCK, "falling rock trap", "A pressure plate which causes heavy rocks to drop from the ceiling.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_TRAP, 20, B_TRUE, 22, NULL); + addflag(lastot->flags, F_RARITY, H_ALL, 90, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_SECRET, 25, NA, NA, NULL); + addot(OT_TRAPARROW, "arrow trap", "A pressure plate which causes arrows to shoot at you.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_TRAP, 25, B_TRUE, NA, NULL); + addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_RARITY, H_ALL, 76, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); + addot(OT_TRAPARROWP, "poison arrow trap", "A pressure plate which causes poisoned arrows to shoot at you.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_TRAP, 25, B_TRUE, NA, NULL); + addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_RARITY, H_ALL, 69, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); + addot(OT_TRAPGAS, "gas trap", "A pressure plate which releases poisonous gas.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_TRAP, 27, B_TRUE, NA, NULL); + addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_RARITY, H_ALL, 69, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); + addot(OT_TRAPFIRE, "fire trap", "A pressure plate which fires a pillar of flame.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_TRAP, 30, B_TRUE, NA, NULL); + addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_RARITY, H_ALL, 59, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); + addot(OT_TRAPMINE, "landmine trap", "A buried, pressure-sensitive explosive device.", MT_NOTHING, 0, OC_MISC); + addflag(lastot->flags, F_TRAP, 30, B_TRUE, NA, NULL); + addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_RARITY, H_ALL, 50, NA, NULL); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); + addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); + // money etc addot(OT_GOLD, "gold coin", "Sparkling nuggets of gold, the standard currency of Nexus.", MT_GOLD, 0.1, OC_MONEY); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); @@ -4503,6 +4668,13 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_POWDER, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, "pile of ash"); + addot(OT_ASHSLEEP, "pile of sleeping powder", "A pile of ash.", MT_STONE, 0.1, OC_ROCK); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, ","); + addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, ""); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_POWDER, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, "pile of ash"); addot(OT_GEMOFSEEING, "gem of seeing", "Magically enhances your eyesight.", MT_STONE, 1, OC_ROCK); addflag(lastot->flags, F_HOLDCONFER, F_XRAYVIS, 2, NA, NULL); addflag(lastot->flags, F_HOLDCONFER, F_SEEINVIS, B_TRUE, NA, NULL); @@ -4550,6 +4722,9 @@ void initobjects(void) { addflag(lastot->flags, F_GLYPH, C_YELLOW, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 85, NA, ""); addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL); + addot(OT_STEW, "stew", "Some kind of meat soaked in water.", MT_FOOD, 1.5, OC_FOOD); + addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "%"); + addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, ""); addot(OT_ROASTMEAT, "chunk of roast meat", "A chunk of flame-roasted flesh.", MT_FLESH, 1, OC_FOOD); // weight normally comes from corpse type addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "%"); addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, ""); @@ -4795,12 +4970,20 @@ void initobjects(void) { /////////////////// // death /////////////////// + // l1 + addot(OT_S_STENCH, "stench", "Nauseates the target.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 4, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l2 addot(OT_S_BLINDNESS, "blindness", "Temporarily blinds the target.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, 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); addot(OT_S_FEAR, "cause fear", "Causes intense fear in the target.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -4859,9 +5042,18 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); // l2 + addot(OT_S_LOCATEOBJECT, "locate object", "Locates any object within an area the caster has previously travelled.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addot(OT_S_MAPPING, "sense surroundings", "Magically imbues the caster with a map of his/her surroundings.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addot(OT_S_SEEINVIS, "see invisible", "Allows the caster to see invisible creatures.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, 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_ONGOING, B_TRUE, NA, NA, NULL); addot(OT_S_DETECTOBS, "detect objects", "Senses objects near the caster.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -4870,6 +5062,10 @@ void initobjects(void) { addot(OT_S_DETECTAURA, "detect aura", "Senses holiness or evil near the caster.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addot(OT_S_LORE, "lore", "Obtain knowledge about any one species.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addot(OT_S_REVEALHIDDEN, "reveal hidden", "Reveals hidden doors or invisible creatures in the caster's line of sight.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -4885,11 +5081,21 @@ void initobjects(void) { /////////////////// // elemental - air /////////////////// + // l1 + addot(OT_S_MIST, "obscuring mist", "Hides the caster from view with a thick cloud of mist.", MT_NOTHING, 0, OC_SPELL); + 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, 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); + // 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); 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); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_NEED, NA, NULL); // l3 addot(OT_S_AIRBLAST, "airblast", "Knocks enemies back with a powerful blast of air.", MT_NOTHING, 0, OC_SPELL); @@ -4914,13 +5120,18 @@ void initobjects(void) { // elemental - fire /////////////////// // l1 - addot(OT_S_SPARK, "spark", "Creates a tiny spark of flame, sufficient to ignite flammable objects but not enough to deal direct damage.", MT_NOTHING, 0, OC_SPELL); + addot(OT_S_SPARK, "spark", "Creates a tiny spark of flame, dealing 1-3 fire damage to creatures and objects.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l2 - addot(OT_S_FIREDART, "flame dart", "Fires a medium-sized dart of fire, dealing 2-6 fire damage.", MT_NOTHING, 0, OC_SPELL); + addot(OT_S_BLADEBURN, "bladeburn", "Ignites the target's bladed weapon, causing it to temporarily deal fire damage.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_FIREDART, "flame dart", "Fires a medium-sized dart of fire, dealing 1d6+power fire damage.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -4953,20 +5164,51 @@ void initobjects(void) { /////////////////// // elemental - ice /////////////////// - // l2 - addot(OT_S_CONECOLD, "cone of cold", "Shoots a blast of ice cold air, dealing 2-6 cold damage.", MT_NOTHING, 0, OC_SPELL); + // l1 + addot(OT_S_FROSTBITE, "frostbite", "Deals minor (1d3) cold damage to a single target.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); - addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); - addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + // l2 addot(OT_S_FREEZEOB, "freezing touch", "Permenantly changes the next object touched into solid ice.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addot(OT_S_ICEEDGE, "ice edge", "Enhances the edge of a bladed weapon with a layer of ice.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_CONECOLD, "cone of cold", "Shoots a blast of ice cold air, dealing 2-10 cold damage.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, 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_FALSE, LOF_NEED, NA, NULL); // l3 addot(OT_S_COLDBURST, "cold burst", "Creates a radial blast of coldness out from the caster.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); + addot(OT_S_ICICLE, "icicle", "A huge icicle rises form the ground, knocking enemies away and blocking passage.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, 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_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + // l4 + addot(OT_S_CHILL, "chill", "Deals 3-24 cold damage to target creature per exposed body part.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, 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_WALLOFICE, "wall of ice", "Creates an impassable wall of solid ice.", MT_ICE, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); /////////////////// // nature /////////////////// @@ -5212,6 +5454,10 @@ void initobjects(void) { // modification /////////////////// // l1 + addot(OT_S_HOLDPORTAL, "hold portal", "Prevents a door from opening.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_INSCRIBE, "inscribe", "Creates a magical inscription viewable to anyone standing nearby.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); @@ -5233,6 +5479,12 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + // l2 + addot(OT_S_GREASE, "grease", "Creates a large pool of greasy oil.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); // l3 addot(OT_S_INVISIBILITY, "invisibility", "Temporarily renders the target invisible.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); @@ -5243,6 +5495,7 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_PASSWALL, "passwall", "Allows the caster to temporarily walk through walls.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -5268,6 +5521,14 @@ void initobjects(void) { /////////////////// // summoning /////////////////// + // l1 + addot(OT_S_SUMMONWEAPON, "summon weapon", "Summons a blade of pure magic into your hands. Deals 1-power damage.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 6, NA, NA, NULL); + addflag(lastot->flags, F_VARPOWER, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); // l2 addot(OT_S_CREATEMONSTER, "create monster", "Summons a (probably hostile) monster to a nearby location.\nAt Power V you can control where the monster appears.\nAt Power VII, you can control the type of monster created.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL); @@ -5288,6 +5549,12 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); + // l3 + addot(OT_S_TWIDDLE, "twiddle", "Swaps places with the target creature.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, 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_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 addot(OT_S_TELEPORT, "teleportation", "Teleports the caster (and Power-1 adjacent allies) to a new location within the same level.\nBecomes semi-controlled at Power V, and fully controlled at Power VII.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); @@ -5364,6 +5631,13 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); addflag(lastot->flags, F_NEEDSGRAB, B_TRUE, NA, NA, NULL); + addot(OT_A_COOK, "cook", "Combine food and water into a healthy meals.", MT_NOTHING, 0, OC_ABILITY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + addot(OT_A_DARKWALK, "darkwalk", "Step between the shadows.", MT_NOTHING, 0, OC_ABILITY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + // ai will cast this spell in a special manner... + addot(OT_A_DISARM, "disarm", "Attempt to disable a known trap.", MT_NOTHING, 0, OC_ABILITY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_FLURRY, "flurry", "Perform a flurry of attacks, forcing your opponent backwards.", MT_NOTHING, 0, OC_ABILITY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); @@ -5413,6 +5687,8 @@ void initobjects(void) { addflag(lastot->flags, F_MANUALOF, SK_ARMOUR, NA, NA, NULL); addot(OT_MAN_ATHLETICS, "manual of athletics", "Teaches you the skill 'athletics'.", MT_PAPER, 3, OC_BOOK); addflag(lastot->flags, F_MANUALOF, SK_ATHLETICS, NA, NA, NULL); + addot(OT_MAN_COOKING, "manual of cooking", "Teaches you the skill 'cooking'.", MT_PAPER, 3, OC_BOOK); + addflag(lastot->flags, F_MANUALOF, SK_COOKING, NA, NA, NULL); addot(OT_MAN_BACKSTAB, "manual of stabbing", "Teaches you the skill 'backstab'.", MT_PAPER, 3, OC_BOOK); addflag(lastot->flags, F_MANUALOF, SK_BACKSTAB, NA, NA, NULL); addot(OT_MAN_FIRSTAID, "manual of first aid", "Teaches you the skill 'first aid'.", MT_PAPER, 3, OC_BOOK); @@ -5425,7 +5701,7 @@ void initobjects(void) { addflag(lastot->flags, F_MANUALOF, SK_CHANNELING, NA, NA, NULL); addot(OT_MAN_SHIELDS, "manual of shields", "Teaches you the skill 'shields'.", MT_PAPER, 3, OC_BOOK); addflag(lastot->flags, F_MANUALOF, SK_SHIELDS, NA, NA, NULL); - addot(OT_MAN_SPELLCASTING, "manual of spellcasting", "Teaches you the skill 'spellcasting'.", MT_PAPER, 3, OC_BOOK); + addot(OT_MAN_SPELLCASTING, "manual of sorcery", "Teaches you the skill 'sorcery'.", MT_PAPER, 3, OC_BOOK); addflag(lastot->flags, F_MANUALOF, SK_SPELLCASTING, NA, NA, NULL); addot(OT_MAN_SPOTHIDDEN, "manual of searching", "Teaches you the skill 'searching'.", MT_PAPER, 3, OC_BOOK); addflag(lastot->flags, F_MANUALOF, SK_SPOTHIDDEN, NA, NA, NULL); @@ -5433,6 +5709,8 @@ void initobjects(void) { addflag(lastot->flags, F_MANUALOF, SK_STEALTH, NA, NA, NULL); addot(OT_MAN_TECHUSAGE, "manual of technology", "Teaches you the skill 'technology'.", MT_PAPER, 3, OC_BOOK); addflag(lastot->flags, F_MANUALOF, SK_TECHUSAGE, NA, NA, NULL); + addot(OT_MAN_TRAPS, "manual of traps", "Teaches you the skill 'traps'.", MT_PAPER, 3, OC_BOOK); + addflag(lastot->flags, F_MANUALOF, SK_TRAPS, NA, NA, NULL); addot(OT_MAN_TWOWEAPON, "manual of dual weilding", "Teaches you the skill 'dual weilding'.", MT_PAPER, 3, OC_BOOK); addflag(lastot->flags, F_MANUALOF, SK_TWOWEAPON, NA, NA, NULL); // knowledge manuals @@ -5506,23 +5784,33 @@ void initobjects(void) { addflag(lastot->flags, F_LINKSPELL, OT_S_PARALYZE, NA, NA, NULL); addot(OT_SB_POISONBOLT, "spellbook of poison bolt", "Teaches the spell 'poison bolt'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_POISONBOLT, NA, NA, NULL); + addot(OT_SB_POSSESSION, "spellbook of possession", "Teaches the spell 'possession'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_POSSESSION, NA, NA, NULL); addot(OT_SB_WEAKEN, "spellbook of weaken", "Teaches the spell 'weaken'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_WEAKEN, NA, NA, NULL); addot(OT_SB_BLINDNESS, "spellbook of blindness", "Teaches the spell 'blindness'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_BLINDNESS, NA, NA, NULL); addot(OT_SB_INFINITEDEATH, "spellbook of infinite death", "Teaches the spell 'infinite death'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_INFINITEDEATH, NA, NA, NULL); + addot(OT_SB_STENCH, "spellbook of stench", "Teaches the spell 'stench'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_STENCH, NA, NA, NULL); // divination addot(OT_SB_DETECTLIFE, "spellbook of detect life", "Teaches the spell 'detect life'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTLIFE, NA, NA, NULL); addot(OT_SB_DETECTOBS, "spellbook of detect objects", "Teaches the spell 'detect objects'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTOBS, NA, NA, NULL); + addot(OT_SB_LOCATEOBJECT, "spellbook of locate object", "Teaches the spell 'locate object'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_LOCATEOBJECT, NA, NA, NULL); + addot(OT_SB_LORE, "spellbook of lore", "Teaches the spell 'lore'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_LORE, NA, NA, NULL); addot(OT_SB_MAPPING, "spellbook of sense surroundings", "Teaches the spell 'surroundings'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_MAPPING, NA, NA, NULL); addot(OT_SB_DETECTAURA, "spellbook of detect aura", "Teaches the spell 'detect aura'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTAURA, NA, NA, NULL); addot(OT_SB_REVEALHIDDEN, "spellbook of reveal hidden", "Teaches the spell 'reveal hidden'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_REVEALHIDDEN, NA, NA, NULL); + addot(OT_SB_SEEINVIS, "spellbook of see invisible", "Teaches the spell 'see invisible'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_SEEINVIS, NA, NA, NULL); addot(OT_SB_IDENTIFY, "spellbook of identification", "Teaches the spell 'identification'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_IDENTIFY, NA, NA, NULL); // elemental - air @@ -5539,6 +5827,10 @@ void initobjects(void) { addot(OT_SB_WINDSHIELD, "spellbook of cyclonic shield", "Teaches the spell 'cyclonic shield'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_WINDSHIELD, NA, NA, NULL); // elemental - fire + addot(OT_SB_BLADEBURN, "spellbook of bladeburn", "Teaches the spell 'bladeburn'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_BLADEBURN, NA, NA, NULL); + addot(OT_SB_BURNINGWAVE, "spellbook of burning wave", "Teaches the spell 'burning wave'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_BURNINGWAVE, NA, NA, NULL); addot(OT_SB_SPARK, "spellbook of spark", "Teaches the spell 'spark'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_SPARK, NA, NA, NULL); addot(OT_SB_FIREDART, "spellbook of flame dart", "Teaches the spell 'flame dart'.", MT_PAPER, 1.5, OC_BOOK); @@ -5562,8 +5854,20 @@ void initobjects(void) { addot(OT_SB_LEVITATION, "spellbook of levitation", "Teaches the spell 'levitation'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_LEVITATION, NA, NA, NULL); // elemental - ice + addot(OT_SB_CHILL, "spellbook of chill", "Teaches the spell 'chill'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_CHILL, NA, NA, NULL); addot(OT_SB_CONECOLD, "spellbook of cone of cold", "Teaches the spell 'cone of cold'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_CONECOLD, NA, NA, NULL); + addot(OT_SB_FROSTBITE, "spellbook of frostbite", "Teaches the spell 'frostbite'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_FROSTBITE, NA, NA, NULL); + addot(OT_SB_FREEZEOB, "spellbook of freezing touch", "Teaches the spell 'freezing touch'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_FREEZEOB, NA, NA, NULL); + addot(OT_SB_ICEEDGE, "spellbook of ice edge", "Teaches the spell 'ice edge'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_ICEEDGE, NA, NA, NULL); + addot(OT_SB_ICICLE, "spellbook of icicle", "Teaches the spell 'icicle'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_ICICLE, NA, NA, NULL); + addot(OT_SB_WALLOFICE, "spellbook of wall of ice", "Teaches the spell 'wall of ice'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_WALLOFICE, NA, NA, NULL); // life addot(OT_SB_HEALINGMIN, "spellbook of minor healing", "Teaches the spell 'minor healing'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_HEALINGMIN, NA, NA, NULL); @@ -5585,6 +5889,8 @@ void initobjects(void) { addot(OT_SB_CHARM, "spellbook of charm", "Teaches the spell 'charm'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_CHARM, NA, NA, NULL); // modification + addot(OT_SB_HOLDPORTAL, "spellbook of hold portal", "Teaches the spell 'hold portal'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_HOLDPORTAL, NA, NA, NULL); addot(OT_SB_INSCRIBE, "spellbook of inscribe", "Teaches the spell 'inscribe'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_INSCRIBE, NA, NA, NULL); addot(OT_SB_INVISIBILITY, "spellbook of invisibility", "Teaches the spell 'invisibility'.", MT_PAPER, 1.5, OC_BOOK); @@ -5595,8 +5901,6 @@ void initobjects(void) { addflag(lastot->flags, F_LINKSPELL, OT_S_LIGHT, NA, NA, NULL); addot(OT_SB_DARKNESS, "spellbook of darkness", "Teaches the spell 'darkness'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_DARKNESS, NA, NA, NULL); - addot(OT_SB_FREEZEOB, "spellbook of freezing touch", "Teaches the spell 'freezing touch'.", MT_PAPER, 1.5, OC_BOOK); - addflag(lastot->flags, F_LINKSPELL, OT_S_FREEZEOB, NA, NA, NULL); addot(OT_SB_COLDBURST, "spellbook of cold burst", "Teaches the spell 'cold burst'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_COLDBURST, NA, NA, NULL); addot(OT_SB_DIG, "spellbook of dig", "Teaches the spell 'dig'.", MT_PAPER, 1.5, OC_BOOK); @@ -5604,7 +5908,8 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_SB_GASEOUSFORM, "spellbook of gaseous form", "Teaches the spell 'gaseous form'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_GASEOUSFORM, NA, NA, NULL); - + addot(OT_SB_GREASE, "spellbook of grease", "Teaches the spell 'grease'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_GREASE, NA, NA, NULL); addot(OT_SB_MENDING, "spellbook of mending", "Teaches the spell 'mending'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_MENDING, NA, NA, NULL); addot(OT_SB_PASSWALL, "spellbook of passwall", "Teaches the spell 'passwall'.", MT_PAPER, 1.5, OC_BOOK); @@ -5619,15 +5924,19 @@ void initobjects(void) { // summoning addot(OT_SB_CREATEMONSTER, "spellbook of create monster", "Teaches the spell 'create monster'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_CREATEMONSTER, NA, NA, NULL); + addot(OT_SB_SUMMONWEAPON, "spellbook of summon weapon", "Teaches the spell 'summon weapon'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_SUMMONWEAPON, NA, NA, NULL); // translocation addot(OT_SB_BLINK, "spellbook of blink", "Teaches the spell 'blink'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_BLINK, NA, NA, NULL); addot(OT_SB_DISPERSAL, "spellbook of dispersal", "Teaches the spell 'dispersal'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_DISPERSAL, NA, NA, NULL); - addot(OT_SB_TELEPORT, "spellbook of teleportation", "Teaches the spell 'teleportation'.", MT_PAPER, 1.5, OC_BOOK); - addflag(lastot->flags, F_LINKSPELL, OT_S_TELEPORT, NA, NA, NULL); addot(OT_SB_GATE, "spellbook of gate", "Teaches the spell 'gate'.", MT_PAPER, 1.5, OC_BOOK); addflag(lastot->flags, F_LINKSPELL, OT_S_GATE, NA, NA, NULL); + addot(OT_SB_TELEPORT, "spellbook of teleportation", "Teaches the spell 'teleportation'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_TELEPORT, NA, NA, NULL); + addot(OT_SB_TWIDDLE, "spellbook of twiddle", "Teaches the spell 'twiddle'.", MT_PAPER, 1.5, OC_BOOK); + addflag(lastot->flags, F_LINKSPELL, OT_S_TWIDDLE, NA, NA, NULL); // assign rarity to books @@ -6123,8 +6432,10 @@ void initobjects(void) { addflag(lastot->flags, F_LINKOB, OT_POT_OIL, NA, NA, NULL); addflag(lastot->flags, F_SLIPPERY, 13, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL); + addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL); + addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBDIETEXT, NA, NA, NA, "evaporates"); // this isn't made out of water, so that it won't put out fire etc addot(OT_SPLASHWATER, "splash of water", "A small splash of water.", MT_NOTHING, 0, OC_MISC); @@ -6334,6 +6645,16 @@ void initobjects(void) { addflag(lastot->flags, F_WALKDAMBP, BP_LEGS, DT_WATER, NA, "1d2"); addflag(lastot->flags, F_WALKDAMBP, BP_FEET, DT_WATER, NA, "1d2"); + addot(OT_MIST, "thick mist", "A thick cloud of obscuring mist.", MT_GAS, 0, OC_EFFECT); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "clears"); + addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL); + addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); + addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); + addot(OT_SMOKECLOUD, "cloud of smoke", "A thick cloud of black smoke.", MT_GAS, 0, OC_EFFECT); addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); addflag(lastot->flags, F_NODIECONVERTTEXT, NA, NA, NA, NULL); @@ -6344,6 +6665,8 @@ void initobjects(void) { addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); + addflag(lastot->flags, F_CAUSESCOUGH, 26, NA, NA, NULL); + addot(OT_SMOKEPUFF, "puff of smoke", "A small puff of black smoke.", MT_GAS, 0, OC_EFFECT); addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); @@ -6354,6 +6677,7 @@ void initobjects(void) { addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_BLOCKSVIEW, 4, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); + addflag(lastot->flags, F_CAUSESCOUGH, 18, NA, NA, NULL); addot(OT_POISONCLOUD, "cloud of poison gas", "A thick cloud of poisonous gas.", MT_GAS, 0, OC_EFFECT); addflag(lastot->flags, F_GLYPH, C_GREEN, NA, NA, "{"); @@ -6395,8 +6719,15 @@ void initobjects(void) { addflag(lastot->flags, F_WALKDAMBP, BP_LEGS, DT_WATER, NA, "1d2"); addflag(lastot->flags, F_WALKDAMBP, BP_FEET, DT_WATER, NA, "1d2"); + addot(OT_ICEWALL, "wall of ice", "A wall made of solid ice.", MT_ICE, 0, OC_EFFECT); + addflag(lastot->flags, F_GLYPH, C_CYAN, NA, NA, "#"); + addflag(lastot->flags, F_IMPASSABLE, SZ_MAX, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 100, 100, NA, NULL); + addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); + addot(OT_MAGICBARRIER, "magical barrier", "A glowing, impassable barrier of magical energy.", MT_MAGIC, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "#"); + addflag(lastot->flags, F_GLYPH, C_YELLOW, NA, NA, "#"); addflag(lastot->flags, F_IMPASSABLE, SZ_MAX, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -6429,6 +6760,7 @@ void initobjects(void) { addflag(lastot->flags, F_DTVULN, DT_ACID, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); + // armour - body addot(OT_COTTONSHIRT, "cotton shirt", "A comfortable white cotton shirt.", MT_CLOTH, 0.7, OC_ARMOUR); addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL); @@ -7198,7 +7530,7 @@ void initobjects(void) { // clubs (bashing) - addot(OT_CLUB, "club", "A heavy, blunt wooden instrument to hit things with.", MT_WOOD, 1.5, OC_WEAPON); + addot(OT_CLUB, "club", "A heavy, blunt wooden instrument to hit things with.", MT_WOOD, 3, OC_WEAPON); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL); addflag(lastot->flags, F_OBATTACKDELAY, 140, NA, NA, NULL); addflag(lastot->flags, F_DAM, DT_BASH, NA, NA, "1d6"); @@ -7330,7 +7662,12 @@ void initobjects(void) { addflag(lastot->flags, F_AMMOOB, OT_STONE, NA, NA, NULL); - // holy weapons + // special weapons + + addot(OT_ENERGYBLADE, "energy blade", "A summoned weapon made of pure magical energy.", MT_MAGIC, 0, OC_WEAPON); + addflag(lastot->flags, F_DAM, DT_MAGIC, NA, NA, "1d4"); // will be replaced when summoned + addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); + addot(OT_HANDOFGOD, "hand of god", "The ultimate power.", MT_FLESH, 0.1, OC_WEAPON); //addflag(lastot->flags, F_RARITY, H_DUNGEON, RR_UNIQUE, NA, NULL); //addflag(lastot->flags, F_DAMTYPE, DT_HOLY, NA, NA, NULL); @@ -7367,6 +7704,24 @@ int isammofor(object_t *ammo, object_t *gun) { return B_FALSE; } +int isbadfood(object_t *o) { + if (hasflag(o->flags, F_TAINTED)) { + return B_TRUE; + } + if (isrotting(o)) { + return B_TRUE; + } + switch (o->type->id) { + case OT_POT_POISON: + case OT_POT_ACID: + return B_TRUE; + default: + break; + } + return B_FALSE; +} + + // is armour 'a' better than armour 'b'? int isbetterarmourthan(object_t *a, object_t *b) { int arma, armb; @@ -7832,7 +8187,7 @@ int isrotting(object_t *o) { flag_t *issecretdoor(object_t *o) { if (isdoor(o, NULL)) { - return hasflag(o->flags, F_SECRETDOOR); + return hasflag(o->flags, F_SECRET); } return NULL; } @@ -7897,6 +8252,8 @@ void killob(object_t *o) { // remove flags conferred by this object if (o->pile->owner) { loseobflags(o->pile->owner, o, ALLCONFERRED); + } else if (o->pile->where && !o->pile->where->lf && haslos(player, o->pile->where)) { + needredraw = B_TRUE; } // free mem @@ -8237,6 +8594,10 @@ void makewet(object_t *o, int amt) { } object_t *moveob(object_t *src, obpile_t *dst, int howmany) { + return real_moveob(src, dst, howmany, B_TRUE); +} + +object_t *real_moveob(object_t *src, obpile_t *dst, int howmany, int stackok) { object_t *o, *existob; int i; int db = B_FALSE; @@ -8245,7 +8606,12 @@ object_t *moveob(object_t *src, obpile_t *dst, int howmany) { if (db) dblog("DB: moveob() - moving %d x %s",howmany, src->type->name); - existob = canstackob(dst, src); + if (stackok) { + existob = canstackob(dst, src); + } else { + existob = NULL; + } + if (existob) { if (db) dblog("DB: moveob() - found stack to join"); if (howmany == ALL) howmany = src->amt; @@ -8315,7 +8681,7 @@ object_t *moveob(object_t *src, obpile_t *dst, int howmany) { if (hasflag(o->flags, F_DOOR)) { killflagsofid(o->flags, F_LOCKED); killflagsofid(o->flags, F_JAMMED); - killflagsofid(o->flags, F_SECRETDOOR); + killflagsofid(o->flags, F_SECRET); if (!hasflag(o->flags, F_OPEN)) { addflag(o->flags, F_OPEN, B_TRUE, NA, NA, NULL); } @@ -8405,9 +8771,6 @@ object_t *obexists(enum OBTYPE obid) { void obdie(object_t *o) { char obname[BUFLEN]; flag_t *f; - cell_t *obloc; - - obloc = o->pile->where; f = hasflag(o->flags, F_DIECONVERT); if (f) { @@ -8458,9 +8821,11 @@ void obdie(object_t *o) { // change into something else newob = addob(o->pile, f->text); - // only set amt if text wasn't "x-y somethings" - if (!strchr(f->text, '-')) { - newob->amt = o->amt; + if (newob) { + // only set amt if text wasn't "x-y somethings" + if (!strchr(f->text, '-')) { + newob->amt = o->amt; + } } } else { char desc[BUFLEN]; @@ -8531,11 +8896,6 @@ void obdie(object_t *o) { } killob(o); - // redraw - if (obloc && !obloc->lf && haslos(player, obloc)) { - needredraw = B_TRUE; - drawscreen(); - } } int obfits(object_t *o, obpile_t *op) { @@ -9157,7 +9517,7 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { noprefix(obname)); } } else { - addflag(oo->flags, F_LOCKED, B_TRUE, NA, NA, NULL); + addflag(oo->flags, F_LOCKED, B_TRUE, 20, NA, NULL); if (isplayer(lf)) { msg("Your %s buzzes.", noprefix(obname)); } else if (cansee(player, lf)) { @@ -9308,7 +9668,7 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { ch = askchar(buf, "yn", "y", B_TRUE); if (ch == 'y') { msg("You tighten the hinges on %s.", obname); - addflag(o->flags, F_JAMMED, rnd(1,5), NA, NA, NULL); + addflag(o->flags, F_JAMMED, rnd(1,10), NA, NA, NULL); taketime(lf, getactspeed(lf)); donesomething = B_TRUE; } @@ -10379,6 +10739,9 @@ object_t *relinkob(object_t *src, obpile_t *dst) { if (obproduceslight(src)) { calclight((getoblocation(src))->map); + if (gamemode == GM_GAMESTARTED) { + precalclos(player); + } drawscreen(); } @@ -10822,7 +11185,7 @@ int real_takedamage(object_t *o, unsigned int howmuch, int damtype, int wantanno - real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE, B_FALSE); + real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_TRUE, B_FALSE, B_FALSE); getobconditionname(o, predamname); hpflag = hasflag(o->flags, F_OBHP); if (hpflag) { @@ -10897,8 +11260,8 @@ int real_takedamage(object_t *o, unsigned int howmuch, int damtype, int wantanno msg("%s %s!", obname, getobhurtname(o, damtype)); } } - // if you see a secret door get damaged, it's not secret anymore. - killflagsofid(o->flags, F_SECRETDOOR); + // if you see a secret object get damaged, it's not secret anymore. + killflagsofid(o->flags, F_SECRET); } } @@ -10929,9 +11292,12 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, int db = B_TRUE; int willcatch = B_FALSE; int announcedmiss = B_FALSE; + float multiplier; reason = E_OK; + multiplier = speed / 2; + if (firearm) { strcpy(throwverbpres, "fire"); strcpy(throwverbpast, "fired"); @@ -11134,6 +11500,21 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, if (!isknown(o)) makeknown(o->type->id); } explodecells(thrower->cell, roll(diebuf), B_FALSE, o, 1, DT_COMPASS, B_FALSE); + } else if (o->type->id == OT_ASHSLEEP) { + int radius; + // make smoke + if (haslos(player, srcloc)) { + msg("%s dispers%s into a wispy mist!", obname, + (amt == 1) ? "es" : "e", + (amt == 1) ? "es" : "e" ); + + if (!isknown(o)) makeknown(o->type->id); + } + radius = o->amt * 2; + if (radius > 10) radius = 10; + + spellcloud(srcloc, radius, '}', C_MAGENTA, OT_S_SLEEP, radius); + } else { if (haslos(player, srcloc)) { msg("%s dispers%s into the air.", obname, (amt == 1) ? "es" : "e"); @@ -11214,7 +11595,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, int throwdam,dam; if (seen) { char shname[BUFLEN]; - getobname(shield, shname, 1); + real_getobname(shield, shname, 1, B_TRUE, B_FALSE, B_TRUE, B_FALSE, B_FALSE); if (isplayer(target)) { msg("You block %s with your %s.", obname, noprefix(shname)); } else { @@ -11224,7 +11605,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, } // damage shield throwdam = getthrowdam(o); - dam = throwdam * speed; + dam = (int)((float)throwdam * multiplier); takedamage(shield, dam, DT_PROJECTILE); youhit = B_FALSE; @@ -11287,8 +11668,8 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, int throwdam; throwdam = getthrowdam(o); - dam = throwdam * speed; - if (db) dblog("fireat(): dam = throwdam(%d) * speed(%d) = %d",throwdam, speed, dam); + dam = (int)((float)throwdam * multiplier); + if (db) dblog("fireat(): dam = throwdam(%d) * speed(%d)/2 = %d",throwdam, speed, dam); // special case if (o->type->id == OT_RUBBERBULLET) { @@ -11354,10 +11735,11 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, } } - // move the object to the cell then take dam or kill it. - newob = moveob(o, where->obpile, amt); + // move the object to the cell then take dam or kill it. don't stack. + newob = real_moveob(o, where->obpile, amt, B_FALSE); // fake its birth time so that it can be damaged newob->birthtime = -1; + if (willshatter(newob->material->id)) { char dambuf[BUFLEN]; sprintf(dambuf, "%s (%s by %s)",obname,throwverbpast, realthrowernamea); @@ -11443,6 +11825,7 @@ void timeeffectsob(object_t *o) { if (!o->blessknown) o->blessknown = B_TRUE; } calclight(ourcell->map); + precalclos(player); } } else { // not near undead if (glowflag) { @@ -11623,6 +12006,13 @@ void timeeffectsob(object_t *o) { if (hasflag(o->flags, F_DEAD)) return; } + if ((f->id == F_EDIBLE) && (o->type->id == OT_STEW)) { + f->val[1] -= 20; + if (f->val[1] < 0) { + f->val[1] = 0; + addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL); + } + } // damaging objects here will damage other objects if (f->id == F_WALKDAM) { @@ -11720,6 +12110,108 @@ void timeeffectsob(object_t *o) { } // end for each object flag } +// both trapob and oid are passed, because trapob might be NULL if +// coming from a door/chest trap. +void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf) { + char lfname[BUFLEN]; + int avoided; + enum CHECKTYPE ct; + objecttype_t *temp = NULL; + getlfname(lf, lfname); + + temp = findot(oid); + + // saving throw + if (temp) { + flag_t *f; + f = hasflag(temp->flags, F_TRAP); + if (f && (f->val[2] != NA)) { + if (isplayer(lf) && hasflag(temp->flags, F_SECRET)) { + avoided = B_FALSE; + } else { + switch (oid) { + case OT_TRAPTRIP: ct = SC_FALL; break; + default: + ct = SC_DODGE; + break; + } + avoided = skillcheck(lf, ct, f->val[2], 0); + } + } + } + + if (oid == OT_TRAPROCK) { + if (haslos(player, lf->cell)) { + msg("A heavy rock drops onto %s%s", lfname, + avoided ? ", but misses." : "!"); + } + if (!avoided) { + losehp(lf, roll("1d4"), DT_BASH, NULL, "a falling rock trap"); + } + addob(lf->cell->obpile, "stone"); + } else if ((oid == OT_TRAPARROW) || (oid == OT_TRAPARROWP)) { + int dir,bestdir = D_NONE; + cell_t *src; + int maxdist=-1; + + // get furthest wall + for (dir = DC_N; dir <= DC_NW; dir++) { + cell_t *c,*prevc; + int thisdist = 0; + prevc = NULL; + c = lf->cell; + c = getcellindir(c, dir); + while (!c->type->solid) { + thisdist++; + prevc = c; + c = getcellindir(c, dir); + } + if (thisdist > maxdist) { + maxdist = thisdist; + bestdir = dir; + src = prevc; + } + } + if (bestdir != D_NONE) { + object_t *o; + if (oid == OT_TRAPARROWP) { + o = addob(src->obpile, "poisoned arrow"); + } else { + o = addob(src->obpile, "arrow"); + } + if (o) { + // dodge check will happen in fireat(). ignore results of the + // one above. + fireat(NULL, o, 1, lf->cell, 5, NULL); + } else { + msg("failed."); + } + } + } else if (oid == OT_TRAPFIRE) { + // can't be dodged + dospelleffects(NULL, OT_S_FLAMEPILLAR, 3, lf, NULL, lf->cell, B_UNCURSED, NULL); + } else if (oid == OT_TRAPGAS) { + // can't be dodged + dospelleffects(NULL, OT_S_CLOUDKILL, 3, lf, NULL, lf->cell, B_UNCURSED, NULL); + } else if (oid == OT_TRAPMINE) { + // can't be dodged + explodecells(lf->cell, roll("2d6"), B_FALSE, trapob, 1, DT_ORTH, B_TRUE); + if (trapob) { + killob(trapob); + } + } else if (oid == OT_TRAPTRIP) { + if (avoided) { + if (isplayer(lf)) { + msg("You retain your balance."); + } else if (cansee(player, lf)) { + msg("%s retains its balance.",lfname); + } + } else { + fall(lf, NULL, B_TRUE); + } + } +} + void turnoff(lifeform_t *lf, object_t *o) { char obname[BUFLEN]; flag_t *f; @@ -11921,8 +12413,10 @@ int validateobs(void) { } } else if (ot->obclass->id == OC_WEAPON) { if (!hasflag(ot->flags, F_USESSKILL) && !hasflag(ot->flags, F_FIREARM)) { - printf("ERROR in object '%s' - weapon has no associated skill.\n", ot->name); - goterror = B_TRUE; + if (ot->id != OT_ENERGYBLADE) { + printf("ERROR in object '%s' - weapon has no associated skill.\n", ot->name); + goterror = B_TRUE; + } } } if (hasflagval(ot->flags, F_PICKLOCKS, NA, B_BLUNTONFAIL, NA, NULL) && hasflag(ot->flags, F_STACKABLE)) { diff --git a/objects.h b/objects.h index 8d19876..2be56b5 100644 --- a/objects.h +++ b/objects.h @@ -10,7 +10,7 @@ material_t *addmaterial(enum MATERIAL id, char *name, float weightrating); objectclass_t *addoc(enum OBCLASS id, char *name, char *desc, char glyph, int glyphcolour); object_t *addob(obpile_t *where, char *name); object_t *addobject(obpile_t *where, char *name, int canstack); -int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fromlf); +int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fromlf, enum LOFTYPE needlof); obmod_t *addobmod(enum OBMOD id, char *prefix); obpile_t *addobpile(lifeform_t *owner, cell_t *where); void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes); @@ -120,6 +120,7 @@ void initobjects(void); flag_t *isarmour(object_t *o); int isactivated(object_t *o); int isammofor(object_t *ammo, object_t *gun); +int isbadfood(object_t *o); int isbetterarmourthan(object_t *a, object_t *b); int isbetterwepthan(object_t *a, object_t *b); int isblessed(object_t *o); @@ -167,6 +168,7 @@ void makeknown(enum OBTYPE otid); void maketried(enum OBTYPE otid); void makewet(object_t *o, int amt); object_t *moveob(object_t *src, obpile_t *dst, int howmany); +object_t *real_moveob(object_t *src, obpile_t *dst, int howmany, int stackok); void modbonus(object_t *o, int amt); //object_t *newobeffects(object_t *o); void obaction(object_t *o, char *text); @@ -198,6 +200,7 @@ int takedamage(object_t *o, unsigned int howmuch, int damtype); int real_takedamage(object_t *o, unsigned int howmuch, int damtype, int wantannounce); int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, object_t *firearm); void timeeffectsob(object_t *o); +void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf); void turnoff(lifeform_t *lf, object_t *o); void turnon(lifeform_t *lf, object_t *o); int uncurseob(object_t *o, int *seen); diff --git a/spell.c b/spell.c index ad19241..99fb1d4 100644 --- a/spell.c +++ b/spell.c @@ -28,8 +28,12 @@ extern WINDOW *msgwin; extern job_t *firstjob; +extern map_t *firstmap; + extern enum ERROR reason; +extern int needredraw, statdirty; + int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifeform_t *target, flag_t *cwflag) { char username[BUFLEN]; char killername[BUFLEN]; @@ -184,6 +188,186 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // attack attackcell(user, targcell); } + } else if (abilid == OT_A_COOK) { + object_t *water,*o; + race_t *r; + char buf[BUFLEN]; + if (!isplayer(user)) { + return B_TRUE; + } + water = hasob(user->pack, OT_POT_WATER); + if (!water || !isknown(water)) { + msg("You need some water before you can cook."); + return B_TRUE; + + } + o = doaskobject(user->pack, "What will you cook?", NULL, AO_EDIBLE, F_CORPSEOF); + if (!o) { + msg("Cancelled."); + return B_TRUE; + } + + f = hasflag(o->flags, F_CORPSEOF); + if (f) { + r = findrace(f->val[0]); + } + + taketime(user, getactspeed(user)); + + // remove object and water + removeob(o,ALL); + removeob(water,ALL); + // give food + if (getskill(user, SK_COOKING >= PR_EXPERT)) { + o = addob(user->pack, "jerky"); + } else { + o = addob(user->pack, "stew"); + } + + if (o) { + if (r) { + addflag(o->flags, F_LINKRACE, r->id, NA, NA, NULL); + } + f = hasflag(o->flags, F_EDIBLE); + if (f) { + f->val[1] = getskill(user, SK_COOKING)*20; + } + + getobname(o, buf, o->amt); + msgnocap("%c - %s", o->letter, buf); + } else { + msg("Your cooking attempt fails (maybe your pack was too full?)."); + } + } else if (abilid == OT_A_DARKWALK) { + if (range <= 0) range = UNLIMITED; + + if (islit(user->cell)) { + if (isplayer(user)) msg("It is too light to darkwalk!"); + return TRUE; + } + + if (!targcell) { + if (isplayer(user)) { + if (range != UNLIMITED) { + sprintf(buf, "Darkwalk to where (max range %d)?", range); + } else { + sprintf(buf, "Darkwalk to where?"); + } + targcell = askcoords(buf, TT_NONE); + } else { + return B_TRUE; + } + } + + if (!targcell) { + msg("Cancelled."); + return TRUE; + } else if (!cellwalkable(user, targcell, NULL)) { + msg("There is something in your way."); + return TRUE; + } else if (!haslos(user, targcell)) { + msg("You cannot see there!"); + return TRUE; + } else if (islit(targcell)) { + msg("It is too light to darkwalk there!"); + return TRUE; + } else if ((range != UNLIMITED) && (getcelldist(user->cell, targcell) > range)) { + msg("You cannot darkwalk that far!"); + return TRUE; + } + + taketime(user, getactspeed(user)); + + if (isplayer(user)) { + msg("You step through the shadows."); + } else if (cansee(player, user)) { + msg("%s steps through the shadows.",username); + } + moveto(user, targcell, B_TRUE, B_TRUE); + } else if (abilid == OT_A_DISARM) { + int dir; + object_t *o,*trapob = NULL; + flag_t *trapflag = NULL; + char buf[BUFLEN]; + // ask for direction + if (!targcell) { + int dirch; + dirch = askchar("Disarm trap in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE); + if (dirch == '-') { + if (isplayer(user)) msg("Cancelled."); + return B_TRUE ; + } + dir = chartodir(dirch); + if (dir == D_NONE) { + targcell = user->cell; + } else { + targcell = getcellindir(user->cell, dir); + } + } + + if (targcell->lf) { + if (isplayer(user)) { + char inwayname[BUFLEN]; + getlfname(targcell->lf, inwayname); + msg("%s is in your way!",inwayname); + } + return B_TRUE; + } + + // trap there? + for (o = targcell->obpile->first ; o ; o = o->next) { + if (hasflag(o->flags, F_TRAP) && !hasflag(o->flags, F_SECRET)) { + trapob = o; + trapflag = hasflag(trapob->flags, F_TRAP); + break; + } + } + if (!trapob) { + if (isplayer(user)) msg("You can't see any traps there!"); + return B_TRUE; + } + + getobname(trapob, buf, 1); + + if (isplayer(user) || cansee(player, user)) { + needredraw = B_TRUE; + } + + // taketime + taketime(user, getactspeed(user)); + + // move player there... + movelf(user, targcell); + + // try to disarm it + if (skillcheck(user, SC_DISARM, trapflag->val[0], 0)) { + if (isplayer(user)) { + msg("You disarm %s.",buf); + } else if (cansee(player, user)) { + msg("%s disarms %s.",username, buf); + } + removeob(trapob, trapob->amt); + + if (isplayer(user) && hasjob(user, J_ROGUE)) { + gainxp(user, trapflag->val[0]); + } + } else { + // failed. another check to see if it goes off + if ((trapflag->val[1] == B_TRUE) && !skillcheck(user, SC_DISARM, trapflag->val[0], 0)) { + if (isplayer(user)) { + msg("Oops - you trigger %s!",buf); + } else if (cansee(player, user)) { + msg("%s triggers %s!",username, buf); + } + trapeffects(trapob, trapob->type->id, user); + } else { + if (isplayer(user)) { + msg("You fail to disarm %s.",buf); + } else if (cansee(player, user)) { + msg("%s fails to disarm %s.",username, buf); + } + } + } } else if (abilid == OT_A_FLURRY) { int dir; int dirch; @@ -815,11 +999,8 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef flag_t *f; wep = getweapon(user); - if (!wep) { - if (isplayer(user)) msg("You need a bashing weapon to perform a heavy blow!"); - return B_TRUE; - } else if (getdamtype(wep) != DT_BASH) { - if (isplayer(user)) msg("You need a bashing weapon to perform a heavy blow!"); + if (!wep || !ismeleeweapon(wep) || (getobunitweight(wep) < 3)) { + if (isplayer(user)) msg("You need a heavy weapon to perform a heavy blow!"); return B_TRUE; } @@ -1020,28 +1201,33 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ sp = findot(spellid); - getlfname(caster, castername); + if (caster) { + getlfname(caster, castername); + } else { + strcpy(castername, "something"); + } // default to unseen if (seenbyplayer) *seenbyplayer = B_FALSE; - // reverse some spells if cursed - if (hasflag(sp->flags, F_ONGOING)) { - if (lfhasflagval(caster, F_BOOSTSPELL, spellid, NA, NA, NULL)) { - // cancel it. - stopspell(caster, spellid); - return B_FALSE; - } else { - objecttype_t *sp; - int cost; - cost = getmpcost(caster, spellid); - sp = findot(spellid); - - if (sp && hasflag(sp->flags, F_VARPOWER)) { - cost *= power; + if (caster) { + if (hasflag(sp->flags, F_ONGOING)) { + if (lfhasflagval(caster, F_BOOSTSPELL, spellid, NA, NA, NULL)) { + // cancel it. + stopspell(caster, spellid); + return B_FALSE; + } else { + objecttype_t *sp; + int cost; + cost = getmpcost(caster, spellid); + sp = findot(spellid); + + if (sp && hasflag(sp->flags, F_VARPOWER)) { + cost *= power; + } + addflag(caster->flags, F_BOOSTSPELL, spellid, cost, power, NULL); } - addflag(caster->flags, F_BOOSTSPELL, spellid, cost, power, NULL); } } @@ -1263,6 +1449,35 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f->obfrom = spellid; f = addtempflag(caster->flags, F_DTVULN, DT_FIRE, NA, NA, "2d4", FROMSPELL); f->obfrom = spellid; + } else if (spellid == OT_S_BLADEBURN) { + object_t *wep; + enum DAMTYPE dt; + if (!validatespellcell(caster, &targcell, TT_PLAYER | TT_ALLY, spellid, power)) return B_TRUE; + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_FALSE; + } + + // does caster have a bladed weapon? + wep = getweapon(target); + if (!wep) { + fizzle(caster); + return B_FALSE; + } + dt = getdamtype(wep); + if ((dt != DT_PIERCE) && (dt != DT_SLASH) && (dt != DT_CHOP)) { + fizzle(caster); + return B_FALSE; + } + + if (isplayer(target)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + + addtempflag(wep->flags, F_ONFIRE, B_TRUE, NA, NA, "1d6", 10 + power*3); } else if (spellid == OT_S_BLINDNESS) { int failed = B_FALSE; // ask for target @@ -1371,7 +1586,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ getlfname(target, targname); if (haslos(player, targcell)) { - animsky(targcell, '}', C_CYAN); + animsky(targcell, '}', C_WHITE); msg("%s %s struck by a bolt of lightning!",targname,is(target)); if (seenbyplayer) *seenbyplayer = B_TRUE; } @@ -1397,6 +1612,13 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ powerleft -= gethitdice(c->lf); makepeaceful(c->lf); + // druids get 25% of the monster's XP value for calming it. + if (hasjob(caster, J_DRUID)) { + if (getallegiance(caster) == AL_FRIENDLY) { + awardxpfor(c->lf, 25); + } + } + if (cansee(player, c->lf)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } @@ -1425,7 +1647,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ radius = power/3; if (radius < 1) radius = 1; - addobburst(targcell, radius, DT_COMPASS, "cloud of gas", caster); + addobburst(targcell, radius, DT_COMPASS, "cloud of poison gas", caster, LOF_WALLSTOP); if (haslos(player, targcell)) { msg("A cloud of poison gas appears!"); @@ -1575,6 +1797,52 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fightback(target, caster); } } + } else if (spellid == OT_S_CHILL) { + char lfname[BUFLEN]; + int exposedlimbs,dam; + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_FALSE; + } + getlfname(target, lfname); + + // how many body parts are impacted? + exposedlimbs = 0; + if (!getouterequippedob(target, BP_HEAD)) exposedlimbs += 1; + if (!getouterequippedob(target, BP_SHOULDERS)) exposedlimbs += 1; + if (!getouterequippedob(target, BP_BODY)) exposedlimbs += 2; + if (!getouterequippedob(target, BP_HANDS)) exposedlimbs += 1; + if (!getouterequippedob(target, BP_LEGS)) exposedlimbs += 2; + if (!getouterequippedob(target, BP_FEET)) exposedlimbs += 1; + + dam = rolldie(exposedlimbs, 3); + + if (isplayer(target)) { + if (isimmuneto(target->flags, DT_COLD)) { + msg("You feel mildly chilly."); + } else { + msg("You feel extremely cold!"); + } + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + if (isimmuneto(target->flags, DT_COLD)) { + msg("%s looks mildly chilly.", lfname); + } else { + msg("%s looks extremely cold!", lfname); + } + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + + // target takes magical damage + // always hit + if (!isimmuneto(target->flags, DT_COLD)) { + losehp(target, dam, DT_COLD, caster, "a chill spell"); + if (!skillcheck(target, SC_CON, 20+(exposedlimbs*3), 0)) { + poison(target, 20+(power*3), P_COLD, 0, "a chill spell"); + } + } } else if (spellid == OT_S_COLDBURST) { int range = 1; int x,y; @@ -1630,7 +1898,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (cansee(caster, target)) { msg("A blast of coldness ray hits %s.",lfname); } - losehp(target, rnd(2,6), DT_COLD, caster, "a blast of coldness"); + losehp(target, rnd(2,5), DT_COLD, caster, "a blast of coldness"); } } else { @@ -1767,6 +2035,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // temporary darkness makelitradius(targcell, power*2, L_PERMDARK, rnd(5,10) + power ); } + calclight(targcell->map); + needredraw = B_TRUE; + drawscreen(); } else if (spellid == OT_S_DETECTAURA) { if (isplayer(caster)) { object_t *o; @@ -2340,7 +2611,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (cansee(caster, target)) { msg("A dart of flame hits %s.",lfname); } - losehp(target, rnd(2,6) + power, DT_FIRE, caster, "a dart of flame"); + losehp(target, rnd(1,6) + power, DT_FIRE, caster, "a dart of flame"); } } else { object_t *o, *nexto; @@ -2384,7 +2655,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int failed = B_FALSE; // ask for a target cell if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; - if (targcell && haslos(caster, targcell)) { + if (targcell) { if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) { flag_t *f; object_t *o; @@ -2611,6 +2882,37 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } + } else if (spellid == OT_S_FROSTBITE) { + char lfname[BUFLEN]; + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_FALSE; + } + getlfname(target, lfname); + + if (isplayer(target)) { + if (isimmuneto(target->flags, DT_COLD)) { + msg("You feel mildly chilly."); + } else { + msg("You feel very cold!"); + } + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + if (isimmuneto(target->flags, DT_COLD)) { + msg("%s looks mildly chilly.", lfname); + } else { + msg("%s looks very cold!", lfname); + } + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + + // target takes magical damage + // always hit + if (!isimmuneto(target->flags, DT_COLD)) { + 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)) { @@ -2628,6 +2930,31 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ addtempflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, 10); setrace(target, R_GASCLOUD, B_TRUE); } + } else if (spellid == OT_S_GREASE) { + int radius; + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + + if (targcell->type->solid) { + fizzle(caster); + return B_FALSE; + } + radius = power/2; + if (radius < 1) radius = 1; + + addobburst(targcell, radius, DT_ORTH, "puddle of oil", caster, LOF_WALLSTOP); + + if (haslos(player, targcell)) { + char underbuf[BUFLEN]; + if (targcell->lf && cansee(player, targcell->lf)) { + char lfname[BUFLEN]; + getlfname(targcell->lf, lfname); + sprintf(underbuf, " under %s",lfname); + } else { + strcpy(underbuf, ""); + } + msg("A %spool of oil appears%s!", (radius == 1) ? "" : "huge ", underbuf); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } } else if (spellid == OT_S_HAILSTORM) { int failed = B_FALSE; // ask for a target cell @@ -2648,8 +2975,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (radius < 1) radius = 1; addobsinradius(targcell, radius, DT_ORTH, "hail storm", B_FALSE); - // replace damage per sec with power d6 - asprintf(&dambuf, "%dd6", power); + // replace damage per sec with power d4 + asprintf(&dambuf, "%dd4", power); for (y = targcell->y - radius; y <= targcell->y + radius; y++) { for (x = targcell->x - radius; x <= targcell->x + radius; x++) { cell_t *c; @@ -2786,6 +3113,99 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ nothinghappens(); } } + } else if (spellid == OT_S_HOLDPORTAL) { + object_t *o; + if (!validatespellcell(caster, &targcell,TT_DOOR, spellid, power)) return B_TRUE; + o = hasobwithflag(targcell->obpile, F_DOOR); + + if (!o) { + fizzle(caster); + return B_FALSE; + } + + closedoor(NULL, o); + addflag(o->flags, F_JAMMED, 100, NA, NA, NULL); + + if (haslos(player, targcell)) { + char buf[BUFLEN]; + getobname(o, buf, 1); + msg("%s glows for a moment.",buf); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + } else if (spellid == OT_S_ICEEDGE) { + object_t *wep; + enum DAMTYPE dt; + char obname[BUFLEN]; + if (!validatespellcell(caster, &targcell, TT_PLAYER | TT_ALLY, spellid, power)) return B_TRUE; + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_FALSE; + } + + // does caster have a bladed weapon? + wep = getweapon(target); + if (!wep) { + fizzle(caster); + return B_FALSE; + } + dt = getdamtype(wep); + if ((dt != DT_PIERCE) && (dt != DT_SLASH) && (dt != DT_CHOP)) { + fizzle(caster); + return B_FALSE; + } + + getobname(wep, obname, wep->amt); + + if (isplayer(target)) { + msg("Your %s%s blade is covered with ice!", obname, getpossessive(obname)); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + char lfname[BUFLEN]; + getlfname(target, lfname); + msg("%s%s %s is covered with ice!", lfname, getpossessive(lfname), lfname); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + + addtempflag(wep->flags, F_EXTRADAM, DT_COLD, NA, NA, "1d6", 10 + power*3); + + } else if (spellid == OT_S_ICICLE) { + object_t *o; + int donesomething = B_FALSE; + + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE; + + // create icicle + o = addob(targcell->obpile, "huge icicle"); + if (o) { + flag_t *f; + if (haslos(player, targcell)) { + drawscreen(); + msg("A massive icicle rises from the ground!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + f = hasflag(o->flags, F_OBHP); + if (f) { + f->val[0] = 3+power; + f->val[1] = 3+power; + } + addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); + + donesomething = B_TRUE; + } + + // knock lfs away + if (targcell->lf) { + knockback(targcell->lf, getdiraway(targcell, targcell, NULL, B_FALSE, DT_COMPASS), 1, NULL, 25+power); + donesomething = B_TRUE; + } + + + if (!donesomething) { + fizzle(caster); + return B_FALSE; + } + } else if (spellid == OT_S_IDENTIFY) { object_t *o; @@ -2957,6 +3377,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (isplayer(caster)) { msg("An image of your surroundings appears in your mind!"); if (seenbyplayer) *seenbyplayer = B_TRUE; + needredraw = B_TRUE; } rv = B_FALSE; } else if (spellid == OT_S_METALHEAL) { @@ -3721,7 +4142,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ addtempflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, rnd(10,20)+(power*2) ); } } - + calclight(targcell->map); + needredraw = B_TRUE; + drawscreen(); } else if (spellid == OT_S_LIGHTNINGSTORM) { char targname[BUFLEN]; lifeform_t *poss[MAXCANDIDATES], *targ[MAXCANDIDATES]; @@ -3778,6 +4201,121 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else { fizzle(caster); } + } else if (spellid == OT_S_LOCATEOBJECT) { + char wantname[BUFLEN]; + char buf[BUFLEN]; + map_t *m; + object_t *o; + + if (!isplayer(caster)) { + fizzle(caster); + return B_FALSE; + } + // ask for object + askstring("Locate what kind of object", '?', wantname, BUFLEN, NULL); + // find it in all maps! + sprintf(buf, "Locations of '%s':", wantname); + initprompt(&prompt, buf); + prompt.maycancel = B_TRUE; + for (m = firstmap ; m ; m = m->next) { + int x,y; + char ch = 'b'; + // on the ground? + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + cell_t *c; + c = getcellat(m, x, y); + if (c) { + for (o = c->obpile->first ; o ; o = o->next) { + char obname[BUFLEN]; + real_getobname(o, obname, o->amt, B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE); + if (strcasestr(obname, wantname)) { + addchoice(&prompt, ch++, obname, NULL, o); + } + } + } + } + } + // carried by someone? + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + cell_t *c; + c = getcellat(m, x, y); + if (c && c->lf) { + for (o = c->lf->pack->first ; o ; o = o->next) { + char obname[BUFLEN]; + real_getobname(o, obname, o->amt, B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE); + if (strcasestr(obname, wantname)) { + char lfname[BUFLEN]; + real_getlfnamea(c->lf, lfname, B_FALSE); + strcat(obname, " (held by "); + strcat(obname, lfname); + strcat(obname, ")"); + + addchoice(&prompt, ch++, obname, NULL, o); + } + } + } + } + } + } + + getchoice(&prompt); + o = (object_t *)prompt.result; + while (o) { + // describe it + describeob(o); + // ask for another one + getchoice(&prompt); + o = (object_t *)prompt.result; + } + + // restore screen + needredraw = B_TRUE; + statdirty = B_TRUE; + drawscreen(); + msg("Your spell finishes."); + } else if (spellid == OT_S_LORE) { + enum SKILL skid; + skill_t *sk; + char ch = 'a'; + + if (!isplayer(caster)) { + fizzle(caster); + return B_TRUE; + } + // always targetted at caster + targcell = caster->cell; + target = caster; + + // ask for which knowledge skill + initprompt(&prompt, "Which knowledge do you desire?"); + prompt.maycancel = B_TRUE; + for (skid = 0; skid < MAXSKILLS; skid++) { + sk = findskill(skid); + if (sk && isloreskill(skid)) { + if (!getskill(caster, skid)) { + addchoice(&prompt, ch++, sk->name, NULL, sk); + } + } + } + if (prompt.nchoices == 0) { + fizzle(caster); + return B_FALSE; + } + + getchoice(&prompt); + sk = (skill_t *)prompt.result; + if (sk) { + flag_t *f; + f = addtempflag(caster->flags, F_HASSKILL, sk->id, (power/2)+1, NA, NULL, FROMSPELL); + f->obfrom = spellid; + + msg("New knowledge floods into your mind!"); + } else { + fizzle(caster); + return B_FALSE; + } } else if (spellid == OT_S_GRAVBOOST) { // ask for target if (!target) { @@ -3881,10 +4419,33 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // easyish save to avoid falling - if (target && !skillcheck(target, SC_FALL, 12, 0)) { + if (target && !skillcheck(target, SC_FALL, 12 + (power*2), 0)) { fall(target, NULL, B_TRUE); } needredraw = B_TRUE; + } else if (spellid == OT_S_MIST) { + object_t *o; + targcell = caster->cell; + + if (haslos(player, targcell)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; + if (targcell->lf) { + char lfname[BUFLEN]; + getlfname(targcell->lf, lfname); + msg("A thick veil of mist surrounds %s!", lfname); + } else { + msg("A thick veil of mist appears!"); + } + } + o = addob(targcell->obpile, "thick mist"); + if (o) { + flag_t *f; + f = hasflag(o->flags, F_OBHP); + if (f) { + f->val[0] += power; + f->val[1] += power; + } + } } else if (spellid == OT_S_MENDING) { object_t *o; @@ -4128,6 +4689,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else { msg("Your %s looks more fresh now.", noprefix(obname)); } + f = hasflagval(o->flags, F_OBHPDRAIN, NA, DT_DECAY, NA, NULL); + if (f) { + killflag(f); + } if (seenbyplayer) *seenbyplayer = B_TRUE; } @@ -4233,10 +4798,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (targcell) { object_t *o; if (isplayer(caster)) { - // reveal secret doors + // reveal secret doors/obs/etc for (o = targcell->obpile->first ; o ; o = o->next) { flag_t *f; - f = hasflag(o->flags, F_SECRETDOOR); + f = hasflag(o->flags, F_SECRET); if (f) { char obname[BUFLEN]; getobname(o, obname, o->amt); @@ -4273,11 +4838,19 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ nothinghappens(); } } + } else if (spellid == OT_S_SEEINVIS) { + flag_t *f; + // always targetted at caster + targcell = caster->cell; + target = caster; + + f = addtempflag(caster->flags, F_SEEINVIS, B_TRUE, NA, NA, NULL, FROMSPELL); + f->obfrom = spellid; } else if (spellid == OT_S_SLEEP) { int howlong; if (!validatespelllf(caster, &target)) return B_TRUE; - if (lfhasflag(target, F_ASLEEP)) { + if (lfhasflag(target, F_ASLEEP) || !cansleep(target)) { fizzle(caster); return B_TRUE; } @@ -4398,15 +4971,46 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } - for (o = targcell->obpile->first ; o ; o = nexto) { - nexto = o->next; - if (isflammable(o)) { - takedamage(o, 0, DT_FIRE); + if (targcell->lf) { + losehp(targcell->lf, rnd(1,3), DT_FIRE, caster, "a spark"); + } else { + for (o = targcell->obpile->first ; o ; o = nexto) { + nexto = o->next; + if (isflammable(o)) { + takedamage(o, rnd(1,3), DT_FIRE); + } donesomething = B_TRUE; } } - if (targcell->lf) { - losehp(targcell->lf, 0, DT_FIRE, caster, "a spark"); + } else if (spellid == OT_S_STENCH) { + int howlong; + if (!validatespellcell(caster, &targcell,TT_OBJECT | TT_MONSTER, spellid, power)) return B_TRUE; + + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_TRUE; + } + + if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) { + if (isplayer(target)) { + msg("You feel momentarily nauseated."); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, target)) { + char buf[BUFLEN]; + getlfname(target, buf); + msg("%s looks momentarily unwell.", buf); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + return B_FALSE; + } + howlong = getspellduration(5,10,blessed) + power; + if (makenauseated(target, power, howlong)) { + fizzle(caster); + return B_FALSE; + } + if (isplayer(target) || haslos(player, target->cell)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_STICKTOSNAKE) { int i; @@ -4573,7 +5177,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // confirm ch = askchar("Are you sure to want to teleport into the unknown?", "yn", "n", B_TRUE); if (ch != 'y') c = NULL; - } else if (c->type->solid) { + } else if (!cellwalkable(caster, c, NULL)) { // confirm ch = askchar("Are you sure to want to teleport into solid rock?", "yn", "n", B_TRUE); if (ch != 'y') c = NULL; @@ -4681,6 +5285,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ targcell = c; + if (!cellwalkable(caster, c, NULL)) { + fizzle(caster); + return B_FALSE; + } // we can take up to 'power-1' allies with us. if (caster == target) { @@ -4835,6 +5443,25 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } + } else if (spellid == OT_S_TWIDDLE) { + if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; + target = targcell->lf; + + if (!target || (target == caster)) { + fizzle(caster); + return B_TRUE; + } + + if (isplayer(caster)) { + msg("You feel a wrenching sensation!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (isplayer(target)) { + msg("You feel a wrenching sensation!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, caster)) { + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + swapplaces(caster, target, B_FALSE); } else if ((spellid == OT_S_SUMMONANIMALSSM) || (spellid == OT_S_SUMMONANIMALSMD) || (spellid == OT_S_SUMMONANIMALSLG)) { @@ -4872,8 +5499,163 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("%s appear around %s!", makeplural(rc->name), castername); } } - } else if (spellid == OT_S_SUMMONANIMALSMD) { - } else if (spellid == OT_S_SUMMONANIMALSLG) { + } else if (spellid == OT_S_SUMMONWEAPON) { + object_t *o; + targcell = caster->cell; + target = caster; + if (getequippedob(target->pack, BP_WEAPON)) { + fizzle(caster); + stopspell(caster, spellid); + return B_FALSE; + } + o = addob(target->pack, "energy blade"); + if (o) { + if (canweild(target, o)) { + flag_t *f; + // announce + if (isplayer(target)) { + msg("A blade of pure energy forms in your hands!"); + } else if (cansee(player, target)) { + msg("A blade of pure energy forms in %s%s hands!",castername, + getpossessive(castername)); + } + weild(target, o); + // set its damage value + f = hasflag(o->flags, F_DAM); + if (f) { + char buf[BUFLEN]; + free(f->text); + sprintf(buf, "2d%d",power); + f->text = strdup(buf); + } + } else { + killob(o); + fizzle(caster); + stopspell(caster, spellid); + } + } else { + fizzle(caster); + stopspell(caster, spellid); + return B_FALSE; + } + } else if (spellid == OT_S_WALLOFICE) { + object_t *o; + int donesomething = B_FALSE; + cell_t *c; + int vdist = 0,hdist = 0; + int walldir; + int seen = B_FALSE; + flag_t *f; + + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE; + + + // get vert distance + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) { + vdist++; + } + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) { + vdist++; + } + // get horz dist + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) { + hdist++; + } + for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) { + hdist++; + } + + if ((vdist == 0) && (hdist == 0)) { + fizzle(caster); + return B_FALSE; + } + + // select direction + if (vdist < hdist) { + walldir = D_N; + } else if (hdist < vdist) { + walldir = D_E; + } else { + if (onein(2)) { + walldir = D_N; + } else { + walldir = D_E; + } + } + + if (walldir == D_N) { + // vert wall + for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_N)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, "wall of ice"); + if (o) { + f = hasflag(o->flags, F_OBHP); + if (f) { f->val[0] = power*10; f->val[1] = power*10; } + addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); + } + // knock lfs away + if (c->lf) { + knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power); + donesomething = B_TRUE; + } + } + for (c = getcellindir(targcell, D_S) ; + (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); + c = getcellindir(c, D_S)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, "wall of ice"); + if (o) { + f = hasflag(o->flags, F_OBHP); + if (f) { f->val[0] = power*10; f->val[1] = power*10; } + addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); + } + // knock lfs away + if (c->lf) { + knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power); + donesomething = B_TRUE; + } + } + } else { + // horz wall + for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_E)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, "wall of ice"); + if (o) { + f = hasflag(o->flags, F_OBHP); + if (f) { f->val[0] = power*10; f->val[1] = power*10; } + addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); + } + // knock lfs away + if (c->lf) { + knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power); + donesomething = B_TRUE; + } + } + for (c = getcellindir(targcell, D_W) ; + (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); + c = getcellindir(c, D_W)) { + if (!seen && haslos(player, c)) seen = B_TRUE; + o = addob(c->obpile, "wall of ice"); + if (o) { + f = hasflag(o->flags, F_OBHP); + if (f) { f->val[0] = power*10; f->val[1] = power*10; } + addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); + } + // knock lfs away + if (c->lf) { + knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power); + donesomething = B_TRUE; + } + } + } + + if (seen) { + drawscreen(); + msg("A wall of ice appears!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + + } else if (spellid == OT_S_WATERJET) { char lfname[BUFLEN]; if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE; @@ -5312,6 +6094,13 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { } } + // player can only ever cast spells up to your level. + if (isplayer(lf) && !hasjob(lf, J_GOD)) { + if (getspelllevel(spellid) > lf->level) { + return 0; + } + } + // statmod is -1 to 1 statmod = getstatmod(lf, A_IQ); if (statmod >= 40) { @@ -5327,11 +6116,6 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { spellskill = getskill(lf, SK_SPELLCASTING); if (hasjob(lf, J_DRUID) && (school == SS_NATURE)) { // druid doesn't use spellcasting skill for nature spells - // instead, they can only cast spells up to their - // player level. - if (getspelllevel(spellid) > lf->level) { - return 0; - } } else { // dont need spellcasting skill for mental/allomancy switch (school) { @@ -5475,6 +6259,9 @@ char *getvarpowerspelldesc(enum OBTYPE spellid, int power, char *buf) { case OT_S_PSYARMOUR: sprintf(buf, "+%d Armour Rating", power*4); break; + case OT_S_SUMMONWEAPON: + sprintf(buf, "Create a 2d%d damage magical weapon",power); + break; case OT_S_WINDSHIELD: sprintf(buf, "Protection from missiles <= %d km/h",speedtokph(power)); break; @@ -5549,8 +6336,32 @@ void stopallspells(lifeform_t *lf) { } } +void spellcloud(cell_t *srcloc, int radius, char ch, enum COLOUR col, enum OBTYPE sid, int power) { + int x,y; + if (ch != '\0') { + if (haslos(player, srcloc)) { + animradialorth(srcloc, radius, ch, col); + drawscreen(); + } + } + for (y = srcloc->y - radius ; y <= srcloc->y + radius; y++) { + for (x = srcloc->x - radius ; x <= srcloc->x + radius; x++) { + cell_t *c; + c = getcellat(srcloc->map, x, y); + if (c && c->lf && (c != srcloc) && !c->type->solid && + haslof(srcloc, c, LOF_WALLSTOP, NULL)) { + if (getcelldistorth(srcloc, c) <= radius) { + // fall asleep + dospelleffects(NULL, sid, power, c->lf, NULL, c, B_UNCURSED, NULL); + } + } + } + } +} + void stopspell(lifeform_t *caster, enum OBTYPE spellid) { flag_t *f,*nextf; + object_t *o, *nexto; for (f = caster->flags->first ; f ; f = nextf) { nextf = f->next; @@ -5561,6 +6372,20 @@ void stopspell(lifeform_t *caster, enum OBTYPE spellid) { } } // remove any other specific effects based on spell type. + for (o = caster->pack->first; o ; o = nexto) { + nexto = o->next; + if (o->type->id == OT_ENERGYBLADE) { + if (cansee(player, caster)) { + char obname[BUFLEN]; + char lfname[BUFLEN]; + getobname(o, obname, 1); + getlfname(caster, lfname); + msg("%s%s %s vanishes.", lfname, getpossessive(lfname), + obname); + } + killob(o); + } + } } @@ -5678,6 +6503,10 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, e int needlos = B_TRUE; enum LOFTYPE needlof = LOF_NEED; + if (!caster) { + return *targcell; + } + sp = findot(spellid); if (sp) { flag_t *f; @@ -5731,7 +6560,7 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, e where = NULL; } - if (where && isplayer(caster) && (where == caster->cell)) { + if (where && where->lf && haslos(caster, where) && isplayer(caster) && areallies(caster, where->lf)) { // warn before targetting yourself! if (getiqname(getattr(caster, A_IQ), NULL) >= IQ_AVERAGE) { objecttype_t *sp; @@ -5739,8 +6568,16 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, e if (sp) { if (hasflagval(sp->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL) || hasflagval(sp->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL)) { + char ques[BUFLEN]; int ch; - ch = askchar("Really target yourself","yn","n", B_TRUE); + if (isplayer(where->lf)) { + sprintf(ques,"Really target yourself"); + } else { + char lfname[BUFLEN]; + getlfname(where->lf, lfname); + sprintf(ques, "Really target %s", lfname); + } + ch = askchar(ques,"yn","n", B_TRUE); if (ch != 'y') { where = NULL; } @@ -5847,6 +6684,9 @@ int getmpcost(lifeform_t *lf, enum OBTYPE oid) { lifeform_t *validatespelllf(lifeform_t *caster, lifeform_t **target) { + if (!caster) { + return *target; + } if (*target) { return *target; } diff --git a/spell.h b/spell.h index c450c0b..95d1947 100644 --- a/spell.h +++ b/spell.h @@ -19,6 +19,7 @@ enum SPELLSCHOOL getspellschoolknown(lifeform_t *lf, enum OBTYPE spellid); int getspellrange(enum OBTYPE spellid, int power); char *getvarpowerspelldesc(enum OBTYPE spellid, int power, char *buf); void pullobto(object_t *o, lifeform_t *lf); +void spellcloud(cell_t *srcloc, int radius, char ch, enum COLOUR col, enum OBTYPE sid, int power); void stopspell(lifeform_t *caster, enum OBTYPE spellid); void stopallspells(lifeform_t *lf); int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, int howmany, int lifetime); diff --git a/text.c b/text.c index 66fcf91..57d7266 100644 --- a/text.c +++ b/text.c @@ -276,7 +276,9 @@ char *gettimetextfuzzy(char *retbuf, int wantpm) { } char *getweighttext(float weight, char *buf) { - if (weight >= 1) { + if (weight == 0) { + sprintf(buf, "nothing"); + } else if (weight >= 1) { if ((int)weight == weight) { // ie. is weight an integer? sprintf(buf, "%0.0f kg",weight); } else {