diff --git a/dat.h b/dat.h --- a/dat.h +++ b/dat.h @@ -8,6 +8,7 @@ typedef struct Tile Tile; typedef struct Portal Portal; typedef struct Spawn Spawn; typedef struct Level Level; +typedef struct World World; typedef struct MonsterData MonsterData; typedef struct Monster Monster; @@ -18,6 +19,17 @@ enum SZNAME = 64, }; +/* directions. ordering matters to drunk1() */ +enum +{ + WEST = 0, + SOUTH, + EAST, + NORTH, + NCARDINAL, + NODIR, +}; + /* hack9.c */ extern int debug; extern int farmsg; @@ -60,6 +72,7 @@ enum IGLOVES, IBOOTS, NEQUIP, + ICONSUME, /* item flags */ IFSTACKS = 0x1, @@ -79,9 +92,9 @@ struct ItemData /* weight */ int weight; - /* if weapon, these are set */ + /* if weapon/heal, these are set */ int rolls; - int atk; + int rcount; /* if armor, this is set */ int ac; @@ -150,6 +163,8 @@ struct Tileset /* tile.c */ Tileset *opentile(char *name, int width, int height); void freetile(Tileset *t); +#pragma varargck type "T" Tile* +int tilefmt(Fmt *f); /* draw i'th tile onto dst */ void drawtile(Tileset *t, Image *dst, Point p, int i); @@ -270,12 +285,19 @@ int xpcalc(int level); enum { /* features */ - TWALL = 840, + TVWALL = 830, + THWALL = 831, + TTLCORNER = 832, + TTRCORNER = 833, + TBLCORNER = 834, + TBRCORNER = 835, + TSQUARE = 841, TTREE = 847, TFLOOR = 848, TUPSTAIR = 851, TDOWNSTAIR = 852, TGRAVE = 856, + TFOUNTAIN = 859, TWATER = 860, TICE = 861, TLAVA = 862, @@ -285,7 +307,6 @@ enum struct Portal { - char name[32]; /* where it takes us to */ Level *to; /* location in to */ @@ -330,8 +351,6 @@ struct Tile /* index into tileset */ int terrain; - - int flags; }; enum @@ -343,16 +362,23 @@ enum Fhasobject = 0x8, Fhasitem = 0x10, Fportal = 0x20, + + Fmask = 0x3f, }; struct Level { + char name[32]; int width; int height; Tile *tiles; - int *flags; + u32int *flags; + /* rectangle of world used for most arith, inset by 1 */ Rectangle r; + + /* rectangle used for ptinrect, Dx/Dy */ + Rectangle or; Point up; Point down; @@ -360,26 +386,58 @@ struct Level int nspawns; Spawn spawns[10]; + + /* one possible exit on each side */ + Point exits[NCARDINAL]; }; /* level.c */ +void lclear(Level *l, Point p, int dist); +void lclearrect(Level *l, Rectangle r); +void lfeature(Level *l, Point p, int type); +void lfillrect(Level *l, Rectangle r, int type); +void lborder(Level *l, Rectangle r, int thickness, int type); +void llink(Level *a, int adir, Level *b, int bdir); Level *genlevel(int width, int height, int type); void freelevel(Level *l); void addspawn(Level *l, Point p, char *what, int freq, int istrap); +void mkportal(Level *l, Point p, int feat, Level *to, Point tp); +void mkroom(Level *l, Rectangle r, int wbase); Tile *tileat(Level *l, Point p); -#define flagat(l, p) (*(l->flags+(p.y*l->width)+p.x)) +#define p2idx(l, p) ((p.y*l->width+p.x)) +#define flagat(l, p) (*(l->flags+p2idx(l, p))) #define hasflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) & (F)) #define setflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) |= (F)) #define clrflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) &= ~(F)) /* levelgen.c */ -Level *mklevel(int width, int height, int floor); +Level *mklevel(int width, int height, int floor, char *name); void several(Level *l, Point *p, int count, char *type, int r); int mkldebug(Level *l); -int mklforest(Level *l); -int mklgraveyard(Level *l); -int mklvolcano(Level *l); +Level *mklforest(void); +Level *mklgraveyard(void); +Level *mklvolcano(void); int mklcastle(Level *l); +Level *mkltown(void); + +struct World { + Level *town; + Level *levels[32]; + int nlevels; +}; + +/* world.c */ +World *mkworld(void); + +enum { + SATTACK = 0, + SDEATH, + NSND, +}; + +/* sound.c */ +int soundinit(void); +void playsound(ulong, int); enum { /* cost to move N S E W */ @@ -401,17 +459,8 @@ void uiexec(AIState *ai); void uiredraw(int justui); /* util.c */ -enum -{ - WEST, - SOUTH, - NORTH, - EAST, - NCARDINAL, - NODIR, -}; - extern Point cardinals[]; +Point pickpoint(Rectangle r, int dir); int roll(int count, int sides); int parseroll(char *str, int *count, int *sides); int min(int a, int b); diff --git a/hack9.c b/hack9.c --- a/hack9.c +++ b/hack9.c @@ -6,6 +6,8 @@ #include "dat.h" #include "alg.h" +int mainstacksize = 4*1024; + enum { LSIZE = 20, @@ -75,6 +77,7 @@ movemons(void) if(hasflagat(l, p, Fhasmonster)){ m = tileat(l, p)->monst; /* i don't think this is supposed to happen. */ + assert(m != nil); if(m == nil) continue; incref(&m->ref); @@ -100,28 +103,41 @@ movemons(void) static void usage(void) { - fprint(2, "usage: %s [-d]", argv0); + fprint(2, "usage: %s [-d] [-S seed]\n", argv0); threadexitsall("usage"); } void threadmain(int argc, char *argv[]) { + long seed; Tile *t; Level *level; + World *world; + + seed = -1; ARGBEGIN{ case 'd': debug++; + seed = 0; + break; + case 'S': + seed = atoi(EARGF(usage())); break; default: usage(); }ARGEND - //#include - //mainmem->flags = POOL_PARANOIA|POOL_ANTAGONISM; +/* + #include + mainmem->flags = POOL_PARANOIA|POOL_ANTAGONISM; +/**/ - srand(truerand()); + if(seed != 0) + seed = truerand(); + fprint(2, "seed %ld\n", seed); + srand(seed); user = getenv("user"); home = getenv("home"); @@ -136,9 +152,14 @@ threadmain(int argc, char *argv[]) if(!monstdbopen("monster.ndb")) sysfatal("monstdbopen: %r"); - /* initial level */ - if((level = genlevel(nrand(LSIZE)+LSIZE, nrand(LSIZE)+LSIZE, debug?0:nrand(3)+1)) == nil) - sysfatal("genlevel: %r"); + if(soundinit() < 0) + sysfatal("soundinit: %r"); + + world = mkworld(); + if(world == nil) + sysfatal("mkworld: %r"); + + level = world->levels[0]; /* the player */ player = mbyname("wizard"); @@ -149,7 +170,7 @@ threadmain(int argc, char *argv[]) incref(&player->ref); player->l = level; - player->pt = level->up; + player->pt = (Point){Dx(level->r)/2, Dy(level->r)/2}; maddinv(player, ibyname("dagger")); diff --git a/item.c b/item.c --- a/item.c +++ b/item.c @@ -38,6 +38,7 @@ static char *itemtype[] = { [IARMOR] "armor", [IGLOVES] "gloves", [IBOOTS] "boots", +[ICONSUME] "consumable", }; char* diff --git a/item.ndb b/item.ndb --- a/item.ndb +++ b/item.ndb @@ -23,6 +23,11 @@ item="old gloves" item="walking shoes" tile=536 type=boots cost=5 weight=1 ac=1 +# consumables +item="banana" + tile=648 type=consumable cost=2 weight=1 + heal=2d2 stacks + # item lists itemlist="common" items="dagger" @@ -30,6 +35,7 @@ itemlist="common" items="tattered cape" items="old gloves" items="walking shoes" + items="food" itemlist="uncommon" items="longsword" items="shield" @@ -37,3 +43,5 @@ itemlist="rare" items="plate armor" items="runesword" +itemlist="food" + items="banana" diff --git a/level.c b/level.c --- a/level.c +++ b/level.c @@ -6,6 +6,126 @@ #include "dat.h" #include "alg.h" +/* removes all monsters/features near p, except p itself */ +void +lclear(Level *l, Point p, int dist) +{ + Rectangle r; + Point p2; + Tile *t; + + r = Rect(p.x-dist, p.y-dist, p.x+dist, p.y+dist); + rectclip(&r, l->r); + + for(p2.x = r.min.x; p2.x <= r.max.x; p2.x++){ + for(p2.y = r.min.y; p2.y <= r.max.y; p2.y++){ + if(eqpt(p, p2)) + continue; + t = tileat(l, p2); + t->unit = t->feat = 0; + if(t->monst){ + mfree(t->monst); + t->monst = nil; + } + flagat(l, p2) = 0; + } + } +} + +void +lclearrect(Level *l, Rectangle r) +{ + Point p; + Tile *t; + + rectclip(&r, l->r); + + for(p.x = r.min.x; p.x <= r.max.x; p.x++){ + for(p.y = r.min.y; p.y <= r.max.y; p.y++){ + t = tileat(l, p); + t->unit = t->feat = 0; + if(t->monst != nil){ + mfree(t->monst); + t->monst = nil; + } + if(t->portal != nil){ + free(t->portal); + t->portal = nil; + } + flagat(l, p) = 0; + } + } +} + +void +lfeature(Level *l, Point p, int type) +{ + Tile *t; + + t = tileat(l, p); + t->feat = type; + flagat(l, p) = (Fblocked|Fhasfeature); +} + +void +lfillrect(Level *l, Rectangle r, int type) +{ + Point p; + + rectclip(&r, l->r); + + for(p.x = r.min.x; p.x <= r.max.x; p.x++) + for(p.y = r.min.y; p.y <= r.max.y; p.y++) + lfeature(l, p, type); +} + +void +lborder(Level *l, Rectangle r, int i, int type) +{ + Rectangle tmp; + + assert(i != 0); + + if(i < 0){ + r = insetrect(r, i); + i = -i; + } + + i -= 1; + + /* top bar */ + tmp = Rect(r.min.x, r.min.y, r.max.x, r.min.y+i); + lclearrect(l, tmp); + lfillrect(l, tmp, type); + + /* bottom bar */ + tmp = Rect(r.min.x, r.max.y-i, r.max.x, r.max.y); + lclearrect(l, tmp); + lfillrect(l, tmp, type); + + /* left bar */ + tmp = Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i-1); + lclearrect(l, tmp); + lfillrect(l, tmp, type); + + /* right bar */ + tmp = Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i-1); + lclearrect(l, tmp); + lfillrect(l, tmp, type); +} + +void +llink(Level *a, int adir, Level *b, int bdir) +{ + Point ap, bp; + + ap = a->exits[adir]; + bp = b->exits[bdir]; + + mkportal(a, ap, TSQUARE, b, bp); + mkportal(b, bp, TSQUARE, a, ap); +} + void addspawn(Level *l, Point p, char *what, int freq, int istrap) { @@ -23,16 +143,64 @@ addspawn(Level *l, Point p, char *what, } } -static void -mkportal(Level *l, Point p, int feat, char *name) +void +mkportal(Level *l, Point p, int feat, Level *to, Point tp) { Tile *t; - setflagat(l, p, Fhasfeature|Fportal); + flagat(l, p) = (Fhasfeature|Fportal); t = tileat(l, p); t->feat = feat; t->portal = emalloc(sizeof(Portal)); - snprint(t->portal->name, sizeof(t->portal->name), "%s", name); + t->portal->to = to; + t->portal->pt = tp; +} + +/* wbase is the base wall tile, the vertical wall, + * followed by horizontal, and tl, tr, bl, br corners. + */ +void +mkroom(Level *l, Rectangle r, int wbase) +{ + Rectangle tmp; + Point p; + Tile *t; + + /* walls */ + + /* top bar */ + tmp = Rect(r.min.x, r.min.y, r.max.x, r.min.y); + lclearrect(l, tmp); + lfillrect(l, tmp, wbase+1); + + /* bottom bar */ + tmp = Rect(r.min.x, r.max.y, r.max.x, r.max.y); + lclearrect(l, tmp); + lfillrect(l, tmp, wbase+1); + + /* left bar */ + tmp = Rect(r.min.x, r.min.y, r.min.x, r.max.y); + lclearrect(l, tmp); + lfillrect(l, tmp, wbase); + + /* right bar */ + tmp = Rect(r.max.x, r.min.y, r.max.x, r.max.y); + lclearrect(l, tmp); + lfillrect(l, tmp, wbase); + + /* corners */ + + /* tl, tr, bl, br */ + lfeature(l, (Point){r.min.x, r.min.y}, wbase+2); + lfeature(l, (Point){r.max.x, r.min.y}, wbase+3); + lfeature(l, (Point){r.min.x, r.max.y}, wbase+4); + lfeature(l, (Point){r.max.x, r.max.y}, wbase+4); + + /* doorway */ + p = pickpoint(r, nrand(NCARDINAL)); + t = tileat(l, p); + t->feat = 0; + flagat(l, p) = 0; } static void @@ -51,8 +219,8 @@ lrandstairs(Level *l) l->up = pup; l->down = pdown; - mkportal(l, pup, TUPSTAIR, "starcase upstairs"); - mkportal(l, pdown, TDOWNSTAIR, "staircase downstairs"); + mkportal(l, pup, TUPSTAIR, nil, ZP); + mkportal(l, pdown, TDOWNSTAIR, nil, ZP); } static int @@ -62,16 +230,20 @@ gen(Level *l, int type) switch(type){ case 0: - mkldebug(l); + //mkldebug(l); + mklcastle(l); break; case 1: - mklforest(l); + //mklforest(l); + sysfatal("fuck"); break; case 2: - mklgraveyard(l); + sysfatal("fuck"); + //mklgraveyard(l); break; case 3: - mklvolcano(l); + //mklvolcano(l); + sysfatal("fuck"); break; } @@ -89,7 +261,7 @@ genlevel(int width, int height, int type if(l != nil) freelevel(l); - l = mklevel(width, height, TFLOOR); + l = mklevel(width, height, TFLOOR, "somewhere"); } while(gen(l, type) < 0); return l; @@ -128,4 +300,3 @@ tileat(Level *l, Point p) { return l->tiles+(p.y*l->width)+p.x; } - diff --git a/levelgen.c b/levelgen.c --- a/levelgen.c +++ b/levelgen.c @@ -6,123 +6,50 @@ #include "dat.h" #include "alg.h" -/* removes all monsters/features near p, except p itself */ +enum { + DRUNKRETRY = 100, +}; + +/* more tpct makes it turn more. */ static void -lclear(Level *l, Point p, int dist) -{ - Rectangle r; - Point p2; - Tile *t; - - r = Rect(p.x-dist, p.y-dist, p.x+dist, p.y+dist); - rectclip(&r, l->r); - - for(p2.x = r.min.x; p2.x <= r.max.x; p2.x++){ - for(p2.y = r.min.y; p2.y <= r.max.y; p2.y++){ - if(eqpt(p, p2)) - continue; - t = tileat(l, p2); - t->unit = t->feat = 0; - if(t->monst){ - mfree(t->monst); - t->monst = nil; - } - flagat(l, p2) = 0; - } - } -} - -static void -lclearrect(Level *l, Rectangle r) -{ - Point p; - Tile *t; - - rectclip(&r, l->r); - - for(p.x = r.min.x; p.x <= r.max.x; p.x++){ - for(p.y = r.min.y; p.y <= r.max.y; p.y++){ - t = tileat(l, p); - t->unit = t->feat = 0; - if(t->monst != nil){ - mfree(t->monst); - t->monst = nil; - } - if(t->portal != nil){ - free(t->portal); - t->portal = nil; - } - flagat(l, p) = 0; - } - } -} - -static void -lfillrect(Level *l, Rectangle r, int type) -{ - Point p; - Tile *t; - - for(p.x = r.min.x; p.x <= r.max.x; p.x++){ - for(p.y = r.min.y; p.y <= r.max.y; p.y++){ - t = tileat(l, p); - t->feat = type; - flagat(l, p) = (Fblocked|Fhasfeature); - } - } -} - -static void -lborder(Level *l, Rectangle r, int i, int type) -{ - Rectangle tmp; - - assert(i != 0); - - if(i < 0){ - r = insetrect(r, i); - i = -i; - } - - i -= 1; - - tmp = Rect(r.min.x, r.min.y, r.max.x, r.min.y+i); - lclearrect(l, tmp); - lfillrect(l, tmp, type); - - tmp = Rect(r.min.x, r.max.y-i, r.max.x, r.max.y); - lclearrect(l, tmp); - lfillrect(l, tmp, type); - - tmp = Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i); - lclearrect(l, tmp); - lfillrect(l, tmp, type); - - tmp = Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i); - lclearrect(l, tmp); - lfillrect(l, tmp, type); -} - -/* more sbias makes it straighter. */ -static void -drunk1(Level *l, Point p, uint count, uint sbias) +drunk1(Level *l, Point p, uint count, uint tpct) { int cur, last; + ulong retry; Point next; Rectangle clipr; Tile *t; - clipr = insetrect(l->r, 1); + assert(tpct <= 100); + retry = 0; + clipr = l->r; + clipr.min.x++; + clipr.min.y++; + + /* pick initial direction */ last = nrand(NCARDINAL); - while(count > 0){ - if(nrand(sbias+1) == 0) - cur = nrand(NCARDINAL); - else + + fprint(2, "drunk1 %P", p); + + while(count > 0 && retry++ < DRUNKRETRY){ + /* turn some percent of the time */ + if(nrand(100+1) <= tpct){ +turn: + if(nrand(2) == 0) + cur = (last+1) % NCARDINAL; + else + cur = (last+3) % NCARDINAL; + } else cur = last; next = addpt(p, cardinals[cur]); - if(!ptinrect(next, clipr)) + fprint(2, " %P", next); + if(!ptinrect(next, clipr)){ + fprint(2, "!!!"); + /* force turn */ + goto turn; continue; + } last = cur; p = next; if(!eqpt(p, l->up) && !eqpt(p, l->down) && hasflagat(l, p, Fblocked|Fhasfeature)){ @@ -132,6 +59,11 @@ drunk1(Level *l, Point p, uint count, ui count--; } } + + fprint(2, "\n"); + + if(retry >= DRUNKRETRY) + fprint(2, "drunk1 failed after %lud retries\n", retry); } /* @@ -141,40 +73,49 @@ drunk1(Level *l, Point p, uint count, ui * reachable from the upstairs. */ static int -drunken(Level *l, int type, int howmuch, int s1, int s2) +drunken(Level *l, int type, int howmuch, int tpct) { - int cnt, npath, redos; - Point p, *path; - Tile *t; + int i, cnt, npath, redos; + Point p, tp, *path; - cnt = ((l->width * l->height) / howmuch); //* 2; + //cnt = ((l->width * l->height) / howmuch); //* 2; + cnt = ((l->width*l->height) * howmuch) / 100; redos = 0; redo: - if(redos++ > 10) - return 0; + fprint(2, "drunken: %R redos %d cnt %d\n", l->r, redos, cnt); + if(redos++ >= 50) + return cnt; /* fill */ - for(p.x = 0; p.x < l->width; p.x++){ - for(p.y = 0; p.y < l->height; p.y++){ - if(!hasflagat(l, p, Fblocked|Fhasfeature)){ - t = tileat(l, p); - t->feat = type; - flagat(l, p) = (Fblocked|Fhasfeature); - } - } + for(p.x = 0; p.x < l->width; p.x++) + for(p.y = 0; p.y < l->height; p.y++) + if(!hasflagat(l, p, Fblocked|Fhasfeature)) + lfeature(l, p, type); + + for(i = 0; i < NCARDINAL; i++){ + p = addpt(l->exits[i], cardinals[(i+2)%NCARDINAL]); + lclearrect(l, Rpt(p, p)); + drunk1(l, p, (cnt/4)+2, tpct); } - drunk1(l, l->up, cnt/2, s1); - drunk1(l, l->down, cnt/2, s2); + //return cnt; + +// drunk1(l, l->up, cnt/2, s1); +// drunk1(l, l->down, cnt/2, s2); /* check reachability */ - npath = pathfind(l, l->up, l->down, &path, Fblocked); - if(npath < 0) - goto redo; - else - free(path); + for(i = 0; i < NCARDINAL-1; i++){ + p = addpt(l->exits[i], cardinals[(i+2)%NCARDINAL]); + tp = addpt(l->exits[i+1], cardinals[(i+3)%NCARDINAL]); + fprint(2, "drunken: pathfind: %d %P → %P\n", i, p, tp); + npath = pathfind(l, p, tp, &path, Fblocked); + if(npath < 0) + goto redo; + else + free(path); + } - return cnt * 2; + return cnt; } /* create a monster in the idle state */ @@ -308,8 +249,9 @@ levelexec(AIState *a) } Level* -mklevel(int width, int height, int floor) +mklevel(int width, int height, int floor, char *name) { + int i; Point p; Level *l; @@ -317,15 +259,18 @@ mklevel(int width, int height, int floor if(l == nil) return nil; + snprint(l->name, sizeof(l->name), "%s", name); + l->width = width; l->height = height; - l->r = Rect(0, 0, width, height); + l->r = Rect(0, 0, width-1, height-1); + l->or = Rect(0, 0, width, height); l->tiles = mallocz(sizeof(Tile) * width * height, 1); if(l->tiles == nil) goto err0; - l->flags = mallocz(sizeof(int) * width * height, 1); + l->flags = mallocz(sizeof(*l->flags) * width * height, 1); if(l->flags == nil) goto err1; @@ -339,6 +284,12 @@ mklevel(int width, int height, int floor } } + /* place exits */ + for(i = 0; i < NCARDINAL; i++){ + p = pickpoint(l->r, i); + l->exits[i] = p; + } + return l; err2: @@ -355,7 +306,7 @@ mkldebug(Level *l) { Tile *t; - drunken(l, TTREE, 3, 3, 3); + drunken(l, TTREE, 40, 20); lclear(l, l->up, 1); lclear(l, l->down, 1); several(l, &l->down, 1, "lich", 1); @@ -366,25 +317,26 @@ mkldebug(Level *l) return 0; } -int -mklforest(Level *l) +Level* +mklforest(void) { int space; Point p, p2; Rectangle in; + Level *l; + + l = mklevel(20+nrand(10), 20+nrand(10), TFLOOR, "a forest"); in = insetrect(l->r, 3); - space = drunken(l, TTREE, 2, 0, 0); - lclear(l, l->up, 1); - lclear(l, l->down, 1); + space = drunken(l, TTREE, 80, 50); do { p = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; } while(flagat(l, p) != 0); do { p2 = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; - } while(flagat(l, p2) != 0 || manhattan(p, p2) < ORTHOCOST*10 || manhattan(p, p2) > ORTHOCOST*20); + } while(flagat(l, p2) != 0 || manhattan(p, p2) < ORTHOCOST*20 || manhattan(p, p2) > ORTHOCOST*40); lclear(l, p, 2); several(l, &p, 1, "captain", 0); @@ -406,28 +358,34 @@ mklforest(Level *l) genmonsters(l, "gnome lord", space/128); genmonsters(l, "sergeant", space/128); - return 0; + return l; } -int -mklgraveyard(Level *l) +Level* +mklgraveyard(void) { int space; Point p, p2; Rectangle in; + Level *l; + + l = mklevel(25, 15, TFLOOR, "the graveyard"); in = insetrect(l->r, 3); + space = drunken(l, TGRAVE, 30, 10); + if(space == 0) + sysfatal("drunken: %r"); - space = drunken(l, TGRAVE, 2, 4, 4); - lclear(l, l->up, 2); - lclear(l, l->down, 2); + //lclear(l, l->up, 2); + //lclear(l, l->down, 2); do { - p = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; - } while(flagat(l, p) != 0); - do { + do { + p = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; + } while(flagat(l, p) != 0); + p2 = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; - } while(flagat(l, p2) != 0 || manhattan(p, p2) < ORTHOCOST*15 || manhattan(p, p2) > ORTHOCOST*20); + } while(flagat(l, p2) != 0 || manhattan(p, p2) < ORTHOCOST*7 || manhattan(p, p2) > ORTHOCOST*30); lclear(l, p, 2); lclear(l, p2, 2); @@ -448,19 +406,27 @@ mklgraveyard(Level *l) genmonsters(l, "ghost", space/64); genmonsters(l, "gnome", space/64); - return 0; + for(space = 0; space < NCARDINAL; space++){ + p = l->exits[space]; + tileat(l, p)->feat = TPORTAL; + } + + return l; } -int -mklvolcano(Level *l) +Level* +mklvolcano(void) { int space; Point p, p2; Rectangle in; + Level *l; + + l = mklevel(25, 15, TFLOOR, "the graveyard"); in = insetrect(l->r, 3); - space = drunken(l, TLAVA, 2, 1, 1); + space = drunken(l, TLAVA, 50, 50); lclear(l, l->up, 2); lclear(l, l->down, 2); @@ -474,12 +440,13 @@ mklvolcano(Level *l) lclear(l, p, 2); lclear(l, p2, 2); - addspawn(l, p, "large cat", 77, 0); + addspawn(l, p, "large cat", 77, 1); addspawn(l, p2, "gnome lord", 27, 2); genmonsters(l, "large cat", space/128); genmonsters(l, "gnome lord", space/64); - return 0; + + return l; } int @@ -498,7 +465,7 @@ mklcastle(Level *l) mid.min.y += n; mid.max.y -= n; - lborder(l, mid, 1, TWALL); + mkroom(l, mid, TVWALL); lborder(l, mid, -1, TWATER); /* create path in */ @@ -515,3 +482,31 @@ mklcastle(Level *l) return 0; } +Level* +mkltown(void) +{ + int bord; + Point p; + Rectangle r; + Level *l; + + bord = 1; + p = (Point){10, 15}; + l = mklevel(p.x, p.y, TFLOOR, "town"); + + lborder(l, l->r, bord, TTREE); + lfeature(l, (Point){bord,bord}, TFOUNTAIN); + lfeature(l, (Point){bord,p.y-2}, TFOUNTAIN); + lfeature(l, (Point){p.x-2,bord}, TFOUNTAIN); + lfeature(l, (Point){p.x-2,p.y-2}, TFOUNTAIN); + lfeature(l, (Point){Dx(l->r)/2-1, Dy(l->r)/2}, TFOUNTAIN); + + //mkportal(l, (Point){10, 0}, TSQUARE, nil, ZP); + + r = Rect(3, 3, 6, 6); + mkroom(l, r, TVWALL); + r = Rect(3, 9, 6, 12); + mkroom(l, r, TVWALL); + + return l; +} diff --git a/mk.common b/mk.common --- a/mk.common +++ b/mk.common @@ -13,7 +13,8 @@ OFILES=\ levelgen.$O\ monst.$O\ path.$O\ + sound.$O\ tile.$O\ ui.$O\ util.$O\ - + world.$O\ diff --git a/mkfile b/mkfile --- a/mkfile +++ b/mkfile @@ -18,3 +18,9 @@ uninstall:V: $O.alg: algtest.$O alg.$O $LD -o $target $prereq + +$O.leveltest: item.$O monst.$O level.$O levelgen.$O leveltest.$O + $LD -o $target $prereq + +test:V: $O.leveltest + $O.leveltest diff --git a/monst.c b/monst.c --- a/monst.c +++ b/monst.c @@ -352,6 +352,14 @@ mattack(Monster *m, Monster *mt) mt->hp -= dmg; + /* attack sounds */ + if(nearyou(m->pt)){ + if(mt->hp <= 0) + playsound(SDEATH, 0); + else + playsound(SATTACK, 0); + } + if(mt->hp <= 0){ /* poor guy is dead. */ mt->hp = 0; @@ -394,6 +402,25 @@ mattack(Monster *m, Monster *mt) return 0; } +static char* +portalname(int tile) +{ + switch(tile){ + case TSQUARE: + return "walkway"; + case TUPSTAIR: + return "staircase going up"; + case TDOWNSTAIR: + return "staircase going down"; + case TPORTAL: + return "magic portal"; + default: + sysfatal("no portal name for tile %d\n", tile); + } + + return nil; +} + /* -1 bad move, 0 didn't move, 1 moved */ int maction(Monster *m, int what, Point where) @@ -402,7 +429,11 @@ maction(Monster *m, int what, Point wher Tile *cur, *targ; Item *i; - if(!ptinrect(where, m->l->r)) + if(isyou(m)){ + warn("move to %P %T %06ub\n", where, tileat(m->l, where), flagat(m->l, where)); + } + + if(!ptinrect(where, m->l->or)) return -1; switch(what){ @@ -443,7 +474,8 @@ maction(Monster *m, int what, Point wher } if(isyou(m) && hasflagat(m->l, where, Fportal)){ - warn("you see a %s here.", targ->portal->name); + warn("you see a %s to %s here.", + portalname(targ->feat), targ->portal->to->name); } return 1; @@ -602,4 +634,3 @@ xpcalc(int level) { return 20 + (10 * (2*level) * (level+pow(0.95, level))); } - diff --git a/monster.ndb b/monster.ndb --- a/monster.ndb +++ b/monster.ndb @@ -65,4 +65,3 @@ monster="captain" # player monster="wizard" tile=349 xpl=1 align=127 mvr=14 hp=64 ac=5 atk=2d3 - diff --git a/path.c b/path.c --- a/path.c +++ b/path.c @@ -54,7 +54,7 @@ pathfind(Level *l, Point start, Point en if(eqpt(start, end)) return npath; - nodes = mallocz(sizeof(PNode) * l->width * l->height, 1); + nodes = emalloc(sizeof(PNode) * l->width * l->height); if(nodes == nil) return npath; @@ -114,7 +114,7 @@ pathfind(Level *l, Point start, Point en npath = i; - *path = mallocz(sizeof(Point) * i + 2, 1); + *path = emalloc(sizeof(Point) * i + 2); for(i = 0; i < npath; i++){ (*path)[i] = rev->pt; rev = rev->parent; diff --git a/tile.c b/tile.c --- a/tile.c +++ b/tile.c @@ -11,6 +11,8 @@ opentile(char *name, int width, int heig Tileset *t; int fd; + fmtinstall('T', tilefmt); + if(name == nil || width < 1 || height < 1){ werrstr("bad tileset: %s %d %d", name, width, height); return nil; @@ -56,6 +58,15 @@ freetile(Tileset *t) free(t); } +int +tilefmt(Fmt *f) +{ + Tile *t; + t = va_arg(f->args, Tile*); + return fmtprint(f, "tile monst %#p portal %#p item head %#p unit %d feat %d terrain %d", + t->monst, t->portal, t->items.head, t->unit, t->feat, t->terrain); +} + void drawtile(Tileset *t, Image *dst, Point p, int i) { diff --git a/ui.c b/ui.c --- a/ui.c +++ b/ui.c @@ -280,6 +280,7 @@ drawlevel(Level *l, Tileset *ts, Rectang Image *c; Tile *t; Monster *m; + Item *item; bot = (Point){0, ts->height - smallfont->height}; @@ -318,7 +319,9 @@ drawlevel(Level *l, Tileset *ts, Rectang } } } else if(hasflagat(l, p, Fhasitem)){ - drawtile(ts, screen, sp, ilnth(&t->items, 0)->id->tile); + item = ilnth(&t->items, 0); + what = item->id->tile; + drawtile(ts, screen, sp, what); } } } diff --git a/util.c b/util.c --- a/util.c +++ b/util.c @@ -13,13 +13,41 @@ Point cardinals[] = { [NODIR] { 0, 0 }, }; +/* pick a random point along some side of the rect, no corners */ +Point +pickpoint(Rectangle r, int dir) +{ + Point p; + + fprint(2, "pickpoint %R\n", r); + + switch(dir){ + case WEST: + p = Pt(r.min.x, r.min.y + nrand(Dy(r)-1) + 1); + break; + case SOUTH: + p = Pt(r.min.x + nrand(Dx(r)-1) + 1, r.max.y); + break; + case NORTH: + p = Pt(r.min.x + nrand(Dx(r)-1) + 1, r.min.y); + break; + case EAST: + p = Pt(r.max.x, r.min.y + nrand(Dy(r)-1) + 1); + break; + default: + abort(); + } + + return p; +} + int roll(int count, int sides) { int d; d = 0; while(count--> 0) - d += 1+lnrand(sides); + d += 1+nrand(sides); return d; }