From 7dfeb42e5a5e291541d284fa48106771f77c04f5 Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Wed, 29 Jun 2011 08:48:48 +0000 Subject: [PATCH] - [+] thrown objects taking no damage due to hardness * [+] DIETY is slow. - [+] map bug - from 0,0, walk NE (off top edge). i end up at 0,0 again!!! * [+] ALLOW ROTATING OF VAULTS - [+] BUG: dig X only goes 1 cell. - [+] add a U shaped turn vault. - [+] change wehre i apply HADRNESS for lfs! - don't use adjustdammaterial cause we inherit there - [+] make obchance/thingchance part of habitat_t - [+] make sense surroundings take cartography skill into account * [+] BUG: rotated at() etc not working. - [+] if you fail stealing, the target should get angry!!! - [+] make sure to redraw stat bar when mind scan finishes. - [+] villages - [+] initial code * [+] town gaurds. stand next to the gate - [+] potion shop vault - [+] implement - [+] potion shop isn't getting potions!! - [+] and getting wrong floor type. * [+] stop shop from always being at the top left. - [+] make sure its x/y arent too close to edge of map (at least 5 away) - [+] vault param. f_mapmargin, 5 - [+] make calcroompos take x/y margin - [+] don't put auto obs inside vaults - [+] town walls - [+] more kinds of shops * [+] signs outside shops - [+] bug with ai swapping between firearms and twohanded weapons. fixed. * [+] make _CELLS_ have habitats! - [+] move vaultchance to a habitat param instead of getvaultchance() * [+] shopkeepers (special race) village things: - [+] barrels, - [+] guards, - [+] statue - [+] thrown objects should still take damage if blocked via a shield * [+] hardness (must do dam >= xx to hurt) * [+] WATER spread mods * [+] very powerful spells have casttime - [+] allow linkstairs() to take a parameter rather than always jsut searching for the other end - [+] when digging holes, only use getrandomadjcell if there is an lf in the way. and then use WE_NOLF. clear out solid cells - [+] show armour EVASION penalty in describeob - [+] teleportation trap. You reintegrate inside a solid object! You die.--More--. - [+] riverroom vault * [+] water: swap { and ~ again?? - [+] make "large puddle of water" evaporate more quickly * [+] special glyph case: for deep water --- Makefile | 2 +- ai.c | 31 +- attack.c | 2 +- defs.h | 92 +++++- doc/vaults.txt | 8 +- flag.c | 228 +++++++++++++- flag.h | 1 + io.c | 122 ++++++- lf.c | 460 +++++++++++++++++++++++---- lf.h | 10 +- map.c | 698 +++++++++++++++++++++++++++-------------- map.h | 26 +- move.c | 173 +++++++--- move.h | 1 + nexus.c | 114 +++++-- objects.c | 283 +++++++++++++---- objects.h | 5 +- save.c | 21 +- spell.c | 280 ++++++++++------- vaults/crosshatch.vlt | 25 ++ vaults/diagcross.vlt | 24 ++ vaults/jimbo.vlt | 31 ++ vaults/labyrinth.vlt | 2 +- vaults/riverroom.vlt | 24 ++ vaults/shop_armour.vlt | 29 ++ vaults/shop_potion.vlt | 29 ++ vaults/shop_weapon.vlt | 29 ++ vaults/uturn.vlt | 19 ++ vaults/vault.vlt | 1 + 29 files changed, 2176 insertions(+), 594 deletions(-) create mode 100644 vaults/crosshatch.vlt create mode 100644 vaults/diagcross.vlt create mode 100644 vaults/jimbo.vlt create mode 100644 vaults/riverroom.vlt create mode 100644 vaults/shop_armour.vlt create mode 100644 vaults/shop_potion.vlt create mode 100644 vaults/shop_weapon.vlt create mode 100644 vaults/uturn.vlt diff --git a/Makefile b/Makefile index 9750be2..ad1ecda 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ nexus: Makefile defs.h nexus.c nexus.h ai.c ai.h attack.c attack.h flag.c flag.h io.c io.h lf.c lf.h map.c map.h move.c move.h objects.c objects.h text.c text.h save.c save.h spell.c spell.h vault.c vault.h - gcc -Wall -g -o nexus nexus.c ai.c attack.c flag.c io.c lf.c map.c move.c objects.c text.c save.c spell.c vault.c vault.h -lncurses + gcc -Wall -g -pg -o nexus nexus.c ai.c attack.c flag.c io.c lf.c map.c move.c objects.c text.c save.c spell.c vault.c vault.h -lncurses diff --git a/ai.c b/ai.c index e4d922b..cf6776a 100644 --- a/ai.c +++ b/ai.c @@ -771,6 +771,12 @@ int aipickup(lifeform_t *lf, object_t *o) { int aipickupok(lifeform_t *lf, object_t *o) { int ok = B_FALSE; + + if (hasflag(o->flags, F_SHOPITEM)) { + // && (getiqname(getattr(lf, A_IQ), NULL) > IQ_ANIMAL)) { + return B_FALSE; + } + if (isedible(o)) { if (caneat(lf, o) && !isinbattle(lf)) { ok = B_TRUE; @@ -944,13 +950,16 @@ void aiturn(lifeform_t *lf) { } // do we have a better firearm ? - curgun = getfirearm(lf); - bestgun = getbestfirearm(lf); + if (curwep && hasflag(curwep->flags, F_TWOHANDED)) { + } else { + curgun = getfirearm(lf); + bestgun = getbestfirearm(lf); - if (curgun != bestgun) { - if (db) dblog(".oO { i have a better gun than my current one (%s > %s) }",bestgun->type->name, curgun ? curgun->type->name : "nothing"); - // weild better one - if (!weild(lf, bestgun)) return; + if (curgun != bestgun) { + if (db) dblog(".oO { i have a better gun than my current one (%s > %s) }",bestgun->type->name, curgun ? curgun->type->name : "nothing"); + // weild better one + if (!weild(lf, bestgun)) return; + } } // do we have better ammo? @@ -1693,7 +1702,7 @@ int lookforobs(lifeform_t *lf) { // if we are in battle only go for it if we covet it if (!target || (f->val[1] == B_COVETS)) { o = hasbetterweapon(lf, lf->cell->obpile); - if (o && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) { + if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o) && canpickup(lf, o, 1)) { if (db) dblog(".oO { current cell has better weapon (%s) }",o->type->name); // try to pick it up if (!aipickup(lf, o)) return B_TRUE; @@ -1710,7 +1719,7 @@ int lookforobs(lifeform_t *lf) { // if we are in battle only go for it if we covet it if (!target || (f->val[1] == B_COVETS)) { o = hasbetterarmour(lf, lf->cell->obpile); - if (o && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) { + if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o) && canpickup(lf, o, 1)) { if (db) dblog(".oO { current cell has better armour (%s) }",o->type->name); // try to pick it up if (!aipickup(lf, o)) return B_TRUE; @@ -1774,7 +1783,8 @@ int lookforobs(lifeform_t *lf) { if (!target || ((f->val[1] != B_COVETS) && (celldist <= targdist)) ) { o = hasbetterweapon(lf, c->obpile); - if (o && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) { + if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o) && + canpickup(lf, o, 1)) { if (db) dblog(".oO { remote cell has better weapon (%s). setting f_targetcell }",o->type->name); gothere = B_TRUE; } @@ -1791,7 +1801,8 @@ int lookforobs(lifeform_t *lf) { ((f->val[1] != B_COVETS) && (celldist <= targdist)) ) { o = hasbetterarmour(lf, c->obpile); - if (o && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) { + if (o && !isdangerousob(o, lf, B_TRUE) && aipickupok(lf, o) && + canpickup(lf, o, 1)) { if (db) dblog(".oO { remote cell has better armour (%s). setting f_targetcell }",o->type->name); gothere = B_TRUE; } diff --git a/attack.c b/attack.c index d2a95c4..2758938 100644 --- a/attack.c +++ b/attack.c @@ -1046,7 +1046,7 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { noise(lf->cell, NULL, 3, "sounds of fighting.", NULL); } - if ((i == 0) && (wep->type->id == OT_FISTS) && hasflag(o->flags, F_HARD)) { + if ((i == 0) && (wep->type->id == OT_FISTS) && hasflag(o->flags, F_HARDNESS)) { char buf[BUFLEN]; sprintf(buf, "punching %s", obname); if ( losehp(lf, 1, DT_BASH, lf, buf)) { diff --git a/defs.h b/defs.h index 3c576cb..b5e33eb 100644 --- a/defs.h +++ b/defs.h @@ -36,6 +36,15 @@ enum COLOUR { C_BOLDGREEN = 14, }; +enum SPEECHVOL { + SV_SILENT = 0, + SV_WHISPER = 1, + SV_TALK = 2, + SV_SHOUT = 3, + SV_ROAR = 4, + SV_BELLOW = 3, +}; + enum SKILL { SK_NONE = 0, SK_ARMOUR = 1, @@ -158,6 +167,7 @@ enum CHECKTYPE { SC_POISON, SC_RESISTMAG, SC_SEARCH, + SC_STEAL, SC_STEALTH, SC_WILL, }; @@ -263,7 +273,9 @@ enum MODTYPE { #define MAXRETCELLS 80 -#define MAXCHOICES 200 +#define MAXFLAGS 500 + +#define MAXCHOICES 400 #define MAXDEPTH 25 // max dungeon depth @@ -425,10 +437,14 @@ enum CELLTYPE { CT_WALL, CT_WALLGLASS, CT_WALLMETAL, + CT_WALLWOOD, CT_ROOMWALL, // empty CT_CORRIDOR, CT_DIRT, + CT_VILLAGEGROUND, + CT_FLOORSHOP, + CT_FLOORWOOD, CT_GRASS, CT_LOOPCORRIDOR, CT_LOWFLOOR, @@ -619,6 +635,7 @@ enum RACE { // human monsters R_HUMAN, R_BANDIT, + R_TOWNGUARD, // monsters R_BEHOLDER, R_BUGBEAR, @@ -728,6 +745,7 @@ enum JOB { J_PLUMBER, J_PRINCE, J_ROGUE, + J_SHOPKEEPER, J_WIZARD, }; @@ -778,7 +796,10 @@ enum OBTYPE { OT_STATUE, OT_DOORWOOD, OT_DOORIRON, - OT_IRONGATE, + OT_GATEIRON, + OT_GATEWOOD, + OT_FENCEWOOD, + OT_SIGN, OT_WOODENTABLE, OT_WOODENBARREL, OT_WOODENSTOOL, @@ -1014,6 +1035,7 @@ enum OBTYPE { OT_S_WATERJET, // -- summoning OT_S_FLOATINGDISC, + OT_S_CLEARLEVEL, OT_S_CREATEMONSTER, OT_S_SUMMONWEAPON, // -- translocation @@ -1034,6 +1056,7 @@ enum OBTYPE { OT_S_CREATEVAULT, OT_S_GIFT, OT_S_WISH, + OT_A_BLINDALL, OT_A_DEBUG, OT_A_ENHANCE, OT_A_LEARN, @@ -1365,6 +1388,7 @@ enum DEPTH { #define WE_EMPTY 2 #define WE_PORTAL 3 #define WE_NOTWALL 4 +#define WE_NOLF 5 enum NOISETYPE { N_GETANGRY, @@ -1427,6 +1451,7 @@ enum FLAG { F_NO_A, // this obname doesn't need to start with 'a' for singular (eg. gold) F_CONTAINSOB, // for vending machiens. v0 is ob letter // text is an object it contains. + F_SIGNTEXT, // for 'sign' objects. f->text is what is says. F_IDWHENUSED, // fully identify an object when worn/weilded/operated/etc F_STARTBLESSED, // v0 = b_blessed or b_cursed F_REPELBLESSED, // v0 = b_blessed or b_cursed. repels other obejcts @@ -1440,7 +1465,11 @@ enum FLAG { F_NOFEEL, // when blind, don't show "you can feel xxx" F_FEELTEXT, // when blind, show "you can feel"+f->text // for items in shops - F_SHOPITEM, // causes shops to show this item as identified + F_VENDITEM, // causes vending machine to show this item as identified + F_SHOPITEM, // v0 is object value. + // v1 is the shop it is from + // causes shops to show (worth $xx) after the ob's name. + // also used for detecting theft! F_VALUE, // how much an item is worth (over its base weight+material) // weapon/armour flags F_EQUIPPED, // val0 = where it is equipped. CLEAR WHEN OB MOVED! @@ -1568,7 +1597,6 @@ enum FLAG { // text = obid to link to // ob interaction flags - F_HARD, // object is hard (ie. punching it will hurt!) F_REDUCEMOVEMENT, // time to move off here is multiplied by v0. F_RESTRICTMOVEMENT, // must pass a diff=v0 STR check to move off it. // if v1 is B_TRUE, then it takes 1 damage if you fail. @@ -1683,6 +1711,7 @@ enum FLAG { F_MAXPOWER, // val0 = max power of this spell (1-10) F_MPCOST, // v0=mp cost of spell. if missing, mpcost if splev^2 F_ONGOING, // this spell has an ongoing cost + F_CASTINGTIME, // this spell takes v0 turns to cast //F_SPELLLETTER, // text[0] = letter to cast this spell F_AICASTTOFLEE, // AI can cast this spell to help flee/heal // v0 is who to target @@ -1693,9 +1722,10 @@ enum FLAG { F_AIHEALITEM, // ai will use this item when low on hp F_AIFLEEITEM, // ai will use this item when fleeing // if using this on wands, update aiobok() ! - // object _AND_ lifeform flags + // object AND lifeform flags F_NOSTRDAMMOD, // this object/lf does not have attacks modified // using their strength + F_HARDNESS, // must do >= v0 damage to hurt this // player only flags F_DONEDARKMSG, // tells the game not to say 'it is very dark here' F_DONELISTEN, // supress further 'you hear xx' messages this turn. @@ -1740,6 +1770,7 @@ enum FLAG { // text can be: // x (single number) // x-y (range) + F_DONTSTARTASLEEP, // this mosnter never starts off asleep F_STARTHIDDENPCT, // val0 = pct chance auto-generated monster will // start off hidden F_CORPSETYPE, // text field specifies what corpse obtype to leave @@ -1753,9 +1784,18 @@ enum FLAG { F_VISRANGEMOD, // modifications to visrange F_GUNTARGET, // current projectile weapon target F_CASTINGSPELL, // set while the player is casting a spell - // v0 is spell id + // for instant spells: + // v0 is spell id + // for noninstant spells: + // v0 is spell id + // v1 is spell power + // v2 is counter until casting + // text is: "targlfid;targobid;mapid;cellx;celly;" + F_AVOIDCURSEDOB, // for AI animals - they will avoid walking on obid 'text' // (text is a long) + F_STAYINHABITAT, // lf will not walk onto a cell of a different + // habitat F_FALLDISTANCE, // how many floors this lf has fallen through. // ABILITY/SPELL FLAGS / ability flags / spell flags F_FAILEDINSPECT, // lf has failed an inspect check for item id v0 @@ -1787,6 +1827,8 @@ enum FLAG { // vanish. // v1 is lifetime left. this decrements each turn. // when at zero, lf vanishes. + F_OWNSSHOP, // v0 is roomid of the shop which this shopkeeper owns. + F_GUARD, // this lf is a guard, who can be called by shopkeepers F_HATESRACE, // lf will attack lfs with race=v0 or baseid=v0 on // sight F_HARMLESS, // it is safe to rest around this lf @@ -2012,7 +2054,8 @@ enum FLAG { // nutrition F_HUNGER, // val0 = hunger, higher = hungrier - // for jobs + // for jobs (job flags) + F_NOPLAYER, // players can't pick this job F_HASPET, // this job starts with a pet of race f->text F_IFPCT, // only add the NEXT job flag if rnd(1,100) <= v0. F_ELSE, @@ -2048,13 +2091,18 @@ enum FLAG { F_VAULTGOESIN, // this vault randomly appears in habitat type v0. // can be repeated multiple times // if a vault doesnt have this flag, it can go anywhere + F_VAULTISSHOP, // this vault is a shop, so add f_shopitem to objects + // here. F_VAULTSCATTER, // v0=thingtype, v1=pctchance // text=x1,y1,x2,y2,mincount-maxcount,thingname // if maxcount is PCT, mincount is a percentage // of the total space. + F_VAULTMAYROTATE, // may rotate this vault in 90degree increments. F_VAULTRANDOMMAP, // v0=minwidth, v1=minheight. this vault's map is // v0/1 can be NA. // just a normal random room + F_KEEPMARGIN, // this vault must be at least v0 from e/w of map + // and at least v1 from n/s of map F_NULL = -1 }; @@ -2263,6 +2311,11 @@ enum COMMAND { CMD_WEILD, }; + +typedef struct coord_s { + int x,y; +} coord_t; + // command types typedef struct command_s { enum COMMAND id; @@ -2281,6 +2334,7 @@ enum HABITAT { H_DUNGEON = 1, H_FOREST = 2, H_PIT = 3, + H_VILLAGE = 4, H_ALL = 999 }; @@ -2290,6 +2344,7 @@ typedef struct regiontype_s { int maxdepth; int stairsperlev; int deeperdir; + int majorbranch; struct regiontype_s *next, *prev; } regiontype_t; @@ -2328,6 +2383,9 @@ typedef struct region_s { typedef struct habitat_s { enum HABITAT id; char *name; + int randthingpct; // % chance each empty cell has something + int randobpct; // % chance that 'something' is an ob rather than monster + int randvaultpct; // % chance that a room will be a vault enum CELLTYPE emptycelltype,solidcelltype; struct habitat_s *next, *prev; } habitat_t; @@ -2380,14 +2438,22 @@ typedef struct vlegend_s { struct vlegend_s *next, *prev; } vlegend_t; +// in map[0], data is the real data +// in others, data is an index into map[0] +typedef struct vaultmap_s { + int data[MAX_MAPW*MAX_MAPH]; + int mlen; + int w,h; +} vaultmap_t; + typedef struct vault_s { char *filename; char *id; + int numid; int valid; int state; - char map[MAX_MAPW*MAX_MAPH]; - int mlen; - int w,h; + struct vaultmap_s map[4]; + int nmaps; struct vlegend_s *legend, *lastlegend; struct vault_s *next, *prev; struct flagpile_s *flags; @@ -2402,11 +2468,13 @@ typedef struct cell_s { map_t *map; // pointer back to map int x,y; // map coords int roomid; + vault_t *vault; struct celltype_s *type; struct obpile_s *obpile; enum LIGHTLEV lit; enum LIGHTLEV origlit; // for timed light enum LIGHTLEV lastlit; + habitat_t *habitat; int origlittimer; int littimer; @@ -2475,6 +2543,7 @@ typedef struct lifeform_s { int mp,maxmp; int alive; char *lastdam; + struct material_s *material; enum DAMTYPE lastdamtype; int timespent; @@ -2526,6 +2595,9 @@ typedef struct flagpile_s { lifeform_t *owner; struct object_s *ob; struct flag_s *first,*last; + + struct flag_s *item[MAXFLAGS]; + int nitems; } flagpile_t; typedef struct flag_s { diff --git a/doc/vaults.txt b/doc/vaults.txt index 1bc1afb..160fbfe 100644 --- a/doc/vaults.txt +++ b/doc/vaults.txt @@ -43,13 +43,15 @@ Flags can be: - a range (x-y) - a pct of the total region cells (x%) coords can be negative ("count back from right/bottom") + pct is optional autodoors:pct // automatically add at least one door to the edges of // this room. // pct is chance of the exit being a door as opposed // to jsut an opening. - autopop // automatically add obs/mons/pillars to this room + autopop // automatically add obs/mons/pillars to this vault as + // if it were a normal room. dlevmin:xxx // can only randomly appear at or below dungeon // level xxx (or map difficulty xxx for world map) @@ -58,6 +60,10 @@ Flags can be: goesin:xxx // can only randomly appear in habitat xxx + margin:x,y // must be x/y away from edges of map + + mayrotate // vault can be rotated randomly + norandom // this vault doesn't appear randomly. it will only // appear when specifically requested via a region's // outline. diff --git a/flag.c b/flag.c index cb6ef08..87d288a 100644 --- a/flag.c +++ b/flag.c @@ -82,27 +82,65 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, } } - // override values sometimes - /* - if ((id == F_WET) && (val2 == NA)) { - val2 = WETTIME; - } - */ + //////////////////////////////// + // ACTUAL FLAG ADDITION IS HERE + //////////////////////////////// if (fp->first == NULL) { fp->first = malloc(sizeof(flag_t)); f = fp->first; f->prev = NULL; + + // also the last + fp->last = f; + f->next = NULL; + } else { + flag_t *ff; + // we will keep flags sorted. + // find correct position in list... + ff = fp->first; + while (ff && (ff->id < id)) { + ff = ff->next; + } + + if (ff) { + // start or middle of list + // insert BEFORE this one + flag_t *prev; + prev = ff->prev; + + // link to previous element + if (prev) { + prev->next = malloc(sizeof(flag_t)); + f = prev->next; + } else { + // first one + fp->first = malloc(sizeof(flag_t)); + f = fp->first; + } + f->prev = prev; + + // link to next element + f->next = ff; + ff->prev = f; + } else { + // last one. insert at end of list. + f = fp->last; + f->next = malloc(sizeof(flag_t)); + f->next->prev = f; + f = f->next; + fp->last = f; + f->next = NULL; + } + /* // go to end of list f = fp->last; f->next = malloc(sizeof(flag_t)); f->next->prev = f; f = f->next; + */ } - fp->last = f; - - f->next = NULL; // fill in props f->id = id; @@ -133,6 +171,8 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, f->pile = fp; + updatefpindex(fp); + // notify if ((gamemode == GM_GAMESTARTED)) { if (f->pile->owner) { @@ -232,6 +272,7 @@ flagpile_t *addflagpile(lifeform_t *owner, object_t *ob) { fp->last = NULL; fp->owner = owner; fp->ob = ob; + fp->nitems = 0; return fp; } @@ -239,6 +280,8 @@ flagpile_t *addflagpile(lifeform_t *owner, object_t *ob) { void copyflag(flagpile_t *dst, flagpile_t *src, enum FLAG id) { flag_t *f; for (f = src->first ; f ; f = f->next) { + // gone past the requrested id's number - ie. it's not there. + if (f->id > id) break; if (f->id == id) { addflag_real(dst, f->id, f->val[0], f->val[1], f->val[2], f->text, f->lifetime, f->known, -1); @@ -355,11 +398,79 @@ flag_t *hasflagknown(flagpile_t *fp, int id) { } flag_t *hasflag_real(flagpile_t *fp, int id, int wantknown, flag_t *exception) { - flag_t *f; + flag_t *f,*foundflag = NULL, *searchfrom = NULL; lifeform_t *owner; owner = fp->owner; + int pos; + int l,r; + int iter = 0; + int db = B_FALSE; + if (db) dblog("hasflag %d in flagpile with %d entries", id, fp->nitems); + // binary search for this flag id. + l = 0; + r = fp->nitems-1; + while (!searchfrom) { + if (db) dblog(" iter#%d: l=%d,r=%d", iter, l, r); + if (r < l) { + // NOT FOUND. + if (db) dblog(" r < l: not found!"); + break; + } else if (fp->item[l]->id > id) { + // NOT FOUND. + if (db) dblog(" leftid %d > wantid %d. not found!",fp->item[l]->id, id); + break; + } else if (fp->item[r]->id < id) { + // NOT FOUND. + if (db) dblog(" rightid %d > wantid %d. not found!",fp->item[r]->id, id); + break; + } + // half way inbetween l and r + pos = ((l+r)/2); + f = fp->item[pos]; + if (db) dblog(" iter#%d: pos=%d. item here is %d (looking for %d)", iter, pos, f->id, id); + + if (f->id == id) { + // go back to first occurence + while (f->prev && (f->prev->id == id)) { + f = f->prev; + } + searchfrom = f; + break; + } else if (f->id > id) { + // go left + r = pos-1; + } else { + // go right + l = pos+1; + } + } + + + // now find a valid one + f = searchfrom; + while (f && (f->id == id)) { + int valid = B_TRUE; + if (f == exception) valid = B_FALSE; + if ((wantknown != NA) && (f->known != wantknown)) valid = B_FALSE; + if (owner && (f->lifetime == FROMJOB) && !getjob(owner)) valid = B_FALSE; + + if (valid) { + foundflag = f; + break; + } else { + f = f->next; + } + } + + if (db) dblog("finished - %s", foundflag ? "found it" : "NOT FOUND"); + return foundflag; + + /* for (f = fp->first ; f ; f = f->next) { + // gone past the requrested id's number - ie. it's not there. + if (f->id > id) break; + if ((f->id == id) && (f != exception)) { int valid = B_TRUE; if ((wantknown != NA) && (f->known != wantknown)) valid = B_FALSE; @@ -372,6 +483,7 @@ flag_t *hasflag_real(flagpile_t *fp, int id, int wantknown, flag_t *exception) { } } } + */ return NULL; } @@ -394,10 +506,82 @@ int getcounter(flagpile_t *fp) { } flag_t *hasflagval_real(flagpile_t *fp, int id, int val1, int val2, int val3, char *text, int wantknown) { - flag_t *f; + flag_t *f,*foundflag = NULL, *searchfrom = NULL; lifeform_t *owner; owner = fp->owner; + int pos; + int l,r; + int iter = 0; + int db = B_FALSE; + + if (db) dblog("hasflag %d in flagpile with %d entries", id, fp->nitems); + // binary search for this flag id. + l = 0; + r = fp->nitems-1; + while (!searchfrom) { + if (db) dblog(" iter#%d: l=%d,r=%d", iter, l, r); + if (r < l) { + // NOT FOUND. + if (db) dblog(" r < l: not found!"); + break; + } else if (fp->item[l]->id > id) { + // NOT FOUND. + if (db) dblog(" leftid %d > wantid %d. not found!",fp->item[l]->id, id); + break; + } else if (fp->item[r]->id < id) { + // NOT FOUND. + if (db) dblog(" rightid %d > wantid %d. not found!",fp->item[r]->id, id); + break; + } + // half way inbetween l and r + pos = ((l+r)/2); + f = fp->item[pos]; + if (db) dblog(" iter#%d: pos=%d. item here is %d (looking for %d)", iter, pos, f->id, id); + + if (f->id == id) { + // go back to first occurence + while (f->prev && (f->prev->id == id)) { + f = f->prev; + } + searchfrom = f; + break; + } else if (f->id > id) { + // go left + r = pos-1; + } else { + // go right + l = pos+1; + } + } + + + // now find a valid one + f = searchfrom; + while (f && (f->id == id)) { + if (owner && (f->lifetime == FROMJOB) && !getjob(owner)) { + // invalid + } else if ( ((val1 == NA) || (f->val[0] == val1)) && + ((val2 == NA) || (f->val[1] == val2)) && + ((val3 == NA) || (f->val[2] == val3)) && + ((text == NULL) || strstr(f->text, text))) { + if (!wantknown || f->known) { + foundflag = f; + break; + } + } + + f = f->next; + } + + if (db) dblog("finished - %s", foundflag ? "found it" : "NOT FOUND"); + return foundflag; + + + /* for (f = fp->first ; f ; f = f->next) { + // gone past the requrested id's number - ie. it's not there. + if (f->id > id) break; + if (f->id == id) { if (owner && (f->lifetime == FROMJOB) && !getjob(owner)) { // invalid @@ -414,6 +598,7 @@ flag_t *hasflagval_real(flagpile_t *fp, int id, int val1, int val2, int val3, ch } } return NULL; + */ } // returns true if we did something @@ -422,6 +607,10 @@ int killflagsofid(flagpile_t *fp, enum FLAG fid) { int donesomething = B_FALSE; for (f = fp->first ; f ; f = nextf) { nextf = f->next; + + // gone past the requrested id's number - ie. it's not there. + if (f->id > fid) break; + if (f->id == fid) { killflag(f); donesomething = B_TRUE; @@ -536,6 +725,8 @@ void killflag(flag_t *f) { lastone->next = nextone; } + updatefpindex(f->pile); + if (gamemode == GM_GAMESTARTED) { if (redolight) { calclight(redolight); @@ -752,6 +943,9 @@ void sumflags(flagpile_t *fp, int id, int *val0, int *val1, int *val2) { if (val1) *val1 = 0; if (val2) *val2 = 0; for (f = fp->first ; f ; f = f->next) { + // gone past the requrested id's number - ie. it's not there. + if (f->id > id) break; + if (f->id == id) { if (val0) *val0 = *val0 + f->val[0]; if (val1) *val1 = *val1 + f->val[1]; @@ -766,5 +960,15 @@ void timeeffectsflags(flagpile_t *fp) { nextf = f->next; timeeffectsflag(f, 1); } - +} + +// generate an index +void updatefpindex(flagpile_t *fp) { + flag_t *f; + fp->nitems = 0; + for (f = fp->first ;f ; f = f->next) { + fp->item[fp->nitems] = f; + fp->nitems++; + + } } diff --git a/flag.h b/flag.h index 2b3e333..d307440 100644 --- a/flag.h +++ b/flag.h @@ -27,3 +27,4 @@ int modcounter(flagpile_t *fp, int amt); void sumflags(flagpile_t *fp, int id, int *val0, int *val1, int *val2); void timeeffectsflag(flag_t *f, int howlong); void timeeffectsflags(flagpile_t *fp); +void updatefpindex(flagpile_t *fp); diff --git a/io.c b/io.c index 7847f71..ec5060d 100644 --- a/io.c +++ b/io.c @@ -85,6 +85,7 @@ void addchoice(prompt_t *p, char ch, char *text, char *desc, void *data) { } p->choice[p->nchoices].data = data; p->choice[p->nchoices].heading = B_FALSE; + p->choice[p->nchoices].valid = B_TRUE; p->nchoices++; } @@ -648,10 +649,20 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src // prevent showing 'prone' AND 'asleep' if (strlen(extrainfo)) strcat(extrainfo, ", "); strcat(extrainfo, "prone"); - } - if (isswimming(c->lf)) { + } else if (isairborne(c->lf)) { if (strlen(extrainfo)) strcat(extrainfo, ", "); - strcat(extrainfo, "swimming"); + if (lfhasflag(c->lf, F_FLYING)) { + strcat(extrainfo, "flying"); + } else if (lfhasflag(c->lf, F_LEVITATING)) { + strcat(extrainfo, "levitating"); + } else { + strcat(extrainfo, "airbourne"); + } + } else { + if (isswimming(c->lf)) { + if (strlen(extrainfo)) strcat(extrainfo, ", "); + strcat(extrainfo, "swimming"); + } } f = lfhasflag(c->lf, F_ATTACHEDTO); @@ -3037,11 +3048,29 @@ void describeob(object_t *o) { } mvwprintw(mainwin, y, 0, "%s",buf); y++; + + f = hasflag(o->flags, F_EVASION); + if (f) { + int evmod; + evmod = adjustarmourpenalty(player, f->val[0]); + if (evmod != 0) { + sprintf(buf, " When worn, it %s your evasion chance by %d%%.", (evmod < 0) ? "reduces" : "increases", abs(evmod)); + } + } + + + f = hasflag(o->flags, F_SCARY); if (f) { mvwprintw(mainwin, y, 0, " It may unnerve others when worn."); y++; } + } else { + // non armour, but still wearable. + f = hasflag(o->flags, F_GOESON); + if (f) { + sprintf(buf, "It is worn %s your %s. ",getbodypartequipname(f->val[0]), getbodypartname(f->val[0])); + } } if (o->type->obclass->id == OC_WAND) { @@ -3538,6 +3567,13 @@ void describespell(objecttype_t *ot) { } } + f = hasflag(ot->flags, F_CASTINGTIME); + if (f) { + wprintw(mainwin, "It takes %d turns to cast.\n",f->val[0]); + } else { + wprintw(mainwin, "It takes effect instantly.\n"); + } + wprintw(mainwin, "\n"); if (ot->obclass->id == OC_SPELL) { @@ -3645,6 +3681,7 @@ void docomms(lifeform_t *lf) { char buf[BUFLEN]; char lfname[BUFLEN]; char ch; + int moneyowing = 0; enum IQBRACKET iqb; flag_t *f; @@ -3691,9 +3728,19 @@ void docomms(lifeform_t *lf) { addchoice(&prompt, 'r', "Rest until you are healed.", NULL, NULL); } } - } else { - addchoice(&prompt, 'y', "Yeeeeeaaaargh!", NULL, NULL); } + + f = lfhasflag(lf, F_OWNSSHOP); + if (f) { + int shopid; + shopid = f->val[0]; + moneyowing = getowing(player, shopid, NULL); + if (moneyowing > 0) { + sprintf(buf, "(pay $%d to the shopkeeper)",moneyowing); + addchoice(&prompt, 'p', buf, NULL, NULL); + } + } + addchoice(&prompt, 'y', "Yeeeeeaaaargh!", NULL, NULL); addchoice(&prompt, 'n', "(nothing)", NULL, NULL); ch = getchoice(&prompt); @@ -3749,6 +3796,25 @@ void docomms(lifeform_t *lf) { case 'n': msg("Cancelled."); return; + case 'p': + // can we afford this? + if (givemoney(player, lf, moneyowing)) { + // can't afford it + msg("You can't afford to pay $%d.", moneyowing); + } else { + object_t *o; + // mark all items as paid for + for (o = player->pack->first ; o ; o = o->next) { + f = hasflag(o->flags, F_SHOPITEM); + if (f) { + killflag(f); + getobname(o, buf, o->amt); + msg("You buy %s.", buf); + } + } + say(lf, "Pleasure doing business with you!", SV_TALK); + } + break; case 'r': f = isresting(lf); if (f) { @@ -3980,7 +4046,7 @@ void dovendingmachine(lifeform_t *lf, object_t *vm) { // remember letter o->letter = ch; // make object fully known - addflag(o->flags, F_SHOPITEM, B_TRUE, NA, NA, NULL); + addflag(o->flags, F_VENDITEM, B_TRUE, NA, NA, NULL); } } @@ -4061,7 +4127,8 @@ void dovendingmachine(lifeform_t *lf, object_t *vm) { sprintf(buf, "Buy %s for $%d?",obname, getobvalue(o)); answer = askchar(buf, "yn","n", B_TRUE); if (answer == 'y') { - gold->amt -= getobvalue(o); + givemoney(player, NULL, getobvalue(o)); + //gold->amt -= getobvalue(o); // clear o->letter o->letter = '\0'; // give object @@ -6433,16 +6500,32 @@ void drawstatus(void) { if (lfhasflag(player, F_RAGE)) { setcol(statwin, C_RED); - wprintw(statwin, " Enraged"); + wprintw(statwin, " Rage"); unsetcol(statwin, C_RED); } if (isswimming(player)) { setcol(statwin, C_BOLDBLUE); - wprintw(statwin, " Swimming"); + wprintw(statwin, " Swim"); unsetcol(statwin, C_BOLDBLUE); } + if (isairborne(player)) { + if (lfhasflag(player, F_FLYING)) { + setcol(statwin, C_BOLDBLUE); + wprintw(statwin, " Fly"); + unsetcol(statwin, C_BOLDBLUE); + } else if (lfhasflag(player, F_LEVITATING)) { + setcol(statwin, C_BOLDBLUE); + wprintw(statwin, " Lev"); + unsetcol(statwin, C_BOLDBLUE); + } else { + setcol(statwin, C_BOLDBLUE); + wprintw(statwin, " Flt"); // "float"ing + unsetcol(statwin, C_BOLDBLUE); + } + } + // paralysed somehow? if (isresting(player)) { setcol(statwin, C_CYAN); @@ -6452,6 +6535,10 @@ void drawstatus(void) { setcol(statwin, C_BLUE); wprintw(statwin, " Asleep"); unsetcol(statwin, C_BLUE); + } else if (isprone(player)) { + setcol(statwin, C_YELLOW); + wprintw(statwin, " Prone"); + unsetcol(statwin, C_YELLOW); } else if (isimmobile(player)) { setcol(statwin, C_RED); wprintw(statwin, " Immobile"); @@ -6506,11 +6593,6 @@ void drawstatus(void) { } } - if (isprone(player) && !lfhasflag(player, F_ASLEEP)) { - setcol(statwin, C_YELLOW); - wprintw(statwin, " Prone"); - unsetcol(statwin, C_YELLOW); - } // burdened somehow? switch (isburdened(player)) { @@ -6781,6 +6863,12 @@ void setobcolour(WINDOW *win, object_t *o, int set) { } if (!o) return; + // unpaid? + if (hasflag(o->flags, F_SHOPITEM)) { + funcptr(win, C_ORANGE); + return; + } + if (o->blessknown) { if (iscursed(o)) { funcptr(win, C_RED); @@ -7647,8 +7735,10 @@ void showlfstats(lifeform_t *lf, int showall) { } // diff materials? - if (lf->race->material->id != MT_FLESH) { - mvwprintw(mainwin, y, 0, "%s %s made out of %s.",you(lf), is(lf), lf->race->material->name); + if (getlfmaterial(lf) != MT_FLESH) { + material_t *mt; + mt = findmaterial(getlfmaterial(lf)); + mvwprintw(mainwin, y, 0, "%s %s made out of %s.",you(lf), is(lf), mt->name); y++; } diff --git a/lf.c b/lf.c index e0f41a4..2cd5e5e 100644 --- a/lf.c +++ b/lf.c @@ -414,6 +414,19 @@ int calcxprace(enum RACE rid) { return xpval; } + +void callguards(lifeform_t *caller, lifeform_t *victim) { + lifeform_t *l; + for (l = caller->cell->map->lf ; l ; l = l->next) { + if (!isplayer(l) && (l != caller) && (l != victim) && lfhasflag(l, F_GUARD)) { + aiattack(l, victim, PERMENANT); + if (isplayer(victim)) { + addflag(l->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + } + } + } +} + int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { int castable = B_FALSE; flag_t *f; @@ -686,34 +699,36 @@ int canpickup(lifeform_t *lf, object_t *o, int amt) { reason = E_TOOBIG; return B_FALSE; } - if (lfhasflag(lf, F_NOPACK)) { - reason = E_NOPACK; - return B_FALSE; - } - if (lfhasflag(lf, F_GRAVBOOSTED)) { - reason = E_GRAVBOOSTED; - return B_FALSE; - } - if ((getlfmaterial(lf) == MT_GAS) || lfhasflag(lf, F_NONCORPOREAL)) { - reason = E_INSUBSTANTIAL; - return B_FALSE; - } + if (lf) { + if (lfhasflag(lf, F_NOPACK)) { + reason = E_NOPACK; + return B_FALSE; + } + if (lfhasflag(lf, F_GRAVBOOSTED)) { + reason = E_GRAVBOOSTED; + return B_FALSE; + } + if ((getlfmaterial(lf) == MT_GAS) || lfhasflag(lf, F_NONCORPOREAL)) { + reason = E_INSUBSTANTIAL; + return B_FALSE; + } - // too heavy to lift? - //max = getlfweight(lf, B_NOOBS) * 2; // twice your body weight - if (getobunitweight(o) + getobpileweight(lf->pack) > (getmaxcarryweight(lf)*2)) { - reason = E_TOOHEAVY; - return B_FALSE; - } - - // space in pack? - if (countobs(lf->pack, B_FALSE) >= MAXPILEOBS) { - reason = E_NOSPACE; - return B_FALSE; - } - if (getnextletter(lf->pack, NULL) == '\0') { - reason = E_NOSPACE; - return B_FALSE; + // too heavy to lift? + //max = getlfweight(lf, B_NOOBS) * 2; // twice your body weight + if (getobunitweight(o) + getobpileweight(lf->pack) > (getmaxcarryweight(lf)*2)) { + reason = E_TOOHEAVY; + return B_FALSE; + } + + // space in pack? + if (countobs(lf->pack, B_FALSE) >= MAXPILEOBS) { + reason = E_NOSPACE; + return B_FALSE; + } + if (getnextletter(lf->pack, NULL) == '\0') { + reason = E_NOSPACE; + return B_FALSE; + } } return B_TRUE; } @@ -1126,7 +1141,10 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar msg("%s %s.", lfname, f->text); } } else { - msg("%s casts a spell.", lfname); + if (hasflag(sp->flags, F_CASTINGTIME)) { + } else { + msg("%s starts casting a spell.", lfname); + } } } @@ -1145,12 +1163,55 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar limit(&power, NA, 10); } + // stop hiding + killflagsofid(lf->flags, F_HIDING); + // cast the spell - addflag(lf->flags, F_CASTINGSPELL, sid, NA, NA, NULL); - rv = dospelleffects(lf, sid, power, targlf, targob, targcell, B_UNCURSED, NULL, B_FALSE); - f = lfhasflag(lf, F_CASTINGSPELL); + f = hasflag(sp->flags, F_CASTINGTIME); if (f) { - killflag(f); + int castingtime; + char tempbuf[BUFLEN]; + char castingbuf[BUFLEN]; + flag_t *castingflag; + castingtime = f->val[0]; + + strcpy(castingbuf, ""); + + if (targlf) { + sprintf(tempbuf, "%d;",targlf->id); + } else { + strcpy(tempbuf, "-1;"); + } + strcat(castingbuf, tempbuf); + + if (targob) { + sprintf(tempbuf, "%ld;",targob->id); + } else { + strcpy(tempbuf, "-1;"); + } + strcat(castingbuf, tempbuf); + + if (targcell) { + sprintf(tempbuf, "%d;%d;%d;",targcell->map->id,targcell->x, targcell->y); + } else { + strcpy(tempbuf, "-1;-1;-1;"); + } + strcat(castingbuf, tempbuf); + + castingflag = addflag(lf->flags, F_CASTINGSPELL, sid, power, castingtime, castingbuf); + rv = B_FALSE; + + if (isplayer(lf)) { + // announce + msg("You start casting %s.", sp->name); + } + } else { // instant cast + addflag(lf->flags, F_CASTINGSPELL, sid, NA, NA, NULL); + rv = dospelleffects(lf, sid, power, targlf, targob, targcell, B_UNCURSED, NULL, B_FALSE); + f = lfhasflag(lf, F_CASTINGSPELL); + if (f) { + killflag(f); + } } // willing this spell? reset counter! @@ -1160,9 +1221,6 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar } } - // stop hiding - killflagsofid(lf->flags, F_HIDING); - // successful cast? if (!rv) { practice(lf, SK_SPELLCASTING, 1); @@ -1209,6 +1267,8 @@ int checkfordrowning(lifeform_t *lf, object_t *o) { // in around 3-4 turns. damamt = lf->maxhp / (getattr(lf, A_CON) / 3); + limit(&damamt, 1, NA); + if (damamt >= lf->hp) { if (isplayer(lf)) { msg("You drown."); @@ -1410,7 +1470,7 @@ int demandbribe(lifeform_t *lf) { amtwanted = rnd(hd*25, hd*100); getlfname(lf, lfname); - if (say(lf, "Hand over all your gold!", 2)) { + if (say(lf, "Hand over all your gold!", SV_TALK)) { heard = B_TRUE; } else { heard = B_FALSE; @@ -1451,15 +1511,15 @@ int demandbribe(lifeform_t *lf) { if ((amtgiven == totmoney) || (amtgiven >= amtwanted)) { // always succeed - say(lf, "Pleasure doing business with you!", 2); + say(lf, "Pleasure doing business with you!", SV_TALK); satisfied = B_TRUE; } else { - say(lf, "Then die!", 3); + say(lf, "Then die!", SV_SHOUT); satisfied = B_FALSE; } } else { // TODO: luck check to receive money ? - say(lf, "Then die!", 3); + say(lf, "Then die!", SV_SHOUT); satisfied = B_FALSE; } @@ -1832,7 +1892,7 @@ int digcell(lifeform_t *lf, cell_t *c, object_t *o) { if (c->type->solid) { if (isdiggable(c)) { // replace wall - setcelltype(c, c->map->habitat->id); + setcelltype(c, c->map->habitat->emptycelltype); if (isplayer(lf)) { msg("You dig through the wall."); needredraw = B_TRUE; @@ -2773,13 +2833,25 @@ race_t *findrace(enum RACE id) { } race_t *findracebyname(char *name) { race_t *r; + raceclass_t *rc; // first check for exact matches for (r = firstrace; r ; r = r->next) { if (!strcmp(r->name, name)) { return r; } } - // ...then partial matches + + // now check raceclasses + for (rc = firstraceclass; rc ; rc = rc->next) { + // using strstarts rather than streq in case there is a job suffix + if (strstarts(name, rc->name)) { + // return a random race from this class + return getreallyrandomrace(rc->id); + } + } + + + // ...then partial matches in names for (r = firstrace; r ; r = r->next) { if (strstr(r->name, name)) { return r; @@ -2800,6 +2872,15 @@ raceclass_t *findraceclass(enum RACECLASS id) { return NULL; } +lifeform_t *findshopkeeper(map_t *m, int roomid) { + lifeform_t *lf; + for (lf = m->lf ; lf ; lf = lf->next) { + if (lfhasflagval(lf, F_OWNSSHOP, roomid, NA, NA, NULL)) { + return lf; + } + } + return NULL; +} skill_t *findskill(enum SKILL id) { skill_t *r; @@ -4180,6 +4261,25 @@ object_t *getouterequippedob(lifeform_t *lf, enum BODYPART bp) { return getequippedob(lf->pack, bp); } +int getowing(lifeform_t *buyer, int shopid, int *retnitems) { + object_t *o; + flag_t *f; + int totcost = 0; + int nitems = 0; + + for (o = buyer->pack->first ; o ; o = o->next) { + f = hasflagval(o->flags, F_SHOPITEM, NA, shopid, NA, NULL); + if (f) { + totcost += f->val[0]; + nitems++; + } + } + if (retnitems) { + *retnitems = nitems; + } + return totcost; +} + // return the healthiest possible hurt condition that 'lf' will // recognise when looking at someone else. // @@ -4250,7 +4350,7 @@ enum MATERIAL getlfmaterial(lifeform_t *lf) { } } - return lf->race->material->id; + return lf->material->id; } enum SKILLLEVEL getlorelevel(lifeform_t *lf, enum RACECLASS rcid) { @@ -4957,7 +5057,6 @@ race_t *getrandomrace(cell_t *c, int forcedepth) { if (db) dblog("finding random lf with rarity val %d-%d and rr <= %d\n",raritymin,raritymax, wantrr); - // try to find a lf of this type which will // fit in the map's habitat nposs = 0; @@ -4970,7 +5069,7 @@ race_t *getrandomrace(cell_t *c, int forcedepth) { rarflag = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL); if (!rarflag) { if (c) { - rarflag = hasflagval(r->flags, F_RARITY, c->map->habitat->id, NA, NA, NULL); + rarflag = hasflagval(r->flags, F_RARITY, c->habitat->id, NA, NA, NULL); } else { rarflag = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL); } @@ -5032,8 +5131,7 @@ race_t *getrandomrace(cell_t *c, int forcedepth) { return r; } - -race_t *getreallyrandomrace(void) { +race_t *getreallyrandomrace(enum RACECLASS wantrc) { race_t **poss; race_t *r; int nposs = 0; @@ -5042,16 +5140,22 @@ race_t *getreallyrandomrace(void) { // count races for (r = firstrace ; r ; r = r->next) { - count++; + if ((wantrc == RC_ANY) || (r->raceclass->id == wantrc)) { + if (appearsrandomly(r->id)) { + count++; + } + } } poss = malloc(count * sizeof(race_t *)); for (r = firstrace ; r ; r = r->next) { - if (appearsrandomly(r->id)) { - poss[nposs] = r; - nposs++; + if ((wantrc == RC_ANY) || (r->raceclass->id == wantrc)) { + if (appearsrandomly(r->id)) { + poss[nposs] = r; + nposs++; + } } } sel = rnd(0,nposs-1); @@ -5566,9 +5670,52 @@ void givejob(lifeform_t *lf, enum JOB jobid) { if ((gamemode != GM_GAMESTARTED)) { autoweild(lf); } + + // special cases + if (j->id == J_PIRATE) { + flag_t *f; + f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL); + if (f) { + f->val[0] = OT_HOOKHAND; + sprintf(f->text, "1d4"); + } + } else if (j->id == J_SHOPKEEPER) { + // shopkeepers are not hostile. + killflagsofid(lf->flags, F_HOSTILE); + killflagsofid(lf->flags, F_HATESRACE); + // mark its home shop + assert(isroom(lf->cell)); + addflag(lf->flags, F_OWNSSHOP, lf->cell->roomid, NA, NA, NULL); + } } +int givemoney(lifeform_t *from, lifeform_t *to, int amt) { + object_t *gold; + gold = hasob(from->pack, OT_GOLD); + if (!gold) { + return B_TRUE; + } + if (gold->amt < amt) { + return B_TRUE; + } + // lose it + removeob(gold, amt); + + // give it to other person + if (to) { + object_t *togold; + togold = hasob(to->pack, OT_GOLD); + if (!togold) { + togold = addob(to->pack, "gold coin"); + amt--; + } + togold += amt; + + } + return B_FALSE; +} + void giveobflags(lifeform_t *lf, object_t *o, enum FLAG whattype) { int flagsknown = 0, flagsfound = 0; flag_t *f,*newflag; @@ -6058,6 +6205,13 @@ void givestartskills(lifeform_t *lf, flagpile_t *fp) { killflagsofid(fp, F_STARTSKILL); } +int hasfreeaction(lifeform_t *lf) { + if (isimmobile(lf)) return B_FALSE; + if (lfhasflag(lf, F_ASLEEP)) return B_FALSE; + if (lfhasflag(lf, F_CASTINGSPELL)) return B_FALSE; + if (lfhasflag(lf, F_EATING)) return B_FALSE; + return B_TRUE; +} job_t *hasjob(lifeform_t *lf, enum JOB job) { job_t *j = NULL; @@ -6673,6 +6827,7 @@ int haslos(lifeform_t *viewer, cell_t *dest) { void initjobs(void) { int i; // job definitions + // NOTE: try to always make the job's weapon be the first object defined. // this will make sure that they have the letter 'a'. addjob(J_GOD, "Diety"); @@ -6708,6 +6863,7 @@ void initjobs(void) { mayusespellschool(lastjob->flags, i, F_CANWILL); } else { mayusespellschool(lastjob->flags, i, F_CANCAST); + //mayusespellschool(lastjob->flags, i, F_CANWILL); } } @@ -7056,6 +7212,13 @@ void initjobs(void) { addflag(lastjob->flags, F_IFPCT, 20, NA, NA, NULL); addflag(lastjob->flags, F_CANCAST, OT_S_HEALING, NA, NA, NULL); addflag(lastjob->flags, F_ENDIFMONSTER, NA, NA, NA, NULL); + + // non-player jobs + addjob(J_SHOPKEEPER, "Shopkeeper"); + addflag(lastjob->flags, F_NOPLAYER, B_TRUE, IQ_AVERAGE, NA, NULL); + addflag(lastjob->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL); + addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "100-1000 gold coins"); + addflag(lastjob->flags, F_WANTS, OT_GOLD, NA, NA, NULL); } void initrace(void) { @@ -7152,6 +7315,30 @@ void initrace(void) { addflag(lastrace->flags, F_NOJOBTEXT, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTJOB, 15, J_WIZARD, NA, NULL); addflag(lastrace->flags, F_STARTJOB, 50, J_ROGUE, NA, NULL); + addrace(R_TOWNGUARD, "town guard", 100, '@', C_GREY, MT_FLESH, RC_HUMANOID); + addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, NA, NA, "12-18"); + addflag(lastrace->flags, F_DONTSTARTASLEEP, NA, NA, NA, NULL); + addflag(lastrace->flags, F_STAYINHABITAT, NA, NA, NA, NULL); + addflag(lastrace->flags, F_GUARD, NA, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_VILLAGE, 80, NA, NULL); + addflag(lastrace->flags, F_VARLEVEL, NA, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 4, 4, 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_ACTIONSPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_FISTS, NA, NA, "1d2"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "good weapon"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "scale armour"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "good armour"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "good armour"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "good armour"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "bow"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-10 arrows"); + addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_WANTSBETTERARM, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); // monsters addrace(R_BEHOLDER, "beholder", 5, 'e', C_MAGENTA, MT_FLESH, RC_MAGIC); @@ -9864,12 +10051,14 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { a->mp = 0; a->maxmp = a->mp; + a->material = findmaterial(MT_FLESH); // might be overridden in setrace + // 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 + // set race - this will inherit race flags and material a->race = NULL; setrace(a, rid, B_FALSE); @@ -10127,8 +10316,9 @@ void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype) { } - // adjust for lifeform material - //adjustdammaterial(amt, damtype, getlfmaterial(lf)); + // don't adjust for lifeform material - we inherit all the material's flags. + //adjustdammaterial((unsigned int *)amt, damtype, getlfmaterial(lf)); + adjustdamhardness((unsigned int *)amt, damtype, getlfmaterial(lf)); if (isdrunk(lf)) { *amt -= rnd(0,3); @@ -10331,6 +10521,29 @@ int areallies(lifeform_t *lf1, lifeform_t *lf2) { return B_FALSE; } +int askforpayment(lifeform_t *shk, lifeform_t *lf) { + char saybuf[BUFLEN]; + int totcost = 0; + int nitems,shopid; + flag_t *f; + f = lfhasflag(shk, F_OWNSSHOP); + if (f) { + shopid = f->val[0]; + } else { + return B_TRUE; + } + + totcost = getowing(lf, shopid, &nitems); + + if (nitems == 1) { + sprintf(saybuf, "That will cost you $%d.", totcost); + } else { + sprintf(saybuf, "That brings your bill to $%d.", totcost); + } + say(shk, saybuf, SV_TALK); + return B_FALSE; +} + // make sure player has at least novice skill in all their start weapons/armour void autoskill(lifeform_t *lf) { skill_t *sk; @@ -10569,6 +10782,9 @@ void loseconcentration(lifeform_t *lf) { // stop sprinting stopsprinting(lf); + // stop casting spells + killflagsofid(lf->flags, F_CASTINGSPELL); + // boost spells end stopallspells(lf); @@ -12008,7 +12224,7 @@ int say(lifeform_t *lf, char *text, int volume) { } else if (volume == 4) { strcpy(verb, "roars"); strcpy(noun, "roaring voices!"); - } else if (volume > 4) { + } else { // ie > 4 strcpy(verb, "bellows"); strcpy(noun, "bellowing voices!"); } @@ -12248,6 +12464,9 @@ void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { // set race lf->race = newrace; + // set material + lf->material = lf->race->material; + // inherit flags from race copyflags(lf->flags, lf->race->flags, FROMRACE); // don't want certain race only flags... @@ -12440,6 +12659,33 @@ void setlftarget(lifeform_t *lf, lifeform_t *victim) { } */ +int setlfmaterial(lifeform_t *lf, enum MATERIAL id) { + if (getlfmaterial(lf) == id) { + return B_TRUE; + } + if (hasactivespell(lf, OT_S_BARKSKIN) && (id != MT_WOOD)) { + stopspell(lf, OT_S_BARKSKIN); + } + + lf->material = findmaterial(id); + + // announce + if (gamemode == GM_GAMESTARTED) { + if (isplayer(lf)) { + msg("Your body %s to %s%c", (id == lf->race->material->id) ? "reverts" : "turns", lf->material->name, + (id == lf->race->material->id) ? '.' : '!' ); + } else if (cansee(player, lf)) { + char lfname[BUFLEN]; + getlfname(lf, lfname); + msg("%s%s body %s to %s%c", lfname, getpossessive(lfname), + (id == lf->race->material->id) ? "reverts" : "turns", + lf->material->name, + (id == lf->race->material->id) ? '.' : '!' ); + } + } + return B_FALSE; +} + int shoot(lifeform_t *lf) { object_t *gun,*ammo; lifeform_t *targ; @@ -12556,6 +12802,9 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r case SC_SEARCH: attrib = (getskill(lf, SK_SPOTHIDDEN)*4); break; + case SC_STEAL: + attrib = (getskill(lf, SK_THIEVERY)); + break; case SC_STEALTH: attrib = (getskill(lf, SK_STEALTH)*4); break; @@ -12617,6 +12866,11 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r int bonus = 0; sumflags(lf->flags, F_ENHANCESEARCH, &bonus, NULL, NULL); othermod += bonus; + } else if (ct == SC_STEAL) { + if (attrib > 0) { + // ie. -3 to 3 + othermod += (getstatmod(lf, A_DEX) / 15); + } } else if (ct == SC_STEALTH) { if (attrib > 0) { if (lfhasflag(lf, F_SNEAK)) { @@ -12828,6 +13082,102 @@ void sortlf(map_t *map, lifeform_t *lf) { } } +// returns TRUE on failure (ie. nothing to steal) +int steal(lifeform_t *lf, obpile_t *op, enum FLAG wantflag) { + enum SKILLLEVEL slev; + object_t *o; + int i,nsteals; + int numgot = 0; + int fromground; + char letter = 'a'; + slev = getskill(lf, SK_THIEVERY); + + if (op->owner) { + fromground = B_FALSE; + } else { + fromground = B_TRUE; + } + // + if (slev >= PR_EXPERT) { + nsteals = 2; + } else { + nsteals = 1; + } + + // what do we steal? + for (i = 0; i < nsteals; i++) { + char buf[BUFLEN]; + sprintf(buf, "Steal what (%d of %d)?", i+1, nsteals); + initprompt(&prompt, buf); + addchoice(&prompt, '-', "Nothing", NULL, NULL); + for (o = op->first ; o ; o = o->next) { + int ok = B_TRUE; + if ((slev < PR_SKILLED) && (getobunitweight(o) >= 3)) { + // too heavy to steal + ok = B_FALSE; + } else if ((slev < PR_MASTER) && isequipped(o)) { + // equipped + ok = B_FALSE; + } else if (!canpickup(lf, o, 1)) { + // can't pick it up + ok = B_FALSE; + } else if ((wantflag != F_NONE) && !hasflag(o->flags, wantflag)) { + // don't have the right flag + ok = B_FALSE; + } + if (ok) { + getobname(o, buf, 1); + addchoice(&prompt, fromground ? letter++ : o->letter, buf, NULL, o); + } + } + if (prompt.nchoices > 1) { + if (slev >= PR_ADEPT) { + // pick what you want + getchoice(&prompt); + o = (object_t *)prompt.result; + } else { + // random + o = (object_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; + } + if (o) { + killflagsofid(o->flags, F_SHOPITEM); + o = moveob(o, lf->pack, 1); + if (o) { + char obname[BUFLEN]; + char lfname[BUFLEN]; + char targname[BUFLEN]; + getlfname(lf, lfname); + getobname(o, obname, 1); + if (op->owner) { + getlfname(op->owner, targname); + if (isplayer(lf)) { + msg("You steal %s from %s!", obname, targname); + } else if (cansee(player, lf)) { + msg("%s steals %s from %s!", lfname, obname, targname); + } + } else { + if (isplayer(lf)) { + msg("You steal %s!", obname); + } else if (cansee(player, lf)) { + msg("%s steals %s!", lfname, obname); + } + } + numgot++; + } + } + } else { + // nothing left to steal + + if (numgot == 0) { + return B_TRUE; + } + break; + } + } // end foreach steal + return B_FALSE; +} + + int stone(lifeform_t *lf) { char lfname[BUFLEN]; char statname[BUFLEN]; @@ -14199,7 +14549,7 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { if (newregion->id != curmap->region->id) { newdepth = 1; } - createmap(newmap, newdepth, newregion, curmap, dir); + createmap(newmap, newdepth, newregion, curmap, dir, o); // if we stayed within the same region, our stairs should // now have a destination, since createmap() will automatically check // previous/next levels in the same region. diff --git a/lf.h b/lf.h index 3918119..db80de9 100644 --- a/lf.h +++ b/lf.h @@ -12,6 +12,7 @@ void adjustspeedforwater(lifeform_t *lf, int *speed); void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o); int areallies(lifeform_t *lf1, lifeform_t *lf2); int areenemies(lifeform_t *lf1, lifeform_t *lf2); +int askforpayment(lifeform_t *shk, lifeform_t *lf); void autoskill(lifeform_t *lf); void autotarget(lifeform_t *lf); void autoweild(lifeform_t *lf); @@ -22,6 +23,7 @@ void breakallgrabs(lifeform_t *lf); long calcscore(lifeform_t *lf); int calcxp(lifeform_t *lf); int calcxprace(enum RACE rid); +void callguards(lifeform_t *caller, lifeform_t *victim); int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost); int candrink(lifeform_t *lf, object_t *o); int caneat(lifeform_t *lf, object_t *o); @@ -67,6 +69,7 @@ lifeform_t *findlf(map_t *m, int lfid); race_t *findrace(enum RACE id); race_t *findracebyname(char *name); raceclass_t *findraceclass(enum RACECLASS id); +lifeform_t *findshopkeeper(map_t *m, int roomid); skill_t *findskill(enum SKILL id); skill_t *findskillbyname(char *name); enum SKILLLEVEL findskilllevbyname(char *name); @@ -119,6 +122,7 @@ int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions); int getnightvisrange(lifeform_t *lf); char *getlfconditionname(enum LFCONDITION cond); object_t *getouterequippedob(lifeform_t *lf, enum BODYPART bp); +int getowing(lifeform_t *buyer, int shopid, int *retnitems); enum LFCONDITION getseenlfconditioncutoff(lifeform_t *lf); char *getseenlfconditionname(lifeform_t *lf, lifeform_t *viewer); glyph_t *getlfglyph(lifeform_t *lf); @@ -156,7 +160,7 @@ int getracerarity(map_t *map, enum RACE rid); object_t *getrandomarmour(lifeform_t *lf); int getrandommonlevel(race_t *r, map_t *m); race_t *getrandomrace(cell_t *c, int forcedepth); -race_t *getreallyrandomrace(void); +race_t *getreallyrandomrace(enum RACECLASS wantrc); enum SKILL getrandomskill(void); object_t *getrestob(lifeform_t *lf); enum SKILLLEVEL getskill(lifeform_t *lf, enum SKILL id); @@ -175,12 +179,14 @@ void getwantdistance(lifeform_t *lf, int *min, int *max, int attacking); object_t *getweapon(lifeform_t *lf); long getxpforlev(int level); void givejob(lifeform_t *lf, enum JOB jobid); +int givemoney(lifeform_t *from, lifeform_t *to, int amt); void giveobflags(lifeform_t *lf, object_t *o, enum FLAG whattype); int giveskill(lifeform_t *lf, enum SKILL id); int giveskilllev(lifeform_t *lf, enum SKILL id, enum SKILLLEVEL slev); void givestartobs(lifeform_t *lf, flagpile_t *fp); void givestartskills(lifeform_t *lf, flagpile_t *fp); map_t *gotolev(lifeform_t *lf, int depth, object_t *fromstairs); +int hasfreeaction(lifeform_t *lf); job_t *hasjob(lifeform_t *lf, enum JOB job); int lfcanbestoned(lifeform_t *lf); flag_t *lfhasflag(lifeform_t *lf, enum FLAG fid); @@ -278,12 +284,14 @@ void setguntarget(lifeform_t *lf, lifeform_t *targ); void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph); void setlastdam(lifeform_t *lf, char *buf); //void setlftarget(lifeform_t *lf, lifeform_t *victim); +int setlfmaterial(lifeform_t *lf, enum MATERIAL id); int shoot(lifeform_t *lf); int skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod); int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *result); int skillcheckvs(lifeform_t *lf1, enum CHECKTYPE ct1, int mod1, lifeform_t *lf2, enum CHECKTYPE ct2, int mod2); int slipon(lifeform_t *lf, object_t *o); void sortlf(map_t *map, lifeform_t *lf); +int steal(lifeform_t *lf, obpile_t *op, enum FLAG wantflag); int stone(lifeform_t *lf); void stopeating(lifeform_t *lf); void stopresting(lifeform_t *lf); diff --git a/map.c b/map.c index d934a11..56fdcff 100644 --- a/map.c +++ b/map.c @@ -41,11 +41,13 @@ cell_t *addcell(map_t *m, int x, int y) { cell->map = m; cell->x = x; cell->y = y; - setcelltype(cell, m->habitat->solidcelltype); + cell->habitat = m->habitat; + setcelltype(cell, cell->habitat->solidcelltype); cell->visited = B_FALSE; cell->obpile = addobpile(NOOWNER, cell); cell->lf = NULL; cell->roomid = -1; + cell->vault = NULL; cell->lit = L_NOTLIT; cell->origlit = L_NOTLIT; cell->littimer = 0; @@ -57,7 +59,7 @@ cell_t *addcell(map_t *m, int x, int y) { return cell; } -habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell) { +habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell, int thingchance, int obchance, int vaultchance) { habitat_t *a; // add to the end of the list if (firsthabitat == NULL) { @@ -79,6 +81,9 @@ habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum a->name = strdup(name); a->emptycelltype = emptycell; a->solidcelltype = solidcell; + a->randthingpct = thingchance; + a->randobpct = obchance; + a->randvaultpct = vaultchance; return a; } @@ -161,7 +166,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto r = findrace(raceid); } if (!r) { - r = getreallyrandomrace(); + r = getreallyrandomrace(RC_ANY); } assert(r); @@ -209,7 +214,8 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto addflag(lf->flags, F_HIDING, 0, NA, NA, NULL); } } - if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_DEAF) && cansleep(lf)) { + if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_DEAF) && cansleep(lf) && + !lfhasflag(lf, F_DONTSTARTASLEEP)) { // 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); @@ -361,7 +367,7 @@ object_t *addrandomob(cell_t *c) { return NULL; } - if (getrandomob(c->map, buf)) { + if (real_getrandomob(c->map, buf, RO_NONE, NA, NA, c->habitat->id)) { if (db) dblog("adding rand obj %s to cell %d,%d",buf,c->x,c->y); o = addob(c->obpile, buf); } @@ -370,6 +376,7 @@ object_t *addrandomob(cell_t *c) { int addrandomthing(cell_t *c, int obchance, int *nadded) { int rv = TT_NONE; + // if there's already someone there, // then add an object. if (!cellwalkable(NULL, c, NULL) || (rnd(1,100) <= obchance)) { @@ -565,7 +572,7 @@ regionthing_t *addregionthing(regionoutline_t *ro, int depth, int x, int y, enum return rt; } -regiontype_t *addregiontype(enum REGIONTYPE id, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir) { +regiontype_t *addregiontype(enum REGIONTYPE id, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major) { regiontype_t *a; // add to the end of the list @@ -589,6 +596,7 @@ regiontype_t *addregiontype(enum REGIONTYPE id, enum HABITAT defaulthabitat, int a->maxdepth = maxdepth; a->stairsperlev = stairsperlev; a->deeperdir = deeperdir; + a->majorbranch = major; return a; } @@ -623,7 +631,7 @@ int autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, in if (rnd(1,100) <= doorpct) { makedoor(cell[i], dooropenchance); } else { - setcelltype(cell[i], map->habitat->emptycelltype); + setcelltype(cell[i], cell[i]->habitat->emptycelltype); } } else { // otherwise mark this as a _potential_ door location. @@ -642,7 +650,7 @@ int autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, in if (rnd(1,100) <= doorpct) { makedoor(poss[sel], dooropenchance); } else { - setcelltype(poss[sel], map->habitat->emptycelltype); + setcelltype(poss[sel], poss[sel]->habitat->emptycelltype); } doorsadded++; @@ -689,7 +697,7 @@ int autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, in makedoor(cell[sel], dooropenchance); doorsadded++; } else { - setcelltype(cell[sel], map->habitat->emptycelltype); + setcelltype(cell[sel], cell[sel]->habitat->emptycelltype); doorsadded++; } } @@ -829,7 +837,7 @@ int cellhaslos(cell_t *c1, cell_t *dest) { void clearcell(cell_t *c) { // kill everything there - if (c->lf) { + if (c->lf && !isplayer(c->lf)) { killlf(c->lf); } while (c->obpile->first) { @@ -842,10 +850,11 @@ void clearcell(cell_t *c) { // returns true if something happened int dowaterspread(cell_t *c) { - float thisdepth,surrounddepth = DP_NONE; + float thisdepth; int i; - int nsurroundwithwater = 0,nsurround = 0; + int nsurround = 0; int db = B_FALSE; + cell_t *surroundcell[8]; if (!c || c->type->solid || hascloseddoor(c)) { return B_FALSE; @@ -854,7 +863,9 @@ int dowaterspread(cell_t *c) { // calculate depth of this cell thisdepth = getcellwaterdepth(c, NULL); - // calculate average depth of surrounding cells + if (!thisdepth) return B_FALSE; + + // count surrounding cells of lower depth for (i = DC_N; i <= DC_NW; i++) { cell_t *newc; @@ -863,8 +874,12 @@ int dowaterspread(cell_t *c) { float newcdepth; int ok = B_FALSE; + /* + FIX THIS CODE LATER if (newc->type->floorheight == c->type->floorheight) { - ok = B_TRUE; + // same height - don't include these??? + ok = B_FALSE; + //ok = B_TRUE; } else if (newc->type->floorheight < c->type->floorheight) { // ie. downhill. don't include these ok = B_FALSE; @@ -872,53 +887,56 @@ int dowaterspread(cell_t *c) { // ie. uphill. ok = B_TRUE; } + */ + if (newc->type->floorheight == c->type->floorheight) { + ok = B_TRUE; + } else { + ok = B_FALSE; + } if (ok) { newcdepth = getcellwaterdepth(newc, NULL); - nsurround++; // ie. a non-solid cell - surrounddepth += newcdepth; - - if (newcdepth > DP_NONE) { - nsurroundwithwater++; // ie. a non-solid cell _with water_ + if (newcdepth < thisdepth) { + surroundcell[nsurround] = newc; + nsurround++; } } } } - // if there was water here or around us... - if (nsurroundwithwater || thisdepth) { + // if there were any posisble cells around us + if (nsurround) { float newdepth; - //surrounddepth /= ((float)nsurround); - surrounddepth /= ((float)nsurroundwithwater); - limitf(&surrounddepth,(float)DP_NONE, (float)DP_MAX); + //newdepth = thisdepth / (pctof(nsurround,50)+1); + newdepth = thisdepth / (pctof(nsurround,55)+1); + //newdepth = thisdepth / (nsurround+1); + limitf(&newdepth,(float)DP_NONE, (float)DP_MAX); - if (thisdepth == surrounddepth) { - // if there are any nonsolid surrounding cells _without_ water, and - // there is water here, our depth drops. - if ((thisdepth > 0) && (nsurround - nsurroundwithwater >= 2)) { - newdepth = thisdepth - 1; + if (db) dblog("waterspread: cell %d,%d (%d/%s) spreads to %d cells-->%d/%s", + c->x, c->y, (int)thisdepth, getwaterdepthname((int)thisdepth), + nsurround, (int)newdepth, getwaterdepthname((int)newdepth)); + + // SET our cell to new depth + setwaterdepth(c, (int)newdepth); + + // ADD water to surrounding cells + for (i = 0; i < nsurround; i++) { + cell_t *sc; + flag_t *f; + + sc = surroundcell[i]; + + f = hasflagval(sc->map->flags, F_NEWWATERDEPTH, sc->x, sc->y, NA, NULL); + if (f) { + f->val[2] += newdepth; } else { - newdepth = thisdepth; + addflag(sc->map->flags, F_NEWWATERDEPTH, sc->x, sc->y, (int)newdepth, NULL); } - } else if (surrounddepth > thisdepth) { - // surrounding cells are deeper - they flow into here - newdepth = (thisdepth + surrounddepth) * 0.8; - } else if (thisdepth > surrounddepth) { - // surrounding cells are shallower - this cell will go down. - newdepth = (thisdepth + surrounddepth) * 0.2; } - addflag(c->map->flags, F_NEWWATERDEPTH, c->x, c->y, (int)newdepth, NULL); - if (db) { - - dblog("%s %d,%d: goes from %d to %d (surrounddepth is %d)\n", - thisdepth ? "water depth at" : "NEW WATER AT ", - c->x, c->y, - (int)thisdepth, (int)newdepth, (int)surrounddepth); - } return B_TRUE; } return B_FALSE; @@ -971,6 +989,7 @@ void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { //drawglyph(&glyph, x, y); return; } + // otherwise fall through } // objects here? @@ -1192,73 +1211,112 @@ void calclight(map_t *map) { } } -int calcroompos(map_t *map, int w, int h, int *bx, int *by, int force) { - int x,y; - int bestx = -1, besty = -1; - int valid = B_FALSE; - int bestscore = 9999; +int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int *by, int force) { + int x,y,i; + int bestscore = 9888; + coord_t coord[MAX_MAPW*MAX_MAPH]; + int coordscore[MAX_MAPW*MAX_MAPH]; + int ncoords = 0; + coord_t poss[MAX_MAPW*MAX_MAPH]; + int nposs = 0; cell_t *cell; + int sel; + int db = B_FALSE; + + // init coords list + for (y = 1 + ymargin; y < (map->h - ymargin); y++) { + for (x = 1 + xmargin; x < (map->w - xmargin); x++) { + coord[ncoords].x = x; + coord[ncoords].y = y; + ncoords++; + } + } // try placing room at all positions - for (y = 1; y < map->h; y++) { - for (x = 1; x < map->w; x++) { - // would the room fit here? - if ( ((x + (w-1)) <= (map->w-1)) && - ((y + (h-1)) <= (map->h-1))) { - int score = 0; - int rx,ry; - int notpossible = B_FALSE; - valid = B_FALSE; - // calculate score based on cells in room - for (ry = y; (ry < y+h) && (!notpossible); ry++) { - for (rx = x; (rx < x+w) && (!notpossible); rx++) { - cell = getcellat(map, rx,ry); - // is this cell adjacent to an empty cell and not a - // corner (ie. a valid door location) - if (countcellexits(cell)) { - score++; - if ( ((ry == y) && (rx == x)) || - ((ry == y) && (rx == (x+w-1))) || - ((ry == y+h-1) && (rx == x)) || - ((ry == y+h-1) && (rx == (x+w-1))) ) { - // corner. - } else { - // not corner - valid = B_TRUE; - } + for (i = 0; i < ncoords; i++) { + int score = 9999; + x = coord[i].x; + y = coord[i].y; + // would the room fit here? + if ( ((x + w) <= (map->w - xmargin)) && + ((y + h) <= (map->h - ymargin))) { + int valid = B_TRUE; + int rx,ry; + score = 0; + // calculate score if we placed the room with its top left corner here. + for (ry = y; (ry < y+h) && valid; ry++) { + for (rx = x; (rx < x+w) && valid; rx++) { + int includethiscell = B_FALSE; + cell = getcellat(map, rx,ry); + // is this cell adjacent to an empty cell and not a + // corner (ie. a valid door location) + if (countcellexits(cell)) { + score++; + if ( ((ry == y) && (rx == x)) || + ((ry == y) && (rx == (x+w-1))) || + ((ry == y+h-1) && (rx == x)) || + ((ry == y+h-1) && (rx == (x+w-1))) ) { + // corner. don't check this cell for scores. + } else { + includethiscell = B_TRUE; } + } + if (includethiscell) { // is this cell empty itself? if (!cell->type->solid) score += 3; // avoid being adjacent to other room walls if (countcellexits(cell)) score++; - score += (countadjcellsoftype(cell, CT_ROOMWALL)*3); - if (!force) { - // overlapping another room? - if (cell->type->id == CT_ROOM) { + + score += (countadjrooms(cell)*3); + + // overlapping another room? + if (isroom(cell)) { + if (force) { + score += 10; + } else { valid = B_FALSE; - notpossible = B_TRUE; - } - if (cell->type->id == CT_ROOMWALL) { - valid = B_FALSE; - notpossible = B_TRUE; } } + // NEVER create it on top of the player! + // (normally this can't happen, but debugging via 'create + // vault' could do it) + if (cell->lf && isplayer(cell->lf)) { + valid = B_FALSE; + } } } - if (valid && (score != 0) && (score < bestscore)) { - bestscore = score; - bestx = x; - besty = y; - } } + if (valid) { + if (score < bestscore) { + bestscore = score; + } + } else { + score = 9999; + } + } + coordscore[i] = score; + if (db) dblog("cell %d,%d - score %d",x,y,score); + } + + // now go through and make a list of all BEST positions + nposs = 0; + for (i = 0; i < ncoords; i++) { + if (coordscore[i] == bestscore) { + poss[nposs++] = coord[i]; } } - *bx = bestx; - *by = besty; - - if ((bestx == -1) || (besty == -1)) { + if (nposs == 0) { + *bx = -1; + *by = -1; return B_TRUE; + } else { + // pick a random one + sel = rnd(0,nposs-1); + if (db) dblog("calcroompos - %d possibilities (bestscore=%d), selected #%d.", nposs, bestscore, sel); + + *bx = poss[sel].x; + *by = poss[sel].y; } return B_FALSE; @@ -1290,6 +1348,19 @@ int countadjcellsoftype(cell_t *cell, int id) { return count; } +int countadjrooms(cell_t *cell) { + int d; + int count = 0; + cell_t *newcell; + for (d = D_N; d < MAXDIR_ORTH; d++) { + newcell = getcellindir(cell, d); + if (newcell && isroom(newcell)) { + count++; + } + } + return count; +} + int countadjwalls(cell_t *cell) { int d; int walls = 0; @@ -1318,7 +1389,8 @@ int countcellexits(cell_t *cell) { return exits; } -void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { +// +void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int wantrooms = B_TRUE; int d; int x,y; @@ -1532,10 +1604,10 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { for (i = 0; i < numrooms; i++) { // maybe make it a special room roomvault[i] = NULL; - if (rnd(1,100) <= getvaultchance(map)) { + if (rnd(1,100) <= map->habitat->randvaultpct) { vault_t *v; v = getvaulttype(map); - if (createvault(map, i, v, &roomw[i],&roomh[i])) { + if (createvault(map, i, v, &roomw[i],&roomh[i], NULL, NULL)) { // failed } else { // success @@ -1544,7 +1616,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { } if (!roomvault[i]) { // just do a normal room - createroom(map, i, NA, NA, NULL, NULL, &roomw[i],&roomh[i], 50, B_FALSE); + createroom(map, i, NA, NA, 0, 0, NULL, NULL, &roomw[i],&roomh[i], 50, B_FALSE); roomvault[i] = B_FALSE; } } @@ -1566,7 +1638,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { // have to force these stairs to go back to a different region. f = hasflag(o->flags, F_CLIMBABLE); f->val[1] = map->region->parentregion->id; - linkstairs(o); + linkstairs(o, NULL); // special case: first dungeon level has barriers over the exit stairs if (map->region->rtype->id == RG_FIRSTDUNGEON) { @@ -1580,7 +1652,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { c = getrandomroomcell(map, ANYROOM); } o = addob(c->obpile, "staircase going up"); - linkstairs(o); + linkstairs(o, NULL); } } @@ -1592,7 +1664,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { c = getrandomroomcell(map, ANYROOM); } o = addob(c->obpile, "staircase going down"); - linkstairs(o); + linkstairs(o, NULL); } } @@ -1652,7 +1724,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { obchance = 100; } else { // slightly more chance of objects in rooms - obchance = getobchance(map->habitat->id) + 10; + obchance = c->habitat->randobpct + 10; } if (addrandomthing(c,obchance, &nadded) == TT_MONSTER) { @@ -1676,7 +1748,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { if (db) dblog("Finished adding objects."); } -void createforest(map_t *map, int depth, map_t *parentmap, int exitdir) { +void createforest(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob, int nclearings) { int x,y; enum CELLTYPE emptycell; int i; @@ -1684,7 +1756,6 @@ void createforest(map_t *map, int depth, map_t *parentmap, int exitdir) { int density; cell_t *c; char buf[BUFLEN]; - int nclearings; //object_t *o; // what kind of cells will 'empty' ones be? @@ -1714,7 +1785,6 @@ void createforest(map_t *map, int depth, map_t *parentmap, int exitdir) { } // clearings - nclearings = rnd(0,5); for (i = 0; i < nclearings; i++) { int w; c = getrandomcell(map); @@ -1750,16 +1820,20 @@ void createforest(map_t *map, int depth, map_t *parentmap, int exitdir) { } -void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir) { +void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { switch (map->habitat->id) { case H_DUNGEON: - createdungeon(map, depth, parentmap, exitdir); + createdungeon(map, depth, parentmap, exitdir, entryob); break; case H_FOREST: - createforest(map, depth, parentmap, exitdir); + createforest(map, depth, parentmap, exitdir, entryob, rnd(0,5)); + break; + case H_VILLAGE: + createforest(map, depth, parentmap, exitdir, entryob, 0); break; case H_PIT: - createpit(map, depth, parentmap, exitdir); + createpit(map, depth, parentmap, exitdir, entryob); + break; break; default: dblog("ERROR - createhabitat with invalid habitat!"); @@ -1767,6 +1841,7 @@ void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir) { } } + /* seed = random number seed turnpct = percentage change of a corridor turning @@ -1774,7 +1849,7 @@ void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir) { looppct = percentage change of turning dead-end into loop maxrooms = max # of rooms */ -void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int exitdir) { +void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int exitdir, object_t *entryob) { lifeform_t *lf; map_t *m; char buf[BUFLEN]; @@ -1827,10 +1902,13 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex } - // did we come from a previous map? + // did we come from a previous map in the same region? if (parentmap) { - parentmap->nextmap[exitdir] = map->id; - map->nextmap[diropposite(exitdir)] = parentmap->id; + if ((parentmap->region->id == map->region->id) || + (map->region->rtype->majorbranch)) { + parentmap->nextmap[exitdir] = map->id; + map->nextmap[diropposite(exitdir)] = parentmap->id; + } } /* @@ -1946,7 +2024,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex // build it... - createhabitat(map, depth, parentmap, exitdir); + createhabitat(map, depth, parentmap, exitdir, entryob); // add home objects for (lf = map->lf ; lf ; lf = lf->next) { @@ -1969,11 +2047,130 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex case RT_VAULT: v = findvault(thing[i]->what); assert(v); - createvault(map, map->nrooms, v, NULL, NULL); + if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { + dblog("ERROR - couldn't create vault %s on map %s", v->id, map->name); + } break; } } + // special cases + // village - add town walls and clear it out + if (map->habitat->id == H_VILLAGE) { + int x1 = 999,y1 = 999,x2 = -1,y2 = -1,x,y; + int gx,gy; + int guardx[2], guardy[2]; + cell_t *c; + // find outermost dimensions of shops + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + c = getcellat(map, x, y); + if (c && isroom(c)) { + if (c->x < x1) x1 = c->x; + if (c->y < y1) y1 = c->y; + if (c->x > x2) x2 = c->x; + if (c->y > y2) y2 = c->y; + } + } + } + + // extend walls a bit more + x1 -= rnd(2,4); + y1 -= rnd(2,4); + x2 += rnd(2,4); + y2 += rnd(2,4); + limit(&x1, 0, map->w-1); + limit(&x2, 0, map->w-1); + limit(&y1, 0, map->h-1); + limit(&y2, 0, map->h-1); + + // decide where the gate will be (not the corner) + switch (rnd(D_N, D_W)) { + case D_N: + gx = rnd(x1+2,x2-2); + gy = y1; + guardx[0] = gx-1; + guardy[0] = gy+1; + guardx[1] = gx+1; + guardy[1] = gy+1; + break; + case D_E: + gx = x2; + gy = rnd(y1+2,y2-2); + guardx[0] = gx-1; + guardy[0] = gy-1; + guardx[1] = gx-1; + guardy[1] = gy+1; + break; + case D_S: + gx = rnd(x1+2,x2-2); + gy = y2; + guardx[0] = gx-1; + guardy[0] = gy-1; + guardx[1] = gx+1; + guardy[1] = gy-1; + break; + case D_W: + gx = x1; + gy = rnd(y1+2,y2-2); + guardx[0] = gx+1; + guardy[0] = gy-1; + guardx[1] = gx+1; + guardy[1] = gy+1; + break; + } + + // fill in town walls and clearing + for (y = 0; y <= map->h; y++) { + for (x = 0; x <= map->w; x++) { + c = getcellat(map, x, y); + if (c) { + if ((c->x >= x1) && (c->y >= y1) && + (c->x <= x2) && (c->y <= y2)) { + // ie. within the village + if (!c->type->solid) { + int i; + + // mark as different habitat to outside + c->habitat = findhabitat(H_VILLAGE); + + if ((c->x == gx) && (c->y == gy)) { + // town gate location + clearcell(c); + setcelltype(c, CT_DIRT); + addob(c->obpile, "wooden gate"); + } else if ((c->x == x1) || (c->y == y1) || + (c->x == x2) || (c->y == y2)) { + // town perimeter (walls) + if (!isroom(c)) { + clearcell(c); + setcelltype(c, CT_DIRT); + addob(c->obpile, "wooden fence"); + } + } else { // elsewhere within the town grounds + if (!isroom(c)) { + // get rid of objects (ie. trees) here + killallobs(c->obpile); + // make the ground dirt + setcelltype(c, CT_DIRT); + } + } + // village guards + for (i = 0; i < 1; i++) { + if ((c->x == guardx[i]) && (c->y == guardy[i])) { + addmonster(c, R_TOWNGUARD, B_FALSE, 1, B_TRUE, NULL); + } + } + } + } else { + // not within the village + c->habitat = findhabitat(H_FOREST); + } + } // end if c + } + } + } + // try to join up any unlinked staircases in this map. for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { @@ -1984,11 +2181,12 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex if (o && !getstairdestination(o)) { // this will join these stairs to existing ones on // existing adjacent maps - linkstairs(o); + linkstairs(o, NULL); } } } + // link up holes - this will create NEW holes in THIS map connecting to // EXISTING unlinked holes in adjacent maps linkholes(map); @@ -1998,9 +2196,10 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex for (x = 0; x < map->w; x++) { cell_t *c; c = getcellat(map, x, y); - if (c && isempty(c)) { - if (rnd(1,100) <= getthingchance(map->habitat->id)) { - addrandomthing(c, getobchance(map->habitat->id), NULL); + // no random obs in vaults + if (c && isempty(c) && !c->vault) { + if (rnd(1,100) <= c->habitat->randthingpct) { + addrandomthing(c, c->habitat->randobpct, NULL); } } } @@ -2032,9 +2231,12 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex map->beingcreated = B_FALSE; } -int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth) { +int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *retx, int *rety) { int w,h,x,y; + int xmargin = 0,ymargin = 0; int minx,miny,maxx,maxy; + int db = B_TRUE; + int rotation = 0; flag_t *f; // default @@ -2045,22 +2247,36 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth) { return B_TRUE; } + f = hasflag(v->flags, F_KEEPMARGIN); + if (f) { + xmargin = f->val[0]; + ymargin = f->val[1]; + } + + // handle rotation. + rotation = rnd(0,v->nmaps-1); + f = hasflag(v->flags, F_VAULTRANDOMMAP); if (f) { + int minw,minh; // just make a normal room to start with. but make sure : // - it is surrounded with walls! // - we don't do autodoors (will handle this further down) ------------- - if (createroom(map, roomid, f->val[0], f->val[1], &minx, &miny, &w, &h, B_NODOORS, B_TRUE)) { + minw = f->val[0]; + minh = f->val[1]; + if (createroom(map, roomid, minw, minh, xmargin, ymargin, &minx, &miny, &w, &h, B_NODOORS, B_TRUE)) { return B_TRUE; } + if (db) dblog("made random vault %s at pos %d,%d on map %s", v->id, minx, miny, map->name); maxx = minx + w - 1; maxy = miny + h - 1; } else { // get width/height from vault - w = v->w; - h = v->h; + //w = v->w; + //h = v->h; + getvaultwh(v, &w, &h, rotation); // find vault position - if (calcroompos(map, w, h, &minx, &miny, B_TRUE)) { + if (calcroompos(map, w, h, xmargin, ymargin, &minx, &miny, B_TRUE)) { // forced calcroompos should never fail since it's // allowed to overlap other rooms. dblog("** couldn't make vault room!\n"); @@ -2071,6 +2287,7 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth) { maxy = miny + (h-1); // now make it + if (db) dblog("making vault %s at pos %d,%d on map %s", v->id, minx, miny, map->name); for (y = miny; y <= maxy; y++) { for (x = minx; x <= maxx; x++) { cell_t *cell; @@ -2080,21 +2297,24 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth) { clearcell(cell); // set cell type - ct = getvaultcelltype(v, x-minx,y-miny); - setcelltype(cell, ct ? ct->id : cell->map->habitat->emptycelltype); + ct = getvaultcelltype(v, x-minx,y-miny, rotation); + setcelltype(cell, ct ? ct->id : cell->habitat->emptycelltype); // set roomid cell->roomid = roomid; // add objects - addvaultcellcontents(cell, v, x-minx,y-miny); + addvaultcellcontents(cell, v, x-minx,y-miny, rotation); } } } if (retw) *retw = w; if (reth) *reth = h; + if (retx) *retx = minx; + if (rety) *rety = miny; - // add other stuff to the vault - addvaultcontents(map, v, minx, miny, maxx, maxy); + // add other stuff to the vault based on flags + // this will also set cell->vault for all cells. + addvaultcontents(map, v, minx, miny, maxx, maxy, rotation); // auto add doors if required f = hasflag(v->flags, F_AUTODOORS); @@ -2194,7 +2414,7 @@ int linkexits(map_t *m, int roomid, int minx, int miny, int maxx, int maxy) { nadded++; c = getcellindir(poss[i], whichway); while (c && !cellwalkable(NULL, c, NULL)) { - setcelltype(c, c->map->habitat->emptycelltype); + setcelltype(c, c->habitat->emptycelltype); c = getcellindir(c, whichway); } } @@ -2204,7 +2424,7 @@ int linkexits(map_t *m, int roomid, int minx, int miny, int maxx, int maxy) { return nadded; } -void createpit(map_t *map, int depth, map_t *parentmap, int exitdir) { +void createpit(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { object_t *o; cell_t *c; int x,y; @@ -2218,9 +2438,11 @@ void createpit(map_t *map, int depth, map_t *parentmap, int exitdir) { c = getrandomcell(map); // clear it setcelltype(c, CT_CORRIDOR); - // put an exit here - don't link it yet - this will happen back in createmap(). + // put an exit here o = addobject(c->obpile, "hole in the roof", B_FALSE, B_FALSE); assert(o); + // link it + linkstairs(o, entryob); } // only need to provide either obname _OR_ o @@ -2250,7 +2472,7 @@ void createregionlink(map_t *m, cell_t *c, object_t *o, char *obname, enum REGIO } // room w/h are returned in *w and *h if given. -int createroom(map_t *map, int roomid, int overrideminw, int overrideminh, int *retx, int *rety, int *retw, int *reth, int doorpct, int forcewalls) { +int createroom(map_t *map, int roomid, int overrideminw, int overrideminh, int xmargin, int ymargin, int *retx, int *rety, int *retw, int *reth, int doorpct, int forcewalls) { int x,y; cell_t *cell; int minx,miny; @@ -2276,7 +2498,7 @@ int createroom(map_t *map, int roomid, int overrideminw, int overrideminh, int * if (reth) *reth = h; // find room position - if (calcroompos(map, w, h, &minx, &miny, B_FALSE)) { + if (calcroompos(map, w, h, xmargin, ymargin, &minx, &miny, B_FALSE)) { dblog("** couldn't make room!\n"); return B_TRUE; } @@ -2471,7 +2693,7 @@ void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *c if (killwalls) { if (c->type->solid) { // make it empty! - setcelltype(c, c->map->habitat->emptycelltype); + setcelltype(c, c->habitat->emptycelltype); // add some rubble addob(c->obpile, "10-50 stones"); } @@ -2851,17 +3073,6 @@ int getnewdigdir(cell_t *cell, int lastdir, int turnpct, int *moved) { return dir; } -// chance of a random thing being an object (as opposed to a monster) -int getobchance(int habitat) { - switch (habitat) { - case H_DUNGEON: - return 50; - case H_FOREST: - return 75; - } - // default of no objects - return 0; -} char *getregionname(char *buf, map_t *m, int withlevel) { region_t *r; @@ -2912,32 +3123,6 @@ char *getregionname(char *buf, map_t *m, int withlevel) { return buf; } -// chance that a room is a 'special' one -int getvaultchance(map_t *m) { - switch (m->habitat->id) { - case H_DUNGEON: - return 10; - default: - return 0; - } - // default of no chance - return 0; -} - - - -// chance of each empty cell in a map has of getting an object/monster -int getthingchance(int habitat) { - switch (habitat) { - case H_DUNGEON: - return 3; - case H_FOREST: - return 3; - } - // default of no objects - return 0; -} - cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand) { return real_getrandomadjcell(c, wantempty, allowexpand, LOF_NEED, NULL); } @@ -2982,6 +3167,8 @@ cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LO //if (!new->type->solid) { ok = B_TRUE; } + } else if (wantempty == WE_NOLF) { + if (!new->lf) ok = B_TRUE; } else { // always ok ok = B_TRUE; @@ -3236,29 +3423,40 @@ object_t *hastrailof(obpile_t *op, lifeform_t *lf, enum OBTYPE oid, flag_t **tfl void initmap(void) { // habitats - addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL); - addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALL); - addhabitat(H_PIT, "pit", CT_CORRIDOR, CT_WALL); + // thingchance, obchance, vaultchance + addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 3, 50, 10); + addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALL, 3, 75, 0); + addhabitat(H_PIT, "pit", CT_CORRIDOR, CT_WALL, 0, 0, 0); + addhabitat(H_VILLAGE, "village", CT_GRASS, CT_WALL, 3, 70, 0); // cell types addcelltype(CT_WALL, "rock wall", '#', C_GREY, B_SOLID, B_OPAQUE, MT_STONE, 0); + addcelltype(CT_WALLWOOD, "wooden wall", '#', C_BROWN, B_SOLID, B_OPAQUE, MT_WOOD, 0); addcelltype(CT_WALLGLASS, "glass wall", '#', C_CYAN, B_SOLID, B_TRANS, MT_GLASS, 0); addcelltype(CT_WALLMETAL, "metal wall", '#', C_WHITE, B_SOLID, B_OPAQUE, MT_METAL, 0); addcelltype(CT_ROOMWALL, "rock wall", '#', C_GREY, B_SOLID, B_OPAQUE, MT_STONE, 0); addcelltype(CT_CORRIDOR, "rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_LOOPCORRIDOR, "rock floor", 'L', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0); + addcelltype(CT_FLOORWOOD, "wood floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0); + addcelltype(CT_FLOORSHOP, "shop floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0); addcelltype(CT_ROOM, "rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_GRASS, "grass", '.', C_GREEN, B_EMPTY, B_TRANS, MT_PLANT, 0); addcelltype(CT_DIRT, "dirt", '.', C_BROWN, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_LOWFLOOR, "low rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, -1); // region types - addregiontype(RG_WORLDMAP, H_FOREST, 10, 0, D_NONE); - addregiontype(RG_FIRSTDUNGEON, H_DUNGEON, 30, 3, D_DOWN); - addregiontype(RG_PIT, H_PIT, 1, 1, D_DOWN); + addregiontype(RG_WORLDMAP, H_FOREST, 10, 0, D_NONE, B_TRUE); + addregiontype(RG_FIRSTDUNGEON, H_DUNGEON, 30, 3, D_DOWN, B_TRUE); + addregiontype(RG_PIT, H_PIT, 1, 1, D_DOWN, B_FALSE); // region outlines addregionoutline(RG_WORLDMAP); - addregionthing(lastregionoutline, NA, 0, 0, RT_REGIONLINK, RG_FIRSTDUNGEON, "staircase going down"); + // link to first dungeon + addregionthing(lastregionoutline, NA, 0, 0, RT_REGIONLINK, RG_FIRSTDUNGEON, "staircase going down"); + // the village + addregionthing(lastregionoutline, NA, 0, -1, RT_HABITAT, H_VILLAGE, NULL); + addregionthing(lastregionoutline, NA, 0, -1, RT_VAULT, NA, "potion_shop"); + addregionthing(lastregionoutline, NA, 0, -1, RT_VAULT, NA, "weapon_shop"); + addregionthing(lastregionoutline, NA, 0, -1, RT_VAULT, NA, "armour_shop"); addregionoutline(RG_FIRSTDUNGEON); addregionthing(lastregionoutline, 6, NA, NA, RT_VAULT, NA, "jimbos_lair"); } @@ -3354,7 +3552,7 @@ int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) { f = lfhasflag(player, F_DETECTMETAL); if (f) { if (getcelldistorth(player->cell, c) <= f->val[0]) { - if (c->lf && ismetal(c->lf->race->material->id) ) { + if (c->lf && ismetal(getlfmaterial(c->lf)) ) { *thing = c->lf; if (glyph) { glyph->ch = '*'; @@ -3478,6 +3676,13 @@ int isoutdoors(map_t *m) { return B_FALSE; } +int isroom(cell_t *c) { + if (c && (c->roomid >= 0)) { + return B_TRUE; + } + return B_FALSE; +} + int iswallindir(cell_t *cell, int dir) { cell_t *newcell; newcell = getcellindir(cell, dir); @@ -3517,16 +3722,21 @@ void linkholes(map_t *map) { ot = getoppositestairs(o->type); // make a link to it in this map, as close as possible to same pos c2 = getcellat(map, x, y); - if (c2->type->solid) { + if (c2->lf || hasobid(c2->obpile, ot->id)) { // this will automatically avoid lifeforms since we're using // we_walkable rather than we_notwall. this saves problems // with someine coming up underneath you! - c2 = real_getrandomadjcell(c2, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, &ot->id); + c2 = real_getrandomadjcell(c2, WE_NOLF, B_ALLOWEXPAND, LOF_DONTNEED, &ot->id); } - // note we specifically say DONT link the new hole! + // clear out the cell if required + if (c2->type->solid) { + setcelltype(c2, c2->habitat->emptycelltype); + } + // note we specifically say DONT link the new hole, to avoid an infinite + // loop! newob = addobject(c2->obpile, ot->name, B_FALSE, B_FALSE); - // link them! - linkstairs(newob); + // link holes manually now. + linkstairs(newob, o); } } } @@ -3537,74 +3747,92 @@ void linkholes(map_t *map) { } // link the staircase 'o' to a free one in adjacent maps. +// o2 is options. if not probided, we will try to find +// something to link to ourself. // returns TRUE if it failed because othermap doesn't exist. -int linkstairs(object_t *o) { +int linkstairs(object_t *o, object_t *o2) { map_t *othermap; - object_t *o2; map_t *stairmap; cell_t *staircell; - cell_t *c2; - objecttype_t *otherstairtype; - int n,found = B_FALSE; - int dir; flag_t *f; staircell = getoblocation(o); stairmap = staircell->map; - otherstairtype = getoppositestairs(o->type); + if (o2) { + cell_t *othercell; + othercell = getoblocation(o2); + othermap = othercell->map; + } else { + objecttype_t *otherstairtype; + cell_t *c2; + int n, dir; + object_t *oo; + // find a valid other end + otherstairtype = getoppositestairs(o->type); - f = hasflag(o->flags, F_CLIMBABLE); - if (f) { - if (f->val[0] == D_UP) { - dir = -1; + f = hasflag(o->flags, F_CLIMBABLE); + if (f) { + if (f->val[0] == D_UP) { + dir = -1; + } else { + dir = 1; + } } else { - dir = 1; + dblog("ERROR: stair object has no f_climbable!"); + msg("ERROR: stair object has no f_climbable!"); + assert(0 == 1); } - } else { - dblog("ERROR: stair object has no f_climbable!"); - msg("ERROR: stair object has no f_climbable!"); - assert(0 == 1); - } - // do stairs go to a particular region? - if (f->val[1] != NA) { - // if so, find the first map in that region (ie depth 1) - othermap = findregionmap(f->val[1], 1); - } else { - othermap = getmapindir(stairmap, f->val[0]); - } - if (othermap) { - // find an empty staircase in other map - for (n = 0; n < othermap->w*othermap->h; n++) { - c2 = othermap->cell[n]; - o2 = hasob(c2->obpile, otherstairtype->id); - if (o2) { - // does it go nowhere? - if (!hasflag(o2->flags, F_MAPLINK)) { - char obid[BUFLEN]; - // link it to here! - sprintf(obid, "%ld", o->id); - addflag(o2->flags, F_MAPLINK, stairmap->id, NA, NA, obid); - // link me to there - sprintf(obid, "%ld", o2->id); - addflag(o->flags, F_MAPLINK, othermap->id, NA, NA, obid); - found = B_TRUE; - // now mark that we are not a new staircase to a new region anymore - f->val[1] = NA; - break; + // do stairs go to a particular region? + if (f->val[1] != NA) { + // if so, find the first map in that region (ie depth 1) + othermap = findregionmap(f->val[1], 1); + } else { + othermap = getmapindir(stairmap, f->val[0]); + } + if (othermap) { + int found = B_FALSE; + // find an empty staircase in other map + for (n = 0; n < othermap->w*othermap->h; n++) { + c2 = othermap->cell[n]; + oo = hasob(c2->obpile, otherstairtype->id); + if (oo) { + // does it go nowhere? + if (!hasflag(oo->flags, F_MAPLINK)) { + o2 = oo; + found = B_TRUE; + break; + } } } - } - if (!found) { - dblog("ERROR - stairs link to existing map %d, but it has no free stairs.",othermap->id); - msg("ERROR - stairs link to existing map %d, but it has no free stairs.",othermap->id); - more(); - assert(0 == 1); + if (!found) { + dblog("ERROR - stairs link to existing map %d, but it has no free stairs.",othermap->id); + msg("ERROR - stairs link to existing map %d, but it has no free stairs.",othermap->id); + more(); + assert(0 == 1); + } + } // end if othermap + } // end if !o2 + + + if (o2) { + char obid[BUFLEN]; + // link it to here! + sprintf(obid, "%ld", o->id); + addflag(o2->flags, F_MAPLINK, stairmap->id, NA, NA, obid); + // link me to there + sprintf(obid, "%ld", o2->id); + addflag(o->flags, F_MAPLINK, othermap->id, NA, NA, obid); + // now mark that we are not a new staircase to a new region anymore + f = hasflag(o->flags, F_CLIMBABLE); + if (f) { + f->val[1] = NA; } } else { return B_TRUE; } + return B_FALSE; } @@ -3620,7 +3848,7 @@ void makedoor(cell_t *cell, int openchance) { m = cell->map; - setcelltype(cell, m->habitat->emptycelltype); + setcelltype(cell, cell->habitat->emptycelltype); if ((rnd(1,100) + m->depth) >= 66) { strcpy(doorbuf, "iron door"); @@ -3757,7 +3985,7 @@ void setcelltype(cell_t *cell, enum CELLTYPE id) { assert(cell); cell->type = findcelltype(id); assert(cell->type); - cell->roomid = 0; + //cell->roomid = -1; if ((gamemode == GM_GAMESTARTED) && haslos(player, cell)) { needredraw = B_TRUE; } diff --git a/map.h b/map.h index d74cb1d..de1d352 100644 --- a/map.h +++ b/map.h @@ -1,7 +1,7 @@ #include "defs.h" cell_t *addcell(map_t *map, int x, int y); -habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell); +habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell, int thingchance, int obchance, int vaultchance); void addhomeobs(lifeform_t *lf); map_t *addmap(void); lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen, int *nadded); @@ -10,7 +10,7 @@ int addrandomthing(cell_t *c, int obchance, int *nadded); region_t *addregion(enum REGIONTYPE rtype, region_t *parent); regionoutline_t *addregionoutline(enum REGIONTYPE rtype); regionthing_t *addregionthing(regionoutline_t *ro, int depth, int x, int y, enum REGIONTHING whatkind, int value, char *what); -regiontype_t *addregiontype(enum REGIONTYPE id, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir); +regiontype_t *addregiontype(enum REGIONTYPE id, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major); int autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, int doorpct, int dooropenchance); int cellhaslos(cell_t *c1, cell_t *dest); void clearcell(cell_t *c); @@ -29,19 +29,20 @@ void getradiuscells(cell_t *centre, int radius, int dirtype, enum LOFTYPE needlo void getroomedge(map_t *m, int roomid, int minx, int miny, int maxx, int maxy, int whichside, cell_t **retcell, int *ncells, int onlywantsolid); object_t *gettopobject(cell_t *where, int forglyph); void calclight(map_t *map); -int calcroompos(map_t *map, int w, int h, int *bx, int *by, int force); +int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int *by, int force); int countadjcellsoftype(cell_t *cell, int id); +int countadjrooms(cell_t *cell); int countadjcellswithflag(cell_t *cell, enum FLAG fid); int countadjwalls(cell_t *cell); int countcellexits(cell_t *cell); -void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir); -void createforest(map_t *map, int depth, map_t *parentmap, int exitdir); -void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir); -void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int exitdir); -void createpit(map_t *map, int depth, map_t *parentmap, int exitdir); +void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); +void createforest(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob, int nclearings); +void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); +void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int exitdir, object_t *entryob); +void createpit(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); void createregionlink(map_t *m, cell_t *c, object_t *o, char *obname, enum REGIONTYPE newregiontype, region_t *parent); -int createroom(map_t *map, int roomid, int overrideminw, int overrideminh, int *retx, int *rety, int *retw, int *reth, int doorpct, int forcewalls); -int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth); +int createroom(map_t *map, int roomid, int overrideminw, int overrideminh, int xmargin, int ymargin, int *retx, int *rety, int *retw, int *reth, int doorpct, int forcewalls); +int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *retx, int *rety); int dirtox(int dt, int dir); int dirtoy(int dt, int dir); void dumpmap(map_t *map); @@ -66,8 +67,6 @@ cell_t *getcellindir(cell_t *cell, int dir); int getnewdigdir(cell_t *cell, int lastdir, int turnpct, int *moved); int getobchance(int habitat); char *getregionname(char *buf, map_t *m, int withlevel); -int getvaultchance(map_t *m); -char getvaultchar(vault_t *v, int x, int y); int getthingchance(int habitat); cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand); cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LOFTYPE needlof, enum OBTYPE *dontwantob); @@ -96,10 +95,11 @@ int isloopdirok(cell_t *cell, int dir); int isnewcellok(cell_t *cell, char *err); int isonmap(map_t *map, int x, int y); int isoutdoors(map_t *m); +int isroom(cell_t *c); int iswallindir(cell_t *cell, int dir); int linkexits(map_t *m, int roomid, int minx, int miny, int maxx, int maxy); void linkholes(map_t *map); -int linkstairs(object_t *o); +int linkstairs(object_t *o, object_t *o2); void makedoor(cell_t *cell, int openchance); void makelit(cell_t *c, enum LIGHTLEV how, int howlong); void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong); diff --git a/move.c b/move.c index f11fc34..95aeb0a 100644 --- a/move.c +++ b/move.c @@ -3,6 +3,7 @@ #include #include #include +#include "ai.h" #include "attack.h" #include "defs.h" #include "flag.h" @@ -286,8 +287,10 @@ int cellwalkable(lifeform_t *lf, cell_t *cell, enum ERROR *error) { for (o = cell->obpile->first ; o ; o = o->next) { if (isimpassableob(o, lf)) { if (lf) { - if ((lf->race->material->id == MT_GAS) || - (lf->race->material->id == MT_SLIME)) { + enum MATERIAL mid; + mid = getlfmaterial(lf); + if ((mid == MT_GAS) || + (mid == MT_SLIME)) { // ok } else if (lfhasflag(lf, F_NONCORPOREAL)) { // ok but still set error @@ -601,30 +604,30 @@ int getwalkoffdir(lifeform_t *lf, int dir) { switch (dir) { case DC_NE: if (lf->cell->y == 0) { - return DC_N; + return D_N; } else if ( lf->cell->x == (lf->cell->map->w-1)) { - return DC_E; + return D_E; } break; case DC_SE: if (lf->cell->y == (lf->cell->map->h-1)) { - return DC_S; + return D_S; } else if ( lf->cell->x == (lf->cell->map->w-1)) { - return DC_E; + return D_E; } break; case DC_SW: if (lf->cell->y == (lf->cell->map->h-1)) { - return DC_S; + return D_S; } else if ( lf->cell->x == 0) { - return DC_W; + return D_W; } break; case DC_NW: if (lf->cell->y == 0) { - return DC_N; + return D_N; } else if ( lf->cell->x == 0) { - return DC_W; + return D_W; } break; } @@ -741,6 +744,24 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc return B_FALSE; } +int makeorthogonal(int dir) { + switch (dir) { + case DC_N: + case D_N: + return D_N; + case DC_E: + case D_E: + return D_E; + case DC_S: + case D_S: + return D_S; + case DC_W: + case D_W: + return D_W; + } + return D_NONE; +} + // see 'movetowards' for description of dirtype int moveawayfrom(lifeform_t *lf, cell_t *dst, int dirtype ) { int dir; @@ -870,6 +891,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) { flag_t *f; int changedlev = B_FALSE; int preroom = -1, postroom = -1; + int preshop = -1; int prespeed = B_FALSE, postspeed = B_FALSE; int prewater = B_FALSE; @@ -888,6 +910,9 @@ int movelf(lifeform_t *lf, cell_t *newcell) { // update current cell + room id prespeed = getmovespeed(lf); preroom = lf->cell->roomid; + if (lf->cell->vault && hasflag(lf->cell->vault->flags, F_VAULTISSHOP)) { + preshop = lf->cell->roomid; + } // getting out of water? if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { @@ -1108,7 +1133,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } // does anyone else see you? - if ((gamemode == GM_GAMESTARTED)) { + if (gamemode == GM_GAMESTARTED) { for (l = newcell->map->lf ; l ; l = l->next) { if (l != lf) { flag_t *alarm; @@ -1153,6 +1178,54 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } } } + + // leaving a shop + if (preshop) { + // are you about to go outside a shop with stolen goods? + if ((lf->cell->roomid == preshop) && (lf->cell->type->id != CT_FLOORSHOP)) { + lifeform_t *shk; + shk = findshopkeeper(lf->cell->map, preshop); + if (shk) { + int nitems = 0; + // do you have any unpaid items from that shop? + if (getowing(lf, preshop, &nitems)) { + char saybuf[BUFLEN]; + // warning... + switch (rnd(1,3)) { + case 1: sprintf(saybuf, "Hey! Where do you think you're going?"); + break; + case 2: sprintf(saybuf, "AHEM!"); + break; + case 3: sprintf(saybuf, "I hope you are going to pay for %s!", + (nitems == 1) ? "that" : "those" ); + break; + } + say(shk, saybuf, SV_SHOUT); + didmsg = B_TRUE; + } + } + } else if (lf->cell->roomid != preshop) { + // you've left the shop + lifeform_t *shk; + shk = findshopkeeper(lf->cell->map, preshop); + if (shk && getowing(lf, preshop, NULL)) { + char saybuf[BUFLEN]; + // call the guards + switch (rnd(1,3)) { + case 1: sprintf(saybuf, "Stop thief!"); + break; + case 2: sprintf(saybuf, "GUARDS!"); + break; + case 3: sprintf(saybuf, "I've been robbed!"); + break; + } + say(shk, saybuf, SV_ROAR); + didmsg = B_TRUE; + fightback(shk, lf); // shopkeeper attacks + callguards(shk, lf); // guards come running + } + } + } } // status bar @@ -1216,7 +1289,7 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { // tell player about things if (!isdead(lf)) { // some lifeforms can go through things - if (lf->race->material->id == MT_GAS) { + if (getlfmaterial(lf) == MT_GAS) { char obname[BUFLEN]; for (o = newcell->obpile->first ; o ; o = o->next) { if (isimpassableob(o, lf)) { @@ -1228,7 +1301,7 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { } } } - } else if (lf->race->material->id == MT_SLIME) { + } else if (getlfmaterial(lf) == MT_SLIME) { char obname[BUFLEN]; for (o = newcell->obpile->first ; o ; o = o->next) { if (isimpassableob(o, lf)) { @@ -2274,6 +2347,10 @@ int walkoffmap(lifeform_t *lf, int dir, int onpurpose) { int nadjallies = 0; int n; + // make sure dircetion is orthogonal + dir = makeorthogonal(dir); + assert(dir != D_NONE); + // announce if (isplayer(lf)) { char dirname[BUFLEN]; @@ -2300,7 +2377,7 @@ int walkoffmap(lifeform_t *lf, int dir, int onpurpose) { if (!adjmap) { // make one adjmap = addmap(); - createmap(adjmap, thismap->depth, thismap->region, thismap, dir); + createmap(adjmap, thismap->depth, thismap->region, thismap, dir, NULL); } if (adjmap) { @@ -2377,14 +2454,28 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { } cell = getcellindir(lf->cell, dir); + if (!cell) { + // won't walk off map + return B_FALSE; + } - if (cell && celldangerous(lf, cell, B_TRUE, error)) { + if (celldangerous(lf, cell, B_TRUE, error)) { + if (error) *error = E_WONT; + return B_FALSE; + } + + if (!isroom(cell) && hasjob(lf, J_SHOPKEEPER)) { + if (error) *error = E_WONT; + return B_FALSE; + } + + if (lfhasflag(lf, F_STAYINHABITAT) && (cell->habitat->id != lf->cell->habitat->id)) { if (error) *error = E_WONT; return B_FALSE; } // don't attack other monsters - if (cell && cell->lf) { // if someone is in the way + if (cell->lf) { // if someone is in the way if (lf->race->raceclass->id == RC_INSECT) { if (hasactivespell(cell->lf, OT_S_REPELINSECTS)) { if (error) *error = E_WONT; @@ -2415,35 +2506,33 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) { } // look for avoided objects (because they are cursed). - if (cell) { - for (o = cell->obpile->first ; o ; o = o->next) { - flag_t *f; - sprintf(buf, "%ld",o->id); - f = lfhasflagval(lf, F_AVOIDCURSEDOB, NA, NA, NA, buf); - if (f) { - // still cursed? - if (iscursed(o)) { + for (o = cell->obpile->first ; o ; o = o->next) { + flag_t *f; + sprintf(buf, "%ld",o->id); + f = lfhasflagval(lf, F_AVOIDCURSEDOB, NA, NA, NA, buf); + if (f) { + // still cursed? + if (iscursed(o)) { + if (error) *error = E_WONT; + return B_FALSE; + } else { + // remove the flag. + 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 { - // remove the flag. - 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; - } + } else { + // non-hidden traps? + if (iq >= IQ_AVERAGE) { + if (error) *error = E_WONT; + return B_FALSE; } } } diff --git a/move.h b/move.h index 7624bd1..d78043f 100644 --- a/move.h +++ b/move.h @@ -12,6 +12,7 @@ int getdiraway(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int d int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int dirtype); int getwalkoffdir(lifeform_t *lf, int dir); int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallcheckdiff); +int makeorthogonal(int dir); int moveawayfrom(lifeform_t *lf, cell_t *dst, int dirtype); int moveclear(lifeform_t *lf, int dir, enum ERROR *error); int moveeffects(lifeform_t *lf); diff --git a/nexus.c b/nexus.c index a89f29c..25ed709 100644 --- a/nexus.c +++ b/nexus.c @@ -16,6 +16,7 @@ #include "nexus.h" #include "objects.h" #include "save.h" +#include "spell.h" #include "text.h" #include "vault.h" @@ -170,7 +171,9 @@ int main(int argc, char **argv) { initprompt(&prompt, "Select your job:"); ch = 'a'; for (j = firstjob ; j ; j = j->next) { - addchoice(&prompt, ch++, j->name, NULL, j); + if (!hasflag(j->flags, F_NOPLAYER)) { + addchoice(&prompt, ch++, j->name, NULL, j); + } } j = NULL; while (!j) { @@ -187,12 +190,12 @@ int main(int argc, char **argv) { // create world map. wregion = addregion(RG_WORLDMAP, NULL); addmap(); - createmap(firstmap, 1, wregion, NULL, D_NONE); + createmap(firstmap, 1, wregion, NULL, D_NONE, NULL); //createmap(firstmap, 1, RG_FIRSTDUNGEON, H_DUNGEON, NULL, D_NONE); // create first dungeon dregion = findregionbytype(RG_FIRSTDUNGEON); dmap = addmap(); - createmap(dmap, 1, dregion, firstmap, D_DOWN); + createmap(dmap, 1, dregion, firstmap, D_DOWN, NULL); } // find staircase @@ -214,14 +217,8 @@ int main(int argc, char **argv) { addflag(player->flags, F_NAME, NA, NA, NA, "Anonymous"); } givejob(player, j->id); - // special cases for jobs: - if (j->id == J_PIRATE) { - flag_t *f; - f = lfhasflagval(player, F_HASATTACK, OT_FISTS, NA, NA, NULL); - assert(f); - f->val[0] = OT_HOOKHAND; - sprintf(f->text, "1d4"); - } else if (j->id == J_WIZARD) { + // extra choices for some jobs + if (j->id == J_WIZARD) { skill_t *sk; initprompt(&prompt, "Select your spell specialty:"); addchoice(&prompt, 'a', getskillname(SK_SS_AIR), NULL, findskill(SK_SS_AIR)); @@ -572,6 +569,79 @@ void donextturn(map_t *map) { int donormalmove = B_TRUE; flag_t *f; + // casting a spell? + if (donormalmove) { + f = lfhasflag(who, F_CASTINGSPELL); + if (f) { + donormalmove = B_FALSE; + + f->val[2]--; + if (f->val[2] <= 0) { + char *p; + char buf[BUFLEN]; + int lfid,mapid,x,y,power; + long obid; + lifeform_t *targlf; + object_t *targob; + cell_t *targcell; + map_t *targmap; + enum OBTYPE sid; + + sid = f->val[0]; + power = f->val[1]; + + + // finished! + p = f->text; + p = readuntil(buf, p, ';'); + lfid = atoi(p); + p = readuntil(buf, p, ';'); + obid = atol(p); + p = readuntil(buf, p, ';'); + mapid = atoi(p); + p = readuntil(buf, p, ';'); + x = atoi(p); + p = readuntil(buf, p, ';'); + y = atoi(p); + + if (lfid >= 0) { + targlf = findlf(NULL, lfid); + } else { + targlf = NULL; + } + if (obid >= 0) { + targob = findobidinmap(who->cell->map, obid); + } else { + targob = NULL; + } + if (mapid >= 0) { + targmap = findmap(mapid); + targcell = getcellat(targmap, x, y); + } + taketime(who, getspellspeed(who)); + dospelleffects(who, sid, power, targlf, targob, targcell, B_UNCURSED, NULL, B_FALSE); + killflagsofid(who->flags, F_CASTINGSPELL); + } else { + if (isplayer(who)) { + objecttype_t *sp; + sp = findot(f->val[0]); + msg("You continue casting %s.", sp->name); + } else if (cansee(player, who)) { + flag_t *f2; + char lfname[BUFLEN]; + // still going... + getlfname(who, lfname); + f2 = lfhasflag(who,F_SPELLCASTTEXT); + if (f2 && strlen(f2->text)) { + msg("%s %s.", lfname, f2->text); + } else { + msg("%s continues casting a spell.", lfname); + } + } + } + } + } + // eating? if (donormalmove) { f = lfhasflag(who, F_EATING); @@ -637,8 +707,14 @@ void donextturn(map_t *map) { // find out what player wants to do handleinput(); } else { + //char lfname[BUFLEN]; + //char buf[BUFLEN]; // do ai move + //real_getlfname(who, lfname, B_FALSE); + //sprintf(buf, "aimove %s",lfname); + //dbtimestart(buf); aiturn(who); + //dbtimeend(buf); } } } @@ -1332,21 +1408,9 @@ void timeeffectsworld(map_t *map) { if (!o) { o = addob(c->obpile, "water"); } - - if (o) { - flag_t *dflag; - dflag = hasflag(o->flags, F_DEEPWATER); - // adjust depth - dflag->val[0] = f->val[2]; - } - } else { - // water depth is now zero. - o = hasobwithflag(c->obpile, F_DEEPWATER); - if (o) { - killob(o); - addob(c->obpile, "large puddle of water"); - } } + + setwaterdepth(c, f->val[2]); } killflag(f); continue; diff --git a/objects.c b/objects.c index f87f856..18d64e5 100644 --- a/objects.c +++ b/objects.c @@ -429,6 +429,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes // for doors enum FLAG doorflag[5]; int ndoorflags = 0; + char *signtext = NULL; // just in case we don't add any addedob[0] = NULL; @@ -646,6 +647,24 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes corpserace = findracebyname(racename); ot = findot(OT_HEAD); + } else if (strstarts(p, "sign ")) { + char *pp; + pp = strchr(p, '\"'); + if (pp) { + char sbuf[BUFLEN]; + char *sbp; + sbp = sbuf; + pp++; + + while (*pp && (*pp != '\"')) { + *sbp = *pp; + sbp++; + pp++; + } + *sbp = '\0'; + signtext = strdup(sbuf); + } + ot = findot(OT_SIGN); } else if (strstr(p, "spellbook of ")) { char *pp; pp = p + 13; @@ -718,7 +737,6 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes //////////////////////////////////// // we now have the objecttype! //////////////////////////////////// - if (hasflag(ot->flags, F_ONEPERCELL)) { if (hasob(where, ot->id)) { nretobs = 0; @@ -901,30 +919,36 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes */ for (i = 0; i < nadded; i++) { + cell_t *obloc; + o = addedob[i]; + obloc = getoblocation(o); + + // fill in sign text + if (signtext) { + addflag(o->flags, F_SIGNTEXT, NA, NA, NA, signtext); + } // fill in door flags if (ndoorflags && isdoor(o, NULL)) { int n; for (n = 0; n < ndoorflags; n++) { - cell_t *c; int val[3]; - c = getoblocation(o); - if (c) { + if (obloc) { // fill in flag vals switch (doorflag[n]) { case F_LOCKED: val[0] = B_TRUE; - val[1] = getdoorlockdiff(c->map->depth); + val[1] = getdoorlockdiff(obloc->map->depth); val[2] = NA; break; case F_JAMMED: - val[0] = rnd(1,c->map->depth+3); + val[0] = rnd(1,obloc->map->depth+3); val[1] = NA; val[2] = NA; break; case F_SECRET: - val[0] = getdoorsecretdiff(c->map->depth); + val[0] = getdoorsecretdiff(obloc->map->depth); val[1] = NA; val[2] = NA; break; @@ -1398,7 +1422,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes objecttype_t *ot2; strcpy(buf, ""); while (!strcmp(buf, "")) { - real_getrandomob(loc->map, buf, RO_NONE, NA, loc->map->depth + rnd(10,15)); + real_getrandomob(loc->map, buf, RO_NONE, NA, loc->map->depth + rnd(10,15), NA); // replace "1 potion" with "a potion" if (strstr(buf, "1 ") == buf) { char temp[BUFLEN]; @@ -1601,6 +1625,22 @@ objecttype_t *addot(enum OBTYPE id, char *name, char *description, int material, return a; } + +void adjustdamhardness(unsigned int *dam, enum DAMTYPE damtype, enum MATERIAL mat) { + // now check for hardness + if (isphysicaldam(damtype)) { + material_t *m; + m = findmaterial(mat); + if (m) { + flag_t *f; + f = hasflag(m->flags, F_HARDNESS); + if (f && (*dam < f->val[0])) { + *dam = 0; + } + } + } +} + // adjust damage based on material being damaged void adjustdammaterial(unsigned int *dam, enum DAMTYPE damtype, enum MATERIAL mat) { // adjust based on material @@ -1694,6 +1734,7 @@ void adjustdammaterial(unsigned int *dam, enum DAMTYPE damtype, enum MATERIAL ma break; } } + } void adjustdamob(object_t *o, unsigned int *dam, enum DAMTYPE damtype) { @@ -1781,8 +1822,9 @@ void adjustdamob(object_t *o, unsigned int *dam, enum DAMTYPE damtype) { } } - // adjust damage for material too + // adjust damage for material & hardness too adjustdammaterial(dam, damtype, o->material->id); + adjustdamhardness(dam, damtype, o->material->id); } // adjust price for magical effects etc @@ -2715,13 +2757,22 @@ glyph_t *getglyph(object_t *o) { } } - // override colour + // special case if (o->type->id == OT_WATERDEEP) { + cell_t *loc; + // override colour if (getobdepth(o, player) >= DP_HEAD) { col = C_BOLDBLUE; } else { col = C_BLUE; } + loc = getoblocation(o); + if (getcellwaterdepth(loc, player) >= DP_WAIST) { + g = '{'; + } else { + g = '~'; + } + } tempglyph.ch = g; @@ -3677,7 +3728,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan char localbuf[BUFLEN]; char buf2[BUFLEN]; char triedbuf[BUFLEN]; - int shopitem = B_FALSE; + int venditem = B_FALSE; flag_t *f; brand_t *br; obmod_t *om; @@ -3685,13 +3736,13 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan cell_t *where; // default to normal name - if (hasflag(o->flags, F_SHOPITEM)) { - shopitem = B_TRUE; + if (hasflag(o->flags, F_VENDITEM)) { + venditem = B_TRUE; } where = getoblocation(o); - if (shopitem) { + if (venditem) { showall = B_TRUE; } @@ -3771,6 +3822,8 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan strcat(basename, dname); } } // end if sight/smell + } else if ((o->type->id == OT_SIGN) && !hasflag(o->flags, F_SIGNTEXT)) { + strcpy(basename, "blank sign"); } else if (o->type->id == OT_WATERDEEP) { sprintf(basename, "%s water", getwaterdepthname(getobdepth(o, player))); } else { @@ -4063,6 +4116,16 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } } + // show sign text + if (o->type->id == OT_SIGN) { + f = hasflag(o->flags, F_SIGNTEXT); + if (f) { + strcat(localbuf, " reading '"); + strcat(localbuf, f->text); + strcat(localbuf, "'"); + } + } + // append inscription if (o->inscription) { strcat(localbuf, " {"); @@ -4102,6 +4165,14 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } } + // in a shop? + f = hasflag(o->flags, F_SHOPITEM); + if (f) { + char pricebuf[BUFLEN]; + sprintf(pricebuf, " [$%d%s]", f->val[0], o->pile->owner ? ", unpaid" : ""); + strcat(localbuf, pricebuf); + } + // apply prefix now! if (count == 1) { if (hasflag(o->flags, F_NO_A)) { @@ -4273,7 +4344,7 @@ objecttype_t *getoppositestairs(objecttype_t *ot) { return findot(f->val[0]); } -char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth) { +char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth, int forcehabitat) { objecttype_t *ot; objecttype_t *poss[MAXRANDOMOBCANDIDATES]; int nposs = 0; @@ -4335,7 +4406,9 @@ char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth // correct rarity? rarflag = hasflagval(ot->flags, F_RARITY, H_ALL, NA, NA, NULL); if (!rarflag) { - if (map) { + if (forcehabitat != NA) { + rarflag = hasflagval(ot->flags, F_RARITY, forcehabitat, NA, NA, NULL); + } else if (map) { rarflag = hasflagval(ot->flags, F_RARITY, map->habitat->id, NA, NA, NULL); } else { rarflag = hasflagval(ot->flags, F_RARITY, NA, NA, NA, NULL); @@ -4394,8 +4467,6 @@ char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth if (db) dblog("got %d possibilities. now adjusting for RR_",nposs); - - // pick a random object from our possiblities selidx = rnd(0,nposs-1); ot = poss[selidx]; @@ -4489,17 +4560,17 @@ char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth } char *getrandomob(map_t *map, char *buf) { - return real_getrandomob(map, buf, RO_NONE, NA, NA); + return real_getrandomob(map, buf, RO_NONE, NA, NA, NA); } char *getrandomobwithdt(map_t *map, enum DAMTYPE damtype, char *buf) { - return real_getrandomob(map, buf, RO_DAMTYPE, damtype, NA); + return real_getrandomob(map, buf, RO_DAMTYPE, damtype, NA, NA); } char *getrandomobwithclass(map_t *map, enum OBCLASS cid, char *buf, int depthmod) { //return real_getrandomob(map, buf, RO_OBCLASS, cid, map->depth + depthmod); if (depthmod == NA) depthmod = 0; - return real_getrandomob(map, buf, RO_OBCLASS, cid, getmapdifficulty(map) + depthmod); + return real_getrandomob(map, buf, RO_OBCLASS, cid, getmapdifficulty(map) + depthmod, NA); } int getobrarity(object_t *o, enum RARITY *rr) { @@ -4598,6 +4669,31 @@ char *getschoolnameshort(enum SPELLSCHOOL sch) { return "unknown school"; } +void setwaterdepth(cell_t *c, int depth) { + object_t *o; + if (depth > 0) { + o = hasobwithflag(c->obpile, F_DEEPWATER); + if (o) { + flag_t *f; + // adjust depth + f = hasflag(o->flags, F_DEEPWATER); + f->val[0] = depth; + } + } else { + int nkilled = 0; + // water depth is now zero. + o = hasobwithflag(c->obpile, F_DEEPWATER); + while (o) { + killob(o); + nkilled++; + o = hasobwithflag(c->obpile, F_DEEPWATER); + } + if (nkilled) { + addob(c->obpile, "large puddle of water"); + } + } +} + int getshatterdam(object_t *o) { int shatterdam = 0; if (willshatter(o->material->id)) { @@ -5090,7 +5186,7 @@ void initobjects(void) { addflag(lastmaterial->flags, F_DTIMMUNE, DT_BASH, NA, NA, NULL); addmaterial(MT_FOOD, "food", 3); addmaterial(MT_PLASTIC, "plastic", 3); - addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 2, NA, NA, NULL); addmaterial(MT_WAX, "wax", 3); addflag(lastmaterial->flags, F_MATCONVERT, MT_FIRE, NA, NA, "lump of melted wax"); addflag(lastmaterial->flags, F_MATCONVERTTEXT, MT_FIRE, NA, NA, "melts"); @@ -5100,12 +5196,12 @@ void initobjects(void) { addflag(lastmaterial->flags, F_CANGETWET, B_TRUE, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_BASH, NA, NA, NULL); addmaterial(MT_BONE, "bone", 5); - addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 3, NA, NA, NULL); addmaterial(MT_OIL, "oil", 5); addmaterial(MT_ICE, "ice",6); - addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 2, NA, NA, NULL); addmaterial(MT_WOOD, "wood", 6); - addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 3, NA, NA, NULL); addflag(lastmaterial->flags, F_FLAMMABLE, 5, NA, NA, NULL); addflag(lastmaterial->flags, F_CANGETWET, B_TRUE, NA, NA, NULL); addmaterial(MT_ACID, "acid", 7); @@ -5113,7 +5209,7 @@ void initobjects(void) { addmaterial(MT_BLOOD, "blood", 7); addmaterial(MT_SLIME, "slime", 9); addmaterial(MT_STONE, "stone", 10); - addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 5, 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); @@ -5121,7 +5217,7 @@ void initobjects(void) { addflag(lastmaterial->flags, F_DTRESIST, DT_CHOP, NA, NA, NULL); addflag(lastmaterial->flags, F_DTRESIST, DT_PROJECTILE, NA, NA, NULL); addmaterial(MT_METAL, "metal", 13); - addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 6, 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); @@ -5129,10 +5225,10 @@ void initobjects(void) { 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); + addflag(lastmaterial->flags, F_HARDNESS, 3, NA, NA, NULL); addflag(lastmaterial->flags, F_DTVULN, DT_BASH, NA, NA, NULL); addmaterial(MT_GOLD, "gold", 16); - addflag(lastmaterial->flags, F_HARD, B_TRUE, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 4, NA, NA, NULL); //addmaterial(MT_GOLD, "gold", 16); // object classes @@ -5261,8 +5357,8 @@ void initobjects(void) { addflag(lastot->flags, F_DTRESIST, DT_CHOP, NA, NA, NULL); // blocks movement, but you can see and fire through them. - addot(OT_IRONGATE, "iron gate", "A gate comprised of a series of vertical iron bars.", MT_METAL, 0, OC_DFEATURE); - addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "|"); + addot(OT_GATEIRON, "iron gate", "A gate comprised of a series of vertical iron bars, raised slightly above the floor.", MT_METAL, 500, OC_DFEATURE); + addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "+"); addflag(lastot->flags, F_DOOR, SZ_MEDIUM, SZ_MAX, NA, NULL); addflag(lastot->flags, F_IMPASSABLE, SZ_MEDIUM, SZ_MAX, NA, NULL); addflag(lastot->flags, F_LOCKABLE, B_TRUE, NA, NA, NULL); @@ -5273,7 +5369,26 @@ void initobjects(void) { addflag(lastot->flags, F_DTIMMUNE, DT_PIERCE, NA, NA, NULL); addflag(lastot->flags, F_DTIMMUNE, DT_SLASH, NA, NA, NULL); addflag(lastot->flags, F_DTRESIST, DT_CHOP, NA, NA, NULL); + addot(OT_GATEWOOD, "wooden gate", "A gate comprised of a series of wooden slats.", MT_WOOD, 200, OC_DFEATURE); + addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "+"); + addflag(lastot->flags, F_DOOR, SZ_MIN, SZ_MAX, NA, NULL); + addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_MAX, NA, NULL); + addflag(lastot->flags, F_LOCKABLE, 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_NOPICKUP, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL); + addflag(lastot->flags, F_DTVULN, DT_CHOP, NA, NA, NULL); + addot(OT_FENCEWOOD, "wooden fence", "A tell fence created from a series of upright logs of wood.", MT_WOOD, 200, OC_DFEATURE); + addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_MAX, NA, NULL); + addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_BLOCKSLOF, 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_LOCKABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL); + addflag(lastot->flags, F_DTVULN, DT_CHOP, NA, NA, NULL); addot(OT_BOULDER, "boulder", "A massive stone boulder.", MT_STONE, 80, OC_ROCK); addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, ""); @@ -5300,7 +5415,8 @@ void initobjects(void) { 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_RARITY, H_DUNGEON, 75, NA, ""); + addflag(lastot->flags, F_RARITY, H_VILLAGE, 80, NA, ""); addflag(lastot->flags, F_GLYPH, NA, NA, NA, "'"); addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_LARGE, NA, NULL); // will be overridden addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL); @@ -5380,7 +5496,7 @@ void initobjects(void) { addot(OT_WATERDEEP, "water", "Deep water.", MT_WATER, 300, OC_TERRAIN); addflag(lastot->flags, F_NO_A, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, C_BOLDBLUE, NA, NA, "~"); + addflag(lastot->flags, F_GLYPH, C_BOLDBLUE, NA, NA, "{"); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DTCONVERT, DT_COLD, NA, NA, "sheet of ice"); addflag(lastot->flags, F_DTCREATEOB, DT_FIRE, 1, DT_COMPASS, "cloud of steam"); @@ -5943,6 +6059,7 @@ void initobjects(void) { addot(OT_S_INFINITEDEATH, "infinite death", "Annihilates all nearby life, including the caster!", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 8, NA, NA, NULL); + addflag(lastot->flags, F_CASTINGTIME, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); /////////////////// // divination @@ -6168,8 +6285,6 @@ void initobjects(void) { addot(OT_S_BARKSKIN, "barkskin", "Covers the caster with a skin of bark, reducing damage but making them vulnerable to fire.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); - addflag(lastot->flags, F_MAXPOWER, 5, 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_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addot(OT_S_CHARMANIMAL, "befriend animal", "Temporarily makes a single animal friendly to you.", MT_NOTHING, 0, OC_SPELL); @@ -6371,15 +6486,18 @@ void initobjects(void) { addot(OT_S_MINDSCAN, "mind scan", "Reveals detailed information about the target.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, 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_SLEEP, "sleep", "Puts the target creature to sleep.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, 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_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_TELEKINESIS, "telekinesis", "Mentally move or manipulate nearby objects.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); // l3 addot(OT_S_PSYARMOUR, "psychic armour", "Mentally block incoming attacks.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); @@ -6573,6 +6691,8 @@ void initobjects(void) { // divine powers (spells/abilities) + addot(OT_A_BLINDALL, "nosight", "Make everyone on the level blind.", MT_NOTHING, 0, OC_ABILITY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_DEBUG, "debug", "You can toggle debugging for a lifeform.", MT_NOTHING, 0, OC_ABILITY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_ENHANCE, "enhance", "Enhance a lifeform's stats.", MT_NOTHING, 0, OC_ABILITY); @@ -6587,6 +6707,10 @@ void initobjects(void) { addot(OT_S_GIFT, "gift", "Grants the target any item of their choice (with some limitations).", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLLEVEL, 9, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINE, NA, NA, NULL); + addot(OT_S_CLEARLEVEL, "blank level", "Blanks out the current map.", MT_NOTHING, 0, OC_SPELL); + addflag(lastot->flags, F_SPELLLEVEL, 9, NA, NA, NULL); + addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINE, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_S_CREATEVAULT, "create vault", "Create a vault of the given type.", MT_NOTHING, 0, OC_SPELL); addflag(lastot->flags, F_SPELLLEVEL, 9, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINE, NA, NA, NULL); @@ -7083,7 +7207,7 @@ void initobjects(void) { addot(OT_ACIDPOOL, "pool of acid", "A pool of corrosive acid.", MT_ACID, 0, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "~"); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "evaporates"); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "puddle of acid"); @@ -7098,7 +7222,7 @@ void initobjects(void) { addot(OT_ACIDPUDDLE, "puddle of acid", "A small puddle of corrosive acid.", MT_ACID, 0, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "~"); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBDIETEXT, NA, NA, NA, "evaporates"); addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL); @@ -7163,7 +7287,7 @@ void initobjects(void) { addot(OT_MUDPOOL, "pool of mud", "A large puddle of wet mud.", MT_WATER, 60, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_BROWN, NA, NA, "~"); addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL); addflag(lastot->flags, F_RARITY, H_FOREST, 90, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); @@ -7183,6 +7307,11 @@ void initobjects(void) { addflag(lastot->flags, F_DTCONVERT, DT_COLD, NA, NA, "sheet of ice"); addflag(lastot->flags, F_DTCONVERT, DT_FIRE, NA, NA, "puff of steam"); addflag(lastot->flags, F_DRINKABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 10, 10, 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_NOOBDIETEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LINKOB, OT_POT_WATER, NA, NA, NULL); //addflag(lastot->flags, F_WALKDAM, DT_WATER, NA, NA, "0d1+1"); addflag(lastot->flags, F_WALKDAMBP, BP_FEET, DT_WATER, FALLTHRU, "0d1+1"); @@ -7190,13 +7319,18 @@ void initobjects(void) { addot(OT_PUDDLEWATERL, "large puddle of water", "A large pool of water.", MT_WATER, 20, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, C_BLUE, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, C_BLUE, NA, NA, "~"); addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL); addflag(lastot->flags, F_RARITY, H_FOREST, 85, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DTCONVERT, DT_COLD, NA, NA, "sheet of ice"); addflag(lastot->flags, F_DTCONVERT, DT_FIRE, NA, NA, "cloud of steam"); addflag(lastot->flags, F_DRINKABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 30, 30, 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_NOOBDIETEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LINKOB, OT_POT_WATER, NA, NA, NULL); addflag(lastot->flags, F_WALKDAMBP, BP_FEET, DT_WATER, FALLTHRU, "0d1+2"); @@ -7245,7 +7379,7 @@ void initobjects(void) { addot(OT_BLOODPOOL, "pool of blood", "A large pool of blood.", MT_BLOOD, 0, OC_MISC); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{"); + addflag(lastot->flags, F_GLYPH, NA, NA, NA, "~"); addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_SLIPPERY, 3, NA, NA, NULL); @@ -7259,7 +7393,15 @@ void initobjects(void) { addflag(lastot->flags, F_LINKOB, OT_POT_BLOOD, NA, NA, NULL); addflag(lastot->flags, F_NOFEEL, B_TRUE, NA, NA, NULL); - addot(OT_WOODENBARREL, "wooden barrel", "A solid wooden barrel.", MT_WOOD, 20, OC_MISC); + addot(OT_SIGN, "sign", "A marker with something written on it.", MT_WOOD, 25, OC_MISC); + addflag(lastot->flags, F_GLYPH, C_BROWN, 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_DAMAGABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_OBHP, 6, 6, NA, NULL); + + addot(OT_WOODENBARREL, "wooden barrel", "A solid wooden barrel.", MT_WOOD, 40, OC_MISC); + addflag(lastot->flags, F_RARITY, H_VILLAGE, 75, RR_COMMON, NULL); addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL); addflag(lastot->flags, F_GLYPH, NA, NA, NA, "("); addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_LARGE, NA, NULL); @@ -7321,7 +7463,7 @@ void initobjects(void) { // effects addot(OT_FIRELARGE, "large fire", "A large, roaring inferno.", MT_FIRE, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_ORANGE, NA, NA, "~"); + addflag(lastot->flags, F_GLYPH, C_ORANGE, NA, NA, "{"); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dies down a little"); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "medium fire"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); @@ -7332,7 +7474,7 @@ void initobjects(void) { addflag(lastot->flags, F_PRODUCESLIGHT, 3, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_FIREMED, "medium fire", "A medium-sized roaring fire.", MT_FIRE, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "~"); + addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "{"); addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "small fire"); addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dies down a little"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); @@ -7343,7 +7485,7 @@ void initobjects(void) { addflag(lastot->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!"); addot(OT_FIRESMALL, "small fire", "A small blaze.", MT_FIRE, 0, OC_EFFECT); - addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "~"); + addflag(lastot->flags, F_GLYPH, C_RED, NA, NA, "{"); addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "goes out"); addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL); addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL); @@ -8981,6 +9123,12 @@ int iswearable(object_t *o) { return B_FALSE; } +void killallobs(obpile_t *op) { + while (op->first) { + killob(op->first); + } +} + void killmaterial(material_t *m) { material_t *nextone, *lastone; @@ -9042,9 +9190,7 @@ void killob(object_t *o) { } void killobpile(obpile_t *op) { - while (op->first) { - killob(op->first); - } + killallobs(op); free(op); } @@ -9472,9 +9618,27 @@ object_t *real_moveob(object_t *src, obpile_t *dst, int howmany, int stackok) { drawscreen(); } - // in case you picked up money, something which changes your AR, etc - if (dst->owner && isplayer(dst->owner)) { - statdirty = B_TRUE; + // special effects if a lifeform picked up an object + if (dst->owner) { + flag_t *f; + // picked up an object in a shop + f = hasflag(o->flags, F_SHOPITEM); + if (f) { + lifeform_t *shk; + shk = findshopkeeper(dst->owner->cell->map, f->val[1]); + if (shk && cansee(shk, dst->owner)) { + askforpayment(shk, dst->owner); + } + + if (!isplayer(dst->owner)) { + msg("xxxxxxxxxxxx"); + } + } + + // in case you picked up money, something which changes your AR, etc + if (isplayer(dst->owner)) { + statdirty = B_TRUE; + } } //o = newobeffects(o); @@ -12242,6 +12406,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, char throwverbpres[BUFLEN]; int acc; int youhit = B_FALSE; + int missiledam = 0; object_t *newob; cell_t *newloc; int db = B_TRUE; @@ -12347,7 +12512,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, if (target && isdead(target)) { target = NULL; } - if (thrower && target && !isprone(target)) { + if (thrower && target && !isprone(target) && !lfhasflag(target, F_CASTINGSPELL)) { if (areallies(thrower, target) && !firearm) { willcatch = B_TRUE; } @@ -12563,7 +12728,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, } // an actual physical shield? shield = getshield(target); - if (shield) { + if (shield && !lfhasflag(target, F_CASTINGSPELL)) { // block chance based on shield skill // ie. ST_AVERAGE = speed3 = 18 // ie. gun = speed20 = 120 = impossible @@ -12585,6 +12750,8 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, takedamage(shield, dam, DT_PROJECTILE); youhit = B_FALSE; practice(target, SK_SHIELDS, 1); + + missiledam += ((speed*2)+1); } } } @@ -12600,7 +12767,7 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, !isimmobile(target) && skillcheck(target, SC_DEX, 15*speed, 0)) { willcatch = B_TRUE; - } else if (skillcheck(target, SC_DODGE, 10*speed, 0)) { + } else if (!lfhasflag(target, F_CASTINGSPELL) && skillcheck(target, SC_DODGE, 10*speed, 0)) { // then check if we dodge it... youhit = B_FALSE; } @@ -12692,6 +12859,8 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, if (firearm) { practice(thrower, SK_RANGED, 1); } + + missiledam += ((speed*2)+1); } } else { // ie. if !youhit if (!announcedmiss) { @@ -12724,10 +12893,10 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, sprintf(dambuf, "%s (%s by %s)",obname,throwverbpast, realthrowernamea); shatter(newob, youhit, dambuf, thrower); } else { - // object only gets damaged if it hit someone - if (youhit) { + // object only gets damaged if it hit someone/something + if (missiledam) { // don't announce damage to the thrown object - real_takedamage(newob, speed-1, DT_BASH, B_FALSE); + real_takedamage(newob, missiledam, DT_BASH, B_FALSE); } } @@ -13209,7 +13378,7 @@ void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf) { } else if (oid == OT_TRAPTELEPORT) { cell_t *newc; // move somewhere else! - newc = getrandomcelloftype(lf->cell->map, lf->cell->map->habitat->id); + newc = getrandomcelloftype(lf->cell->map, lf->cell->map->habitat->emptycelltype); if (newc) { teleportto(lf, newc, B_TRUE); } diff --git a/objects.h b/objects.h index c0a018d..8c526e3 100644 --- a/objects.h +++ b/objects.h @@ -16,6 +16,7 @@ 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); objecttype_t *addot(enum OBTYPE id, char *name, char *description, int material, float weight, int obclassid); +void adjustdamhardness(unsigned int *dam, enum DAMTYPE damtype, enum MATERIAL mat); void adjustdammaterial(unsigned int *dam, enum DAMTYPE damtype, enum MATERIAL mat); void adjustdamob(object_t *o, unsigned int *dam, enum DAMTYPE damtype); int adjustarmourpenalty(lifeform_t *lf, float amt); @@ -100,7 +101,7 @@ char *getobhurtname(object_t *o, enum DAMTYPE damtype); float getobweight(object_t *o); float getobunitweight(object_t *o); objecttype_t *getoppositestairs(objecttype_t *ot); -char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth); +char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth, int forcehabitat); char *getrandomob(map_t *map, char *buf); char *getrandomobwithdt(map_t *map, enum DAMTYPE damtype, char *buf); char *getrandomobwithclass(map_t *map, enum OBCLASS cid, char *buf, int depthmod); @@ -165,6 +166,7 @@ int istried(object_t *o); int istriedot(objecttype_t *ot); int isweapon(object_t *o); int iswearable(object_t *o); +void killallobs(obpile_t *op); void killmaterial(material_t *m); void killob(object_t *o); void killobpile(obpile_t *o); @@ -204,6 +206,7 @@ void setblessed(object_t *o, enum BLESSTYPE wantbless); int sethiddenname(objecttype_t *o, char *text); void setinscription(object_t *o, char *text); void setobcreatedby(object_t *o, lifeform_t *lf); +void setwaterdepth(cell_t *c, int depth); int shatter(object_t *o, int hitlf, char *damstring, lifeform_t *fromlf); void shufflehiddennames(void); object_t *splitob(object_t *o); diff --git a/save.c b/save.c index 11b7eea..caa5add 100644 --- a/save.c +++ b/save.c @@ -13,6 +13,7 @@ #include "nexus.h" #include "objects.h" #include "save.h" +#include "vault.h" extern long curtime; @@ -144,6 +145,7 @@ lifeform_t *loadlf(FILE *f, cell_t *where) { map_t *m; int x,y,level,newlevel; int db = B_TRUE; + int matid; if (db) dblog("--> Loading lifeform...\n"); @@ -191,6 +193,8 @@ lifeform_t *loadlf(FILE *f, cell_t *where) { buf[strlen(buf)-1] = '\0'; // strip newline l->lastdam = strdup(buf + 9); // after 'lastdam: ' + fscanf(f, "material: %d\n",&matid); + l->material = findmaterial(matid); fscanf(f, "timespent: %d\n",&l->timespent); fscanf(f, "sorted: %d\n",&l->sorted); @@ -318,6 +322,8 @@ map_t *loadmap(char *basefile) { celltype_t *ct; int celltypeid; long obid; + int temphab; + int vid; //if (db) dblog("cell %d,%d...",x,y); @@ -334,8 +340,14 @@ map_t *loadmap(char *basefile) { */ // cell info - fscanf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", - &c->roomid, &celltypeid, &c->known, &c->knownglyph.ch, &c->knownglyph.colour, &c->visited, &c->lit, &c->origlit, &c->littimer); + fscanf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + &c->roomid, &celltypeid, &c->known, &c->knownglyph.ch, &c->knownglyph.colour, &c->visited, &c->lit, &c->origlit, &c->littimer,&temphab,&vid); + c->habitat = findhabitat(temphab); + if (vid == -1) { + c->vault = NULL; + } else { + c->vault = findvaultbyid(vid); + } ct = findcelltype(celltypeid); @@ -655,6 +667,7 @@ int savelf(FILE *f, lifeform_t *l) { fprintf(f, "alive: %d\n",l->alive); fprintf(f, "lastdamtype: %d\n",l->lastdamtype); fprintf(f, "lastdam: %s\n",l->lastdam); + fprintf(f, "material: %d\n",l->material->id); fprintf(f, "timespent: %d\n",l->timespent); fprintf(f, "sorted: %d\n",l->sorted); fprintf(f, "polyrevert: %d\n",l->polyrevert); @@ -751,8 +764,8 @@ int savemap(map_t *m) { cell_t *c; c = getcellat(m, x, y); // cell info - fprintf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", - c->roomid, c->type->id, c->known, c->knownglyph.ch, c->knownglyph.colour, c->visited,c->lit,c->origlit,c->littimer); + fprintf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + c->roomid, c->type->id, c->known, c->knownglyph.ch, c->knownglyph.colour, c->visited,c->lit,c->origlit,c->littimer,c->habitat->id, c->vault ? c->vault->numid : -1); // cell objects for (o = c->obpile->first ; o ; o = o->next) { fprintf(f, "ob:%ld\n",o->id); diff --git a/spell.c b/spell.c index c2c0e89..b0405dd 100644 --- a/spell.c +++ b/spell.c @@ -1093,6 +1093,16 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef gainlevel(user); } } + } else if (abilid == OT_A_BLINDALL) { + lifeform_t *lf; + for (lf = user->cell->map->lf ; lf ; lf = lf->next) { + if (!isplayer(lf)) { + addflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL); + killflagsofid(lf->flags, F_WANTS); + killflagsofid(lf->flags, F_WANTSOBFLAG); + } + } + msg("all blinded!"); return B_FALSE; } else if (abilid == OT_A_DEBUG) { cell_t *where; @@ -1159,6 +1169,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef char dirch; char targetname[BUFLEN]; flag_t *f; + int heavyamt = 8; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You lack the stability for a heavy blow while swimming."); @@ -1166,8 +1177,8 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } wep = getweapon(user); - if (!wep || !ismeleeweapon(wep) || (getobunitweight(wep) < 8)) { // ie. 8 is weight of a mace - if (isplayer(user)) msg("You need a heavy weapon to perform a heavy blow!"); + if (!wep || !ismeleeweapon(wep) || (getobunitweight(wep) < heavyamt)) { // ie. 8 is weight of a mace + if (isplayer(user)) msg("You need a heavy weapon (%dkg or more) to perform a heavy blow!", heavyamt); return B_TRUE; } @@ -1207,113 +1218,120 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef flag_t *penalty = NULL; int failed = B_TRUE; - // ask for direction - if (!targcell) { - dirch = askchar("Steal in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE); - int dir; - dir = chartodir(dirch); - if (dir == D_NONE) { - if (isplayer(user)) msg("Cancelled."); - return B_TRUE ; - } else { - targcell = getcellindir(user->cell, dir); - } - } - - target = targcell->lf; - if (!target) { - if (isplayer(user)) msg("There is nobody there to steal from!"); - return B_TRUE; - } - - taketime(user, getactspeed(user)); - slev = getskill(user, SK_THIEVERY); if (slev == PR_INEPT) { if (isplayer(user)) msg("You are too unskilled to steal anything!"); return B_TRUE; } - getlfname(target, targetname); + // stealing shop items + if (isroom(user->cell) && hasobwithflagval(user->cell->obpile, F_SHOPITEM, NA, user->cell->roomid, NA, NULL)) { + // stealing from a shop + char yn; + yn = askchar("Steal something from this shop?", "yn","n", B_TRUE); + if (yn == 'y') { + object_t *o; + flag_t *f; + int value; - if (slev == PR_NOVICE) { - penalty = addflag(user->flags, F_ACCURACYMOD, -14, NA, NA, NULL); - } else if (slev == PR_BEGINNER) { - penalty = addflag(user->flags, F_ACCURACYMOD, -7, NA, NA, NULL); - } + taketime(user, getactspeed(user)); - // use empty handed attack accuracy - wep = getweapon(user); - if (rolltohit(user, target, wep, NULL)) { - object_t *o; - int i,nsteals; - // - if (slev >= PR_EXPERT) { - nsteals = 2; - } else { - nsteals = 1; - } - // what do we steal? - for (i = 0; i < nsteals; i++) { - sprintf(buf, "Steal what (%d of %d)?", i+1, nsteals); - initprompt(&prompt, buf); - addchoice(&prompt, '-', "Nothing", NULL, NULL); - for (o = target->pack->first ; o ; o = o->next) { - int ok = B_TRUE; - if ((slev < PR_SKILLED) && (getobunitweight(o) >= 3)) { - // too heavy to steal - ok = B_FALSE; - } else if ((slev < PR_MASTER) && isequipped(o)) { - // equipped - ok = B_FALSE; - } else if (!canpickup(user, o, 1)) { - // can't pick it up - ok = B_FALSE; - } - if (ok) { - getobname(o, buf, 1); - addchoice(&prompt, o->letter, buf, NULL, o); + value = 0; + for (o = user->cell->obpile->first ; o ; o = o->next) { + f = hasflagval(o->flags, F_SHOPITEM, NA, user->cell->roomid, NA, NULL); + if (f) { + value += f->val[0]; } } - if (prompt.nchoices > 1) { - if (slev >= PR_ADEPT) { - // pick what you want - getchoice(&prompt); - o = (object_t *)prompt.result; - } else { - // random - o = (object_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; - } - if (o) { - o = moveob(o, user->pack, 1); - if (o) { - getobname(o, buf, 1); - if (isplayer(user)) { - msg("You steal %s from %s!", buf, targetname); - } else if (cansee(player, user)) { - msg("%s steals %s from %s!", username, buf, targetname); - } - failed = B_FALSE; + // skillcheck - difficulty based on total value of objects here + // 15 + value/50 means: + // $50 is diff 16 + // $100 is diff 17 + // $200 is diff 19 + // $500 is diff 25 + // $1000 is diff 35 + if (skillcheck(user, SC_STEAL, 15+(value/50), 0)) { + // success + if (steal(user, user->cell->obpile, F_SHOPITEM)) { + if (isplayer(user)) { + msg("There doesn't seem to be anything here which you could steal."); } } } else { - break; + lifeform_t *shk; + msg("You try to steal something, but fail."); + // failed + shk = findshopkeeper(user->cell->map, user->cell->roomid); + if (shk) { // doesn't need to see you - he SENSES it! + say(shk, "THIEF!", SV_ROAR); + fightback(shk, user); + callguards(shk, user); + } } - } - } - - if (penalty) { - killflag(penalty); - } - - if (failed) { - if (isplayer(user)) { - msg("You try to steal from %s, but fail.", targetname); - } else if (cansee(player, user)) { - msg("%s tries to steal from %s, but fails.", username, targetname); + } } else { - practice(user, SK_THIEVERY, 1); + // stealing from a lifeform + // ask for direction + if (!targcell) { + dirch = askchar("Steal in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE); + int dir; + dir = chartodir(dirch); + if (dir == D_NONE) { + if (isplayer(user)) msg("Cancelled."); + return B_TRUE ; + } else { + targcell = getcellindir(user->cell, dir); + } + } + + target = targcell->lf; + if (!target) { + if (isplayer(user)) msg("There is nobody there to steal from!"); + return B_TRUE; + } + + taketime(user, getactspeed(user)); + + getlfname(target, targetname); + + if (slev == PR_NOVICE) { + penalty = addflag(user->flags, F_ACCURACYMOD, -14, NA, NA, NULL); + } else if (slev == PR_BEGINNER) { + penalty = addflag(user->flags, F_ACCURACYMOD, -7, NA, NA, NULL); + } + + // use empty handed attack accuracy + wep = getweapon(user); + if (rolltohit(user, target, wep, NULL)) { + // success! + failed = B_FALSE; + if (steal(user, target->pack, F_NONE)) { + if (isplayer(user)) { + msg("%s has nothing for you to steal!", targetname); + } + } + } else { + failed = B_TRUE; + } + + if (penalty) { + killflag(penalty); + } + + if (failed) { + if (isplayer(user)) { + msg("You try to steal from %s, but fail.", targetname); + } else if (cansee(player, user)) { + msg("%s tries to steal from %s, but fails.", username, targetname); + } + // ai will get angry! + if (cansee(target, user) && !isplayer(target)) { + fightback(target, user); + } + } else { + practice(user, SK_THIEVERY, 1); + } } } else if (abilid == OT_A_WARCRY) { // announce @@ -1749,8 +1767,10 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ targcell = caster->cell; target = caster; - f = addtempflag(caster->flags, F_MAGICARMOUR, power*2, NA, NA, "skin of bark", FROMSPELL); - f->obfrom = spellid; + //f = addtempflag(caster->flags, F_MAGICARMOUR, power*2, NA, NA, "skin of bark", FROMSPELL); + //f->obfrom = spellid; + + setlfmaterial(target, MT_WOOD); f = addtempflag(caster->flags, F_DTVULN, DT_FIRE, NA, NA, "2d4", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_BLADEBURN) { @@ -2473,7 +2493,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else { // random one - r = getreallyrandomrace(); + r = getreallyrandomrace(RC_ANY); } @@ -2497,16 +2517,50 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } return B_TRUE; } + } else if (spellid == OT_S_CLEARLEVEL) { + int x,y; + cell_t *c; + for (y = 0; y < caster->cell->map->h ; y++) { + for (x = 0; x < caster->cell->map->w ; x++) { + c = getcellat(caster->cell->map, x, y); + if (c) { + if (c->lf && isplayer(c->lf)) { + } else { + clearcell(c); + setcelltype(c, c->map->habitat->solidcelltype); + } + } + } + } + needredraw = B_TRUE; } else if (spellid == OT_S_CREATEVAULT) { vault_t *v; + int vx,vy; + int vw,vh; // ask for a vaulttype v = askvault("Create which vault?"); - if (createvault(caster->cell->map, caster->cell->map->nrooms, v, NULL, NULL)) { + if (createvault(caster->cell->map, caster->cell->map->nrooms, v, &vw, &vh, &vx, &vy)) { msg("Couldn't create a vault."); } else { - msg("BAM! A vault has appeared nearby."); + char ch; + msg("BAM! A vault has appeared nearby."); more(); needredraw = B_TRUE; + ch = askchar("Teleport to the new vault", "yn","y", B_TRUE); + if (ch == 'y') { + int x,y; + cell_t *c; + // find it + for (y = vy; y < vy+vh; y++) { + for (x = vy; x < vx + vw; x++) { + c = getcellat(caster->cell->map, x, y); + if (c && cellwalkable(caster, c, NULL)) { + teleportto(caster, c, B_TRUE); + return B_FALSE; + } + } + } + } } } else if (spellid == OT_S_CUREPOISON) { @@ -2653,6 +2707,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } ndigs++; + // go to next cell + c = getcellindir(c, dir); } // announce destruction of any walls seen @@ -2963,12 +3019,15 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ targcell = getcellat(caster->cell->map, x,y); if (targcell && (getcelldist(caster->cell, targcell) <= range)) { if (targcell->lf && (targcell->lf != caster) && haslof(caster->cell, targcell, B_FALSE, NULL)) { - char lfname[BUFLEN]; // automatic hit - getlfname(targcell->lf, lfname); + if (isplayer(targcell->lf)) { + msg("A blast of energy hits you!"); + } + /* if (haslos(caster, targcell)) { msg("A blast of energy hits %s.",lfname); } + */ losehp(targcell->lf, rnd(2,6), DT_MAGIC, caster, "an energy blast"); } } @@ -3085,8 +3144,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int depth; depth = DP_SHOULDERS + power; - amt = ((power+1) * (power+1)) - 1; + //amt = ((power+1) * (power+1)) - 1; //amt = power; + amt = power*2; badoid[0] = OT_WATERDEEP; badoid[1] = OT_NONE; for (i = 0; i < amt; i++) { @@ -4021,9 +4081,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ c = getcellat(m, x, y); if (c) { if (range == UNLIMITED) { - setcellknown(c, PR_ADEPT); + setcellknown(c, MAXOF(PR_ADEPT, getskill(caster, SK_CARTOGRAPHY))); } else if (getcelldist(caster->cell, c) <= range) { - setcellknown(c, PR_ADEPT); + setcellknown(c, MAXOF(PR_ADEPT, getskill(caster, SK_CARTOGRAPHY))); } } } @@ -4131,6 +4191,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // restore player pointer player = oldplayer; //showlfstats(where->lf, B_TRUE); + + needredraw = B_TRUE; + statdirty = B_TRUE; } else { failed = B_TRUE; } @@ -4580,7 +4643,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (!newmap) { // create new map newmap = addmap(); - createmap(newmap, newdepth, caster->cell->map->region, NULL, D_NONE); + createmap(newmap, newdepth, caster->cell->map->region, NULL, D_NONE, NULL); } @@ -6945,7 +7008,7 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { default: if (spellskill == PR_INEPT) { if (db) { - dblog("-->power = 0 (inept in SORCERY)"); + dblog("-->power = 0 (inept in sorcery)"); } return 0; } @@ -7096,9 +7159,6 @@ char *getvarpowerspelldesc(enum OBTYPE spellid, int power, char *buf) { // default strcpy(buf, ""); switch (spellid) { - case OT_S_BARKSKIN: - sprintf(buf, "+%d Armour Rating, Fire Vulnerability", power*2); - break; case OT_S_PSYARMOUR: sprintf(buf, "+%d Armour Rating", power*4); break; @@ -7281,7 +7341,9 @@ void stopspell(lifeform_t *caster, enum OBTYPE spellid) { killob(o); } } - if (spellid == OT_S_FLOATINGDISC) { + if ((spellid == OT_S_BARKSKIN) && (getlfmaterial(caster) == MT_WOOD)) { + setlfmaterial(caster, caster->race->material->id); + } else if (spellid == OT_S_FLOATINGDISC) { map_t *m; lifeform_t *lf; for (m = firstmap ; m ; m = m->next) { diff --git a/vaults/crosshatch.vlt b/vaults/crosshatch.vlt new file mode 100644 index 0000000..213c0bc --- /dev/null +++ b/vaults/crosshatch.vlt @@ -0,0 +1,25 @@ +@id:crosshatch +@map +########### +##.#.#.#.## +#.#.#.#.#.# +##.#.#.#.## +#.#.#.#.#.# +##.#.#.#.## +#.#.#.#.#.# +##.#.#.#.## +#.#.#.#.#.# +##.#.#.#.## +########### +@end +@legend +#:cell:rock wall +@end + +@flags +! autoscale, lock xy +goesin:dungeon +autodoors:50 +autopop +@end + diff --git a/vaults/diagcross.vlt b/vaults/diagcross.vlt new file mode 100644 index 0000000..0c2c3b0 --- /dev/null +++ b/vaults/diagcross.vlt @@ -0,0 +1,24 @@ +@id:diagonal_cross +@map +#X.#####.X# +##..###..## +###..#..### +####...#### +####...#### +####.#.#### +###.###.### +##..###..## +#X.#####.X# +@end + +@legend +#:cell:rock wall +X:exit +@end + +@flags +! autoscale, lock xy +goesin:dungeon +mayrotate +@end + diff --git a/vaults/jimbo.vlt b/vaults/jimbo.vlt new file mode 100644 index 0000000..1059b15 --- /dev/null +++ b/vaults/jimbo.vlt @@ -0,0 +1,31 @@ +! Boss room for Jimbo the dungeonkeeper +@id:jimbos_lair + +@map +############ +#........|,# +#....-/-.### ++......@.|,# +#..-/-...### +#........|,# +############ +@end + +@legend +#:cell:rock wall +|:ob:locked iron gate +,:ob:1-4 bones:50 ++:ob:wooden door +/:ob:wooden table +-:ob:wooden footstool +@:mon:Jimbo +@end + +@flags +goesin:dungeon +norandom +scatter(1,1,-2,-2) ob:wooden footstool:0-3 +scatter(1,1,-2,-2) ob:random food:0-2 +mayrotate +@end + diff --git a/vaults/labyrinth.vlt b/vaults/labyrinth.vlt index f6ef51d..00274b1 100644 --- a/vaults/labyrinth.vlt +++ b/vaults/labyrinth.vlt @@ -36,7 +36,7 @@ scatter(0,0,-1,-1) ob:gnoll corpse:1-3:33 ! scattered weapons scatter(0,0,-1,-1) ob:common weapon:4-5 ! TODO: scattered minions around ?? -! mayrotate +mayrotate ! mayscale @end diff --git a/vaults/riverroom.vlt b/vaults/riverroom.vlt new file mode 100644 index 0000000..c4da369 --- /dev/null +++ b/vaults/riverroom.vlt @@ -0,0 +1,24 @@ +! a room split in half by a river +@id:river_room +@map +########## +#...~~...# +#...~~...# +#...~~...# +#...~~...# +#...~~...# +########## +@end + +@legend +#:cell:rock wall +~:cell:low rock floor +~:ob:very deep water +@end + +@flags +goesin:dungeon +autodoors:50 +mayrotate +@end + diff --git a/vaults/shop_armour.vlt b/vaults/shop_armour.vlt new file mode 100644 index 0000000..4f140e8 --- /dev/null +++ b/vaults/shop_armour.vlt @@ -0,0 +1,29 @@ +@id:armour_shop +@map +.##### +.#ppp# +s+ppp# +.#ppp# +.##### +@end + +@legend +.:cell:dirt +#:cell:wooden wall +p:cell:shop floor +p:ob:armour ++:cell:dirt ++:ob:wooden door +s:ob:sign "Armour shop" +@end + +@flags +shop +! because this is the first lf in a shop, it will +! become the shopkeeper. +scatter(2,1,-2,-2) mon:humanoid:1 +norandom +mayrotate +margin:5,5 +@end + diff --git a/vaults/shop_potion.vlt b/vaults/shop_potion.vlt new file mode 100644 index 0000000..43d1fb6 --- /dev/null +++ b/vaults/shop_potion.vlt @@ -0,0 +1,29 @@ +@id:potion_shop +@map +.##### +.#ppp# +s+ppp# +.#ppp# +.##### +@end + +@legend +.:cell:dirt +#:cell:wooden wall +p:cell:shop floor +p:ob:potion ++:cell:dirt ++:ob:wooden door +s:ob:sign "Potion shop" +@end + +@flags +shop +! because this is the first lf in a shop, it will +! become the shopkeeper. +scatter(2,1,-2,-2) mon:humanoid:1 +norandom +mayrotate +margin:5,5 +@end + diff --git a/vaults/shop_weapon.vlt b/vaults/shop_weapon.vlt new file mode 100644 index 0000000..9c7dc5f --- /dev/null +++ b/vaults/shop_weapon.vlt @@ -0,0 +1,29 @@ +@id:weapon_shop +@map +.##### +.#ppp# +s+ppp# +.#ppp# +.##### +@end + +@legend +.:cell:dirt +#:cell:wooden wall +p:cell:shop floor +p:ob:weapon ++:cell:dirt ++:ob:wooden door +s:ob:sign "Weapon shop" +@end + +@flags +shop +! because this is the first lf in a shop, it will +! become the shopkeeper. +scatter(2,1,-2,-2) mon:humanoid:1 +norandom +mayrotate +margin:5,5 +@end + diff --git a/vaults/uturn.vlt b/vaults/uturn.vlt new file mode 100644 index 0000000..6b3e20e --- /dev/null +++ b/vaults/uturn.vlt @@ -0,0 +1,19 @@ +@id:uturn +@map +########### +###.....### +##..###..## +#X.#####.X# +@end + +@legend +#:cell:rock wall +X:exit +@end + +@flags +! autoscale +goesin:dungeon +mayrotate +@end + diff --git a/vaults/vault.vlt b/vaults/vault.vlt index efb7adb..60e6018 100644 --- a/vaults/vault.vlt +++ b/vaults/vault.vlt @@ -16,6 +16,7 @@ $:ob:25-200 gold @flags goesin:dungeon +mayrotate ! no auto doors. ie this can be in the middle of nowhere. @end