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