1 /* Tower Toppler - Nebulus
2  * Copyright (C) 2000-2012  Andreas R�ver
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18 
19 #include "robots.h"
20 
21 #include "decl.h"
22 #include "level.h"
23 #include "screen.h"
24 #include "toppler.h"
25 #include "sound.h"
26 
27 #include <stdlib.h>
28 
29 #define MAX_OBJECTS 4
30 
31 /* this field contains the robots */
32 static struct {
33 
34   /* the position of the robot */
35   int anglepos;
36   long verticalpos;
37 
38   /* what kind of robot it is, an under classification
39    and what kind it will be after the appearing animation */
40   rob_kinds kind;
41   long subKind;
42   rob_kinds futureKind;
43 
44   /* a timer for the animations of the robots */
45   long time;
46 } object[MAX_OBJECTS];
47 
48 
49 /* the position up to where the robots are worked out */
50 static int robots_ready;
51 static int robots_angle;
52 
53 /* the data for the next cross that will appear */
54 static int next_cross_timer;
55 static int cross_direction;
56 static int nextcrosscolor;
57 
58 
59 /******** PRIVATE FUNCTIONS ********/
60 
61 /* returns the index of the figure the given figure (nr) collides
62  with or -1 if there is no such object */
figurecollision(int nr)63 static int figurecollision(int nr) {
64 
65   /* help field for collisions between two objects */
66   static unsigned char collision[16] = {
67     0x1c, 0x3e, 0x3e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x3e, 0x3e, 0x1c, 0x00
68   };
69 
70   int t;
71   long i, j;
72 
73   for (t = 0; t < MAX_OBJECTS; t++) {
74     if (t != nr &&
75         object[t].kind != OBJ_KIND_CROSS &&
76         object[t].kind != OBJ_KIND_NOTHING &&
77         object[t].kind != OBJ_KIND_DISAPPEAR &&
78         object[t].kind != OBJ_KIND_APPEAR) {
79       i = object[nr].anglepos - object[t].anglepos;
80       j = object[t].verticalpos - object[nr].verticalpos;
81       if ((-4 < i) && (i < 4)  && (-8 < j) && (j < 8)) {
82         if ((collision[j + 7] >> (i + 3)) & 1)
83           return t;
84         else
85           return -1;
86       }
87     }
88   }
89   return -1;
90 }
91 
92 /* returns true, if the robot cannot be at the given position without colliding
93  with an element from the tower */
testroboter(int nr)94 static bool testroboter(int nr) {
95   return (!lev_testfigure((long)object[nr].anglepos, object[nr].verticalpos, -2L, 1L, 1L, 1L, 7L));
96 }
97 
98 /* makes the robot disappear when it falls into water */
drown_robot(int nr)99 static void drown_robot(int nr) {
100   object[nr].verticalpos = 0;
101   object[nr].time = 0;
102   object[nr].kind = OBJ_KIND_DISAPPEAR;
103 }
104 
105 /* tests the underground of the given object (only used for freeze ball) returns
106  0 if nothing needs to be done
107  1 for falling
108  2 for reverse direction
109  */
test_robot_undergr(int nr)110 static int test_robot_undergr(int nr) {
111 
112   int row, col;
113   row = object[nr].verticalpos / 4 - 1;
114   col = (object[nr].anglepos / 8) & 0xf;
115 
116   if (lev_is_box(row, col)) return 0;
117   if (lev_is_platform(row, col) || lev_is_stick(row, col)) {
118     if (lev_is_elevator(row, col))
119       return 2;
120     else
121       return 0;
122   }
123 
124   if ((object[nr].anglepos & 7) < 2) {
125     if (lev_is_empty(row, (col - 1) & 0xf)) return 1;
126     if (lev_is_door(row, (col - 1) & 0xf)) return 1;
127     if ((object[nr].subKind & 0x80) == 0)
128       return 2;
129     else
130       return 0;
131   }
132 
133   if ((object[nr].anglepos & 7) > 6) {
134     if (lev_is_empty(row, (col + 1) & 0xf)) return 1;
135     if (lev_is_door(row, (col + 1) & 0xf)) return 1;
136     if ((object[nr].subKind & 0x80) == 0)
137       return 0;
138     else
139       return 2;
140   }
141 
142   return 1;
143 }
144 
145 /* update the position for the cross */
updatecross(int t)146 static void updatecross(int t) {
147 
148   object[t].anglepos += object[t].subKind;
149   object[t].time -= object[t].subKind;
150 
151   /* after the cross reached the middle speed it up */
152   if (object[t].anglepos == 60)
153     object[t].subKind *= 2;
154 
155   /* if cross reached screenedge, remove it an reinitialize
156    counter for next one */
157   if (((object[t].subKind > 0) && (object[t].anglepos >= 120)) ||
158       ((object[t].subKind < 0) && (object[t].anglepos <= 0))) {
159     object[t].kind = OBJ_KIND_NOTHING;
160     next_cross_timer = 125;
161   }
162 }
163 
164 /* remove objects that drop below the screen */
checkverticalposition(int verticalpos,int t)165 static bool checkverticalposition(int verticalpos, int t) {
166 
167   if (object[t].verticalpos + 48 < verticalpos) {
168     object[t].kind = OBJ_KIND_DISAPPEAR;
169     object[t].time = 0;
170     return true;
171   } else
172     return false;
173 
174 }
175 
176 /* checks if the robot is at a position that it can not be
177  if not remove it */
checkvalidposition(int t)178 static bool checkvalidposition(int t) {
179 
180   if (testroboter(t)) {
181     object[t].kind = OBJ_KIND_DISAPPEAR;
182     object[t].time = 0;
183     return true;
184   } else
185     return false;
186 
187 }
188 
189 /* move the robot a the horizontal amount, if this places it
190  at an impossible position, reverse its direction and don't move it */
moverobothorizontal(int t)191 static void moverobothorizontal(int t) {
192 
193   object[t].anglepos += object[t].subKind;
194   object[t].anglepos &= 0x7f;
195   if (testroboter(t) || (figurecollision(t) != -1)) {
196     object[t].subKind = -object[t].subKind;
197     object[t].anglepos += object[t].subKind;
198     object[t].anglepos &= 0x7f;
199   }
200 
201 }
202 
203 
204 /******* PUBLIC FUNCTIONS *********/
205 
rob_kind(int nr)206 rob_kinds rob_kind(int nr) { return object[nr].kind; }
rob_time(int nr)207 int rob_time(int nr) { return object[nr].time; }
rob_angle(int nr)208 int rob_angle(int nr) { return object[nr].anglepos; }
rob_vertical(int nr)209 int rob_vertical(int nr) { return object[nr].verticalpos; }
210 
rob_initialize(void)211 void rob_initialize(void) {
212 
213   for (int b = 0; b < MAX_OBJECTS; b++) {
214     object[b].kind = OBJ_KIND_NOTHING;
215     object[b].time = -1;
216   }
217 
218   next_cross_timer = 125;
219   nextcrosscolor = rand() / (RAND_MAX / 8);
220   cross_direction = 1;
221 
222   robots_ready = 0;
223   robots_angle = 0;
224 
225 }
226 
rob_topplercollision(int angle,int vertical)227 int rob_topplercollision(int angle, int vertical) {
228   long i, j;
229 
230   for (int t = 0; t < MAX_OBJECTS; t++) {
231     if (object[t].kind != OBJ_KIND_CROSS &&
232         object[t].kind != OBJ_KIND_NOTHING &&
233         object[t].kind != OBJ_KIND_DISAPPEAR &&
234         object[t].kind != OBJ_KIND_APPEAR) {
235       i = angle - object[t].anglepos;
236       j = object[t].verticalpos - vertical;
237       if ((-4 < i) && (i < 4)  && (-8 < j) && (j < 8))
238         return t;
239     } else if (object[t].kind == OBJ_KIND_CROSS) {
240       if (object[t].anglepos >= 53 && object[t].anglepos <= 67 &&
241           object[t].verticalpos + 7 >= vertical &&
242           vertical + 9 >= object[t].verticalpos)
243         return t;
244     }
245   }
246   return -1;
247 }
248 
rob_snowballcollision(int angle,int vertical)249 int rob_snowballcollision(int angle, int vertical) {
250 
251   /* help field for collisions between two objects */
252   static unsigned char collision[16] = {
253      0x00, 0x1c, 0x1c, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x10
254   };
255 
256   long i, j;
257   for (int t = 0; t < MAX_OBJECTS; t++) {
258     if (object[t].kind != OBJ_KIND_CROSS &&
259         object[t].kind != OBJ_KIND_NOTHING &&
260         object[t].kind != OBJ_KIND_DISAPPEAR &&
261         object[t].kind != OBJ_KIND_APPEAR) {
262       i = angle - object[t].anglepos;
263       j = object[t].verticalpos - vertical;
264       if ((-4 < i) && (i < 4)  && (-8 < j) && (j < 8)) {
265         if ((collision[j + 7] >> (i + 3)) & 1)
266           return t;
267         else
268           return -1;
269       }
270     }
271   }
272   return -1;
273 }
274 
rob_new(int verticalpos)275 void rob_new(int verticalpos) {
276 
277   static struct {
278     unsigned char r, g, b;
279   } crosscols[8] = {
280     { 0xff, 0x00, 0x00 },
281     { 0x00, 0xff, 0x00 },
282     { 0x00, 0x00, 0xff },
283     { 0xff, 0xff, 0x00 },
284     { 0x00, 0xff, 0xff },
285     { 0xff, 0x00, 0xff },
286     { 0x80, 0x80, 0x80 },
287     { 0xff, 0xff, 0xff }
288   };
289 
290   int a, b;
291 
292   int h = verticalpos / 4 + 9;
293 
294   for (int t = 0; t < MAX_OBJECTS; t++) {
295     if (object[t].kind == OBJ_KIND_NOTHING) {
296 
297 
298       /* if there is currently no chance for a new robot check for a cross */
299       if ((robots_ready == lev_towerrows() * 8) || (h <= robots_ready)) {
300 
301         /* check if time is ripe */
302         if (next_cross_timer == -1) return;
303         next_cross_timer--;
304         if (next_cross_timer != -1) return;
305 
306         /* set colors for the cross */
307         scr_setcrosscolor(crosscols[nextcrosscolor].r,
308                           crosscols[nextcrosscolor].g,
309                           crosscols[nextcrosscolor].b);
310         nextcrosscolor = (nextcrosscolor + 1) & 7;
311 
312         /* fill in the data for the robot */
313         object[t].kind = OBJ_KIND_CROSS;
314         object[t].time = 0;
315         cross_direction *= -1;
316         object[t].subKind = cross_direction;
317         if (cross_direction > 0)
318           object[t].anglepos = 0;
319         else
320           object[t].anglepos = 120;
321         object[t].verticalpos = verticalpos;
322 
323         ttsounds::instance()->startsound(SND_CROSS);
324 
325       } else {
326         bool is_robo = false;
327 
328         /* find next robot */
329         do {
330           b = lev_tower(robots_ready, robots_angle);
331           is_robo = lev_is_robot(robots_ready, robots_angle);
332           a = robots_angle;
333           h = robots_ready;
334           robots_angle = (robots_angle + 1) & 0xf;
335         } while ((!is_robo) && robots_angle != 0);
336 
337         if (robots_angle == 0)
338           robots_ready++;
339 
340         /* no robot found */
341         if (!is_robo) return;
342 
343         /* fill in data for robot */
344         switch (b) {
345 
346         case TB_ROBOT1:
347           object[t].subKind = 1;
348           object[t].futureKind = OBJ_KIND_FREEZEBALL;
349           break;
350 
351         case TB_ROBOT2:
352           object[t].subKind = 1;
353           object[t].futureKind = OBJ_KIND_JUMPBALL;
354           break;
355 
356         case TB_ROBOT3:
357           object[t].subKind = 0;
358           object[t].futureKind = OBJ_KIND_JUMPBALL;
359           break;
360 
361         case TB_ROBOT4:
362           object[t].subKind = 1;
363           object[t].futureKind = OBJ_KIND_ROBOT_VERT;
364           break;
365 
366         case TB_ROBOT5:
367           object[t].subKind = 2;
368           object[t].futureKind = OBJ_KIND_ROBOT_VERT;
369           break;
370 
371         case TB_ROBOT6:
372           object[t].subKind = 1;
373           object[t].futureKind = OBJ_KIND_ROBOT_HORIZ;
374           break;
375 
376         case TB_ROBOT7:
377           object[t].subKind = 2;
378           object[t].futureKind = OBJ_KIND_ROBOT_HORIZ;
379           break;
380         }
381         object[t].anglepos = (a * 8) + 4;
382         object[t].verticalpos = h * 4;
383         object[t].kind = OBJ_KIND_APPEAR;
384         object[t].time = 0;
385 
386         /* empty the field in the level datastructure */
387         lev_clear(h, a);
388       }
389 
390       return;
391     }
392   }
393 }
394 
rob_update(void)395 void rob_update(void) {
396 
397   /* the vertical movement of the jumping ball */
398   static long jumping_ball[11] = {
399     2, 2, 1, 1, 0, 0, -1, -1, -2, -2, -4
400   };
401 
402   int h;
403 
404   for (int t = 0; t < MAX_OBJECTS; t++) {
405     switch (object[t].kind) {
406       case OBJ_KIND_NOTHING:
407         break;
408 
409       case OBJ_KIND_CROSS:
410         updatecross(t);
411         break;
412 
413       case OBJ_KIND_DISAPPEAR:
414         if (object[t].time == 6)
415           object[t].kind = OBJ_KIND_NOTHING;
416         else
417           object[t].time++;
418         break;
419 
420       case OBJ_KIND_APPEAR:
421         if (object[t].time == 6) {
422           object[t].kind = object[t].futureKind;
423           object[t].time = 0;
424         } else
425           object[t].time++;
426         break;
427 
428       case OBJ_KIND_FREEZEBALL_FROZEN:
429         object[t].time--;
430         if (object[t].time > 0)
431           break;
432         object[t].kind = OBJ_KIND_FREEZEBALL;
433 
434       case OBJ_KIND_FREEZEBALL:
435 
436         if (checkverticalposition(top_verticalpos(), t) ||
437             checkvalidposition(t)) break;
438 
439         switch (test_robot_undergr(t)) {
440           case 1:
441             object[t].kind = OBJ_KIND_FREEZEBALL_FALLING;
442             object[t].time = 10;
443             break;
444           case 2:
445             object[t].subKind = -object[t].subKind;
446             break;
447         }
448         moverobothorizontal(t);
449         break;
450 
451       case OBJ_KIND_FREEZEBALL_FALLING:
452 
453         if (checkverticalposition(top_verticalpos(), t) ||
454             checkvalidposition(t)) break;
455 
456         moverobothorizontal(t);
457 
458         if (object[t].verticalpos + jumping_ball[object[t].time] < 0) {
459           drown_robot(t);
460           break;
461         }
462 
463         h = jumping_ball[object[t].time];
464 
465         while (h != 0) {
466 
467           object[t].verticalpos += h;
468           if (!testroboter(t) || (figurecollision(t) != -1)) break;
469           object[t].verticalpos -= h;
470 
471           if (h > 0)
472             h--;
473           else
474             h++;
475         }
476 
477         if ((h == 0) && (jumping_ball[object[t].time] < 0)) {
478           object[t].kind = OBJ_KIND_FREEZEBALL;
479           object[t].time = 0;
480         }
481         break;
482 
483       case OBJ_KIND_JUMPBALL:
484 
485         if (checkverticalposition(top_verticalpos(), t) ||
486             checkvalidposition(t)) break;
487 
488         h = object[t].subKind;
489         moverobothorizontal(t);
490 
491         if (h * object[t].subKind < 0) {
492           unsigned char w = (object[t].anglepos - top_anglepos()) & 0x7f;
493           if (w >= 0x40)
494             w |= 0x80;
495           if (w >= 0x80)
496             w = 0xff & (~w + 1);
497 
498           ttsounds::instance()->setsoundvol(SND_BOINK, 120-w);
499           ttsounds::instance()->startsound(SND_BOINK);
500         }
501 
502         if (object[t].verticalpos + jumping_ball[object[t].time] < 0) {
503           drown_robot(t);
504           break;
505         }
506 
507         h = jumping_ball[object[t].time];
508 
509         while (h != 0) {
510 
511           object[t].verticalpos += h;
512           if (!testroboter(t) || (figurecollision(t) != -1)) break;
513           object[t].verticalpos -= h;
514 
515           if (h > 0)
516             h--;
517           else
518             h++;
519         }
520 
521         /* ball is bouncing */
522         if ((h == 0) && (jumping_ball[object[t].time] < 0)) {
523 
524           unsigned char w = (object[t].anglepos - top_anglepos()) & 0x7f;
525           if (w >= 0x40)
526             w |= 0x80;
527           if (w >= 0x80)
528             w = 0xff & (~w + 1);
529 
530           ttsounds::instance()->setsoundvol(SND_BOINK, 128-2*w);
531           ttsounds::instance()->startsound(SND_BOINK);
532 
533           /* restart bounce cyclus */
534           object[t].time = 0;
535 
536           /* start the bounding ball moving sideways in direction of the animal */
537           if (object[t].subKind == 0 && top_visible() && top_walking() &&
538               object[t].verticalpos == top_verticalpos()) {
539 
540             /* check if the animal is near enough to the ball */
541             unsigned char w = (object[t].anglepos - top_anglepos()) & 0x7f;
542             h = -1;
543             if (w >= 0x40)
544               w |= 0x80;
545             if (w >= 0x80) {
546               w = 0xff & (~w + 1);
547               h = 1;
548             }
549 
550             if (w < 0x20) object[t].subKind = h;
551           }
552           break;
553         }
554 
555         object[t].time++;
556         if (object[t].time > 10)
557           object[t].time = 10;
558 
559         break;
560 
561       case OBJ_KIND_ROBOT_HORIZ:
562 
563         if (checkverticalposition(top_verticalpos(), t) ||
564             checkvalidposition(t)) break;
565 
566         moverobothorizontal(t);
567 
568         object[t].time = (object[t].time + 1) & 0x1f;
569         break;
570 
571       case OBJ_KIND_ROBOT_VERT:
572 
573         if (checkverticalposition(top_verticalpos(), t) ||
574             checkvalidposition(t)) break;
575 
576         if ((object[t].verticalpos + object[t].subKind) < 0)
577           drown_robot(t);
578         else {
579           object[t].verticalpos += object[t].subKind;
580 
581           if (testroboter(t) || (figurecollision(t) != -1)) {
582             object[t].subKind *= -1;
583             object[t].verticalpos += object[t].subKind;
584           }
585         }
586 
587         object[t].time = (object[t].time + 1) & 0x1f;
588         break;
589     }
590   }
591 }
592 
rob_gothit(int nr)593 int rob_gothit(int nr) {
594 
595   if (object[nr].kind == OBJ_KIND_FREEZEBALL) {
596     object[nr].time = 0x4b;
597     object[nr].kind = OBJ_KIND_FREEZEBALL_FROZEN;
598     return 0;
599   } else if (object[nr].kind == OBJ_KIND_JUMPBALL) {
600     object[nr].kind = OBJ_KIND_DISAPPEAR;
601     object[nr].time = 0;
602     return 100;
603   } else
604     return 0;
605 }
606 
rob_disappearall(void)607 void rob_disappearall(void) {
608 
609   for (int t = 0; t < MAX_OBJECTS; t++) {
610     if (object[t].kind != OBJ_KIND_NOTHING) {
611       object[t].kind = OBJ_KIND_DISAPPEAR;
612       object[t].time = 0;
613     }
614   }
615 }
616 
617