History

implement xp. gain maxhp when you get a level. various cleanup.

mischief committed 11 years ago

Commit cca5e331 parent 0ca63d011 files · +408 −125Browse files

Unified · Split

11 files
  1. ai.c +7 −1
  2. alg.c +41 −0
  3. alg.h +4 −0
  4. algtest.c +33 −0
  5. dat.h +23 −7
  6. hack9.c +19 −3
  7. level.c +130 −65
  8. monst.c +70 −12
  9. monstdata.c +22 −10
  10. path.c +14 −13
  11. ui.c +45 −14
ai.c+7−1
@@ -172,6 +172,7 @@ typedef struct walktodata walktodata; · 171 unmodified lines
172172 struct walktodata
173173 {
174174 Point dst;
175+ Point last;
175176 int npath;
176177 int next;
177178 Point *path;
@@ -188,7 +189,9 @@ walktoinner(AIState *a) · 10 unmodified lines
188189 d = a->aux;
189190 m = a->m;
190191
191− if(d->blocked > d->wait){
192+ /* d->last might not equal m->pt if some other
193+ * state pre-empted us, like attack */
194+ if(!eqpt(d->last, m->pt) || d->blocked > d->wait){
192195 goto cleanup;
193196 }
194197
@@ -228,6 +231,7 @@ walktoinner(AIState *a) · 33 unmodified lines
228231 d->next++;
229232
230233 maction(m, MMOVE, p);
234+ d->last = p;
231235 return 0;
232236 cleanup:
233237 free(d->path);
@@ -267,6 +271,7 @@ walkto(Monster *m, Point p, int wait) · 33 unmodified lines
267271 AIState *i;
268272 d = mallocz(sizeof(*d), 1);
269273 d->dst = p;
274+ d->last = m->pt;
270275 d->wait = wait;
271276 assert(d != nil);
272277 i = mkstate("walk", m, d, nil, walktoexec, walktoexit);
@@ -313,6 +318,7 @@ attackexec(AIState *a) · 40 unmodified lines
313318 d->wd->dst = mt->pt;
314319 }
315320 d->wd->wait = 2;
321+ d->wd->last = m->pt;
316322 if(walktoinner(d->w) < 0){
317323 a = mpopstate(m);
318324 freestate(a);
alg.c+41−0
@@ -1,5 +1,6 @@
11 #include <u.h>
22 #include <libc.h>
3+#include <draw.h>
34 #include "alg.h"
45
56 Priq*
@@ -94,3 +95,43 @@ priqhas(Priq *q, void *val) · 88 unmodified lines
9495 return 0;
9596 }
9697
98+/* von neumann neighborhood */
99+Point*
100+neighbor(Rectangle in, Point p, int *n)
101+{
102+ int i, count;
103+ Point *neigh, p2, diff[] = { {1, 0}, {0, -1}, {-1, 0}, {0, 1} };
104+
105+ count = 0;
106+ neigh = mallocz(sizeof(Point) * nelem(diff), 1);
107+
108+ for(i = 0; i < nelem(diff); i++){
109+ p2 = addpt(p, diff[i]);
110+ if(ptinrect(p2, in)){
111+ neigh[count++] = p2;
112+ }
113+ }
114+
115+ *n = count;
116+ return neigh;
117+}
118+
119+/* fisher-yates shuffle */
120+void
121+shuffle(void *base, long nel, long width)
122+{
123+ unsigned char *b, *swap1, *swap2;
124+ long i, j, k;
125+ b = base;
126+ for(i = nel - 1; i > 0; i--){
127+ j = nrand(i);
128+ swap1 = &b[i*width];
129+ swap2 = &b[j*width];
130+ for(k = 0; k < width; k++){
131+ *swap1 ^= *swap2;
132+ *swap2 ^= *swap1;
133+ *swap1++ ^= *swap2++;
134+ }
135+ }
136+}
137+
alg.h+4−0
@@ -6,6 +6,7 @@ struct Priq · 5 unmodified lines
66 int alloc;
77 };
88
9+/* alg.c */
910 typedef int (*pricmp)(void *v1, void *v2);
1011 Priq *priqnew(int size);
1112 void priqfree(Priq *q);
@@ -14,3 +15,6 @@ void *priqpop(Priq *q, pricmp cmp); · 2 unmodified lines
1415 void *priqtop(Priq *q);
1516 int priqhas(Priq *q, void *val);
1617
18+Point *neighbor(Rectangle in, Point p, int *n);
19+void shuffle(void *base, long nel, long width);
20+
algtest.c+33−0
@@ -1,5 +1,6 @@
11 #include <u.h>
22 #include <libc.h>
3+#include <draw.h>
34
45 #include "alg.h"
56
@@ -37,11 +38,43 @@ testpriq(void) · 31 unmodified lines
3738 return 0;
3839 }
3940
41+int
42+testshuffle(void)
43+{
44+ int arr[] = {1, 3, 5, 7, 9, 11, 13, 17};
45+ Point pt[] = {{0,0}, {1, 1}, {2, 2}, {3, 3}};
46+ long i, sz;
47+
48+ fmtinstall('P', Pfmt);
49+
50+ sz = sizeof(arr)/sizeof(arr[0]);
51+
52+ print("\n");
53+ for(i=0;i<sz;i++)
54+ print("%d ", arr[i]);
55+ print("\n");
56+ shuffle(arr, sz, sizeof(arr[0]));
57+ for(i=0;i<sz;i++)
58+ print("%d ", arr[i]);
59+ print("\n");
60+
61+ sz = sizeof(pt)/sizeof(pt[0]);
62+ for(i=0;i<sz;i++)
63+ print("%P ", pt[i]);
64+ print("\n");
65+ shuffle(pt, sz, sizeof(pt[0]));
66+ for(i=0;i<sz;i++)
67+ print("%P ", pt[i]);
68+ print("\n");
69+ return 0;
70+}
71+
4072 struct {
4173 char *n;
4274 tfun f;
4375 } tfuns[] = {
4476 {"priq", testpriq},
77+ {"shuffle", testshuffle},
4578 {nil, nil},
4679 };
4780
dat.h+23−7
@@ -10,15 +10,13 @@ typedef struct Camera Camera; · 9 unmodified lines
1010
1111 /* hack9.c */
1212 extern int debug;
13+extern int farmsg;
1314 extern Monster *player;
1415 extern char *user;
1516 extern char *home;
1617 extern long turn;
17−void msg(char *fmt, ...);
18−void good(char *fmt, ...);
19−void warn(char *fmt, ...);
20−void bad(char *fmt, ...);
21−void dbg(char *fmt, ...);
18+int isyou(Monster *m);
19+int nearyou(Point p);
2220
2321 typedef void (*AIFun)(AIState *);
2422
@@ -65,12 +63,13 @@ void drawtile(Tileset *t, Image *dst, Point p, int i); · 40 unmodified lines
6563 struct MonsterData
6664 {
6765 char *name;
66+ uint basexpl;
6867 char align;
6968 uint mvr;
7069 uint maxhp;
7170 long def;
72− uint atk;
7371 uint rolls;
72+ uint atk;
7473 };
7574
7675 extern MonsterData monstdata[1200];
@@ -114,6 +113,8 @@ struct Monster · 37 unmodified lines
114113
115114 /* current hp */
116115 double hp;
116+ /* maximum hp */
117+ double maxhp;
117118
118119 /* armor class */
119120 long ac;
@@ -121,6 +122,11 @@ struct Monster · 1 unmodified line
121122 /* kill count */
122123 long kills;
123124
125+ /* experience level */
126+ long xpl;
127+ /* experience gained */
128+ long xp;
129+
124130 /* flags */
125131 u32int flags;
126132
@@ -139,13 +145,17 @@ int mupdate(Monster *m); · 12 unmodified lines
139145 void mpushstate(Monster *m, AIState *a);
140146 AIState *mpopstate(Monster *m);
141147 int maction(Monster *m, int what, Point where);
148+long xpcalc(long level);
142149
143150 /* well known tiles, also indexes into monstdata */
144151 enum
145152 {
146153 /* monsters */
147154 TLARGECAT = 39,
155+ TGNOME = 166,
156+ TGNOMELORD = 167,
148157 TGWIZARD = 168,
158+ TGNOMEKING = 169,
149159 TLICH = 185,
150160 TSOLDIER = 280,
151161 TSERGEANT = 281,
@@ -168,6 +178,7 @@ enum · 16 unmodified lines
168178
169179 struct Portal
170180 {
181+ char name[32];
171182 /* where it takes us to */
172183 Level *to;
173184 /* location in to */
@@ -237,6 +248,7 @@ struct Level · 63 unmodified lines
237248 Spawn spawns[10];
238249 };
239250
251+/* level.c */
240252 Level *genlevel(int width, int height, int type);
241253 void freelevel(Level *l);
242254 Tile *tileat(Level *l, Point p);
@@ -244,7 +256,6 @@ Tile *tileat(Level *l, Point p); · 1 unmodified line
244256 #define hasflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) & (F))
245257 #define setflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) |= (F))
246258 #define clrflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) &= ~(F))
247−Point *lneighbor(Level *l, Point p, int *n);
248259
249260 struct Camera
250261 {
@@ -267,6 +278,11 @@ int manhattan(Point cur, Point targ); · 16 unmodified lines
267278 int pathfind(Level *l, Point start, Point end, Point **path, int not);
268279
269280 /* ui.c */
281+void msg(char *fmt, ...);
282+void good(char *fmt, ...);
283+void warn(char *fmt, ...);
284+void bad(char *fmt, ...);
285+void dbg(char *fmt, ...);
270286 void uiinit(char *name);
271287 void uiexec(AIState *ai);
272288 void uiredraw(int justui);
hack9.c+19−3
@@ -12,11 +12,26 @@ enum · 11 unmodified lines
1212 };
1313
1414 int debug;
15+int farmsg = 0;
1516 Monster *player;
1617 char *user;
1718 char *home;
1819 long turn = 0;
1920
21+int
22+isyou(Monster *m)
23+{
24+ return m == player;
25+}
26+
27+int
28+nearyou(Point p)
29+{
30+ if(farmsg)
31+ return 1;
32+ return manhattan(player->pt, p) < ORTHOCOST*5;
33+}
34+
2035 /*
2136 * sort monsters for movement order.
2237 * player comes first, followed by monsters,
@@ -40,7 +55,7 @@ monsort(void *a, void *b) · 17 unmodified lines
4055 return sa < sb;
4156 }
4257
43−void
58+static void
4459 movemons(void)
4560 {
4661 Point p;
@@ -81,7 +96,8 @@ movemons(void) · 34 unmodified lines
8196
8297 priqfree(tomove);
8398 }
84−void
99+
100+static void
85101 usage(void)
86102 {
87103 fprint(2, "usage: %s [-d]", argv0);
@@ -142,7 +158,7 @@ threadmain(int argc, char *argv[]) · 54 unmodified lines
142158
143159 if(debug > 0){
144160 player->hp = 500;
145− player->md->maxhp = 500;
161+ player->maxhp = 500;
146162 }
147163
148164 uiredraw(0);
level.c+130−65
@@ -4,6 +4,7 @@ · 3 unmodified lines
44 #include <thread.h>
55
66 #include "dat.h"
7+#include "alg.h"
78
89 /* clear removes all monsters/features near p. */
910 static void
@@ -71,13 +72,16 @@ drunk1(Level *l, Point p, uint count, uint sbias) · 61 unmodified lines
7172 static int
7273 drunken(Level *l, int type, int howmuch, int s1, int s2)
7374 {
74− int cnt, npath;
75+ int cnt, npath, redos;
7576 Point p, *path;
7677 Tile *t;
7778
7879 cnt = ((l->width * l->height) / howmuch); //* 2;
80+ redos = 0;
7981
8082 redo:
83+ if(redos++ > 10)
84+ return 0;
8185 /* fill */
8286 for(p.x = 0; p.x < l->width; p.x++){
8387 for(p.y = 0; p.y < l->height; p.y++){
@@ -149,14 +153,14 @@ several(Level *l, Point *p, int count, int type, int r) · 65 unmodified lines
149153 Point *neigh;
150154 Tile *t;
151155 for(i = 0; i < count; i++){
152− if(!hasflagat(l, p[i], Fhasmonster)){
156+ if(!hasflagat(l, p[i], Fhasmonster|Fblocked)){
153157 t = tileat(l, p[i]);
154158 t->unit = type;
155159 t->monst = mkmons(l, p[i], type);
156160 setflagat(l, p[i], Fhasmonster);
157161 }
158162 if(r > 0){
159− neigh = lneighbor(l, p[i], &n);
163+ neigh = neighbor(l->r, p[i], &n);
160164 for(j = 0; j < n; j++){
161165 several(l, neigh, n, type, r-1);
162166 }
@@ -174,26 +178,55 @@ static void addspawn(Level *l, Point p, int what, int freq) · 11 unmodified lines
174178 static void
175179 levelexec(AIState *a)
176180 {
177− int i;
181+ int i, j, n;
182+ Point p, np, *neigh;
178183 Level *l;
184+ Tile *t;
185+ MonsterData *md;
186+ Monster *m;
187+
179188 l = a->aux;
180189
190+ assert(l > (void*)0x1000);
191+
192+ assert(l != nil);
193+
181194 for(i = 0; i < l->nspawns; i++){
182195 if(turn % l->spawns[i].freq == 0){
183− several(l, &l->spawns[i].pt, 1, l->spawns[i].what, 0);
196+ p = l->spawns[i].pt;
197+
198+ /* le trap */
199+ neigh = neighbor(l->r, p, &n);
200+ for(j = 0; j < n; j++){
201+ np = neigh[j];
202+ if(hasflagat(l, np, Fhasmonster)){
203+ t = tileat(l, np);
204+ m = t->monst;
205+ md = &monstdata[l->spawns[i].what];
206+ if(m->type != l->spawns[i].what && abs(m->align - md->align) > 15){
207+ several(l, &np, 1, l->spawns[i].what, 1);
208+ }
209+ }
210+ }
211+
212+ free(neigh);
213+ several(l, &p, 1, l->spawns[i].what, 0);
184214 }
185215 }
186216 }
187217
188−static void
218+static int
189219 gen(Level *l, int type)
190220 {
191− int space, npath;
192− Point pup, pdown, p, *path;
221+ int space, npath, try;
222+ Point pup, pdown, p, p2, *path;
223+ Rectangle in;
193224 Tile *t;
194225
195226 space = 0;
227+ try = 0;
196228
229+ in = insetrect(l->r, 3);
197230 pup = ZP;
198231 pdown = ZP;
199232
@@ -202,13 +235,13 @@ gen(Level *l, int type) · 2 unmodified lines
202235 flagat(l, pup) = 0;
203236 do{
204237 pup = (Point){nrand(l->width), nrand(l->height)};
205− }while(!ptinrect(pup, insetrect(l->r, 3)));
238+ }while(!ptinrect(pup, in));
206239
207240 if(!eqpt(pdown, ZP))
208241 flagat(l, pup) = 0;
209242 do{
210243 pdown = (Point){nrand(l->width), nrand(l->height)};
211− }while(!ptinrect(pdown, insetrect(l->r, 3)));
244+ }while(!ptinrect(pdown, in));
212245
213246 /* already upstair? */
214247 if(flagat(l, pdown) & Fportal)
@@ -225,50 +258,89 @@ gen(Level *l, int type) · 10 unmodified lines
225258 t = tileat(l, pup);
226259 t->feat = TUPSTAIR;
227260 t->portal = mallocz(sizeof(Portal), 1);
261+ strcpy(t->portal->name, "staircase upstairs");
228262 l->up = pup;
229263
230264 setflagat(l, pdown, Fhasfeature|Fportal);
231265 t = tileat(l, pdown);
232266 t->feat = TDOWNSTAIR;
233267 t->portal = mallocz(sizeof(Portal), 1);
268+ strcpy(t->portal->name, "staircase downstairs");
234269 l->down = pdown;
235270 break;
236271 }
237272
273+ enum { DIST = ORTHOCOST*10, };
274+
238275 switch(type){
239276 case 0:
240277 drunken(l, TTREE, 3, 3, 3);
241278 clear(l, pup, 1);
242279 clear(l, pdown, 1);
243− //several(l, &l->down, 1, TLICH, 0);
280+ several(l, &l->down, 1, TLICH, 1);
244281 several(l, &l->down, 1, TGWIZARD, 1);
245− //several(l, &l->up, 1, TGWIZARD, 0);
246282 break;
247283 break;
248284 case 1:
249285 space += drunken(l, TTREE, 2, 0, 0);
250286 clear(l, pup, 1);
251287 clear(l, pdown, 1);
252− genmonsters(l, TGWIZARD, space/128);
253− genmonsters(l, TSOLDIER, space/64);
288+ do{
289+ p = (Point){nrand(l->width), nrand(l->height)};
290+ if(try++ > 10)
291+ return -1;
292+ }while(!ptinrect(p, in) || manhattan(p, l->down) < DIST || manhattan(p, l->up) < DIST || hasflagat(l, p, Fblocked|Fhasmonster|Fhasfeature));
293+ clear(l, p, 2);
294+ several(l, &p, 1, TCAPTAIN, 0);
295+ several(l, &p, 1, TLIEUTENANT, 1);
296+ addspawn(l, p, TSOLDIER, 37);
297+ addspawn(l, p, TSERGEANT, 51);
298+ addspawn(l, p, TLIEUTENANT, 97);
299+ addspawn(l, p, TCAPTAIN, 131);
300+ do{
301+ p2 = (Point){nrand(l->width), nrand(l->height)};
302+ if(try++ > 10)
303+ return -1;
304+ }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));
305+ clear(l, p2, 2);
306+ several(l, &p2, 1, TGNOMEKING, 0);
307+ several(l, &p2, 1, TGWIZARD, 1);
308+ addspawn(l, p2, TGNOME, 11);
309+ addspawn(l, p2, TGNOMELORD, 25);
310+ addspawn(l, p2, TGWIZARD, 73);
311+ addspawn(l, p2, TGNOMEKING, 131);
312+
313+ /* don't start empty */
314+ genmonsters(l, TGNOMELORD, space/128);
254315 genmonsters(l, TSERGEANT, space/128);
255− several(l, &l->down, 1, TCAPTAIN, 0);
256− several(l, &l->down, 1, TLIEUTENANT, 1);
257− addspawn(l, l->down, TSOLDIER, 20);
258− addspawn(l, l->down, TSERGEANT, 40);
259− addspawn(l, l->up, TGWIZARD, 20);
260316 break;
261317 case 2:
262318 space += drunken(l, TGRAVE, 2, 4, 4);
263319 clear(l, l->up, 2);
264320 clear(l, l->down, 2);
265− genmonsters(l, TGWIZARD, space/48);
321+ do{
322+ p = (Point){nrand(l->width), nrand(l->height)};
323+ if(try++ > 10)
324+ return -1;
325+ }while(!ptinrect(p, in) || manhattan(p, l->down) < DIST || manhattan(p, l->up) < DIST || hasflagat(l, p, Fblocked|Fhasmonster|Fhasfeature));
326+ clear(l, p, 2);
327+ addspawn(l, p, TLICH, 31);
328+ addspawn(l, p, TGHOST, 5);
329+ do{
330+ p2 = (Point){nrand(l->width), nrand(l->height)};
331+ if(try++ > 10)
332+ return -1;
333+ }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));
334+ clear(l, p2, 2);
335+ several(l, &p2, 1, TGNOMEKING, 0);
336+ several(l, &p2, 1, TGWIZARD, 1);
337+ addspawn(l, p2, TGNOME, 11);
338+ addspawn(l, p2, TGNOMELORD, 25);
339+ addspawn(l, p2, TGWIZARD, 73);
340+ addspawn(l, p2, TGNOMEKING, 131);
341+
266342 genmonsters(l, TGHOST, space/64);
267− several(l, &l->down, 1, TLICH, 2);
268− clear(l, pdown, 1);
269− addspawn(l, l->up, TGWIZARD, 11);
270− addspawn(l, l->down, TLICH, 61);
271− addspawn(l, l->down, TGHOST, 31);
343+ genmonsters(l, TGNOME, space/64);
272344 break;
273345 case 3:
274346 drunken(l, TLAVA, 2, 1, 1);
@@ -294,6 +366,8 @@ gen(Level *l, int type) · 19 unmodified lines
294366 }
295367
296368 l->ai = mkstate("level", nil, l, nil, levelexec, nil);
369+
370+ return 0;
297371 }
298372
299373 Level*
@@ -302,29 +376,34 @@ genlevel(int width, int height, int type) · 2 unmodified lines
302376 int x, y;
303377 Level *l;
304378
305− l = mallocz(sizeof(Level), 1);
306− if(l == nil)
307− return nil;
379+ l = nil;
308380
309− l->width = width;
310− l->height = height;
311− l->r = Rect(0, 0, width, height);
381+ do {
382+ if(l != nil)
383+ freelevel(l);
312384
313− l->tiles = mallocz(sizeof(Tile) * width * height, 1);
314− if(l->tiles == nil)
315− goto err0;
385+ l = mallocz(sizeof(Level), 1);
386+ if(l == nil)
387+ return nil;
316388
317− l->flags = mallocz(sizeof(int) * width * height, 1);
318− if(l->flags == nil)
319− goto err1;
389+ l->width = width;
390+ l->height = height;
391+ l->r = Rect(0, 0, width, height);
320392
321− for(x = 0; x < width; x++){
322− for(y = 0; y < height; y++){
323− tileat(l, Pt(x, y))->terrain = TFLOOR;
324− }
325− }
393+ l->tiles = mallocz(sizeof(Tile) * width * height, 1);
394+ if(l->tiles == nil)
395+ goto err0;
326396
327− gen(l, type);
397+ l->flags = mallocz(sizeof(int) * width * height, 1);
398+ if(l->flags == nil)
399+ goto err1;
400+
401+ for(x = 0; x < width; x++){
402+ for(y = 0; y < height; y++){
403+ tileat(l, Pt(x, y))->terrain = TFLOOR;
404+ }
405+ }
406+ } while(gen(l, type) < 0);
328407
329408 return l;
330409
@@ -340,16 +419,23 @@ freelevel(Level *l) · 9 unmodified lines
340419 {
341420 int x, y;
342421 Point p;
422+ Tile *t;
343423
344424 for(x = 0; x < l->width; x++){
345425 for(y = 0; y < l->height; y++){
346426 p = (Point){x, y};
427+ t = tileat(l, p);
347428 if(hasflagat(l, p, Fhasmonster))
348− mfree(tileat(l, Pt(x, y))->monst);
429+ mfree(t->monst);
430+ if(hasflagat(l, p, Fhasmonster))
431+ free(t->portal);
349432 }
350433 }
351434
352435 free(l->tiles);
436+ free(l->flags);
437+ if(l->ai != nil)
438+ freestate(l->ai);
353439 free(l);
354440 }
355441
@@ -359,24 +445,3 @@ tileat(Level *l, Point p) · 3 unmodified lines
359445 return l->tiles+(p.y*l->width)+p.x;
360446 }
361447
362−/* von neumann neighborhood */
363−Point*
364−lneighbor(Level *l, Point p, int *n)
365−{
366− int i, count;
367− Point *neigh, p2, diff[] = { {1, 0}, {0, -1}, {-1, 0}, {0, 1} };
368−
369− count = 0;
370− neigh = mallocz(sizeof(Point) * nelem(diff), 1);
371−
372− for(i = 0; i < nelem(diff); i++){
373− p2 = addpt(p, diff[i]);
374− if(ptinrect(p2, l->r)){ // && !hasflagat(l, p2, Fblocked)){ //|| hasflagat(l, p2, Fhasmonster))){
375− neigh[count++] = p2;
376− }
377− }
378−
379− *n = count;
380− return neigh;
381−}
382−
monst.c+70−12
@@ -27,7 +27,9 @@ monst(int idx) · 26 unmodified lines
2727 m->mvr = md->mvr;
2828 m->mvp = nrand(m->mvr);
2929 m->hp = md->maxhp;
30+ m->maxhp = md->maxhp;
3031 m->ac = md->def;
32+ m->xpl = md->basexpl;
3133 m->md = md;
3234
3335 incref(&m->ref);
@@ -80,10 +82,14 @@ mupdate(Monster *m) · 46 unmodified lines
8082
8183 /* gain some hp if you aren't dead. */
8284 if((m->flags & Mdead) == 0)
83− if(m->hp < m->md->maxhp){
84− m->hp += (double)m->md->maxhp/100.0;
85− if(m->hp > m->md->maxhp)
86− m->hp = m->md->maxhp;
85+ if(m->hp < m->maxhp){
86+ /* below half, 2%. above, 0.5%. */
87+ if(m->hp < m->maxhp/2)
88+ m->hp += 1.0 + (double)m->maxhp/50.0;
89+ else
90+ m->hp += (double)m->maxhp/200.0;
91+ if(m->hp > m->maxhp)
92+ m->hp = m->maxhp;
8793 }
8894 }
8995
@@ -116,17 +122,40 @@ mpopstate(Monster *m) · 26 unmodified lines
116122 return a;
117123 }
118124
125+/* determines what happens when a monster gets some xp */
126+static void
127+maddxp(Monster *m, long xp)
128+{
129+ long hpgain, last;
130+
131+ dbg("%s get %ld xp total %ld next %ld", m->md->name, xp, m->xp, xpcalc(m->xpl));
132+
133+ last = m->xpl;
134+ m->xp += xp;
135+ while(m->xp >= xpcalc(m->xpl)){
136+ m->xpl++;
137+ hpgain = 1 + roll(1, 8);
138+ m->maxhp += hpgain;
139+ m->hp += hpgain*2;
140+ }
141+
142+ if(m->xpl > last)
143+ if(nearyou(m->pt))
144+ good("the %s becomes level %d!", m->md->name, m->xpl);
145+}
146+
119147 static int
120148 mattack(Monster *m, Monster *mt)
121149 {
122− int hit, crit, dmg;
150+ int hit, crit, dmg, xp;
123151 hit = roll(1, 100);
124152 if(hit < 10+abs(mt->ac-10)*2){
125− warn("the %s misses the %s!", m->md->name, mt->md->name);
153+ if(nearyou(m->pt))
154+ warn("the %s misses the %s!", m->md->name, mt->md->name);
126155 return 0;
127156 }
128157
129− dmg = roll(m->md->atk, m->md->rolls);
158+ dmg = roll(m->md->rolls, m->md->atk);
130159
131160 if(dmg && mt->ac < 0){
132161 dmg -= 1+nrand(-mt->ac);
@@ -137,9 +166,11 @@ mattack(Monster *m, Monster *mt) · 4 unmodified lines
137166 crit = roll(1, 5);
138167 if(crit == 1){
139168 dmg *= 2;
140− warn("the %s critically strikes the %s for %d!", m->md->name, mt->md->name, dmg);
169+ if(nearyou(m->pt))
170+ warn("the %s critically strikes the %s for %d!", m->md->name, mt->md->name, dmg);
141171 } else {
142− warn("the %s hits the %s for %d!", m->md->name, mt->md->name, dmg);
172+ if(nearyou(m->pt))
173+ msg("the %s hits the %s for %d.", m->md->name, mt->md->name, dmg);
143174 }
144175
145176 mt->hp -= dmg;
@@ -148,7 +179,22 @@ mattack(Monster *m, Monster *mt) · 2 unmodified lines
148179 mt->hp = 0;
149180 mt->flags = Mdead;
150181 m->kills++;
151− bad("the %s kills the %s!", m->md->name, mt->md->name);
182+
183+ xp = 10+10*pow(mt->xpl, 1.75);
184+
185+ if(nearyou(m->pt))
186+ bad("the %s kills the %s, and gains %ld xp.", m->md->name, mt->md->name, xp);
187+
188+ /* gain some xp */
189+ maddxp(m, xp);
190+
191+ /* gain 1/4 your target's max hp */
192+ if(m->hp < m->maxhp){
193+ m->hp += (double)mt->maxhp/4.0;
194+ if(m->hp > m->maxhp)
195+ m->hp = m->maxhp;
196+ }
197+
152198 return 1;
153199 }
154200
@@ -179,7 +225,7 @@ maction(Monster *m, int what, Point where) · 24 unmodified lines
179225 targ->unit = 0;
180226 }
181227 return 0;
182− } else if(!hasflagat(m->l, where, Fblocked)){
228+ } else if(!hasflagat(m->l, where, Fblocked|Fhasmonster)){
183229 /* move */
184230 cur = tileat(m->l, m->pt);
185231 setflagat(m->l, where, (Fhasmonster));
@@ -190,6 +236,11 @@ maction(Monster *m, int what, Point where) · 4 unmodified lines
190236 cur->monst = nil;
191237
192238 m->pt = where;
239+
240+ if(isyou(m) && hasflagat(m->l, where, Fportal)){
241+ warn("you see a %s here.", targ->portal->name);
242+ }
243+
193244 return 1;
194245 }
195246 break;
@@ -202,7 +253,7 @@ maction(Monster *m, int what, Point where) · 6 unmodified lines
202253 m = cur->monst;
203254 cur->monst = nil;
204255 if(cur->portal->to == nil){
205− if((cur->portal->to = genlevel(nrand(20)+20, nrand(20)+20, nrand(3)+1)) == nil)
256+ if((cur->portal->to = genlevel(nrand(20)+50, nrand(20)+10, nrand(3)+1)) == nil)
206257 sysfatal("genlevel: %r");
207258
208259 cur->portal->pt = cur->portal->to->up;
@@ -231,3 +282,10 @@ maction(Monster *m, int what, Point where) · 22 unmodified lines
231282 return -1;
232283 }
233284
285+/* return the amount of xp required to gain a level */
286+long
287+xpcalc(long level)
288+{
289+ return 20 + (10 * (2*level) * (level+pow(0.95, level)));
290+}
291+
monstdata.c+22−10
@@ -5,16 +5,28 @@ · 4 unmodified lines
55
66 #include "dat.h"
77
8+enum
9+{
10+ UNALIGNED = 0,
11+ GNOME = 50,
12+ UNDEAD = -50,
13+ HUMAN = -20,
14+
15+};
16+
817 MonsterData monstdata[1200] = {
9− /* name, align, mvr, hp, ac, atk, rolls */
10−[TLARGECAT] {"large cat", 1, 16, 16, 8, 2, 2},
11−[TGWIZARD] {"gnomish wizard", 50, 10, 16, 6, 2, 2},
12−[TLICH] {"lich", -50, 16, 32, -1, 2, 3},
13−[TSOLDIER] {"soldier", -20, 12, 8, 8, 2, 2},
14−[TSERGEANT] {"sergeant", -20, 12, 16, 6, 5, 1},
15−[TLIEUTENANT] {"lieutenant", -20, 12, 32, 4, 3, 2},
16−[TCAPTAIN] {"captain", -20, 15, 64, 2, 3, 3},
17−[TGHOST] {"ghost", -50, 6, 8, -10, 2, 1},
18−[TWIZARD] {"wizard", 20, 16, 64, -1, 2, 4},
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},
1931 };
2032
path.c+14−13
@@ -45,7 +45,7 @@ pathfind(Level *l, Point start, Point end, Point **path, int not) · 44 unmodified lines
4545 {
4646 int i, nneigh, npath;
4747 Point *neighborp, pt;
48− PNode *nodes, *p, *neighbor, *head, *tmp, *rev;
48+ PNode *nodes, *p, *neigh, *head, *tmp, *rev;
4949 Priq *q;
5050
5151 npath = -1;
@@ -72,21 +72,22 @@ pathfind(Level *l, Point start, Point end, Point **path, int not) · 20 unmodified lines
7272 if(eqpt(p->pt, end))
7373 break;
7474
75− neighborp = lneighbor(l, p->pt, &nneigh);
75+ neighborp = neighbor(l->r, p->pt, &nneigh);
76+ shuffle(neighborp, nneigh, sizeof(Point));
7677 for(i = 0; i < nneigh; i++){
7778 pt = neighborp[i];
78− neighbor = &nodes[pt.y*l->width + pt.x];
79+ neigh = &nodes[pt.y*l->width + pt.x];
7980 if(eqpt(pt, end) || (flagat(l, pt) & not) == 0)
80− if(!neighbor->closed){
81− if(!priqhas(q, neighbor)){
82− neighbor->pt = pt;
83− neighbor->G = p->G + ORTHOCOST;
84− neighbor->H = manhattan(pt, end);
85− priqpush(q, neighbor, pnodecmp);
86− neighbor->parent = p;
87− } else if(p->G + ORTHOCOST < neighbor->G){
88− neighbor->G = p->G + ORTHOCOST;
89− neighbor->parent = p;
81+ if(!neigh->closed){
82+ if(!priqhas(q, neigh)){
83+ neigh->pt = pt;
84+ neigh->G = p->G + ORTHOCOST;
85+ neigh->H = manhattan(pt, end);
86+ priqpush(q, neigh, pnodecmp);
87+ neigh->parent = p;
88+ } else if(p->G + ORTHOCOST < neigh->G){
89+ neigh->G = p->G + ORTHOCOST;
90+ neigh->parent = p;
9091 }
9192 }
9293 }
ui.c+45−14
@@ -44,20 +44,40 @@ struct Msg · 43 unmodified lines
4444 typedef struct UI UI;
4545 struct UI
4646 {
47+ /* current ui size */
4748 int uisz;
49+
50+ /* colors */
4851 Image **cols;
52+
53+ /* mouse, keyboard */
4954 Mousectl *mc;
5055 Keyboardctl *kc;
56+
57+ /* log chan */
5158 Channel *msgc;
59+
60+ /* linked list of current msgs */
5261 Msg *msg;
5362 int nmsg;
63+
64+ /* tileset */
5465 Tileset *tiles;
66+
67+ /* level view rectangle, in tiles */
5568 Rectangle viewr;
69+ /* ui rectangle, in pixels */
5670 Rectangle uir;
5771 Point camp;
58− int autoidle;
72+
73+ /* timer tick, see /^tickproc */
5974 Channel *tickcmd;
6075 Channel *tick;
76+
77+ /* should auto-move? */
78+ int autoidle;
79+ /* show far away messages? */
80+ int farmsg;
6181 } ui;
6282
6383
@@ -115,7 +135,7 @@ uiinit(char *name) · 51 unmodified lines
115135
116136 proccreate(tickproc, nil, 4096);
117137
118− if((ui.msgc = chancreate(sizeof(Msg*), 100)) == nil)
138+ if((ui.msgc = chancreate(sizeof(Msg*), 1000)) == nil)
119139 sysfatal("chancreate: %r");
120140
121141 tileset = getenv("tileset");
@@ -223,8 +243,7 @@ static void · 101 unmodified lines
223243 drawlevel(Level *l, Tileset *ts, Rectangle r)
224244 {
225245 int what;
226− ulong maxhp;
227− double hp;
246+ double hp, maxhp;
228247 char buf[16];
229248 Point p, p2, sp, bot;
230249 Image *c;
@@ -249,10 +268,10 @@ drawlevel(Level *l, Tileset *ts, Rectangle r) · 18 unmodified lines
249268 else if(t->feat)
250269 what = t->feat;
251270 drawtile(ts, screen, sp, what);
252− if(t->monst != nil && t->monst != player){
271+ if(t->monst != nil){
253272 m = t->monst;
254− hp = m->hp, maxhp = m->md->maxhp;
255− snprint(buf, sizeof(buf), "%.0f/%lud HP", hp, maxhp);
273+ hp = m->hp, maxhp = m->maxhp;
274+ snprint(buf, sizeof(buf), "%.0f/%.0f HP", hp, maxhp);
256275 if(hp > (maxhp/4)*3)
257276 c = ui.cols[CGREEN];
258277 else if(hp > maxhp/2)
@@ -263,6 +282,9 @@ drawlevel(Level *l, Tileset *ts, Rectangle r) · 4 unmodified lines
263282 c = ui.cols[CRED];
264283 p2 = addpt(sp, bot);
265284 string(screen, p2, c, ZP, smallfont, buf);
285+ p2.y -= smallfont->height;
286+ snprint(buf, sizeof(buf), "L%ld", m->xpl);
287+ string(screen, p2, display->white, ZP, smallfont, buf);
266288 /* debugging the ai state */
267289 if(debug){
268290 if(m->ai != nil){
@@ -281,8 +303,7 @@ static void · 12 unmodified lines
281303 drawui(Rectangle r)
282304 {
283305 int i, logline, sw;
284− ulong maxhp;
285− double hp;
306+ double hp, maxhp;
286307 Point p;
287308 Image *c;
288309 char buf[256];
@@ -322,8 +343,12 @@ drawui(Rectangle r) · 33 unmodified lines
322343 p = string(screen, p, display->white, ZP, font, buf);
323344 p.x += sw;
324345
325− hp = player->hp, maxhp = player->md->maxhp;
326− snprint(buf, sizeof(buf), "%.0f/%lud HP", hp, maxhp);
346+ snprint(buf, sizeof(buf), "L:%ld XP:%ld/%ld", player->xpl, player->xp, xpcalc(player->xpl));
347+ p = string(screen, p, display->white, ZP, font, buf);
348+ p.x += sw;
349+
350+ hp = player->hp, maxhp = player->maxhp;
351+ snprint(buf, sizeof(buf), "HP:%.0f/%.0f", hp, maxhp);
327352 if(hp > (maxhp/4)*3)
328353 c = ui.cols[CGREEN];
329354 else if(hp > maxhp/2)
@@ -335,7 +360,7 @@ drawui(Rectangle r) · 5 unmodified lines
335360 p = string(screen, p, c, ZP, font, buf);
336361 p.x += sw;
337362
338− snprint(buf, sizeof(buf), "%ld AC", player->ac);
363+ snprint(buf, sizeof(buf), "AC:%ld", player->ac);
339364 p = string(screen, p, display->white, ZP, font, buf);
340365 p.x += sw;
341366
@@ -405,6 +430,9 @@ dbgmenu(int idx, char **s) · 63 unmodified lines
405430 case 1:
406431 *s = "revive and gain max hp";
407432 return 'r';
433+ case 2:
434+ *s = "toggle far messages";
435+ return 'f';
408436 }
409437 return Runemax;
410438 }
@@ -483,7 +511,7 @@ uiexec(AIState *ai) · 72 unmodified lines
483511 if(c == 0x7f)
484512 threadexitsall(nil);
485513
486− if(gameover > 0){
514+ if(gameover > 0 && gameover++ < 2){
487515 bad("you are dead. game over, man.");
488516 }
489517
@@ -515,12 +543,15 @@ uiexec(AIState *ai) · 25 unmodified lines
515543
516544 incref(&player->ref);
517545 player->flags &= ~Mdead;
518− player->hp = player->md->maxhp;
546+ player->hp = player->maxhp;
519547 setflagat(player->l, player->pt, Fhasmonster);
520548 tileat(player->l, player->pt)->monst = player;
521549 tileat(player->l, player->pt)->unit = player->type;
522550 gameover = 0;
523551 break;
552+ case 2:
553+ farmsg = !farmsg;
554+ break;
524555 }
525556 redraw(&ui, 0, 0);
526557 continue;