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 "toppler.h"
20 
21 #include "robots.h"
22 #include "elevators.h"
23 #include "snowball.h"
24 #include "level.h"
25 #include "sound.h"
26 
27 /* the position of the animal on the tower */
28 int anglepos;
29 long verticalpos;
30 
31 /* the state of the toppler */
32 int state, substate;
33 
34 /* have we entered the target door */
35 bool targetdoor;
36 
37 /* some help variables for the falling toppler */
38 int falling_howmuch;
39 long falling_direction;
40 int falling_minimum;
41 
42 /* some variables defining how to jump */
43 int jumping_direction, jumping_how, jumping_howlong;
44 
45 /* used to time the turning of the tower when a door was entered */
46 int door_turner;
47 
48 long elevator_direction;
49 
50 /* how much must the toppler topple down */
51 int topple_min;
52 
53 /* used when on an elevator to delay the toppling until we
54  reached the next brick layer */
55 bool topple_delay;
56 
57 /* technique points; is decreased each time the toppler gets
58  thrown down */
59 int technic;
60 
61 /* true if the toppler is visible */
62 static bool tvisible;
63 /* should the output routine put an elevator
64  platform below the toppler ? */
65 static bool on_elevator;
66 /* the actual shape of the toppler */
67 static int topplershape;
68 /* the direction the toppler is looking at */
69 static bool look_left;
70 
71 
72 /* values for status */
73 #define STATE_STANDING 0
74 #define STATE_JUMPING 1
75 #define STATE_FALLING 2
76 #define STATE_TURNING 3
77 #define STATE_DOOR 4
78 #define STATE_SHOOTING 5
79 #define STATE_ELEVATOR 6
80 #define STATE_TOPPLING 7
81 #define STATE_DROWN 8
82 #define STATE_DROWNED 9
83 #define STATE_FINISHED 10
84 
85 
top_init(void)86 void top_init(void) {
87   anglepos = 4;
88   verticalpos = 8;
89   state = 0;
90   substate = 0;
91   tvisible = true;
92   on_elevator = false;
93   look_left = true;
94   targetdoor = false;
95   topple_delay = false;
96   door_turner = 0;
97   technic = 0x100;
98 }
99 
100 /* tests the underground of the animal at the given position returning
101  0 if everything is all right
102  1 if there is no underground below us (fall vertical)
103  2 if there is no underground behind us (fall backwards)
104  3 if there is no underground in front of us (fall forwards) */
testunderground(int verticalpos,int anglepos,bool look_left)105 static int testunderground(int verticalpos, int anglepos, bool look_left) {
106   static unsigned char unter[32] = {
107     0x11, 0x20, 0x02, 0x00,
108     0x11, 0x00, 0x32, 0x00,
109     0x11, 0x00, 0x32, 0x00,
110     0x11, 0x00, 0x11, 0x00,
111     0x11, 0x00, 0x11, 0x00,
112     0x11, 0x00, 0x11, 0x00,
113     0x11, 0x23, 0x00, 0x00,
114     0x11, 0x23, 0x00, 0x00
115   };
116 
117   int erg;
118 
119   int r = (verticalpos / 4) - 1;
120   int c = ((anglepos + 0x7a) / 8) & 0xf;
121 
122   erg = (lev_is_empty(r, c) || lev_is_door(r, c)) ? 0 : 2;
123 
124   c = ((anglepos + 0x7a) / 8 + 1) & 0xf;
125 
126   if ((!lev_is_empty(r, c)) && (!lev_is_door(r, c))) erg++;
127 
128   erg = unter[(anglepos & 0x7) * 4 + erg];
129 
130   if (look_left)
131     return erg >> 4;
132   else
133     return erg & 0xf;
134 }
135 
136 
137 
138 /* makes the toppler fall down, the parameter specifies in
139  which direction:
140  0 = down start falling
141  1 = backwards
142  2 = forwards
143  3 = down but already at a high speed (e.g. after a jump) */
falling(int nr)144 static void falling(int nr) {
145 
146   state = STATE_FALLING;
147   substate = 0;
148 
149   switch (nr) {
150     case 0:
151       topplershape = 0;
152       falling_direction = 0;
153       falling_minimum = -1;
154       falling_howmuch = 1;
155       break;
156 
157     case 1:
158       topplershape = 0xf;
159       falling_direction = look_left ? -1 : 1;
160       falling_minimum = -1;
161       falling_howmuch = 1;
162       break;
163 
164     case 2:
165       topplershape = 0xe;
166       falling_direction = look_left ? 1 : -1;
167       falling_minimum = -1;
168       falling_howmuch = 1;
169       break;
170 
171     case 3:
172       topplershape = 0;
173       falling_direction = 0;
174       falling_minimum = -1;
175       falling_howmuch = 3;
176       break;
177   }
178 }
179 
walking(void)180 static void walking(void) {
181 
182   state = STATE_STANDING;
183   substate = 0;
184 }
185 
elevator(long dir)186 static void elevator(long dir) {
187 
188   elevator_direction = dir;
189   substate = 0;
190   state = STATE_ELEVATOR;
191 
192   ele_select((Uint16)verticalpos, anglepos);
193 }
194 
shooting(void)195 static void shooting(void) {
196 
197   if (snb_exists()) {
198     walking();
199     return;
200   }
201   state = STATE_SHOOTING;
202   substate = 0;
203   topplershape = 0;
204   ttsounds::instance()->startsound(SND_SHOOT);
205 }
206 
door(void)207 static void door(void) {
208 
209   state = STATE_DOOR;
210   substate = 0;
211 }
212 
turn(void)213 static void turn(void) {
214 
215   state = STATE_TURNING;
216   substate = 0;
217 }
218 
jump(int left_right)219 static void jump(int left_right) {
220 
221   state = STATE_JUMPING;
222   substate = 0;
223   jumping_direction = left_right;
224   jumping_how = 0;
225   jumping_howlong = 0xc;
226 }
227 
step(int left_right)228 static void step(int left_right) {
229 
230   state = STATE_JUMPING;
231   substate = 0;
232   jumping_direction = left_right;
233   jumping_how = 1;
234   jumping_howlong = 0x7;
235 }
236 
drown(void)237 static void drown(void) {
238 
239   state = STATE_DROWN;
240   substate = 0;
241   verticalpos = 0;
242 
243   ttsounds::instance()->setsoundvol(SND_SPLASH, 128);
244   ttsounds::instance()->startsound(SND_SPLASH);
245 }
246 
topple(void)247 static void topple(void) {
248 
249   state = STATE_TOPPLING;
250   substate = 0;
251   targetdoor = false;
252 
253   technic -= 2;
254   if (technic < 0) technic = 0;
255 }
256 
257 /* tries to move the toppler by the given amount, returns true on
258  success and false if the move is not possible (collision with
259  a tower element) */
movetoppler(long x,long y)260 static bool movetoppler(long x, long y) {
261 
262   if (verticalpos + y < 0) {
263     drown();
264     return false;
265   }
266   if (!lev_testfigure((anglepos + x) & 0x7f, verticalpos + y, -3, 2, 0, 0, 9))
267     return false;
268   anglepos = (anglepos + x) & 0x7f;
269   verticalpos += y;
270   return true;
271 }
272 
slidestep(int left_right,bool look_left)273 static void slidestep(int left_right, bool look_left) {
274 
275   if (left_right)
276     return;
277 
278   int dir;
279 
280   if (look_left)
281     dir = lev_is_sliding(verticalpos / 4 - 1, (anglepos) / 8);
282   else
283     dir = lev_is_sliding(verticalpos / 4 - 1, (anglepos-1) / 8);
284 
285   movetoppler((long)dir, 0);
286 }
287 
288 /* the state machine of the animal */
top_updatetoppler(int left_right,int up_down,bool space)289 void top_updatetoppler(int left_right, int up_down, bool space) {
290 
291   /* table specifying the number for the animal sprite if it is turning
292    around */
293   static unsigned char umdreh[7] = {
294     0x8, 0x9, 0xa, 0xb, 0xa, 0x9, 0x8
295   };
296 
297   /* the sprites if the animal turns to enter the door */
298   static unsigned char door1[4] = {
299     0x10, 0x11, 0x12, 0x13
300   };
301 
302   /* and when it enters the door */
303   static unsigned char door2[6] = {
304     0x13, 0x14, 0x14, 0x15, 0x15, 0x16
305   };
306 
307   /* when it leaves the door */
308   static unsigned char door3[6] = {
309     0x17, 0x18, 0x18, 0x19, 0x19, 0xb
310   };
311 
312   /* the shapes of the toppler when it turns after leaving a door*/
313   static unsigned char door4[4] = { 0xa, 0x9, 0x8, 0x0 };
314 
315   /* the height differences for jumping */
316   static long jump0[12] = { 3, 2, 2, 1, 1, 0, 0, -1, -1, -2, -2, -3 };
317   static long jump1[7] = { 2, 2, 1, 0, -1, -2, -2 };
318 
319   /* sprites for throwing the snowball */
320   static unsigned char schiessen[3] = {
321     0x00, 0x1a, 0x1b
322   };
323 
324   /* the sprites for toppling over */
325   static unsigned char toppler1[4] = {
326     0x00, 0x1c, 0x1d, 0x1e
327   };
328 
329   /* the vertical movement for toppling */
330   static long toppler2[16] = {
331     3, 2, 1, 1, 1, 0, 0, -1, -2, -2, -3, -3, -3, -3, -3, -4
332   };
333 
334   int inh, b;
335 
336   switch (state) {
337 
338   case STATE_STANDING:
339     lev_removevanishstep(verticalpos / 4 - 1, anglepos / 8);
340     switch (testunderground(verticalpos, anglepos, look_left)) {
341       case 0:
342         if (left_right == 0) {
343           if (space)
344             shooting();
345           else {
346             if (lev_is_up_station(verticalpos / 4 - 1, anglepos / 8) &&
347                 (up_down == 1)) {
348               elevator(up_down);
349               break;
350             }
351             if (lev_is_down_station(verticalpos / 4 - 1, anglepos / 8) &&
352                 (up_down == -1)) {
353               elevator(up_down);
354               break;
355             }
356 
357             if (lev_is_door_upperend(verticalpos / 4, anglepos / 8) &&
358                 (up_down == 1)) {
359               targetdoor = lev_is_targetdoor(verticalpos / 4, anglepos / 8);
360               door();
361               break;
362             }
363 
364             slidestep(left_right, look_left);
365             topplershape = 0xc;
366           }
367         } else {
368           if ((substate == 2) || (substate == 6)) ttsounds::instance()->startsound(SND_TAP);
369           if (left_right == -1) {
370             if (look_left)
371               turn();
372             else {
373               if (space)
374                 jump(left_right);
375               else {
376                 if (!movetoppler(left_right, 0L))
377                   step(left_right);
378                 else {
379                   substate = (substate + 1) & 0x7;
380                   topplershape = substate;
381                 }
382               }
383             }
384           } else {
385             if (!look_left)
386               turn();
387             else {
388               if (space)
389                 jump(left_right);
390               else {
391                 if (!movetoppler(left_right, 0L))
392                   step(left_right);
393                 else {
394                   substate = (substate + 1) & 0x7;
395                   topplershape = substate;
396                 }
397               }
398             }
399           }
400         }
401         break;
402 
403       case 1:
404         falling(0);
405         break;
406 
407       case 2:
408         falling(1);
409         break;
410 
411       case 3:
412         falling(2);
413         break;
414     }
415     break;
416 
417   case STATE_JUMPING:
418     topplershape = substate & 7;
419     movetoppler(jumping_direction, 0L);
420     if (jumping_how == 0)
421       inh = jump0[substate];
422     else
423       inh = jump1[substate];
424 
425     if (movetoppler(0, inh)) {
426       substate++;
427       if (substate >= jumping_howlong) {
428         if (movetoppler(0, -1))
429           falling(3);
430         else {
431           state = STATE_FALLING;
432           substate = 1;
433         }
434       }
435     } else {
436       b = inh;
437       do {
438         if (inh < 0)
439           inh++;
440         else
441           inh--;
442       } while (!((inh == 0) || movetoppler(0L, inh)));
443       if (b < 0) {
444         walking();
445         ttsounds::instance()->startsound(SND_TAP);
446       } else {
447         substate++;
448         if (substate >= jumping_howlong) {
449           if (movetoppler(0L, -1L))
450             falling(3);
451           else {
452             state = STATE_FALLING;
453             substate = 1;
454           }
455         }
456       }
457     }
458     break;
459 
460   case STATE_FALLING:
461     if (substate == 0) {
462       falling_minimum++;
463       if (!movetoppler(falling_direction, 0))
464         falling_direction = 0;
465       if (movetoppler(0, -falling_howmuch)) {
466         falling_howmuch++;
467         if (falling_howmuch > 4)
468           falling_howmuch = 4;
469       } else {
470         do {
471           falling_howmuch--;
472         } while (falling_howmuch && !movetoppler(0, -falling_howmuch));
473         ttsounds::instance()->startsound(SND_TAP);
474         if (falling_howmuch != 0) {
475           falling_howmuch++;
476           if (falling_howmuch > 4)
477             falling_howmuch = 4;
478         } else {
479           if ((falling_direction == 0) || (falling_minimum >= 2)) {
480             substate++;
481             topplershape = 14 - substate;
482             substate++;
483             if (substate == 3) {
484               walking();
485             }
486           } else {
487             falling_howmuch++;
488             if (falling_howmuch > 4)
489               falling_howmuch = 4;
490           }
491         }
492       }
493     } else {
494       topplershape = 14 - substate;
495       substate++;
496       if (substate == 3)
497         walking();
498     }
499     break;
500 
501   case STATE_TURNING:
502     topplershape = umdreh[substate];
503     substate++;
504     if ((substate == 4) || (substate == 7)) ttsounds::instance()->startsound(SND_TAP);
505     if (substate == 4) look_left = !look_left;
506     if (substate == 7) walking();
507     break;
508 
509   case STATE_DOOR:
510 
511 
512     switch (substate) {
513 
514     case 0:
515     case 1:
516     case 2:
517     case 3:
518       topplershape = door1[substate];
519       if (((anglepos) & 7) == 4)
520         substate++;
521       else if (((anglepos) & 7) > 4)
522         movetoppler(-1L, 0L);
523       else
524         movetoppler(1L, 0L);
525 
526       break;
527 
528     case 4:
529     case 5:
530     case 6:
531       substate = 7;
532       break;
533 
534     case 7:
535     case 8:
536     case 9:
537     case 10:
538     case 11:
539     case 12:
540       topplershape = door2[substate - 7];
541       substate++;
542       break;
543 
544     case 29:
545     case 30:
546     case 31:
547     case 32:
548     case 33:
549     case 34:
550       topplershape = door3[substate - 29];
551       tvisible = true;
552       substate++;
553       break;
554 
555     case 35:
556       if (!movetoppler(0L, -4L)) {
557         if (left_right == -1)
558           look_left = false;
559         else
560           look_left = true;
561         substate++;
562       }
563       break;
564 
565     case 36:
566     case 37:
567     case 38:
568     case 39:
569       topplershape = door4[substate - 36];
570       substate++;
571       break;
572 
573     case 40:
574       walking();
575       break;
576 
577     default:
578       if (substate >= 13 && substate <= 28) {
579         if ((door_turner % 4) == 0)
580           ttsounds::instance()->startsound(SND_DOORTAP);
581 
582         tvisible = false;
583         if (targetdoor) {
584           state = STATE_FINISHED;
585           ttsounds::instance()->startsound(SND_FANFARE);
586         } else {
587           if (look_left)
588             anglepos += 2;
589           else
590             anglepos -= 2;
591 
592           anglepos &= 0x7f;
593           door_turner++;
594           if ((door_turner & 1) == 0)
595             substate++;
596         }
597       }
598       break;
599     }
600     break;
601 
602   case STATE_SHOOTING:
603     slidestep(left_right, look_left);
604     if (substate == 3) {
605       walking();
606     } else {
607       topplershape = schiessen[substate];
608       if (substate == 1)
609         snb_start(verticalpos, anglepos, look_left);
610       substate++;
611     }
612     break;
613 
614   case STATE_ELEVATOR:
615 
616     if (substate == 0) {
617       /* move the animal to the center of the elevator platform */
618       if (((anglepos) & 7) != 4) {
619         if (((anglepos) & 7) < 4)
620           (anglepos)++;
621         else
622           (anglepos)--;
623         anglepos &= 0x7f;
624         return;
625       }
626       substate++;
627       on_elevator = true;
628       ele_activate((Sint8)elevator_direction);
629       ttsounds::instance()->startsound(SND_TICK);
630       return;
631     }
632     verticalpos += elevator_direction;
633 
634     if ((substate > 0) && (verticalpos & 3) == 0) {
635 
636       if (ele_is_atstop()) {
637         on_elevator = false;
638         ele_deactivate();
639         walking();
640       } else {
641         ele_move();
642         ttsounds::instance()->startsound(SND_TICK);
643       }
644     }
645     break;
646 
647   case STATE_TOPPLING:
648     if (substate < topple_min) {
649 
650       topplershape = toppler1[(substate / 2) & 3];
651 
652       if (substate < 15)
653         verticalpos += toppler2[substate];
654       else
655         verticalpos += toppler2[15];
656 
657       if (verticalpos < 0)
658         drown();
659       else
660         substate++;
661 
662     } else {
663       topplershape = 0;
664       verticalpos -= 4;
665       if (verticalpos < 0)
666         drown();
667       else if (movetoppler(0, 0))
668         falling(3);
669     }
670     break;
671 
672   case STATE_DROWN:
673     if (substate == 0x8) ttsounds::instance()->startsound(SND_DROWN);
674     if (substate < 0x18) {
675       topplershape = substate / 4 + 31;
676       substate++;
677     } else {
678       tvisible = false;
679       if (substate < 0x20)
680         substate++;
681       else {
682         state = STATE_DROWNED;
683       }
684     }
685     break;
686 
687   case STATE_DROWNED:
688   case STATE_FINISHED:
689     break;
690 
691   }
692 }
693 
top_testcollision(void)694 void top_testcollision(void) {
695   int nr;
696 
697   if ((state == STATE_TOPPLING) ||
698       (state == STATE_DROWN) ||
699       ((state == STATE_DOOR) && (substate >= 10) && (substate < 31)) ||
700       (!tvisible))
701     return;
702 
703   if (topple_delay) {
704     if (state == STATE_ELEVATOR) {
705       if ((verticalpos & 3) == 0) {
706         ele_deactivate();
707         on_elevator = false;
708 
709         topple_delay = false;
710         topple();
711       }
712     } else {
713       topple_delay = false;
714       topple();
715     }
716     return;
717   } else {
718     nr = rob_topplercollision(anglepos, verticalpos + 1);
719     if (nr != -1) {
720 
721       if (rob_kind(nr) == OBJ_KIND_CROSS)
722         topple_min = 15;
723       else
724         topple_min = 20;
725 
726       if (state == STATE_ELEVATOR) {
727         if ((verticalpos & 3) == 0) {
728           ele_deactivate();
729           topple_delay = false;
730           on_elevator = false;
731           topple();
732         } else {
733           topple_delay = true;
734         }
735       } else {
736         topple();
737       }
738     } else if ((state == STATE_ELEVATOR) && !movetoppler(0L, 0L)) {
739 
740       topple_min = 20;
741       if ((verticalpos & 3) == 0) {
742         ele_deactivate();
743         topple_delay = false;
744         on_elevator = false;
745         topple();
746       } else {
747         topple_delay = true;
748       }
749     }
750   }
751 }
752 
753 
top_verticalpos(void)754 int top_verticalpos(void) { return verticalpos; }
top_anglepos(void)755 int top_anglepos(void) { return anglepos; }
top_visible(void)756 bool top_visible(void) { return tvisible; }
top_look_left(void)757 bool top_look_left(void) { return look_left; }
top_shape(void)758 int top_shape(void) { return topplershape; }
top_onelevator(void)759 bool top_onelevator(void) { return on_elevator; }
top_technic(void)760 int top_technic(void) { return technic; }
761 
top_died(void)762 bool top_died(void) { return state == STATE_DROWNED; }
top_targetreached(void)763 bool top_targetreached(void) { return state == STATE_FINISHED; }
top_ended(void)764 bool top_ended(void) { return ((state == STATE_DROWNED) || (state == STATE_FINISHED)); }
top_dying(void)765 bool top_dying(void) {
766   return ((state == STATE_DROWN) ||
767           (state == STATE_DROWNED) ||
768           (state == STATE_FINISHED));
769 }
top_walking(void)770 bool top_walking(void) { return state == STATE_STANDING; }
771 
top_drop1layer(void)772 void top_drop1layer(void) { verticalpos -= 4; }
773 
top_hide(void)774 void top_hide(void) { tvisible = false; }
top_show(int shape,int vpos,int apos)775 void top_show(int shape, int vpos, int apos) {
776   tvisible = true;
777   topplershape = shape;
778   verticalpos = vpos;
779   anglepos = apos;
780   look_left = true;
781 }
782 
top_sidemove(void)783 void top_sidemove(void) {
784   // FIXME: the toppler needs the be brought out of the way of the elevater when they fall down
785   // that needs to happen at least when standing, walking and jumping, but it might be also in other
786   // cases
787   // it is definitively not necessary when falling and drowning
788   // i hope this if is right
789   if ((state != STATE_STANDING) &&
790       (state != STATE_JUMPING) &&
791       (state != STATE_TURNING) &&
792       (state != STATE_SHOOTING))
793     return;
794 
795   if (movetoppler( 0, 0)) return;
796 
797   int i = 1;
798   while (1) {
799     if (movetoppler( i, 0)) break;
800     if (movetoppler(-i, 0)) break;
801     i++;
802   }
803 }
804 
805