1 #include "Monster.h"
2 
3 Monster monsters[256];
4 LiveMonster live_monsters[256];
5 int live_monster_count;
6 int move_monsters;
7 
8 int getLiveMonsterFace(LiveMonster * live);
9 
10 SDL_Rect extended_screen_rect;
11 
12 void
defaultMoveMonster(LiveMonster * live)13 defaultMoveMonster(LiveMonster * live)
14 {
15   // no-op
16 }
17 
18 void
defaultDrawMonster(SDL_Rect * pos,LiveMonster * live,SDL_Surface * surface,SDL_Surface * img)19 defaultDrawMonster(SDL_Rect * pos, LiveMonster * live, SDL_Surface * surface,
20                    SDL_Surface * img)
21 {
22   SDL_BlitSurface(img, NULL, surface, pos);
23 }
24 
25 void
defaultBreedMonster(LiveMonster * live,SDL_Rect * pos)26 defaultBreedMonster(LiveMonster * live, SDL_Rect * pos)
27 {
28   // no-op
29 }
30 
31 void
initMonsterPos(Position * pos,LiveMonster * live)32 initMonsterPos(Position * pos, LiveMonster * live)
33 {
34   pos->pos_x = live->pos_x;
35   pos->pos_y = live->pos_y;
36   pos->pixel_x = live->pixel_x;
37   pos->pixel_y = live->pixel_y;
38   pos->w = images[live->monster->image_index[0]]->image->w / TILE_W;
39   pos->h = images[live->monster->image_index[0]]->image->h / TILE_H;
40 }
41 
42 int
stepMonsterLeft(LiveMonster * live,int float_ok)43 stepMonsterLeft(LiveMonster * live, int float_ok)
44 {
45   Position pos;
46   int fail = 0;
47   LiveMonster old;
48   memcpy(&old, live, sizeof(LiveMonster));
49   live->pixel_x -= live->speed_x + map.max_speed_boost;
50   if(live->pixel_x < 0) {
51     live->pos_x--;
52     live->pixel_x = TILE_W + live->pixel_x;
53     if(live->pos_x < 0) {
54       fail = 1;
55     }
56   }
57   // collision detection
58   if(!fail) {
59     initMonsterPos(&pos, live);
60     if(containsType(&pos, TYPE_WALL | TYPE_DOOR)
61        || !(float_ok || onSolidGround(&pos)))
62       fail = 1;
63   }
64   if(fail) {
65     memcpy(live, &old, sizeof(LiveMonster));
66     return 0;
67   }
68   return 1;
69 }
70 
71 int
stepMonsterUp(LiveMonster * live)72 stepMonsterUp(LiveMonster * live)
73 {
74   Position pos;
75   int fail = 0;
76   LiveMonster old;
77   memcpy(&old, live, sizeof(LiveMonster));
78   live->pixel_y -= live->speed_y + map.max_speed_boost;
79   if(live->pixel_y < 0) {
80     live->pos_y--;
81     live->pixel_y = TILE_H + live->pixel_y;
82     if(live->pos_y < 0) {
83       fail = 1;
84     }
85   }
86   // collision detection
87   if(!fail) {
88     initMonsterPos(&pos, live);
89     if(containsType(&pos, TYPE_WALL | TYPE_DOOR))
90       fail = 1;
91   }
92   if(fail) {
93     memcpy(live, &old, sizeof(LiveMonster));
94     return 0;
95   }
96   return 1;
97 }
98 
99 int
stepMonsterRight(LiveMonster * live,int float_ok)100 stepMonsterRight(LiveMonster * live, int float_ok)
101 {
102   Position pos;
103   int fail = 0;
104   LiveMonster old;
105   memcpy(&old, live, sizeof(LiveMonster));
106   live->pixel_x += live->speed_x + map.max_speed_boost;
107   if(live->pixel_x >= TILE_W) {
108     live->pos_x++;
109     live->pixel_x = live->pixel_x - TILE_W;
110     if(live->pos_x >= map.w) {
111       fail = 1;
112     }
113   }
114   // collision detection
115   if(!fail) {
116     initMonsterPos(&pos, live);
117     if(containsType(&pos, TYPE_WALL | TYPE_DOOR)
118        || !(float_ok || onSolidGround(&pos)))
119       fail = 1;
120   }
121   if(fail) {
122     memcpy(live, &old, sizeof(LiveMonster));
123     return 0;
124   }
125   return 1;
126 }
127 
128 int
stepMonsterDown(LiveMonster * live)129 stepMonsterDown(LiveMonster * live)
130 {
131   Position pos;
132   int fail = 0;
133   LiveMonster old;
134 
135   memcpy(&old, live, sizeof(LiveMonster));
136 
137   live->pixel_y += live->speed_y + map.max_speed_boost;
138   if(live->pixel_y >= TILE_H) {
139     live->pos_y++;
140     live->pixel_y = live->pixel_y - TILE_H;
141     if(live->pos_y >= map.h)
142       fail = 1;
143   }
144   // collision detection
145   if(!fail) {
146     initMonsterPos(&pos, live);
147     if(containsType(&pos, TYPE_WALL | TYPE_DOOR))
148       fail = 1;
149   }
150 
151   if(fail) {
152     memcpy(live, &old, sizeof(LiveMonster));
153     return 0;
154   }
155   return 1;
156 }
157 
158 void
moveDemon(LiveMonster * live_monster)159 moveDemon(LiveMonster * live_monster)
160 {
161   int j;
162 
163   // move sideways until you hit a wall or an edge
164   j = 1 + (int) (10.0 * rand() / (RAND_MAX));
165   if(j > 7) {
166     // increment the face to display
167     live_monster->face++;
168     if(live_monster->face >=
169        live_monster->monster->image_count * live_monster->monster->face_mod)
170       live_monster->face = 0;
171 
172     if(live_monster->dir == DIR_LEFT) {
173       if(!stepMonsterLeft(live_monster, 0)) {
174         live_monster->dir = DIR_RIGHT;
175       }
176     } else {
177       if(!stepMonsterRight(live_monster, 0)) {
178         live_monster->dir = DIR_LEFT;
179       }
180     }
181   }
182 }
183 
184 void
movePlatform(LiveMonster * live_monster)185 movePlatform(LiveMonster * live_monster)
186 {
187   // increment the face to display
188   live_monster->face++;
189   if(live_monster->face >=
190      live_monster->monster->image_count * live_monster->monster->face_mod)
191     live_monster->face = 0;
192 
193   // move sideways until you hit a wall or an edge
194   if(live_monster->dir == DIR_LEFT) {
195     if(!stepMonsterLeft(live_monster, 1)) {
196       live_monster->dir = DIR_RIGHT;
197     }
198   } else {
199     if(!stepMonsterRight(live_monster, 1)) {
200       live_monster->dir = DIR_LEFT;
201     }
202   }
203 }
204 
205 void
movePlatform2(LiveMonster * live_monster)206 movePlatform2(LiveMonster * live_monster)
207 {
208   // increment the face to display
209   live_monster->face++;
210   if(live_monster->face >=
211      live_monster->monster->image_count * live_monster->monster->face_mod)
212     live_monster->face = 0;
213 
214   // move up and down
215   if(live_monster->dir == DIR_DOWN) {
216     if(!stepMonsterDown(live_monster)) {
217       live_monster->dir = DIR_UP;
218     }
219   } else {
220     if(!stepMonsterUp(live_monster)) {
221       live_monster->dir = DIR_DOWN;
222     }
223   }
224 }
225 
226 void
moveEndGame(LiveMonster * live_monster)227 moveEndGame(LiveMonster * live_monster)
228 {
229   int *p;
230   p = (int *) (live_monster->custom);
231   // if our custom int is set
232   if(*p > 0) {
233     // move up and down
234     if(live_monster->dir == DIR_DOWN) {
235       if(!stepMonsterDown(live_monster)) {
236         live_monster->dir = DIR_UP;
237       }
238     } else {
239       if(!stepMonsterUp(live_monster)) {
240         live_monster->dir = DIR_DOWN;
241       } else {
242         if((*p)++ >= 2) {
243           *p = 1;
244           live_monster->dir = DIR_DOWN;
245         }
246       }
247     }
248   }
249 }
250 
251 void
moveCrab(LiveMonster * live_monster)252 moveCrab(LiveMonster * live_monster)
253 {
254   // increment the face to display
255   live_monster->face++;
256   if(live_monster->face >=
257      live_monster->monster->image_count * live_monster->monster->face_mod)
258     live_monster->face = 0;
259 
260   // move sideways until you hit a wall or an edge
261   if(live_monster->dir == DIR_LEFT) {
262     if(!stepMonsterLeft(live_monster, 0)) {
263       live_monster->dir = DIR_RIGHT;
264     }
265   } else {
266     if(!stepMonsterRight(live_monster, 0)) {
267       live_monster->dir = DIR_LEFT;
268     }
269   }
270 }
271 
272 void
moveGhost(LiveMonster * live_monster)273 moveGhost(LiveMonster * live_monster)
274 {
275   int j;
276   // increment the face to display
277   j = (int) (10.0 * rand() / (RAND_MAX));
278   if(!j) {
279     live_monster->face++;
280     if(live_monster->face >=
281        live_monster->monster->image_count * live_monster->monster->face_mod)
282       live_monster->face = 0;
283   }
284   // move up/down until you hit a wall or an edge
285   if(live_monster->dir == DIR_UP) {
286     if(!stepMonsterUp(live_monster)) {
287       live_monster->dir = DIR_DOWN;
288     }
289   } else {
290     if(!stepMonsterDown(live_monster)) {
291       live_monster->dir = DIR_UP;
292     }
293   }
294 }
295 
296 void
moveBat(LiveMonster * live_monster)297 moveBat(LiveMonster * live_monster)
298 {
299   int j;
300 
301   // increment the face to display
302   live_monster->face++;
303   if(live_monster->face >=
304      live_monster->monster->image_count * live_monster->monster->face_mod)
305     live_monster->face = 0;
306 
307   if(live_monster->dir == DIR_UPDATE) {
308     j = (int) (30.0 * rand() / (RAND_MAX));
309     if(j == 0) {
310       live_monster->dir = DIR_LEFT;
311       if(!stepMonsterLeft(live_monster, 1)) {
312         live_monster->dir = DIR_RIGHT;
313         stepMonsterRight(live_monster, 1);
314       }
315     }
316   } else {
317     // move sideways until you hit a wall or an edge
318     if(live_monster->pos_y % 6 < 3)
319       stepMonsterDown(live_monster);
320     else
321       stepMonsterUp(live_monster);
322 
323     if(live_monster->dir == DIR_LEFT) {
324       if(!stepMonsterLeft(live_monster, 1)) {
325         live_monster->dir = DIR_UPDATE;
326       }
327     } else {
328       if(!stepMonsterRight(live_monster, 1)) {
329         live_monster->dir = DIR_UPDATE;
330       }
331     }
332   }
333 }
334 
335 void
breedBullet(LiveMonster * live,SDL_Rect * pos)336 breedBullet(LiveMonster * live, SDL_Rect * pos)
337 {
338   addLiveMonsterChangeMap(MONSTER_BULLET, img_bullet[0], live->pos_x + 2,
339                           live->pos_y, 0);
340   live_monsters[live_monster_count - 1].pixel_x = 10;
341   live_monsters[live_monster_count - 1].dir = DIR_RIGHT;
342   live_monsters[live_monster_count - 1].parent = live;
343   live->child_count++;
344 }
345 
346 void
breedBullet2(LiveMonster * live,SDL_Rect * pos)347 breedBullet2(LiveMonster * live, SDL_Rect * pos)
348 {
349   addLiveMonsterChangeMap(MONSTER_BULLET, img_bullet[0], live->pos_x - 1,
350                           live->pos_y, 0);
351   live_monsters[live_monster_count - 1].dir = DIR_LEFT;
352   live_monsters[live_monster_count - 1].parent = live;
353   live->child_count++;
354 }
355 
356 void
moveBullet(LiveMonster * live_monster)357 moveBullet(LiveMonster * live_monster)
358 {
359   // increment the face to display
360   live_monster->face++;
361   if(live_monster->face >=
362      live_monster->monster->image_count * live_monster->monster->face_mod)
363     live_monster->face = 0;
364 
365   // move sideways until you hit a wall
366   if(live_monster->dir == DIR_LEFT) {
367     if(!stepMonsterLeft(live_monster, 1)) {
368       live_monster->remove_me = 1;
369     }
370   } else {
371     if(!stepMonsterRight(live_monster, 1)) {
372       live_monster->remove_me = 1;
373     }
374   }
375 }
376 
377 void
moveArrow(LiveMonster * live_monster)378 moveArrow(LiveMonster * live_monster)
379 {
380   // increment the face to display
381   int old = getLiveMonsterFace(live_monster);
382   int face;
383   int n = (int) (100.0 * rand() / (RAND_MAX + 1.0));
384   if(n > 0)
385     return;
386 
387   live_monster->face++;
388   if(live_monster->face >=
389      live_monster->monster->image_count * live_monster->monster->face_mod) {
390     live_monster->face = 0;
391   }
392   face = getLiveMonsterFace(live_monster);
393   if(old < face) {
394     live_monster->pos_y--;
395   } else if(old > face) {
396     live_monster->pos_y++;
397   }
398 }
399 
400 void
moveTorch(LiveMonster * live_monster)401 moveTorch(LiveMonster * live_monster)
402 {
403   // increment the face to display
404   live_monster->face++;
405   if(live_monster->face >=
406      live_monster->monster->image_count * live_monster->monster->face_mod)
407     live_monster->face = 0;
408 }
409 
410 void
moveBear(LiveMonster * live_monster)411 moveBear(LiveMonster * live_monster)
412 {
413   // increment the face to display
414   int n = live_monster->monster->image_count / 2;
415   live_monster->face++;
416   // move sideways until you hit a wall or an edge
417   if(live_monster->dir == DIR_LEFT) {
418     if(live_monster->face >= n * live_monster->monster->face_mod)
419       live_monster->face = 0;
420     if(!stepMonsterLeft(live_monster, 0)) {
421       live_monster->dir = DIR_RIGHT;
422       live_monster->face = 0;
423     }
424   } else {
425     if(live_monster->face >=
426        live_monster->monster->image_count * live_monster->monster->face_mod)
427       live_monster->face = n * live_monster->monster->face_mod;
428     if(!stepMonsterRight(live_monster, 0)) {
429       live_monster->dir = DIR_LEFT;
430       live_monster->face = n * live_monster->monster->face_mod;
431     }
432   }
433 }
434 
435 void
moveFire(LiveMonster * live_monster)436 moveFire(LiveMonster * live_monster)
437 {
438   // move up and down until you hit an edge
439   if(live_monster->dir == DIR_DOWN) {
440     if(!stepMonsterDown(live_monster)) {
441       live_monster->dir = DIR_UP;
442       live_monster->speed_y =
443         live_monster->monster->start_speed_y +
444         ((int) ((MAX_RANDOM_SPEED / 2) * rand() / (RAND_MAX)));
445       if(live_monster->speed_y <= 0)
446         live_monster->speed_y = 1;
447     }
448   } else {
449     if(!stepMonsterUp(live_monster)) {
450       live_monster->dir = DIR_DOWN;
451       live_monster->speed_y =
452         live_monster->monster->start_speed_y +
453         ((int) ((MAX_RANDOM_SPEED / 2) * rand() / (RAND_MAX)));
454       if(live_monster->speed_y <= 0)
455         live_monster->speed_y = 1;
456     }
457   }
458 }
459 
460 void
drawFire(SDL_Rect * pos,LiveMonster * live,SDL_Surface * surface,SDL_Surface * img)461 drawFire(SDL_Rect * pos, LiveMonster * live, SDL_Surface * surface,
462          SDL_Surface * img)
463 {
464   int y, original;
465   SDL_Rect p, q;
466   Position position;
467   int index;
468 
469   p.x = pos->x;
470   //  p.y = (pos->y / TILE_H) * TILE_H - TILE_H;
471   original = y = p.y = pos->y;
472   p.w = pos->w;
473   p.h = pos->h;
474 
475   position.pos_x = live->pos_x;
476   position.pos_y = live->pos_y;
477   position.pixel_x = live->pixel_x;
478   position.pixel_y = live->pixel_y;
479   position.w = p.w / TILE_W;
480   position.h = p.h / TILE_H;
481 
482   while(position.pos_y < map.h &&
483         !containsType(&position, TYPE_WALL | TYPE_DOOR)) {
484     index = (int) ((double) (live->monster->image_count) * rand() / RAND_MAX);
485     SDL_BlitSurface(images[live->monster->image_index[index]]->image, NULL,
486                     surface, &p);
487     p.x = pos->x;
488     y += TILE_H;
489     p.y = y;
490     p.w = pos->w;
491     p.h = pos->h;
492     position.pos_y++;
493   }
494 
495   // save the fire column's height in the custom field
496   *((int *) (live->custom)) = y - original;
497 
498   // draw the last one
499   if(position.pos_y > live->pos_y && live->pixel_y) {
500     index = (int) ((double) (live->monster->image_count) * rand() / RAND_MAX);
501     q.x = 0;
502     q.y = 0;
503     q.w = images[live->monster->image_index[index]]->image->w;
504     q.h = images[live->monster->image_index[index]]->image->h - live->pixel_y;
505     SDL_BlitSurface(images[live->monster->image_index[index]]->image, &q,
506                     surface, &p);
507   }
508 }
509 
510 void
moveSmasher(LiveMonster * live_monster)511 moveSmasher(LiveMonster * live_monster)
512 {
513   // move up and down until you hit an edge
514   if(live_monster->dir == DIR_DOWN) {
515     if(!stepMonsterDown(live_monster)) {
516       live_monster->dir = DIR_UP;
517       live_monster->speed_y =
518         live_monster->monster->start_speed_y / 2 +
519         ((int) ((MAX_RANDOM_SPEED / 2) * rand() / (RAND_MAX)));
520       if(live_monster->speed_y <= 0)
521         live_monster->speed_y = 1;
522     }
523   } else {
524     if(!stepMonsterUp(live_monster)) {
525       live_monster->dir = DIR_DOWN;
526       live_monster->speed_y =
527         live_monster->monster->start_speed_y +
528         ((int) ((MAX_RANDOM_SPEED / 2) * rand() / (RAND_MAX)));
529       if(live_monster->speed_y <= 0)
530         live_monster->speed_y = 1;
531     }
532   }
533 }
534 
535 void
drawSmasher(SDL_Rect * pos,LiveMonster * live,SDL_Surface * surface,SDL_Surface * img)536 drawSmasher(SDL_Rect * pos, LiveMonster * live, SDL_Surface * surface,
537             SDL_Surface * img)
538 {
539   int y = 0;
540   SDL_Rect p, q;
541   Position position;
542   int first_image, first, second;
543   int skip;
544 
545   first_image = live->monster->image_index[0];
546   first = (first_image == img_smash || first_image == img_smash2 ? img_smash :
547            (first_image == img_smash3
548             || first_image == img_smash4 ? img_smash3 : img_spider));
549   second =
550     (first ==
551      img_smash ? img_smash2 : (first ==
552                                img_smash3 ? img_smash4 : img_spider2));
553 
554   p.x = pos->x;
555   //  p.y = (pos->y / TILE_H) * TILE_H - TILE_H;
556   p.y = pos->y - TILE_H;
557   p.w = pos->w;
558   p.h = pos->h;
559 
560   position.pos_x = live->pos_x;
561   position.pos_y = live->pos_y - 1;
562   position.pixel_x = 0;
563   position.pixel_y = 0;
564   position.w = p.w / TILE_W;
565   position.h = p.h / TILE_H;
566 
567   skip = 0;
568   while(position.pos_y >= 0 &&
569         !containsType(&position, TYPE_WALL | TYPE_DOOR)) {
570     SDL_BlitSurface(images[second]->image, NULL, surface, &p);
571     // HACK part 1: if p->y is reset to 0 the image was cropped.
572     y = p.y;
573     if(!y) {
574       skip = 1;
575       break;
576     }
577     p.x = pos->x;
578     p.y -= TILE_H;
579     p.w = pos->w;
580     p.h = pos->h;
581     position.pos_y--;
582   }
583   // draw the top one.
584   // HACK part 2: if y is 0 the image was cropped, don't draw
585   //  if(y && live->pixel_y) {
586   if(!skip && live->pixel_y != 0) {
587     p.x = pos->x;
588     p.y += TILE_H;
589     p.y -= live->pixel_y;
590     p.w = pos->w;
591     p.h = pos->h;
592 
593     q.x = 0;
594     q.y = TILE_H - live->pixel_y;
595     q.w = p.w;
596     q.h = images[second]->image->h - q.y;
597     SDL_BlitSurface(images[second]->image, &q, surface, &p);
598   }
599 
600   SDL_BlitSurface(images[first]->image, NULL, surface, pos);
601 }
602 
603 int
defaultDetectMonster(Position * pos,LiveMonster * live)604 defaultDetectMonster(Position * pos, LiveMonster * live)
605 {
606   SDL_Rect monster, check;
607   SDL_Surface *img;
608 
609   // convert pos to pixels
610   check.x = pos->pos_x * TILE_W + pos->pixel_x;
611   check.y = pos->pos_y * TILE_H + pos->pixel_y;
612   check.w = pos->w * TILE_W;
613   check.h = pos->h * TILE_H;
614 
615   // get the live monster's pixel rect.
616   img = images[live->monster->image_index[getLiveMonsterFace(live)]]->image;
617   monster.x = live->pos_x * TILE_W + live->pixel_x;
618   monster.y = live->pos_y * TILE_H + live->pixel_y;
619   monster.w = img->w;
620   monster.h = img->h;
621 
622   // compare
623   return intersectsBy(&check, &monster,
624                       (live->monster->harmless ? 1 : MONSTER_COLLISION_FUZZ));
625 }
626 
627 int
detectFire(Position * pos,LiveMonster * live)628 detectFire(Position * pos, LiveMonster * live)
629 {
630   SDL_Rect monster, check;
631 
632   // convert pos to pixels
633   check.x = pos->pos_x * TILE_W + pos->pixel_x;
634   check.y = pos->pos_y * TILE_H + pos->pixel_y;
635   check.w = pos->w * TILE_W;
636   check.h = pos->h * TILE_H;
637 
638   // get the live monster's pixel rect.
639   //  img = images[live->monster->image_index[getLiveMonsterFace(live)]]->image;
640   monster.x = live->pos_x * TILE_W + live->pixel_x;
641   monster.y = live->pos_y * TILE_H + live->pixel_y;
642   monster.w = TILE_W;
643   monster.h = *((int *) (live->custom));
644 
645   // compare
646   return intersectsBy(&check, &monster,
647                       (live->monster->harmless ? 1 : MONSTER_COLLISION_FUZZ));
648 }
649 
650 void
allocFireCustom(LiveMonster * live)651 allocFireCustom(LiveMonster * live)
652 {
653 //warning: use of cast expressions as lvalues is deprecated
654 //if(((int *) live->custom = (int *) malloc(sizeof(int))) == NULL) {
655   if((live->custom = (int *) malloc(sizeof(int))) == NULL) {
656     fprintf(stderr,
657             "Out of memory when trying to allocate custom storage for fire column.\n");
658   }
659 }
660 
661 void
allocEndGameCustom(LiveMonster * live)662 allocEndGameCustom(LiveMonster * live)
663 {
664 //if(((int *) live->custom = (int *) malloc(sizeof(int))) == NULL) {
665   if((live->custom = (int *) malloc(sizeof(int))) == NULL) {
666     fprintf(stderr,
667             "Out of memory when trying to allocate custom storage for end game.\n");
668   }
669   *((int *) (live->custom)) = 0;
670 }
671 
672 /**
673    Remember here, images are not yet initialized!
674  */
675 void
initMonsters()676 initMonsters()
677 {
678   int i;
679 
680   move_monsters = 1;
681   live_monster_count = 0;
682 
683   // init the screen rectangle.
684   extended_screen_rect.x = -MONSTER_EXTRA_X * TILE_W;
685   extended_screen_rect.y = -MONSTER_EXTRA_Y * TILE_H;
686   extended_screen_rect.w = screen->w + 2 * MONSTER_EXTRA_X * TILE_W;
687   extended_screen_rect.h = screen->h + 2 * MONSTER_EXTRA_Y * TILE_H;
688 
689   // common properties.
690   for(i = 0; i < MONSTER_COUNT; i++) {
691     monsters[i].start_speed_x = 2;
692     monsters[i].start_speed_y = 2;
693     monsters[i].image_count = 0;
694     monsters[i].moveMonster = defaultMoveMonster;
695     monsters[i].drawMonster = defaultDrawMonster;
696     monsters[i].face_mod = 1;
697     monsters[i].type = i;
698     monsters[i].harmless = 0;
699     monsters[i].random_speed = 1;
700     monsters[i].detectMonster = defaultDetectMonster;
701     monsters[i].allocCustom = NULL;
702     monsters[i].breeds = NULL;
703     monsters[i].breedMonster = defaultBreedMonster;
704     monsters[i].damage = 1;
705   }
706 
707   // crab monster
708   strcpy(monsters[MONSTER_CRAB].name, "dungenous crab");
709   monsters[MONSTER_CRAB].moveMonster = moveCrab;
710   monsters[MONSTER_CRAB].start_speed_x = 2;
711   monsters[MONSTER_CRAB].start_speed_y = 2;
712   // animation 2x slower
713   monsters[MONSTER_CRAB].face_mod = 2;
714   monsters[MONSTER_CRAB].random_speed = 0;
715 
716   // smasher monster
717   strcpy(monsters[MONSTER_SMASHER].name, "smasher");
718   monsters[MONSTER_SMASHER].moveMonster = moveSmasher;
719   monsters[MONSTER_SMASHER].drawMonster = drawSmasher;
720   monsters[MONSTER_SMASHER].start_speed_x = 4;
721   monsters[MONSTER_SMASHER].start_speed_y = 4;
722   monsters[MONSTER_SMASHER].damage = 2;
723 
724   // purple smasher
725   strcpy(monsters[MONSTER_SMASHER2].name, "cursed smasher");
726   monsters[MONSTER_SMASHER2].moveMonster = moveSmasher;
727   monsters[MONSTER_SMASHER2].drawMonster = drawSmasher;
728   monsters[MONSTER_SMASHER2].start_speed_x = 4;
729   monsters[MONSTER_SMASHER2].start_speed_y = 4;
730   monsters[MONSTER_SMASHER2].damage = 4;
731 
732   // demon monster
733   strcpy(monsters[MONSTER_DEMON].name, "little demon");
734   monsters[MONSTER_DEMON].moveMonster = moveDemon;
735   monsters[MONSTER_DEMON].start_speed_x = 4;
736   monsters[MONSTER_DEMON].start_speed_y = 4;
737   // animation 2x slower
738   monsters[MONSTER_DEMON].face_mod = 4;
739   monsters[MONSTER_DEMON].damage = 2;
740 
741   // platforms
742   strcpy(monsters[MONSTER_PLATFORM].name, "platform");
743   monsters[MONSTER_PLATFORM].moveMonster = movePlatform;
744   monsters[MONSTER_PLATFORM].start_speed_x = 4;
745   monsters[MONSTER_PLATFORM].start_speed_y = 4;
746   monsters[MONSTER_PLATFORM].harmless = 1;
747 
748   // platform2
749   strcpy(monsters[MONSTER_PLATFORM2].name, "platform2");
750   monsters[MONSTER_PLATFORM2].moveMonster = movePlatform2;
751   monsters[MONSTER_PLATFORM2].start_speed_x = 4;
752   monsters[MONSTER_PLATFORM2].start_speed_y = 4;
753   monsters[MONSTER_PLATFORM2].harmless = 1;
754 
755   // spider
756   strcpy(monsters[MONSTER_SPIDER].name, "patient spider");
757   monsters[MONSTER_SPIDER].moveMonster = moveSmasher;
758   monsters[MONSTER_SPIDER].drawMonster = drawSmasher;
759   monsters[MONSTER_SPIDER].start_speed_x = 2;
760   monsters[MONSTER_SPIDER].start_speed_y = 2;
761   monsters[MONSTER_SPIDER].damage = 5;
762 
763   // bear monster
764   strcpy(monsters[MONSTER_BEAR].name, "arctic cave bear");
765   monsters[MONSTER_BEAR].moveMonster = moveBear;
766   monsters[MONSTER_BEAR].start_speed_x = 1;
767   monsters[MONSTER_BEAR].start_speed_y = 1;
768   monsters[MONSTER_BEAR].face_mod = 8;
769   monsters[MONSTER_BEAR].random_speed = 0;
770   monsters[MONSTER_BEAR].damage = 7;
771 
772   // torch
773   strcpy(monsters[MONSTER_TORCH].name, "torch");
774   monsters[MONSTER_TORCH].moveMonster = moveTorch;
775   monsters[MONSTER_TORCH].start_speed_x = 1;
776   monsters[MONSTER_TORCH].start_speed_y = 1;
777   monsters[MONSTER_TORCH].face_mod = 6;
778   monsters[MONSTER_TORCH].harmless = 1;
779 
780   // arrow trap
781   strcpy(monsters[MONSTER_ARROW].name, "arrow trap");
782   monsters[MONSTER_ARROW].moveMonster = moveArrow;
783   monsters[MONSTER_ARROW].start_speed_x = 1;
784   monsters[MONSTER_ARROW].start_speed_y = 1;
785   monsters[MONSTER_ARROW].face_mod = 1;
786   monsters[MONSTER_ARROW].damage = 1;
787 
788   // fire
789   strcpy(monsters[MONSTER_FIRE].name, "fire trap");
790   monsters[MONSTER_FIRE].moveMonster = moveFire;
791   monsters[MONSTER_FIRE].drawMonster = drawFire;
792   monsters[MONSTER_FIRE].start_speed_x = 2;
793   monsters[MONSTER_FIRE].start_speed_y = 2;
794   monsters[MONSTER_FIRE].face_mod = 3;
795   monsters[MONSTER_FIRE].detectMonster = detectFire;
796   monsters[MONSTER_FIRE].allocCustom = allocFireCustom;
797   monsters[MONSTER_FIRE].damage = 4;
798 
799   // star
800   strcpy(monsters[MONSTER_STAR].name, "star");
801   monsters[MONSTER_STAR].moveMonster = moveTorch; // re-use moveTorch; not a bug
802   monsters[MONSTER_STAR].start_speed_x = 1;
803   monsters[MONSTER_STAR].start_speed_y = 1;
804   monsters[MONSTER_STAR].face_mod = 4;
805   monsters[MONSTER_STAR].harmless = 1;
806 
807   // bullet
808   strcpy(monsters[MONSTER_BULLET].name, "curious cannonball");
809   monsters[MONSTER_BULLET].moveMonster = moveBullet;
810   monsters[MONSTER_BULLET].start_speed_x = 8;
811   monsters[MONSTER_BULLET].start_speed_y = 4;
812   monsters[MONSTER_BULLET].face_mod = 1;
813   monsters[MONSTER_BULLET].random_speed = 0;
814   monsters[MONSTER_BULLET].damage = 10;
815 
816   // cannon
817   strcpy(monsters[MONSTER_CANNON].name, "cannon");
818   monsters[MONSTER_CANNON].harmless = 1;
819   monsters[MONSTER_CANNON].breeds = &monsters[MONSTER_BULLET];
820   monsters[MONSTER_CANNON].breedMonster = breedBullet;
821   monsters[MONSTER_CANNON].max_children = 1;  // 1 bullet active at a time
822 
823   // cannon2
824   strcpy(monsters[MONSTER_CANNON2].name, "cannon");
825   monsters[MONSTER_CANNON2].harmless = 1;
826   monsters[MONSTER_CANNON2].breeds = &monsters[MONSTER_BULLET];
827   monsters[MONSTER_CANNON2].breedMonster = breedBullet2;
828   monsters[MONSTER_CANNON2].max_children = 1; // 1 bullet active at a time
829 
830   // bat
831   strcpy(monsters[MONSTER_BAT].name, "vampire bat");
832   monsters[MONSTER_BAT].moveMonster = moveBat;
833   monsters[MONSTER_BAT].start_speed_x = 6;
834   monsters[MONSTER_BAT].start_speed_y = 4;
835   monsters[MONSTER_BAT].face_mod = 3;
836   monsters[MONSTER_BAT].damage = 3;
837 
838   // ghost
839   strcpy(monsters[MONSTER_GHOST].name, "entombed spirit");
840   monsters[MONSTER_GHOST].moveMonster = moveGhost;
841   monsters[MONSTER_GHOST].start_speed_y = 2;
842   monsters[MONSTER_GHOST].damage = 2;
843   monsters[MONSTER_GHOST].face_mod = 4;
844   monsters[MONSTER_GHOST].random_speed = 0;
845 
846   // end game
847   strcpy(monsters[MONSTER_END_GAME].name, "lost friend!");
848   monsters[MONSTER_END_GAME].harmless = 1;
849   monsters[MONSTER_END_GAME].moveMonster = moveEndGame;
850   monsters[MONSTER_END_GAME].start_speed_y = 3;
851   monsters[MONSTER_END_GAME].allocCustom = allocEndGameCustom;
852   monsters[MONSTER_END_GAME].random_speed = 0;
853 
854   // add additional monsters here
855 
856   for(i = 0; i < MONSTER_COUNT; i++) {
857     fprintf(stderr, "Added monster: %s.\n", monsters[i].name);
858   }
859   fflush(stderr);
860 }
861 
862 void
resetMonsters()863 resetMonsters()
864 {
865   live_monster_count = 0;
866 }
867 
868 void
addMonsterImage(int monster_index,int image_index)869 addMonsterImage(int monster_index, int image_index)
870 {
871   monsters[monster_index].image_index[monsters[monster_index].image_count++] =
872     image_index;
873   fprintf(stderr, "monster image added. monster=%d image_count=%d\n",
874           monster_index, monsters[monster_index].image_count);
875   fflush(stderr);
876 }
877 
878 int
isMonsterImage(int image_index)879 isMonsterImage(int image_index)
880 {
881   // new fast way of doing this.
882   if(image_index == EMPTY_MAP)
883     return -1;
884   return images[image_index]->monster_index;
885 }
886 
887 void
addLiveMonster(int monster_index,int image_index,int x,int y)888 addLiveMonster(int monster_index, int image_index, int x, int y)
889 {
890   addLiveMonsterChangeMap(monster_index, image_index, x, y, 1);
891 }
892 
893 void
addLiveMonsterChangeMap(int monster_index,int image_index,int x,int y,int change_map)894 addLiveMonsterChangeMap(int monster_index, int image_index, int x, int y,
895                         int change_map)
896 {
897   int i;
898   Monster *m = &monsters[monster_index];
899   live_monsters[live_monster_count].parent = NULL;
900   live_monsters[live_monster_count].pos_x = x;
901   live_monsters[live_monster_count].pos_y = y;
902   live_monsters[live_monster_count].pixel_x = 0;
903   live_monsters[live_monster_count].pixel_y = 0;
904   if(m->random_speed) {
905     live_monsters[live_monster_count].speed_x =
906       m->start_speed_x + ((int) (MAX_RANDOM_SPEED * rand() / (RAND_MAX)));
907     live_monsters[live_monster_count].speed_y =
908       m->start_speed_y + ((int) (MAX_RANDOM_SPEED * rand() / (RAND_MAX)));
909   } else {
910     live_monsters[live_monster_count].speed_x = m->start_speed_x;
911     live_monsters[live_monster_count].speed_y = m->start_speed_y;
912   }
913   live_monsters[live_monster_count].dir = DIR_NONE;
914   live_monsters[live_monster_count].face = 0;
915   for(i = 0; i < m->image_count; i++) {
916     if(m->image_index[i] == image_index) {
917       live_monsters[live_monster_count].face = i;
918     }
919   }
920   live_monsters[live_monster_count].remove_me = 0;
921   live_monsters[live_monster_count].monster = m;
922   live_monsters[live_monster_count].child_count = 0;
923   // allocate custom storage
924   if(live_monsters[live_monster_count].monster->allocCustom) {
925     live_monsters[live_monster_count].monster->
926       allocCustom(&live_monsters[live_monster_count]);
927   }
928   live_monster_count++;
929 
930   // remove image from map
931   if(change_map) {
932     map.image_index[LEVEL_MAIN][x + (y * map.w)] = EMPTY_MAP;
933   }
934 }
935 
936 void
removeLiveMonster(int live_monster_index)937 removeLiveMonster(int live_monster_index)
938 {
939   int i, t;
940   LiveMonster *p;
941 
942   // debug
943   if(live_monster_index >= live_monster_count) {
944     fprintf(stderr,
945             "Trying to remove monster w. index out of bounds: count=%d index=%d\n",
946             live_monster_count, live_monster_index);
947     for(t = 0; t < live_monster_count; t++) {
948       fprintf(stderr, "\tmonster=%s x=%d y=%d\n",
949               live_monsters[t].monster->name, live_monsters[t].pos_x,
950               live_monsters[t].pos_y);
951     }
952     fflush(stderr);
953     exit(-1);
954   }
955   // add it back to the map
956   p = &live_monsters[live_monster_index];
957   // If the monster was bred (bullets) it doesn't need to be saved in the map.
958   if(!p->parent) {
959     setImageNoCheck(LEVEL_MAIN,
960                     p->pos_x, p->pos_y,
961                     p->monster->image_index[getLiveMonsterFace(p)]);
962     // remove its children
963     for(t = 0; p->child_count && t < live_monster_count; t++) {
964       if(live_monsters[t].parent == p) {
965         removeLiveMonster(t);
966         t--;
967       }
968     }
969   } else {
970     p->parent->child_count--;
971   }
972 
973   // free custom storage
974   if(live_monsters[live_monster_index].monster->allocCustom) {
975     free(live_monsters[live_monster_index].custom);
976   }
977   // remove it from memory
978   for(t = live_monster_index; t < live_monster_count - 1; t++) {
979     for(i = 0; i < live_monster_count; i++) {
980       if(live_monsters[i].parent == &live_monsters[t + 1]) {
981         live_monsters[i].parent = &live_monsters[t];
982       }
983     }
984     memcpy(&live_monsters[t], &live_monsters[t + 1], sizeof(LiveMonster));
985   }
986   live_monster_count--;
987 }
988 
989 void
debugMonsters()990 debugMonsters()
991 {
992   int t, i, n;
993   fprintf(stderr, "Monsters:\n");
994   for(t = 0; t < live_monster_count; t++) {
995     n = -1;
996     for(i = 0; live_monsters[t].parent && i < live_monster_count; i++) {
997       if(&live_monsters[i] == live_monsters[t].parent) {
998         n = i;
999         break;
1000       }
1001     }
1002     fprintf(stderr,
1003             "\t%d monster=%s x=%d y=%d child_count=%d remove_me=%d parent=%d\n",
1004             t, live_monsters[t].monster->name, live_monsters[t].pos_x,
1005             live_monsters[t].pos_y, live_monsters[t].child_count,
1006             live_monsters[t].remove_me, n);
1007   }
1008   fflush(stderr);
1009 }
1010 
1011 void
removeAllLiveMonsters()1012 removeAllLiveMonsters()
1013 {
1014   while(live_monster_count > 0) {
1015     removeLiveMonster(0);
1016   }
1017 }
1018 
1019 /**
1020    Here rect is in pixels where 0, 0 is the screen's left top corner.
1021    Returns 0 for false and non-0 for true.
1022  */
1023 int
isOnScreen(SDL_Rect * rect)1024 isOnScreen(SDL_Rect * rect)
1025 {
1026   return intersects(rect, &extended_screen_rect);
1027 }
1028 
1029 /**
1030    Draw all currently tracked creatures.
1031    start_x, start_y are the offset of the screen's top left edge in pixels.
1032  */
1033 void
drawLiveMonsters(SDL_Surface * surface,int start_x,int start_y)1034 drawLiveMonsters(SDL_Surface * surface, int start_x, int start_y)
1035 {
1036   SDL_Rect pos;
1037   SDL_Surface *img;
1038   int i;
1039 
1040   for(i = 0; i < live_monster_count; i++) {
1041     if(move_monsters)
1042       live_monsters[i].monster->moveMonster(&live_monsters[i]);
1043 
1044     img =
1045       images[live_monsters[i].monster->
1046              image_index[getLiveMonsterFace(&live_monsters[i])]]->image;
1047     pos.x =
1048       live_monsters[i].pos_x * TILE_W - start_x + live_monsters[i].pixel_x;
1049     pos.y =
1050       live_monsters[i].pos_y * TILE_H - start_y + live_monsters[i].pixel_y;
1051     pos.w = img->w;
1052     pos.h = img->h;
1053 
1054     if(live_monsters[i].remove_me || !isOnScreen(&pos)) {
1055       removeLiveMonster(i);
1056     } else {
1057       live_monsters[i].monster->drawMonster(&pos, &live_monsters[i], surface,
1058                                             img);
1059 
1060       if(live_monsters[i].monster->breeds != NULL &&
1061          live_monsters[i].monster->max_children >
1062          live_monsters[i].child_count) {
1063         live_monsters[i].monster->breedMonster(&live_monsters[i], &pos);
1064       }
1065     }
1066   }
1067 }
1068 
1069 int
getLiveMonsterFace(LiveMonster * live)1070 getLiveMonsterFace(LiveMonster * live)
1071 {
1072   return live->face / live->monster->face_mod;
1073 }
1074 
1075 /**
1076    Return live monster if there's a one at position pos,
1077    NULL otherwise.
1078  */
1079 LiveMonster *
detectMonster(Position * pos)1080 detectMonster(Position * pos)
1081 {
1082   int i;
1083   for(i = 0; i < live_monster_count; i++) {
1084     if(live_monsters[i].monster->detectMonster(pos, &live_monsters[i])) {
1085       return &live_monsters[i];
1086     }
1087   }
1088   return NULL;
1089 }
1090