History

convert monstdata array to ndb file, make monsters by name. make some types more consistent

mischief committed 11 years ago

Commit 4bbc7bc1 parent c99276c11 files · +312 −134Browse files

Unified · Split

11 files
  1. ai.c +2 −0
  2. dat.h +32 −22
  3. hack9.c +7 −1
  4. level.c +59 −52
  5. mk.common +0 −1
  6. mkfile +3 −1
  7. monst.c +147 −16
  8. monstdata.c +0 −32
  9. monster.ndb +35 −0
  10. ui.c +8 −8
  11. util.c +19 −1
ai.c+2−0
@@ -4,6 +4,7 @@ · 3 unmodified lines
44 #include <thread.h>
55
66 #include "dat.h"
7+#include "alg.h"
78
89 AIState*
910 mkstate(char *name, Monster *m, void *aux, AIFun enter, AIFun exec, AIFun exit)
@@ -87,6 +88,7 @@ idleexec(AIState *a) · 77 unmodified lines
8788 ys = c.y - yd;
8889
8990 Point pts[] = { p, Pt(p.x, ys), Pt(xs, p.y), Pt(xs, ys) };
91+ shuffle(pts, 4, sizeof(Point));
9092 for(i = 0; i < 4; i++){
9193 s = pts[i];
9294 if(!ptinrect(s, m->l->r))
dat.h+32−22
@@ -8,6 +8,12 @@ typedef struct MonsterData MonsterData; · 7 unmodified lines
88 typedef struct Monster Monster;
99 typedef struct Camera Camera;
1010
11+/* sizes */
12+enum
13+{
14+ SZMONNAME = 32,
15+};
16+
1117 /* hack9.c */
1218 extern int debug;
1319 extern int farmsg;
@@ -62,18 +68,20 @@ void drawtile(Tileset *t, Image *dst, Point p, int i); · 48 unmodified lines
6268
6369 struct MonsterData
6470 {
65− char *name;
66− uint basexpl;
71+ char name[SZMONNAME];
72+ /* offset into tileset */
73+ int tile;
74+
75+ /* base experience level */
76+ int basexpl;
6777 char align;
68− uint mvr;
69− uint maxhp;
70− long def;
71− uint rolls;
72− uint atk;
78+ int mvr;
79+ int maxhp;
80+ int def;
81+ int rolls;
82+ int atk;
7383 };
7484
75−extern MonsterData monstdata[1200];
76−
7785 /* possible moves */
7886 enum
7987 {
@@ -93,8 +101,6 @@ struct Monster · 13 unmodified lines
93101 {
94102 /* references */
95103 Ref ref;
96− /* type */
97− int type;
98104
99105 /* current level */
100106 Level *l;
@@ -105,11 +111,11 @@ struct Monster · 4 unmodified lines
105111 char align;
106112
107113 /* move rate */
108− long mvr;
114+ int mvr;
109115 /* move points */
110− long mvp;
116+ int mvp;
111117 /* turn counter */
112− long turns;
118+ int turns;
113119
114120 /* current hp */
115121 double hp;
@@ -117,15 +123,15 @@ struct Monster · 1 unmodified line
117123 double maxhp;
118124
119125 /* armor class */
120− long ac;
126+ int ac;
121127
122128 /* kill count */
123− long kills;
129+ int kills;
124130
125131 /* experience level */
126− long xpl;
132+ int xpl;
127133 /* experience gained */
128− long xp;
134+ int xp;
129135
130136 /* flags */
131137 u32int flags;
@@ -139,15 +145,17 @@ struct Monster · 7 unmodified lines
139145 };
140146
141147 /* monst.c */
142−Monster *monst(int idx);
148+int monstdbopen(char *file);
149+MonsterData *mdbyname(char *monster);
150+Monster *mbyname(char *monster);
143151 void mfree(Monster *m);
144152 int mupdate(Monster *m);
145153 void mpushstate(Monster *m, AIState *a);
146154 AIState *mpopstate(Monster *m);
147155 int maction(Monster *m, int what, Point where);
148−long xpcalc(long level);
156+int xpcalc(int level);
149157
150−/* well known tiles, also indexes into monstdata */
158+/* well known tiles, indexes into tileset */
151159 enum
152160 {
153161 /* monsters */
@@ -173,6 +181,7 @@ enum · 19 unmodified lines
173181 TWATER = 860,
174182 TICE = 861,
175183 TLAVA = 862,
184+ TSPAWN = 888,
176185 TPORTAL = 891,
177186 };
178187
@@ -190,7 +199,7 @@ struct Spawn · 11 unmodified lines
190199 /* where spawn occurs */
191200 Point pt;
192201 /* what to spawn */
193− int what;
202+ char what[SZMONNAME];
194203 /* frequency of spawn, modulo turns */
195204 int freq;
196205 };
@@ -300,6 +309,7 @@ enum · 103 unmodified lines
300309
301310 extern Point cardinals[];
302311 int roll(int count, int sides);
312+int parseroll(char *str, int *count, int *sides);
303313 int min(int a, int b);
304314 int max(int a, int b);
305315
hack9.c+7−1
@@ -107,6 +107,7 @@ usage(void) · 106 unmodified lines
107107 void
108108 threadmain(int argc, char *argv[])
109109 {
110+ char buf[256];
110111 /* of viewport, in size of tiles */
111112 Tile *t;
112113 Level *level;
@@ -129,6 +130,11 @@ threadmain(int argc, char *argv[]) · 16 unmodified lines
129130
130131 uiinit(argv0);
131132
133+ snprint(buf, sizeof(buf), "%s/lib/hack9/%s", home, "monster.ndb");
134+ if(monstdbopen(buf) < 0)
135+ if(monstdbopen("monster.ndb") < 0)
136+ sysfatal("monstdbopen: %r");
137+
132138 /* initial level */
133139 if(debug > 0){
134140 if((level = genlevel(11, 11, 0)) == nil)
@@ -139,7 +145,7 @@ threadmain(int argc, char *argv[]) · 4 unmodified lines
139145 }
140146
141147 /* the player */
142− player = monst(TWIZARD);
148+ player = mbyname("wizard");
143149 if(player == nil)
144150 sysfatal("monst: %r");
145151
level.c+59−52
@@ -108,10 +108,12 @@ redo: · 107 unmodified lines
108108
109109 /* create a monster in the idle state */
110110 static Monster*
111−mkmons(Level *l, Point p, int type)
111+mkmons(Level *l, Point p, char *type)
112112 {
113113 Monster *m;
114− m = monst(type);
114+ m = mbyname(type);
115+ if(m == nil)
116+ sysfatal("monstbyname: %r");
115117 m->l = l;
116118 m->pt = p;
117119 /* setup ai state */
@@ -121,7 +123,7 @@ mkmons(Level *l, Point p, int type) · 3 unmodified lines
121123
122124 /* genmonsters spawns count of type monsters at random places on l. */
123125 static void
124−genmonsters(Level *l, int type, int count)
126+genmonsters(Level *l, char *type, int count)
125127 {
126128 int i;
127129 Point p;
@@ -133,8 +135,8 @@ genmonsters(Level *l, int type, int count) · 5 unmodified lines
133135 }while(hasflagat(l, p, Fhasmonster|Fhasfeature|Fblocked));
134136
135137 t = tileat(l, p);
136− t->unit = type;
137138 t->monst = mkmons(l, p, type);
139+ t->unit = t->monst->md->tile;
138140 setflagat(l, p, Fhasmonster);
139141 }
140142 }
@@ -147,7 +149,7 @@ genmonsters(Level *l, int type, int count) · 6 unmodified lines
147149 * spawns 13 ghosts centered at p on l.
148150 */
149151 static void
150−several(Level *l, Point *p, int count, int type, int r)
152+several(Level *l, Point *p, int count, char *type, int r)
151153 {
152154 int i, j, n;
153155 Point *neigh;
@@ -155,8 +157,8 @@ several(Level *l, Point *p, int count, int type, int r) · 1 unmodified line
155157 for(i = 0; i < count; i++){
156158 if(!hasflagat(l, p[i], Fhasmonster|Fblocked)){
157159 t = tileat(l, p[i]);
158− t->unit = type;
159160 t->monst = mkmons(l, p[i], type);
161+ t->unit = t->monst->md->tile;
160162 setflagat(l, p[i], Fhasmonster);
161163 }
162164 if(r > 0){
@@ -169,10 +171,18 @@ several(Level *l, Point *p, int count, int type, int r) · 6 unmodified lines
169171 }
170172 }
171173
172−static void addspawn(Level *l, Point p, int what, int freq)
174+static void addspawn(Level *l, Point p, char *what, int freq)
173175 {
174− if(l->nspawns < nelem(l->spawns)-1)
175− l->spawns[l->nspawns++] = (Spawn){p, what, freq};
176+ Spawn sp;
177+
178+ if(l->nspawns < nelem(l->spawns)-1){
179+ sp.pt = p;
180+ strcpy(sp.what, what);
181+ sp.freq = freq;
182+ l->spawns[l->nspawns++] = sp;
183+ tileat(l, p)->feat = TSPAWN;
184+ setflagat(l, p, Fhasfeature);
185+ }
176186 }
177187
178188 static void
@@ -198,9 +208,9 @@ levelexec(AIState *a) · 19 unmodified lines
198208 if(hasflagat(l, np, Fhasmonster)){
199209 t = tileat(l, np);
200210 m = t->monst;
201− md = &monstdata[l->spawns[i].what];
202− if(m->type != l->spawns[i].what && abs(m->align - md->align) > 15){
203− several(l, &np, 1, l->spawns[i].what, 1);
211+ md = mdbyname(l->spawns[i].what);
212+ if(strcmp(m->md->name, l->spawns[i].what) != 0 && abs(m->align - md->align) > 15){
213+ several(l, &p, 1, l->spawns[i].what, 2);
204214 }
205215 }
206216 }
@@ -273,8 +283,8 @@ gen(Level *l, int type) · 66 unmodified lines
273283 drunken(l, TTREE, 3, 3, 3);
274284 clear(l, pup, 1);
275285 clear(l, pdown, 1);
276− several(l, &l->down, 1, TLICH, 1);
277− several(l, &l->down, 1, TGWIZARD, 1);
286+ several(l, &l->down, 1, "lich", 1);
287+ several(l, &l->down, 1, "gnome wizard", 1);
278288 break;
279289 break;
280290 case 1:
@@ -287,28 +297,28 @@ gen(Level *l, int type) · 6 unmodified lines
287297 return -1;
288298 }while(!ptinrect(p, in) || manhattan(p, l->down) < DIST || manhattan(p, l->up) < DIST || hasflagat(l, p, Fblocked|Fhasmonster|Fhasfeature));
289299 clear(l, p, 2);
290− several(l, &p, 1, TCAPTAIN, 0);
291− several(l, &p, 1, TLIEUTENANT, 1);
292− addspawn(l, p, TSOLDIER, 37);
293− addspawn(l, p, TSERGEANT, 51);
294− addspawn(l, p, TLIEUTENANT, 97);
295− addspawn(l, p, TCAPTAIN, 131);
300+ several(l, &p, 1, "captain", 0);
301+ several(l, &p, 1, "lieutenant", 1);
302+ addspawn(l, p, "soldier", 37);
303+ addspawn(l, p, "sergeant", 51);
304+ addspawn(l, p, "lieutenant", 97);
305+ addspawn(l, p, "captain", 131);
296306 do{
297307 p2 = (Point){nrand(l->width), nrand(l->height)};
298308 if(try++ > 10)
299309 return -1;
300310 }while(!ptinrect(p2, in) || manhattan(p, p2) < DIST || manhattan(p, p2) > DIST*2 || manhattan(p2, l->down) < DIST || manhattan(p2, l->up) < DIST || hasflagat(l, p2, Fblocked|Fhasmonster|Fhasfeature));
301311 clear(l, p2, 2);
302− several(l, &p2, 1, TGNOMEKING, 0);
303− several(l, &p2, 1, TGWIZARD, 1);
304− addspawn(l, p2, TGNOME, 11);
305− addspawn(l, p2, TGNOMELORD, 25);
306− addspawn(l, p2, TGWIZARD, 73);
307− addspawn(l, p2, TGNOMEKING, 131);
312+ several(l, &p2, 1, "gnome king", 0);
313+ several(l, &p2, 1, "gnome wizard", 1);
314+ addspawn(l, p2, "gnome", 11);
315+ addspawn(l, p2, "gnome lord", 25);
316+ addspawn(l, p2, "gnome wizard", 73);
317+ addspawn(l, p2, "gnome king", 131);
308318
309319 /* don't start empty */
310− genmonsters(l, TGNOMELORD, space/128);
311− genmonsters(l, TSERGEANT, space/128);
320+ genmonsters(l, "gnome lord", space/128);
321+ genmonsters(l, "sergeant", space/128);
312322 break;
313323 case 2:
314324 space += drunken(l, TGRAVE, 2, 4, 4);
@@ -320,44 +330,41 @@ gen(Level *l, int type) · 5 unmodified lines
320330 return -1;
321331 }while(!ptinrect(p, in) || manhattan(p, l->down) < DIST || manhattan(p, l->up) < DIST || hasflagat(l, p, Fblocked|Fhasmonster|Fhasfeature));
322332 clear(l, p, 2);
323− addspawn(l, p, TLICH, 31);
324− addspawn(l, p, TGHOST, 5);
333+ addspawn(l, p, "lich", 31);
334+ addspawn(l, p, "ghost", 5);
325335 do{
326336 p2 = (Point){nrand(l->width), nrand(l->height)};
327337 if(try++ > 10)
328338 return -1;
329339 }while(!ptinrect(p2, in) || manhattan(p, p2) < DIST*3 || manhattan(p2, l->down) < DIST || manhattan(p2, l->up) < DIST || hasflagat(l, p2, Fblocked|Fhasmonster|Fhasfeature));
330340 clear(l, p2, 2);
331− several(l, &p2, 1, TGNOMEKING, 0);
332− several(l, &p2, 1, TGWIZARD, 1);
333− addspawn(l, p2, TGNOME, 11);
334− addspawn(l, p2, TGNOMELORD, 25);
335− addspawn(l, p2, TGWIZARD, 73);
336− addspawn(l, p2, TGNOMEKING, 131);
337−
338− genmonsters(l, TGHOST, space/64);
339− genmonsters(l, TGNOME, space/64);
341+ several(l, &p2, 1, "gnome king", 0);
342+ several(l, &p2, 1, "gnome wizard", 1);
343+ addspawn(l, p2, "gnome", 11);
344+ addspawn(l, p2, "gnome lord", 25);
345+ addspawn(l, p2, "gnome wizard", 37);
346+ addspawn(l, p2, "gnome king", 53);
347+
348+ genmonsters(l, "ghost", space/64);
349+ genmonsters(l, "gnome", space/64);
340350 break;
341351 case 3:
342− drunken(l, TLAVA, 2, 1, 1);
352+ space += drunken(l, TLAVA, 2, 1, 1);
343353 clear(l, pup, 2);
344354 clear(l, pdown, 2);
345355 do{
346356 p = (Point){nrand(l->width), nrand(l->height)};
347− }while(hasflagat(l, p, Fblocked|Fhasfeature));
348− addspawn(l, p, TLARGECAT, 11);
349− do{
350− p = (Point){nrand(l->width), nrand(l->height)};
351− }while(hasflagat(l, p, Fblocked|Fhasfeature));
352− addspawn(l, p, TLARGECAT, 13);
353− do{
354− p = (Point){nrand(l->width), nrand(l->height)};
355− }while(hasflagat(l, p, Fblocked|Fhasfeature));
356− addspawn(l, p, TGWIZARD, 9);
357+ }while(!ptinrect(p, in) || hasflagat(l, p, Fblocked|Fhasfeature));
358+ clear(l, p, 2);
359+ addspawn(l, p, "large cat", 67);
357360 do{
358361 p = (Point){nrand(l->width), nrand(l->height)};
359− }while(hasflagat(l, p, Fblocked|Fhasfeature));
360− addspawn(l, p, TGWIZARD, 15);
362+ }while(!ptinrect(p, in) || hasflagat(l, p, Fblocked|Fhasfeature));
363+ clear(l, p, 2);
364+ addspawn(l, p, "gnome lord", 27);
365+
366+ genmonsters(l, "large cat", space/64);
367+ genmonsters(l, "gnome lord", space/64);
361368 break;
362369 }
363370
mk.common+0−1
@@ -11,7 +11,6 @@ OFILES=\ · 10 unmodified lines
1111 hack9.$O\
1212 level.$O\
1313 monst.$O\
14− monstdata.$O\
1514 path.$O\
1615 tile.$O\
1716 ui.$O\
mkfile+3−1
@@ -7,7 +7,9 @@ BIN=$home/bin/$objtype · 6 unmodified lines
77 </sys/src/cmd/mkone
88
99 install:V: $BIN/$TARG
10− cp nethack.32x32 $home/lib/
10+ mkdir $home/lib/hack9
11+ cp nethack.32x32 $home/lib/hack9
12+ cp monster.ndb $home/lib/hack9
1113
1214 $O.alg: algtest.$O alg.$O
1315 $LD -o $target $prereq
monst.c+147−16
@@ -2,27 +2,157 @@ · 1 unmodified line
22 #include <libc.h>
33 #include <draw.h>
44 #include <thread.h>
5+#include <bio.h>
6+#include <ndb.h>
57
68 #include "dat.h"
79
8−Monster*
9−monst(int idx)
10+enum
11+{
12+ MONNAME = 0,
13+ MONTILE,
14+ MONXPL,
15+ MONALIGN,
16+ MONMVRATE,
17+ MONHP,
18+ MONAC,
19+ MONATK,
20+};
21+
22+static char *monstdbcmd[] = {
23+[MONNAME] "monster",
24+[MONTILE] "tile",
25+[MONXPL] "xpl",
26+[MONALIGN] "align",
27+[MONMVRATE] "mvr",
28+[MONHP] "hp",
29+[MONAC] "ac",
30+[MONATK] "atk",
31+};
32+
33+static Ndb *monstdb;
34+
35+int
36+monstdbopen(char *file)
37+{
38+ monstdb = ndbopen(file);
39+ if(monstdb == nil)
40+ return 0;
41+ return 1;
42+}
43+
44+typedef struct mdcache mdcache;
45+struct mdcache
1046 {
11− Monster *m;
1247 MonsterData *md;
48+ mdcache *next;
49+};
1350
14− if(idx < 0 || idx > nelem(monstdata))
15− goto missing;
51+static mdcache *monstcache = nil;
52+
53+/* TODO: sanity checks */
54+static int
55+mdaddattr(MonsterData *md, int type, char *attr)
56+{
57+ switch(type){
58+ case MONNAME:
59+ strncpy(md->name, attr, sizeof(md->name)-1);
60+ md->name[sizeof(md->name)-1] = 0;
61+ break;
62+ case MONTILE:
63+ md->tile = atol(attr);
64+ case MONXPL:
65+ md->basexpl = atol(attr);
66+ break;
67+ case MONALIGN:
68+ md->align = (char)atol(attr);
69+ break;
70+ case MONMVRATE:
71+ md->mvr = atol(attr);
72+ break;
73+ case MONHP:
74+ md->maxhp = atol(attr);
75+ break;
76+ case MONAC:
77+ md->def = atol(attr);
78+ break;
79+ case MONATK:
80+ if(!parseroll(attr, &md->atk, &md->rolls)){
81+ werrstr("bad roll '%s'", attr);
82+ return 0;
83+ }
84+ break;
85+ default:
86+ return 0;
87+ }
88+
89+ return 1;
90+}
91+
92+MonsterData*
93+mdbyname(char *monster)
94+{
95+ int i;
96+ Ndbtuple *t, *nt;
97+ Ndbs search;
98+ MonsterData *md;
99+ mdcache *mdc;
100+
101+ /* cached? */
102+ for(mdc = monstcache; mdc != nil; mdc = mdc->next){
103+ if(strcmp(monster, mdc->md->name) == 0){
104+ return mdc->md;
105+ }
106+ }
107+
108+ t = ndbsearch(monstdb, &search, "monster", monster);
109+ if(t == nil)
110+ return nil;
111+
112+ md = mallocz(sizeof(MonsterData), 1);
113+ assert(md != nil);
114+
115+ for(nt = t; nt != nil; nt = nt->entry){
116+ for(i = 0; i < nelem(monstdbcmd); i++){
117+ if(strcmp(nt->attr, monstdbcmd[i]) == 0){
118+ if(!mdaddattr(md, i, nt->val))
119+ sysfatal("bad monster value '%s=%s': %r", nt->attr, nt->val);
120+ goto okattr;
121+ }
122+ }
123+ sysfatal("unknown monster attribute '%s'", nt->attr);
124+okattr:
125+ continue;
126+ }
127+
128+ mdc = mallocz(sizeof(mdcache), 1);
129+ assert(mdc != nil);
130+
131+ if(monstcache == nil){
132+ monstcache = mdc;
133+ monstcache->md = md;
134+ } else {
135+ mdc->md = md;
136+ mdc->next = monstcache;
137+ monstcache = mdc;
138+ }
139+
140+ return md;
141+}
142+
143+Monster*
144+mbyname(char *monster)
145+{
146+ MonsterData *md;
147+ Monster *m;
16148
17− md = &monstdata[idx];
18− if(md->name == nil)
149+ md = mdbyname(monster);
150+ if(md == nil)
19151 goto missing;
20152
21153 m = mallocz(sizeof(Monster), 1);
22− if(m == nil)
23− return nil;
154+ assert(m != nil);
24155
25− m->type = idx;
26156 m->align = md->align;
27157 m->mvr = md->mvr;
28158 m->mvp = nrand(m->mvr);
@@ -30,13 +160,14 @@ monst(int idx) · 1 unmodified line
30160 m->maxhp = md->maxhp;
31161 m->ac = md->def;
32162 m->xpl = md->basexpl;
163+
33164 m->md = md;
34165
35166 incref(&m->ref);
36− return m;
37167
168+ return m;
38169 missing:
39− werrstr("missing monster %d", idx);
170+ werrstr("no data for monster '%s'", monster);
40171 return nil;
41172 }
42173
@@ -124,9 +255,9 @@ mpopstate(Monster *m) · 81 unmodified lines
124255
125256 /* determines what happens when a monster gets some xp */
126257 static void
127−maddxp(Monster *m, long xp)
258+maddxp(Monster *m, int xp)
128259 {
129− long hpgain, last;
260+ int hpgain, last;
130261
131262 dbg("%s get %ld xp total %ld next %ld", m->md->name, xp, m->xp, xpcalc(m->xpl));
132263
@@ -283,8 +414,8 @@ maction(Monster *m, int what, Point where) · 150 unmodified lines
283414 }
284415
285416 /* return the amount of xp required to gain a level */
286−long
287−xpcalc(long level)
417+int
418+xpcalc(int level)
288419 {
289420 return 20 + (10 * (2*level) * (level+pow(0.95, level)));
290421 }
monstdata.c+0−32
@@ -1,32 +0,0 @@
1−#include <u.h>
2−#include <libc.h>
3−#include <draw.h>
4−#include <thread.h>
5−
6−#include "dat.h"
7−
8−enum
9−{
10− UNALIGNED = 0,
11− GNOME = 50,
12− UNDEAD = -50,
13− HUMAN = -20,
14−
15−};
16−
17−MonsterData monstdata[1200] = {
18− /* name, xpl,align, mvr, hp, ac, rolls, atk */
19−[TLARGECAT] {"large cat", 0, UNALIGNED, 16, 16, 8, 1, 3},
20−[TGNOME] {"gnome", 1, GNOME, 6, 16, 10, 1, 6},
21−[TGNOMELORD] {"gnome lord", 3, GNOME, 8, 32, 10, 1, 8},
22−[TGWIZARD] {"gnome wizard", 3, GNOME, 10, 32, 4, 1, 6},
23−[TGNOMEKING] {"gnome king", 5, GNOME, 10, 48, 10, 2, 6},
24−[TLICH] {"lich", 2, UNDEAD, 6, 32, 3, 3, 2},
25−[TSOLDIER] {"soldier", 6, HUMAN, 10, 16, 10, 1, 8},
26−[TSERGEANT] {"sergeant", 8, HUMAN, 10, 16, 10, 2, 6},
27−[TLIEUTENANT] {"lieutenant", 10, HUMAN, 10, 32, 10, 3, 4},
28−[TCAPTAIN] {"captain", 12, HUMAN, 10, 48, 10, 4, 4},
29−[TGHOST] {"ghost", 1, UNDEAD, 6, 16, -5, 1, 4},
30−[TWIZARD] {"wizard", 0, 127, 14, 64, 5, 2, 3},
31−};
32−
monster.ndb+35−0
@@ -0,0 +1,35 @@
1+# monster database
2+
3+monster="large cat"
4+ tile=39 xpl=6 align=0 mvr=15 hp=16 ac=4 atk=2d4
5+
6+# gnomes
7+monster="gnome"
8+ tile=166 xpl=1 align=50 mvr=6 hp=16 ac=10 atk=1d6
9+monster="gnome lord"
10+ tile=167 xpl=3 align=50 mvr=8 hp=32 ac=10 atk=1d8
11+monster="gnome wizard"
12+ tile=168 xpl=3 align=50 mvr=10 hp=32 ac=4 atk=1d6
13+monster="gnome king"
14+ tile=169 xpl=5 align=50 mvr=10 hp=48 ac=10 atk=2d6
15+
16+# undead
17+monster="lich"
18+ tile=185 xpl=2 align=-50 mvr=6 hp=32 ac=3 atk=3d2
19+monster="ghost"
20+ tile=290 xpl=1 align=-50 mvr=6 hp=16 ac=-5 atk=1d4
21+
22+# military
23+monster="soldier"
24+ tile=280 xpl=6 align=-20 mvr=10 hp=16 ac=10 atk=1d8
25+monster="sergeant"
26+ tile=281 xpl=8 align=-20 mvr=10 hp=16 ac=10 atk=2d6
27+monster="lieutenant"
28+ tile=282 xpl=10 align=-20 mvr=10 hp=32 ac=10 atk=3d4
29+monster="captain"
30+ tile=283 xpl=12 align=-20 mvr=10 hp=48 ac=10 atk=4d4
31+
32+# player
33+monster="wizard"
34+ tile=349 xpl=0 align=127 mvr=14 hp=64 ac=5 atk=2d3
35+
ui.c+8−8
@@ -142,9 +142,9 @@ uiinit(char *name) · 141 unmodified lines
142142 if(tileset == nil)
143143 tileset = strdup("nethack.32x32");
144144
145− snprint(buf, sizeof(buf), "%s/lib/%s", home, tileset);
146− if((ui.tiles = opentile("nethack.32x32", 32, 32)) == nil)
147− if((ui.tiles = opentile(buf, 32, 32)) == nil)
145+ snprint(buf, sizeof(buf), "%s/lib/hack9/%s", home, tileset);
146+ if((ui.tiles = opentile(buf, 32, 32)) == nil)
147+ if((ui.tiles = opentile("nethack.32x32", 32, 32)) == nil)
148148 sysfatal("opentile: %r");
149149 free(tileset);
150150
@@ -283,7 +283,7 @@ drawlevel(Level *l, Tileset *ts, Rectangle r) · 132 unmodified lines
283283 p2 = addpt(sp, bot);
284284 string(screen, p2, c, ZP, smallfont, buf);
285285 p2.y -= smallfont->height;
286− snprint(buf, sizeof(buf), "L%ld", m->xpl);
286+ snprint(buf, sizeof(buf), "L%d", m->xpl);
287287 string(screen, p2, display->white, ZP, smallfont, buf);
288288 /* debugging the ai state */
289289 if(debug){
@@ -334,7 +334,7 @@ drawui(Rectangle r) · 44 unmodified lines
334334 p = string(screen, p, display->white, ZP, font, buf);
335335 p.x += sw*2;
336336
337− snprint(buf, sizeof(buf), "%ld kills", player->kills);
337+ snprint(buf, sizeof(buf), "%d kills", player->kills);
338338 p = string(screen, p, display->white, ZP, font, buf);
339339
340340 p.x = r.min.x;
@@ -343,7 +343,7 @@ drawui(Rectangle r) · 2 unmodified lines
343343 p = string(screen, p, display->white, ZP, font, buf);
344344 p.x += sw;
345345
346− snprint(buf, sizeof(buf), "L:%ld XP:%ld/%ld", player->xpl, player->xp, xpcalc(player->xpl));
346+ snprint(buf, sizeof(buf), "L:%d XP:%d/%d", player->xpl, player->xp, xpcalc(player->xpl));
347347 p = string(screen, p, display->white, ZP, font, buf);
348348 p.x += sw;
349349
@@ -360,7 +360,7 @@ drawui(Rectangle r) · 10 unmodified lines
360360 p = string(screen, p, c, ZP, font, buf);
361361 p.x += sw;
362362
363− snprint(buf, sizeof(buf), "AC:%ld", player->ac);
363+ snprint(buf, sizeof(buf), "AC:%d", player->ac);
364364 p = string(screen, p, display->white, ZP, font, buf);
365365 p.x += sw;
366366
@@ -546,7 +546,7 @@ uiexec(AIState *ai) · 179 unmodified lines
546546 player->hp = player->maxhp;
547547 setflagat(player->l, player->pt, Fhasmonster);
548548 tileat(player->l, player->pt)->monst = player;
549− tileat(player->l, player->pt)->unit = player->type;
549+ tileat(player->l, player->pt)->unit = player->md->tile;
550550 gameover = 0;
551551 break;
552552 case 2:
util.c+19−1
@@ -19,10 +19,28 @@ roll(int count, int sides) · 18 unmodified lines
1919 int d;
2020 d = 0;
2121 while(count--> 0)
22− d += 1+nrand(sides);
22+ d += 1+lnrand(sides);
2323 return d;
2424 }
2525
26+int
27+parseroll(char *str, int *count, int *sides)
28+{
29+ int i;
30+ char *f[2];
31+
32+ if(str == nil || count == nil || sides == nil)
33+ return 0;
34+
35+ i = gettokens(str, f, 2, "d");
36+ if(i != 2)
37+ return 0;
38+
39+ *count = atoi(f[0]);
40+ *sides = atoi(f[1]);
41+ return 1;
42+}
43+
2644 int
2745 min(int a, int b)
2846 {