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