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