1 /***********************************************************
2 * Mirror Magic -- McDuffin's Revenge *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 Artsoft Entertainment *
5 * Holger Schemel *
6 * Detmolder Strasse 189 *
7 * 33604 Bielefeld *
8 * Germany *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
11 * game.c *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "tools.h"
18 #include "screens.h"
19 #include "init.h"
20 #include "files.h"
21
22 /* graphic position values for game controls */
23 #define ENERGY_XSIZE 32
24 #define ENERGY_YSIZE MAX_LASER_ENERGY
25 #define OVERLOAD_XSIZE ENERGY_XSIZE
26 #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD
27
28 /* values for Explode() */
29 #define EX_PHASE_START 0
30 #define EX_NORMAL 0
31 #define EX_KETTLE 1
32 #define EX_SHORT 2
33
34 /* special positions in the game control window (relative to control window) */
35 #define XX_LEVEL 36
36 #define YY_LEVEL 23
37 #define XX_KETTLES 29
38 #define YY_KETTLES 63
39 #define XX_SCORE 22
40 #define YY_SCORE 101
41 #define XX_ENERGY 8
42 #define YY_ENERGY 158
43 #define XX_OVERLOAD 60
44 #define YY_OVERLOAD YY_ENERGY
45
46 /* special positions in the game control window (relative to main window) */
47 #define DX_LEVEL (DX + XX_LEVEL)
48 #define DY_LEVEL (DY + YY_LEVEL)
49 #define DX_KETTLES (DX + XX_KETTLES)
50 #define DY_KETTLES (DY + YY_KETTLES)
51 #define DX_SCORE (DX + XX_SCORE)
52 #define DY_SCORE (DY + YY_SCORE)
53 #define DX_ENERGY (DX + XX_ENERGY)
54 #define DY_ENERGY (DY + YY_ENERGY)
55 #define DX_OVERLOAD (DX + XX_OVERLOAD)
56 #define DY_OVERLOAD (DY + YY_OVERLOAD)
57
58 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
59 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
60
61 /* game button identifiers */
62 #define GAME_CTRL_ID_LEFT 0
63 #define GAME_CTRL_ID_MIDDLE 1
64 #define GAME_CTRL_ID_RIGHT 2
65
66 #define NUM_GAME_BUTTONS 3
67
68 /* values for DrawLaser() */
69 #define DL_LASER_DISABLED 0
70 #define DL_LASER_ENABLED 1
71
72 /* values for 'click_delay_value' in ClickElement() */
73 #define CLICK_DELAY_SHORT 125
74 #define CLICK_DELAY_LONG 250
75 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
76
77 /* forward declaration for internal use */
78 static void MapGameButtons();
79 static void HandleGameButtons(struct GadgetInfo *);
80
81 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
82
GetPlayerConfig()83 void GetPlayerConfig()
84 {
85 if (!audio.sound_available)
86 setup.sound = FALSE;
87
88 if (!audio.loops_available)
89 {
90 setup.sound_loops = FALSE;
91 setup.sound_music = FALSE;
92 }
93
94 if (!video.fullscreen_available)
95 setup.fullscreen = FALSE;
96
97 setup.sound_simple = setup.sound;
98
99 SetAudioMode(setup.sound);
100 }
101
get_element_angle(int element)102 static int get_element_angle(int element)
103 {
104 int element_phase = get_element_phase(element);
105
106 if (IS_MIRROR_FIXED(element) ||
107 IS_MCDUFFIN(element) ||
108 IS_LASER(element) ||
109 IS_RECEIVER(element))
110 return 4 * element_phase;
111 else
112 return element_phase;
113 }
114
get_opposite_angle(int angle)115 static int get_opposite_angle(int angle)
116 {
117 int opposite_angle = angle + ANG_RAY_180;
118
119 /* make sure "opposite_angle" is in valid interval [0, 15] */
120 return (opposite_angle + 16) % 16;
121 }
122
get_mirrored_angle(int laser_angle,int mirror_angle)123 static int get_mirrored_angle(int laser_angle, int mirror_angle)
124 {
125 int reflected_angle = 16 - laser_angle + mirror_angle;
126
127 /* make sure "reflected_angle" is in valid interval [0, 15] */
128 return (reflected_angle + 16) % 16;
129 }
130
InitMovDir(int x,int y)131 void InitMovDir(int x, int y)
132 {
133 int element = Feld[x][y];
134 static int direction[3][4] =
135 {
136 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
137 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
138 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
139 };
140
141 switch(element)
142 {
143 case EL_PACMAN_RIGHT:
144 case EL_PACMAN_UP:
145 case EL_PACMAN_LEFT:
146 case EL_PACMAN_DOWN:
147 Feld[x][y] = EL_PACMAN;
148 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
149 break;
150
151 default:
152 break;
153 }
154 }
155
InitField(int x,int y,boolean init_game)156 static void InitField(int x, int y, boolean init_game)
157 {
158 int element = Feld[x][y];
159
160 switch (element)
161 {
162 case EL_DF_EMPTY:
163 Feld[x][y] = EL_EMPTY;
164 break;
165
166 case EL_KETTLE:
167 case EL_CELL:
168 if (level.auto_count_kettles)
169 game.kettles_still_needed++;
170 break;
171
172 case EL_LIGHTBULB_OFF:
173 game.lights_still_needed++;
174 break;
175
176 default:
177 if (IS_MIRROR(element) ||
178 IS_BEAMER_OLD(element) ||
179 IS_BEAMER(element) ||
180 IS_POLAR(element) ||
181 IS_POLAR_CROSS(element) ||
182 IS_DF_MIRROR(element) ||
183 IS_DF_MIRROR_AUTO(element) ||
184 IS_GRID_STEEL_AUTO(element) ||
185 IS_GRID_WOOD_AUTO(element) ||
186 IS_FIBRE_OPTIC(element))
187 {
188 if (IS_BEAMER_OLD(element))
189 {
190 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
191 element = Feld[x][y];
192 }
193
194 if (!IS_FIBRE_OPTIC(element))
195 {
196 static int steps_grid_auto = 0;
197
198 if (game.num_cycle == 0) /* initialize cycle steps for grids */
199 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
200
201 if (IS_GRID_STEEL_AUTO(element) ||
202 IS_GRID_WOOD_AUTO(element))
203 game.cycle[game.num_cycle].steps = steps_grid_auto;
204 else
205 game.cycle[game.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
206
207 game.cycle[game.num_cycle].x = x;
208 game.cycle[game.num_cycle].y = y;
209 game.num_cycle++;
210 }
211
212 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
213 {
214 int beamer_nr = BEAMER_NR(element);
215 int nr = laser.beamer[beamer_nr][0].num;
216
217 laser.beamer[beamer_nr][nr].x = x;
218 laser.beamer[beamer_nr][nr].y = y;
219 laser.beamer[beamer_nr][nr].num = 1;
220 }
221 }
222 else if (IS_PACMAN(element))
223 {
224 #if 0
225 int phase = element - EL_PACMAN_RIGHT;
226
227 game.pacman[game.num_pacman].x = x;
228 game.pacman[game.num_pacman].y = y;
229 game.pacman[game.num_pacman].dir = phase + ((phase + 1) % 2) * 2;
230 game.num_pacman++;
231 #else
232 InitMovDir(x, y);
233 #endif
234 }
235 else if (IS_MCDUFFIN(element) || IS_LASER(element))
236 {
237 laser.start_edge.x = x;
238 laser.start_edge.y = y;
239 laser.start_angle = get_element_angle(element);
240 }
241
242 break;
243 }
244 }
245
InitCycleElements()246 static void InitCycleElements()
247 {
248 int i, j;
249
250 if (game.num_cycle == 0) /* no elements to cycle */
251 return;
252
253 for(i=0; i<16; i++)
254 {
255 for(j=0; j<game.num_cycle; j++)
256 {
257 int x = game.cycle[j].x;
258 int y = game.cycle[j].y;
259 int step = SIGN(game.cycle[j].steps);
260 int last_element = Feld[x][y];
261 int next_element = get_rotated_element(last_element, step);
262
263 if (!game.cycle[j].steps)
264 continue;
265
266 Feld[x][y] = next_element;
267
268 DrawField(x, y);
269 game.cycle[j].steps -= step;
270 }
271
272 BackToFront();
273 ColorCycling();
274
275 #ifdef DEBUG
276 if (setup.quick_doors)
277 continue;
278 #endif
279
280 Delay(AUTO_ROTATE_DELAY);
281 }
282 }
283
InitLaser()284 static void InitLaser()
285 {
286 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
287 int step = (IS_LASER(start_element) ? 4 : 0);
288
289 LX = laser.start_edge.x * TILEX;
290 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
291 LX += 14;
292 else
293 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
294
295 LY = laser.start_edge.y * TILEY;
296 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
297 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
298 else
299 LY += 14;
300
301 XS = 2 * Step[laser.start_angle].x;
302 YS = 2 * Step[laser.start_angle].y;
303
304 laser.current_angle = laser.start_angle;
305
306 laser.num_damages = 0;
307 laser.num_edges = 0;
308 laser.num_beamers = 0;
309 laser.beamer_edge[0] = 0;
310
311 AddLaserEdge(LX, LY); /* set laser starting edge */
312
313 pen_ray = GetPixelFromRGB(window,
314 level.laser_red * 0xFF,
315 level.laser_green * 0xFF,
316 level.laser_blue * 0xFF);
317 }
318
InitGame()319 void InitGame()
320 {
321 int i, x, y;
322
323 /* set global editor control values */
324 editor.draw_walls_masked = FALSE;
325
326 /* set global game control values */
327 game.num_cycle = 0;
328 game.num_pacman = 0;
329
330 game.score = 0;
331 game.energy_left = level.time;
332 game.kettles_still_needed =
333 (level.auto_count_kettles ? 0 : level.kettles_needed);
334 game.lights_still_needed = 0;
335 game.num_keys = 0;
336
337 game.level_solved = FALSE;
338 game.game_over = FALSE;
339 game.game_over_cause = 0;
340
341 /* set global laser control values (must be set before "InitLaser()") */
342 laser.start_edge.x = 0;
343 laser.start_edge.y = 0;
344 laser.start_angle = 0;
345
346 for (i=0; i<MAX_NUM_BEAMERS; i++)
347 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
348
349 laser.overloaded = FALSE;
350 laser.overload_value = 0;
351 laser.fuse_off = FALSE;
352 laser.fuse_x = laser.fuse_y = -1;
353
354 laser.dest_element = EL_EMPTY;
355 laser.wall_mask = 0;
356
357 CT = Ct = 0;
358
359 for (x=0; x<lev_fieldx; x++)
360 {
361 for (y=0; y<lev_fieldy; y++)
362 {
363 Feld[x][y] = Ur[x][y];
364 Hit[x][y] = Box[x][y] = 0;
365 Angle[x][y] = 0;
366 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
367 Store[x][y] = Store2[x][y] = 0;
368 Frame[x][y] = 0;
369 Stop[x][y] = FALSE;
370
371 InitField(x, y, TRUE);
372 }
373 }
374
375 CloseDoor(DOOR_CLOSE_1);
376
377 DrawLevel();
378 InitCycleElements();
379 InitLaser();
380
381 /* copy default game door content to main double buffer */
382 BlitBitmap(pix[PIX_DOOR], drawto,
383 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
384
385 DrawText(DX_LEVEL, DY_LEVEL,
386 int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
387 DrawText(DX_KETTLES, DY_KETTLES,
388 int2str(game.kettles_still_needed, 3), FS_SMALL, FC_YELLOW);
389 DrawText(DX_SCORE, DY_SCORE,
390 int2str(game.score, 4), FS_SMALL, FC_YELLOW);
391
392 UnmapGameButtons();
393 /*
394 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
395 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
396 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
397 */
398 MapGameButtons();
399
400 /* copy actual game door content to door double buffer for OpenDoor() */
401 BlitBitmap(drawto, pix[PIX_DB_DOOR],
402 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
403
404 OpenDoor(DOOR_OPEN_ALL);
405
406 if (setup.sound_loops)
407 PlaySoundExt(SND_FUEL, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
408
409 for(i=0; i<=game.energy_left; i+=2)
410 {
411 if (!setup.sound_loops)
412 PlaySoundStereo(SND_FUEL, PSND_MAX_RIGHT);
413
414 BlitBitmap(pix[PIX_DOOR], drawto,
415 DOOR_GFX_PAGEX4 + XX_ENERGY,
416 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
417 ENERGY_XSIZE, i,
418 DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
419
420 redraw_mask |= REDRAW_DOOR_1;
421 BackToFront();
422
423 ColorCycling();
424
425 #ifdef DEBUG
426 if (setup.quick_doors)
427 continue;
428 #endif
429
430 Delay(20);
431 }
432
433 if (setup.sound_loops)
434 StopSound(SND_FUEL);
435
436 if (setup.sound_music && num_bg_loops)
437 PlayMusic(level_nr % num_bg_loops);
438
439 ScanLaser();
440 }
441
AddLaserEdge(int lx,int ly)442 void AddLaserEdge(int lx, int ly)
443 {
444 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
445 {
446 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
447 return;
448 }
449
450 laser.edge[laser.num_edges].x = SX + 2 + lx;
451 laser.edge[laser.num_edges].y = SY + 2 + ly;
452 laser.num_edges++;
453
454 laser.redraw = TRUE;
455 }
456
AddDamagedField(int ex,int ey)457 void AddDamagedField(int ex, int ey)
458 {
459 laser.damage[laser.num_damages].is_mirror = FALSE;
460 laser.damage[laser.num_damages].angle = laser.current_angle;
461 laser.damage[laser.num_damages].edge = laser.num_edges;
462 laser.damage[laser.num_damages].x = ex;
463 laser.damage[laser.num_damages].y = ey;
464 laser.num_damages++;
465 }
466
StepBehind()467 boolean StepBehind()
468 {
469 if (laser.num_edges)
470 {
471 int x = LX - XS;
472 int y = LY - YS;
473 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
474 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
475
476 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
477 }
478 else
479 return FALSE;
480 }
481
getMaskFromElement(int element)482 static int getMaskFromElement(int element)
483 {
484 if (IS_GRID(element))
485 return GFX_MASK_GRID_00 + get_element_phase(element);
486 else if (IS_MCDUFFIN(element))
487 return GFX_MASK_MCDUFFIN_00 + get_element_phase(element);
488 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
489 return GFX_MASK_RECTANGLE;
490 else
491 return GFX_MASK_CIRCLE;
492 }
493
ScanPixel()494 int ScanPixel()
495 {
496 int hit_mask = 0;
497
498 #if 0
499 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
500 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
501 #endif
502
503 /* follow laser beam until it hits something (at least the screen border) */
504 while (hit_mask == HIT_MASK_NO_HIT)
505 {
506 int i;
507
508 #if 0
509 /* for security */
510 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
511 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
512 {
513 printf("ScanPixel: touched screen border!\n");
514
515 return HIT_MASK_ALL;
516 }
517 #endif
518
519 for (i=0; i<4; i++)
520 {
521 Pixel pixel;
522 int px, py, lx, ly;
523
524 px = SX + LX + (i % 2) * 2;
525 py = SY + LY + (i / 2) * 2;
526 lx = (px - SX + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
527 ly = (py - SY + TILEY) / TILEY - 1; /* negative values! */
528
529 if (IN_LEV_FIELD(lx, ly))
530 {
531 int element = Feld[lx][ly];
532
533 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
534 pixel = 0;
535 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
536 {
537 int pos =
538 ((py - SY - ly * TILEY) / MINI_TILEX) * 2 +
539 (px - SX - lx * TILEX) / MINI_TILEY;
540
541 pixel = ((element & (1 << pos)) ? 1 : 0);
542 }
543 else
544 {
545 int graphic_mask = getMaskFromElement(element);
546 int mask_x, mask_y;
547 int dx = px - lx * TILEX;
548 int dy = py - ly * TILEY;
549
550 mask_x = (graphic_mask % GFX_PER_LINE) * TILEX + dx;
551 mask_y = (graphic_mask / GFX_PER_LINE) * TILEY + dy;
552
553 pixel = (ReadPixel(pix[PIX_BACK], mask_x, mask_y) ? 1 : 0);
554 }
555 }
556 else
557 {
558 if (px < REAL_SX || px >= REAL_SX + FULL_SXSIZE ||
559 py < REAL_SY || py >= REAL_SY + FULL_SYSIZE)
560 pixel = 1;
561 else
562 pixel = 0;
563 }
564
565 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
566 hit_mask |= (1 << i);
567 }
568
569 if (hit_mask == HIT_MASK_NO_HIT)
570 {
571 /* hit nothing -- go on with another step */
572 LX += XS;
573 LY += YS;
574 }
575 }
576
577 return hit_mask;
578 }
579
ScanLaser()580 void ScanLaser()
581 {
582 int element;
583 int end = 0, rf = laser.num_edges;
584 #if 0
585 unsigned short color_scale = 0xFFFF / 15;
586 #endif
587 int testx, testy;
588
589 laser.overloaded = FALSE;
590 laser.stops_inside_element = FALSE;
591
592 #if 0
593 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
594 SetRGB(pen_ray,
595 (laser.overload_value / 6) * color_scale, 0x0000,
596 (15 - (laser.overload_value / 6)) * color_scale);
597 #endif
598
599 DrawLaser(0, DL_LASER_ENABLED);
600
601 #if 0
602 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
603 LX, LY, XS, YS);
604 #endif
605
606 while (1)
607 {
608 int hit_mask;
609
610 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
611 {
612 end = 1;
613 laser.overloaded = TRUE;
614 break;
615 }
616
617 hit_mask = ScanPixel();
618
619 #if 0
620 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
621 LX, LY, XS, YS);
622 #endif
623
624 /* hit something -- check out what it was */
625 ELX = (LX + XS) / TILEX;
626 ELY = (LY + YS) / TILEY;
627
628 #if 0
629 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
630 hit_mask, LX, LY, ELX, ELY);
631 #endif
632
633 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
634 {
635 element = EL_EMPTY;
636 laser.dest_element = element;
637
638 break;
639 }
640
641 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
642 {
643 /* we have hit the top-right and bottom-left element --
644 choose the bottom-left one */
645 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
646 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
647 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
648 ELX = (LX - 2) / TILEX;
649 ELY = (LY + 2) / TILEY;
650 }
651
652 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
653 {
654 /* we have hit the top-left and bottom-right element --
655 choose the top-left one */
656 /* !!! SEE ABOVE !!! */
657 ELX = (LX - 2) / TILEX;
658 ELY = (LY - 2) / TILEY;
659 }
660
661
662 #if 0
663 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
664 hit_mask, LX, LY, ELX, ELY);
665 #endif
666
667 element = Feld[ELX][ELY];
668 laser.dest_element = element;
669
670 #if 0
671 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
672 element, ELX, ELY,
673 LX, LY,
674 LX % TILEX, LY % TILEY,
675 hit_mask);
676 #endif
677
678 #if 0
679 if (!IN_LEV_FIELD(ELX, ELY))
680 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
681 #endif
682
683 testx = ELX;
684 testy = ELY;
685
686 if (element == EL_EMPTY)
687 {
688 if (!HitOnlyAnEdge(element, hit_mask))
689 break;
690 }
691 else if (element == EL_FUSE_ON)
692 {
693 if (HitPolarizer(element, hit_mask))
694 break;
695 }
696 else if (IS_GRID(element) || IS_DF_GRID(element))
697 {
698 if (HitPolarizer(element, hit_mask))
699 break;
700 }
701 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
702 element == EL_GATE_STONE || element == EL_GATE_WOOD)
703 {
704 if (HitBlock(element, hit_mask))
705 {
706 rf = 1;
707 break;
708 }
709 }
710 else if (IS_MCDUFFIN(element))
711 {
712 if (HitLaserSource(element, hit_mask))
713 break;
714 }
715 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
716 IS_RECEIVER(element))
717 {
718 if (HitLaserDestination(element, hit_mask))
719 break;
720 }
721 else if (IS_WALL(element))
722 {
723 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
724 {
725 if (HitReflectingWalls(element, hit_mask))
726 break;
727 }
728 else
729 {
730 if (HitAbsorbingWalls(element, hit_mask))
731 break;
732 }
733 }
734 else
735 {
736 if (HitElement(element, hit_mask))
737 break;
738 }
739
740 if (rf)
741 DrawLaser(rf - 1, DL_LASER_ENABLED);
742 rf = laser.num_edges;
743 }
744
745 /*
746 element = Feld[ELX][ELY];
747 laser.dest_element = element;
748 */
749
750
751
752
753 #if 0
754 if (laser.dest_element != Feld[ELX][ELY])
755 {
756 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
757 laser.dest_element, Feld[ELX][ELY]);
758 }
759 #endif
760
761
762 if (!end && !laser.stops_inside_element && !StepBehind())
763 {
764 #if 0
765 printf("ScanLaser: Go one step back\n");
766 #endif
767
768 LX -= XS;
769 LY -= YS;
770 AddLaserEdge(LX, LY);
771 }
772
773 if (rf)
774 DrawLaser(rf - 1, DL_LASER_ENABLED);
775
776 Ct = CT = Counter();
777
778
779 #if 0
780 if (!IN_LEV_FIELD(ELX, ELY))
781 printf("WARNING! (2) %d, %d\n", ELX, ELY);
782 #endif
783
784
785 #if 0
786 printf("(%d, %d) == %d [(%d, %d) == %d]\n",
787 testx, testy, laser.dest_element,
788 ELX, ELY, (IN_SCR_FIELD(ELX,ELY) ? Feld[ELX][ELY] : -1));
789 #endif
790
791 }
792
DrawLaserExt(int start_edge,int num_edges,int mode)793 void DrawLaserExt(int start_edge, int num_edges, int mode)
794 {
795 int element;
796 int elx, ely;
797
798 #if 0
799 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
800 start_edge, num_edges, mode);
801 #endif
802
803 if (start_edge < 0)
804 {
805 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
806 return;
807 }
808
809 if (num_edges < 0)
810 {
811 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
812 return;
813 }
814
815 #if 0
816 if (mode == DL_LASER_DISABLED)
817 {
818 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
819 }
820 #endif
821
822 /* now draw the laser to the backbuffer and (if enabled) to the screen */
823 DrawLines(drawto, &laser.edge[start_edge], num_edges,
824 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
825
826 redraw_mask |= REDRAW_FIELD;
827
828 if (mode == DL_LASER_ENABLED)
829 return;
830
831 /* after the laser was deleted, the "damaged" graphics must be restored */
832 if (laser.num_damages)
833 {
834 int damage_start = 0;
835 int i;
836
837 /* determine the starting edge, from which graphics need to be restored */
838 if (start_edge > 0)
839 {
840 for(i=0; i<laser.num_damages; i++)
841 {
842 if (laser.damage[i].edge == start_edge + 1)
843 {
844 damage_start = i;
845 break;
846 }
847 }
848 }
849
850 /* restore graphics from this starting edge to the end of damage list */
851 for(i=damage_start; i<laser.num_damages; i++)
852 {
853 int lx = laser.damage[i].x;
854 int ly = laser.damage[i].y;
855 int element = Feld[lx][ly];
856
857 if (Hit[lx][ly] == laser.damage[i].edge)
858 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
859 i == damage_start))
860 Hit[lx][ly] = 0;
861 if (Box[lx][ly] == laser.damage[i].edge)
862 Box[lx][ly] = 0;
863
864 if (IS_DRAWABLE(element))
865 DrawField(lx, ly);
866 }
867
868 elx = laser.damage[damage_start].x;
869 ely = laser.damage[damage_start].y;
870 element = Feld[elx][ely];
871
872
873 #if 0
874 if (IS_BEAMER(element))
875 {
876 int i;
877
878 for (i=0; i<laser.num_beamers; i++)
879 printf("-> %d\n", laser.beamer_edge[i]);
880 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
881 mode, elx, ely, Hit[elx][ely], start_edge);
882 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
883 get_element_angle(element), laser.damage[damage_start].angle);
884 }
885 #endif
886
887 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
888 laser.num_beamers > 0 &&
889 start_edge == laser.beamer_edge[laser.num_beamers - 1])
890 {
891 /* element is outgoing beamer */
892 laser.num_damages = damage_start + 1;
893 if (IS_BEAMER(element))
894 laser.current_angle = get_element_angle(element);
895 }
896 else
897 {
898 /* element is incoming beamer or other element */
899 laser.num_damages = damage_start;
900 laser.current_angle = laser.damage[laser.num_damages].angle;
901 }
902 }
903 else
904 {
905 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
906
907 elx = laser.start_edge.x;
908 ely = laser.start_edge.y;
909 element = Feld[elx][ely];
910 }
911
912 laser.num_edges = start_edge + 1;
913 if (start_edge == 0)
914 laser.current_angle = laser.start_angle;
915 LX = laser.edge[start_edge].x - (SX + 2);
916 LY = laser.edge[start_edge].y - (SY + 2);
917 XS = 2 * Step[laser.current_angle].x;
918 YS = 2 * Step[laser.current_angle].y;
919
920 #if 0
921 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
922 LX, LY, element);
923 #endif
924
925 if (start_edge > 0)
926 {
927 if (IS_BEAMER(element) ||
928 IS_FIBRE_OPTIC(element) ||
929 IS_PACMAN(element) ||
930 IS_POLAR(element) ||
931 IS_POLAR_CROSS(element) ||
932 element == EL_FUSE_ON)
933 {
934 int step_size;
935
936 #if 0
937 printf("element == %d\n", element);
938 #endif
939
940 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90� nor 45� angle */
941 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
942 else
943 step_size = 8;
944
945 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
946 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
947 (laser.num_beamers == 0 ||
948 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
949 {
950 /* element is incoming beamer or other element */
951 step_size = -step_size;
952 laser.num_edges--;
953 }
954
955 #if 0
956 if (IS_BEAMER(element))
957 {
958 printf("start_edge == %d, laser.beamer_edge == %d\n",
959 start_edge, laser.beamer_edge);
960 }
961 #endif
962
963 LX += step_size * XS;
964 LY += step_size * YS;
965 }
966 else if (element != EL_EMPTY)
967 {
968 LX -= 3 * XS;
969 LY -= 3 * YS;
970 laser.num_edges--;
971 }
972 }
973
974 #if 0
975 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
976 LX, LY, element);
977 #endif
978 }
979
DrawLaser(int start_edge,int mode)980 void DrawLaser(int start_edge, int mode)
981 {
982 if (laser.num_edges - start_edge < 0)
983 {
984 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
985 return;
986 }
987
988 /* check if laser is interrupted by beamer element */
989 if (laser.num_beamers > 0 &&
990 start_edge < laser.beamer_edge[laser.num_beamers - 1])
991 {
992 if (mode == DL_LASER_ENABLED)
993 {
994 int i;
995 int tmp_start_edge = start_edge;
996
997 /* draw laser segments forward from the start to the last beamer */
998 for (i=0; i<laser.num_beamers; i++)
999 {
1000 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
1001
1002 if (tmp_num_edges <= 0)
1003 continue;
1004
1005 #if 0
1006 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
1007 i, laser.beamer_edge[i], tmp_start_edge);
1008 #endif
1009
1010 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
1011 tmp_start_edge = laser.beamer_edge[i];
1012 }
1013
1014 /* draw last segment from last beamer to the end */
1015 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
1016 DL_LASER_ENABLED);
1017 }
1018 else
1019 {
1020 int i;
1021 int last_num_edges = laser.num_edges;
1022 int num_beamers = laser.num_beamers;
1023
1024 /* delete laser segments backward from the end to the first beamer */
1025 for (i=num_beamers-1; i>=0; i--)
1026 {
1027 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
1028
1029 if (laser.beamer_edge[i] - start_edge <= 0)
1030 break;
1031
1032 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1033 last_num_edges = laser.beamer_edge[i];
1034 laser.num_beamers--;
1035 }
1036
1037 #if 0
1038 if (last_num_edges - start_edge <= 0)
1039 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1040 last_num_edges, start_edge);
1041 #endif
1042
1043 /* delete first segment from start to the first beamer */
1044 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1045 }
1046 }
1047 else
1048 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1049 }
1050
HitElement(int element,int hit_mask)1051 boolean HitElement(int element, int hit_mask)
1052 {
1053 if (HitOnlyAnEdge(element, hit_mask))
1054 return FALSE;
1055
1056 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1057 element = MovingOrBlocked2Element(ELX, ELY);
1058
1059 #if 0
1060 printf("HitElement (1): element == %d\n", element);
1061 #endif
1062
1063 #if 0
1064 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1065 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1066 else
1067 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1068 #endif
1069
1070 AddDamagedField(ELX, ELY);
1071
1072 #if 0
1073 if (ELX != (LX + 5 * XS) / TILEX ||
1074 ELY != (LY + 5 * YS) / TILEY)
1075 {
1076 LX += 2 * XS;
1077 LY += 2 * YS;
1078
1079 return FALSE;
1080 }
1081
1082 #else
1083
1084 /* this is more precise: check if laser would go through the center */
1085 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1086 {
1087 /* skip the whole element before continuing the scan */
1088 do
1089 {
1090 LX += XS;
1091 LY += YS;
1092 }
1093 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1094
1095 if (LX/TILEX > ELX || LY/TILEY > ELY)
1096 {
1097 /* skipping scan positions to the right and down skips one scan
1098 position too much, because this is only the top left scan position
1099 of totally four scan positions (plus one to the right, one to the
1100 bottom and one to the bottom right) */
1101
1102 LX -= XS;
1103 LY -= YS;
1104 }
1105
1106 return FALSE;
1107 }
1108 #endif
1109
1110 #if 0
1111 printf("HitElement (2): element == %d\n", element);
1112 #endif
1113
1114 if (LX + 5 * XS < 0 ||
1115 LY + 5 * YS < 0)
1116 {
1117 LX += 2 * XS;
1118 LY += 2 * YS;
1119
1120 return FALSE;
1121 }
1122
1123 #if 0
1124 printf("HitElement (3): element == %d\n", element);
1125 #endif
1126
1127 if (IS_POLAR(element) &&
1128 ((element - EL_POLAR_START) % 2 ||
1129 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1130 {
1131 PlaySoundStereo(SND_KINK, ST(ELX));
1132 laser.num_damages--;
1133
1134 return TRUE;
1135 }
1136
1137 if (IS_POLAR_CROSS(element) &&
1138 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1139 {
1140 PlaySoundStereo(SND_KINK, ST(ELX));
1141 laser.num_damages--;
1142
1143 return TRUE;
1144 }
1145
1146 if (!IS_BEAMER(element) &&
1147 !IS_FIBRE_OPTIC(element) &&
1148 !IS_GRID_WOOD(element) &&
1149 element != EL_FUEL_EMPTY)
1150 {
1151 #if 0
1152 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1153 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1154 else
1155 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1156 #endif
1157
1158 LX = ELX * TILEX + 14;
1159 LY = ELY * TILEY + 14;
1160 AddLaserEdge(LX, LY);
1161 }
1162
1163 if (IS_MIRROR(element) ||
1164 IS_MIRROR_FIXED(element) ||
1165 IS_POLAR(element) ||
1166 IS_POLAR_CROSS(element) ||
1167 IS_DF_MIRROR(element) ||
1168 IS_DF_MIRROR_AUTO(element) ||
1169 element == EL_PRISM ||
1170 element == EL_REFRACTOR)
1171 {
1172 int current_angle = laser.current_angle;
1173 int step_size;
1174
1175 laser.num_damages--;
1176 AddDamagedField(ELX, ELY);
1177 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1178
1179 if (!Hit[ELX][ELY])
1180 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1181
1182 if (IS_MIRROR(element) ||
1183 IS_MIRROR_FIXED(element) ||
1184 IS_DF_MIRROR(element) ||
1185 IS_DF_MIRROR_AUTO(element))
1186 laser.current_angle = get_mirrored_angle(laser.current_angle,
1187 get_element_angle(element));
1188
1189 if (element == EL_PRISM || element == EL_REFRACTOR)
1190 laser.current_angle = RND(16);
1191
1192 XS = 2 * Step[laser.current_angle].x;
1193 YS = 2 * Step[laser.current_angle].y;
1194
1195 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90� or 45� angle */
1196 step_size = 8;
1197 else
1198 step_size = 4;
1199
1200 LX += step_size * XS;
1201 LY += step_size * YS;
1202
1203 #if 0
1204 /* draw sparkles on mirror */
1205 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1206 current_angle != laser.current_angle)
1207 {
1208 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1209 }
1210 #endif
1211
1212 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1213 current_angle != laser.current_angle)
1214 PlaySoundStereo(SND_LASER, ST(ELX));
1215
1216 laser.overloaded =
1217 (get_opposite_angle(laser.current_angle) ==
1218 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1219
1220 return (laser.overloaded ? TRUE : FALSE);
1221 }
1222
1223 if (element == EL_FUEL_FULL)
1224 {
1225 laser.stops_inside_element = TRUE;
1226
1227 return TRUE;
1228 }
1229
1230 if (element == EL_BOMB || element == EL_MINE)
1231 {
1232 PlaySoundStereo(SND_KINK, ST(ELX));
1233
1234 if (element == EL_MINE)
1235 laser.overloaded = TRUE;
1236 }
1237
1238 if (element == EL_KETTLE ||
1239 element == EL_CELL ||
1240 element == EL_KEY ||
1241 element == EL_LIGHTBALL ||
1242 element == EL_PACMAN ||
1243 IS_PACMAN(element))
1244 {
1245 if (!IS_PACMAN(element))
1246 Bang(ELX, ELY);
1247
1248 if (element == EL_PACMAN)
1249 Bang(ELX, ELY);
1250
1251 if (element == EL_KETTLE || element == EL_CELL)
1252 {
1253 if (game.kettles_still_needed)
1254 DrawText(DX_KETTLES, DY_KETTLES,
1255 int2str(--game.kettles_still_needed, 3), FS_SMALL, FC_YELLOW);
1256 RaiseScore(10);
1257
1258 if (game.kettles_still_needed == 0)
1259 {
1260 int x, y;
1261 static int xy[4][2] =
1262 {
1263 { +1, 0 },
1264 { 0, -1 },
1265 { -1, 0 },
1266 { 0, +1 }
1267 };
1268
1269 PlaySoundStereo(SND_KLING, ST(ELX));
1270
1271 for(y=0; y<lev_fieldy; y++)
1272 {
1273 for(x=0; x<lev_fieldx; x++)
1274 {
1275 /* initiate opening animation of exit door */
1276 if (Feld[x][y] == EL_EXIT_CLOSED)
1277 Feld[x][y] = EL_EXIT_OPENING;
1278
1279 /* remove field that blocks receiver */
1280 if (IS_RECEIVER(Feld[x][y]))
1281 {
1282 int phase = Feld[x][y] - EL_RECEIVER_START;
1283 int blocking_x, blocking_y;
1284
1285 blocking_x = x + xy[phase][0];
1286 blocking_y = y + xy[phase][1];
1287
1288 if (IN_LEV_FIELD(blocking_x, blocking_y))
1289 {
1290 Feld[blocking_x][blocking_y] = EL_EMPTY;
1291 DrawField(blocking_x, blocking_y);
1292 }
1293 }
1294 }
1295 }
1296
1297 DrawLaser(0, DL_LASER_ENABLED);
1298 }
1299 }
1300 else if (element == EL_KEY)
1301 game.num_keys++;
1302 else if (element == EL_LIGHTBALL)
1303 RaiseScore(10);
1304 else if (IS_PACMAN(element))
1305 {
1306 DeletePacMan(ELX, ELY);
1307 RaiseScore(50);
1308 }
1309
1310 return FALSE;
1311 }
1312
1313 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1314 {
1315 PlaySoundStereo(SND_KINK, ST(ELX));
1316
1317 DrawLaser(0, DL_LASER_ENABLED);
1318
1319 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1320 {
1321 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1322 game.lights_still_needed--;
1323 }
1324 else
1325 {
1326 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1327 game.lights_still_needed++;
1328 }
1329
1330 DrawField(ELX, ELY);
1331 DrawLaser(0, DL_LASER_ENABLED);
1332
1333 /*
1334 BackToFront();
1335 */
1336 laser.stops_inside_element = TRUE;
1337
1338 return TRUE;
1339 }
1340
1341 #if 0
1342 printf("HitElement (4): element == %d\n", element);
1343 #endif
1344
1345 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1346 laser.num_beamers < MAX_NUM_BEAMERS &&
1347 laser.beamer[BEAMER_NR(element)][1].num)
1348 {
1349 int beamer_angle = get_element_angle(element);
1350 int beamer_nr = BEAMER_NR(element);
1351 int step_size;
1352
1353 #if 0
1354 printf("HitElement (BEAMER): element == %d\n", element);
1355 #endif
1356
1357 laser.num_damages--;
1358
1359 if (IS_FIBRE_OPTIC(element) ||
1360 laser.current_angle == get_opposite_angle(beamer_angle))
1361 {
1362 int pos;
1363
1364 LX = ELX * TILEX + 14;
1365 LY = ELY * TILEY + 14;
1366 AddLaserEdge(LX, LY);
1367 AddDamagedField(ELX, ELY);
1368 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1369
1370 if (!Hit[ELX][ELY])
1371 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1372
1373 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1374 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1375 ELX = laser.beamer[beamer_nr][pos].x;
1376 ELY = laser.beamer[beamer_nr][pos].y;
1377 LX = ELX * TILEX + 14;
1378 LY = ELY * TILEY + 14;
1379
1380 if (IS_BEAMER(element))
1381 {
1382 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1383 XS = 2 * Step[laser.current_angle].x;
1384 YS = 2 * Step[laser.current_angle].y;
1385 }
1386
1387 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1388 AddLaserEdge(LX, LY);
1389 AddDamagedField(ELX, ELY);
1390 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1391
1392 if (!Hit[ELX][ELY])
1393 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1394
1395 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1396 step_size = 8;
1397 else
1398 step_size = 4;
1399
1400 LX += step_size * XS;
1401 LY += step_size * YS;
1402
1403 laser.num_beamers++;
1404
1405 return FALSE;
1406 }
1407 }
1408
1409 return TRUE;
1410 }
1411
HitOnlyAnEdge(int element,int hit_mask)1412 boolean HitOnlyAnEdge(int element, int hit_mask)
1413 {
1414 /* check if the laser hit only the edge of an element and, if so, go on */
1415
1416 #if 0
1417 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1418 #endif
1419
1420 if ((hit_mask == HIT_MASK_TOPLEFT ||
1421 hit_mask == HIT_MASK_TOPRIGHT ||
1422 hit_mask == HIT_MASK_BOTTOMLEFT ||
1423 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1424 laser.current_angle % 4) /* angle is not 90� */
1425 {
1426 int dx, dy;
1427
1428 if (hit_mask == HIT_MASK_TOPLEFT)
1429 {
1430 dx = -1;
1431 dy = -1;
1432 }
1433 else if (hit_mask == HIT_MASK_TOPRIGHT)
1434 {
1435 dx = +1;
1436 dy = -1;
1437 }
1438 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1439 {
1440 dx = -1;
1441 dy = +1;
1442 }
1443 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1444 {
1445 dx = +1;
1446 dy = +1;
1447 }
1448
1449 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1450 LX += XS;
1451 LY += YS;
1452
1453 #if 0
1454 printf("[HitOnlyAnEdge() == TRUE]\n");
1455 #endif
1456
1457 return TRUE;
1458 }
1459
1460 #if 0
1461 printf("[HitOnlyAnEdge() == FALSE]\n");
1462 #endif
1463
1464 return FALSE;
1465 }
1466
HitPolarizer(int element,int hit_mask)1467 boolean HitPolarizer(int element, int hit_mask)
1468 {
1469 if (HitOnlyAnEdge(element, hit_mask))
1470 return FALSE;
1471
1472 if (IS_DF_GRID(element))
1473 {
1474 int grid_angle = get_element_angle(element);
1475
1476 #if 0
1477 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1478 grid_angle, laser.current_angle);
1479 #endif
1480
1481 AddLaserEdge(LX, LY);
1482 AddDamagedField(ELX, ELY);
1483
1484 if (!Hit[ELX][ELY])
1485 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1486
1487 if (laser.current_angle == grid_angle ||
1488 laser.current_angle == get_opposite_angle(grid_angle))
1489 {
1490 #if 0
1491 int step_size;
1492
1493 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90� or 45� angle */
1494 step_size = 8;
1495 else
1496 step_size = 4;
1497
1498 LX += step_size * XS;
1499 LY += step_size * YS;
1500 #else
1501
1502 /* skip the whole element before continuing the scan */
1503 do
1504 {
1505 LX += XS;
1506 LY += YS;
1507 }
1508 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1509
1510 if (LX/TILEX > ELX || LY/TILEY > ELY)
1511 {
1512 /* skipping scan positions to the right and down skips one scan
1513 position too much, because this is only the top left scan position
1514 of totally four scan positions (plus one to the right, one to the
1515 bottom and one to the bottom right) */
1516
1517 LX -= XS;
1518 LY -= YS;
1519 }
1520 #endif
1521
1522 AddLaserEdge(LX, LY);
1523
1524 LX += XS;
1525 LY += YS;
1526
1527 #if 0
1528 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1529 LX, LY,
1530 LX / TILEX, LY / TILEY,
1531 LX % TILEX, LY % TILEY);
1532 #endif
1533
1534 return FALSE;
1535 }
1536 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1537 return HitReflectingWalls(element, hit_mask);
1538 else
1539 return HitAbsorbingWalls(element, hit_mask);
1540 }
1541 else if (IS_GRID_STEEL(element))
1542 return HitReflectingWalls(element, hit_mask);
1543 else /* IS_GRID_WOOD */
1544 return HitAbsorbingWalls(element, hit_mask);
1545
1546 return TRUE;
1547 }
1548
HitBlock(int element,int hit_mask)1549 boolean HitBlock(int element, int hit_mask)
1550 {
1551 boolean check = FALSE;
1552
1553 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1554 game.num_keys == 0)
1555 check = TRUE;
1556
1557 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1558 {
1559 int i, x, y;
1560 int ex = ELX * TILEX + 14;
1561 int ey = ELY * TILEY + 14;
1562
1563 check = TRUE;
1564
1565 for(i=1; i<32; i++)
1566 {
1567 x = LX + i * XS;
1568 y = LY + i * YS;
1569
1570 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1571 check = FALSE;
1572 }
1573 }
1574
1575 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1576 return HitAbsorbingWalls(element, hit_mask);
1577
1578 if (check)
1579 {
1580 AddLaserEdge(LX - XS, LY - YS);
1581 AddDamagedField(ELX, ELY);
1582
1583 if (!Box[ELX][ELY])
1584 Box[ELX][ELY] = laser.num_edges;
1585
1586 return HitReflectingWalls(element, hit_mask);
1587 }
1588
1589 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1590 {
1591 int xs = XS / 2, ys = YS / 2;
1592 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1593 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1594
1595 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1596 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1597 {
1598 laser.overloaded = (element == EL_GATE_STONE);
1599 return TRUE;
1600 }
1601
1602 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1603 (hit_mask == HIT_MASK_TOP ||
1604 hit_mask == HIT_MASK_LEFT ||
1605 hit_mask == HIT_MASK_RIGHT ||
1606 hit_mask == HIT_MASK_BOTTOM))
1607 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1608 hit_mask == HIT_MASK_BOTTOM),
1609 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1610 hit_mask == HIT_MASK_RIGHT));
1611 AddLaserEdge(LX, LY);
1612
1613 Bang(ELX, ELY);
1614
1615 game.num_keys--;
1616 if (element == EL_GATE_STONE && Box[ELX][ELY])
1617 {
1618 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1619 /*
1620 BackToFront();
1621 */
1622 ScanLaser();
1623
1624 return TRUE;
1625 }
1626
1627 return FALSE;
1628 }
1629
1630 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1631 {
1632 int xs = XS / 2, ys = YS / 2;
1633 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1634 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1635
1636 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1637 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1638 {
1639 laser.overloaded = (element == EL_BLOCK_STONE);
1640
1641 return TRUE;
1642 }
1643
1644 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1645 (hit_mask == HIT_MASK_TOP ||
1646 hit_mask == HIT_MASK_LEFT ||
1647 hit_mask == HIT_MASK_RIGHT ||
1648 hit_mask == HIT_MASK_BOTTOM))
1649 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1650 hit_mask == HIT_MASK_BOTTOM),
1651 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1652 hit_mask == HIT_MASK_RIGHT));
1653 AddDamagedField(ELX, ELY);
1654
1655 LX = ELX * TILEX + 14;
1656 LY = ELY * TILEY + 14;
1657 AddLaserEdge(LX, LY);
1658
1659 laser.stops_inside_element = TRUE;
1660
1661 return TRUE;
1662 }
1663
1664 return TRUE;
1665 }
1666
HitLaserSource(int element,int hit_mask)1667 boolean HitLaserSource(int element, int hit_mask)
1668 {
1669 if (HitOnlyAnEdge(element, hit_mask))
1670 return FALSE;
1671
1672 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1673 laser.overloaded = TRUE;
1674
1675 return TRUE;
1676 }
1677
HitLaserDestination(int element,int hit_mask)1678 boolean HitLaserDestination(int element, int hit_mask)
1679 {
1680 if (HitOnlyAnEdge(element, hit_mask))
1681 return FALSE;
1682
1683 if (element != EL_EXIT_OPEN &&
1684 !(IS_RECEIVER(element) &&
1685 game.kettles_still_needed == 0 &&
1686 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1687 {
1688 PlaySoundStereo(SND_HOLZ, ST(ELX));
1689 return TRUE;
1690 }
1691
1692 if (IS_RECEIVER(element) ||
1693 (IS_22_5_ANGLE(laser.current_angle) &&
1694 (ELX != (LX + 6 * XS) / TILEX ||
1695 ELY != (LY + 6 * YS) / TILEY ||
1696 LX + 6 * XS < 0 ||
1697 LY + 6 * YS < 0)))
1698 {
1699 LX -= XS;
1700 LY -= YS;
1701 }
1702 else
1703 {
1704 LX = ELX * TILEX + 14;
1705 LY = ELY * TILEY + 14;
1706
1707 laser.stops_inside_element = TRUE;
1708 }
1709
1710 AddLaserEdge(LX, LY);
1711 AddDamagedField(ELX, ELY);
1712
1713 if (game.lights_still_needed == 0)
1714 game.level_solved = TRUE;
1715
1716 return TRUE;
1717 }
1718
HitReflectingWalls(int element,int hit_mask)1719 boolean HitReflectingWalls(int element, int hit_mask)
1720 {
1721 /* check if laser hits side of a wall with an angle that is not 90� */
1722 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1723 hit_mask == HIT_MASK_LEFT ||
1724 hit_mask == HIT_MASK_RIGHT ||
1725 hit_mask == HIT_MASK_BOTTOM))
1726 {
1727 PlaySoundStereo(SND_HUI, ST(ELX));
1728 LX -= XS;
1729 LY -= YS;
1730 if (!IS_DF_GRID(element))
1731 AddLaserEdge(LX, LY);
1732
1733 /* check if laser hits wall with an angle of 45� */
1734 if (!IS_22_5_ANGLE(laser.current_angle))
1735 {
1736 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1737 {
1738 LX += 2 * XS;
1739 laser.current_angle = get_mirrored_angle(laser.current_angle,
1740 ANG_MIRROR_0);
1741 }
1742 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1743 {
1744 LY += 2 * YS;
1745 laser.current_angle = get_mirrored_angle(laser.current_angle,
1746 ANG_MIRROR_90);
1747 }
1748
1749 AddLaserEdge(LX, LY);
1750 XS = 2 * Step[laser.current_angle].x;
1751 YS = 2 * Step[laser.current_angle].y;
1752
1753 return FALSE;
1754 }
1755 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1756 {
1757 laser.current_angle = get_mirrored_angle(laser.current_angle,
1758 ANG_MIRROR_0);
1759 if (ABS(XS) == 4)
1760 {
1761 LX += 2 * XS;
1762 if (!IS_DF_GRID(element))
1763 AddLaserEdge(LX, LY);
1764 }
1765 else
1766 {
1767 LX += XS;
1768 if (!IS_DF_GRID(element))
1769 AddLaserEdge(LX, LY + YS / 2);
1770
1771 LX += XS;
1772 if (!IS_DF_GRID(element))
1773 AddLaserEdge(LX, LY);
1774 }
1775
1776 YS = 2 * Step[laser.current_angle].y;
1777
1778 return FALSE;
1779 }
1780 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1781 {
1782 laser.current_angle = get_mirrored_angle(laser.current_angle,
1783 ANG_MIRROR_90);
1784 if (ABS(YS) == 4)
1785 {
1786 LY += 2 * YS;
1787 if (!IS_DF_GRID(element))
1788 AddLaserEdge(LX, LY);
1789 }
1790 else
1791 {
1792 LY += YS;
1793 if (!IS_DF_GRID(element))
1794 AddLaserEdge(LX + XS / 2, LY);
1795
1796 LY += YS;
1797 if (!IS_DF_GRID(element))
1798 AddLaserEdge(LX, LY);
1799 }
1800
1801 XS = 2 * Step[laser.current_angle].x;
1802
1803 return FALSE;
1804 }
1805 }
1806
1807 /* reflection at the edge of reflecting DF style wall */
1808 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1809 {
1810 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1811 hit_mask == HIT_MASK_TOPRIGHT) ||
1812 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1813 hit_mask == HIT_MASK_TOPLEFT) ||
1814 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1815 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1816 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1817 hit_mask == HIT_MASK_BOTTOMRIGHT))
1818 {
1819 int mirror_angle =
1820 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1821 ANG_MIRROR_135 : ANG_MIRROR_45);
1822
1823 PlaySoundStereo(SND_HUI, ST(ELX));
1824 AddDamagedField(ELX, ELY);
1825 AddLaserEdge(LX, LY);
1826
1827 laser.current_angle = get_mirrored_angle(laser.current_angle,
1828 mirror_angle);
1829 XS = 8 / -XS;
1830 YS = 8 / -YS;
1831
1832 LX += XS;
1833 LY += YS;
1834 AddLaserEdge(LX, LY);
1835
1836 return FALSE;
1837 }
1838 }
1839
1840 /* reflection inside an edge of reflecting DF style wall */
1841 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1842 {
1843 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1844 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1845 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1846 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1847 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1848 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1849 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1850 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1851 {
1852 int mirror_angle =
1853 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1854 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1855 ANG_MIRROR_135 : ANG_MIRROR_45);
1856
1857 PlaySoundStereo(SND_HUI, ST(ELX));
1858 /*
1859 AddDamagedField(ELX, ELY);
1860 */
1861 AddLaserEdge(LX - XS, LY - YS);
1862 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1863 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1864
1865 laser.current_angle = get_mirrored_angle(laser.current_angle,
1866 mirror_angle);
1867 XS = 8 / -XS;
1868 YS = 8 / -YS;
1869
1870 LX += XS;
1871 LY += YS;
1872 AddLaserEdge(LX, LY);
1873
1874 return FALSE;
1875 }
1876 }
1877
1878 /* check if laser hits DF style wall with an angle of 90� */
1879 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1880 {
1881 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1882 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1883 (IS_VERT_ANGLE(laser.current_angle) &&
1884 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1885 {
1886 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1887
1888 /* laser at last step touched nothing or the same side of the wall */
1889 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1890 {
1891 AddDamagedField(ELX, ELY);
1892 LX += 8 * XS;
1893 LY += 8 * YS;
1894
1895 last_LX = LX;
1896 last_LY = LY;
1897 last_hit_mask = hit_mask;
1898
1899 return FALSE;
1900 }
1901 }
1902 }
1903
1904 if (!HitOnlyAnEdge(element, hit_mask))
1905 {
1906 laser.overloaded = TRUE;
1907 return TRUE;
1908 }
1909
1910 return FALSE;
1911 }
1912
HitAbsorbingWalls(int element,int hit_mask)1913 boolean HitAbsorbingWalls(int element, int hit_mask)
1914 {
1915 if (HitOnlyAnEdge(element, hit_mask))
1916 return FALSE;
1917
1918 if (ABS(XS) == 4 &&
1919 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1920 {
1921 AddLaserEdge(LX - XS, LY - YS);
1922 LX = LX + XS / 2;
1923 LY = LY + YS;
1924 }
1925
1926 if (ABS(YS) == 4 &&
1927 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1928 {
1929 AddLaserEdge(LX - XS, LY - YS);
1930 LX = LX + XS;
1931 LY = LY + YS / 2;
1932 }
1933
1934 if (IS_WALL_WOOD(element) ||
1935 IS_DF_WALL_WOOD(element) ||
1936 IS_GRID_WOOD(element) ||
1937 IS_GRID_WOOD_FIXED(element) ||
1938 IS_GRID_WOOD_AUTO(element) ||
1939 element == EL_FUSE_ON ||
1940 element == EL_BLOCK_WOOD ||
1941 element == EL_GATE_WOOD)
1942 {
1943 PlaySoundStereo(SND_HOLZ, ST(ELX));
1944 return TRUE;
1945 }
1946
1947 if (IS_WALL_ICE(element))
1948 {
1949 int mask;
1950
1951 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1952 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1953
1954 /* check if laser hits wall with an angle of 90� */
1955 if (IS_90_ANGLE(laser.current_angle))
1956 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1957
1958 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1959 {
1960 int i;
1961
1962 for(i=0; i<4; i++)
1963 {
1964 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1965 mask = 15 - (8 >> i);
1966 else if (ABS(XS) == 4 &&
1967 mask == (1 << i) &&
1968 (XS > 0) == (i % 2) &&
1969 (YS < 0) == (i / 2))
1970 mask = 3 + (i / 2) * 9;
1971 else if (ABS(YS) == 4 &&
1972 mask == (1 << i) &&
1973 (XS < 0) == (i % 2) &&
1974 (YS > 0) == (i / 2))
1975 mask = 5 + (i % 2) * 5;
1976 }
1977 }
1978
1979 laser.wall_mask = mask;
1980 }
1981 else if (IS_WALL_AMOEBA(element))
1982 {
1983 int elx = (LX - 2 * XS) / TILEX;
1984 int ely = (LY - 2 * YS) / TILEY;
1985 int element2 = Feld[elx][ely];
1986 int mask;
1987
1988 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1989 {
1990 laser.dest_element = EL_EMPTY;
1991 return TRUE;
1992 }
1993
1994 ELX = elx;
1995 ELY = ely;
1996
1997 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1998 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1999
2000 if (IS_90_ANGLE(laser.current_angle))
2001 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2002
2003 laser.dest_element = element2 | EL_WALL_AMOEBA;
2004
2005 laser.wall_mask = mask;
2006 }
2007
2008 return TRUE;
2009 }
2010
OpenExit(int x,int y)2011 void OpenExit(int x, int y)
2012 {
2013 int delay = 6;
2014
2015 if (!MovDelay[x][y]) /* next animation frame */
2016 MovDelay[x][y] = 4 * delay;
2017
2018 if (MovDelay[x][y]) /* wait some time before next frame */
2019 {
2020 int phase;
2021
2022 MovDelay[x][y]--;
2023 phase = MovDelay[x][y] / delay;
2024 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2025 DrawGraphic(x, y, EL_EXIT_OPEN - phase);
2026
2027 if (!MovDelay[x][y])
2028 {
2029 Feld[x][y] = EL_EXIT_OPEN;
2030 DrawField(x, y);
2031 }
2032 }
2033 }
2034
OpenSurpriseBall(int x,int y)2035 void OpenSurpriseBall(int x, int y)
2036 {
2037 int delay = 2;
2038
2039 if (!MovDelay[x][y]) /* next animation frame */
2040 MovDelay[x][y] = 50 * delay;
2041
2042 if (MovDelay[x][y]) /* wait some time before next frame */
2043 {
2044 int phase;
2045
2046 MovDelay[x][y]--;
2047 phase = MovDelay[x][y] / delay;
2048 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2049 {
2050 int graphic = el2gfx(Store[x][y]);
2051 int bitmap_nr, gx, gy;
2052 int dx = RND(26), dy = RND(26);
2053
2054 getGraphicSource(graphic, &bitmap_nr, &gx, &gy);
2055 BlitBitmap(pix[bitmap_nr], drawto, gx + dx, gy + dy, 6, 6,
2056 SX + x * TILEX + dx, SY + y * TILEY + dy);
2057 MarkTileDirty(x, y);
2058 }
2059
2060 if (!MovDelay[x][y])
2061 {
2062 Feld[x][y] = Store[x][y];
2063 Store[x][y] = 0;
2064 DrawField(x, y);
2065
2066 ScanLaser();
2067 }
2068 }
2069 }
2070
MeltIce(int x,int y)2071 void MeltIce(int x, int y)
2072 {
2073 int frames = 5;
2074 int delay = 5;
2075
2076 if (!MovDelay[x][y]) /* next animation frame */
2077 MovDelay[x][y] = frames * delay;
2078
2079 if (MovDelay[x][y]) /* wait some time before next frame */
2080 {
2081 int phase;
2082 int wall_mask = Store2[x][y];
2083 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2084
2085 MovDelay[x][y]--;
2086 phase = frames - MovDelay[x][y] / delay - 1;
2087
2088 if (!MovDelay[x][y])
2089 {
2090 int i;
2091
2092 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2093 Store[x][y] = Store2[x][y] = 0;
2094
2095 DrawWalls(x, y, Feld[x][y]);
2096
2097 if (Feld[x][y] == EL_WALL_ICE)
2098 Feld[x][y] = EL_EMPTY;
2099
2100 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
2101 if (laser.damage[i].is_mirror)
2102 break;
2103
2104 if (i > 0)
2105 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2106 else
2107 DrawLaser(0, DL_LASER_DISABLED);
2108
2109 ScanLaser();
2110 }
2111 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2112 {
2113 DrawWallsAnimation(x, y, real_element, phase, wall_mask);
2114
2115 laser.redraw = TRUE;
2116 }
2117 }
2118 }
2119
GrowAmoeba(int x,int y)2120 void GrowAmoeba(int x, int y)
2121 {
2122 int frames = 5;
2123 int delay = 1;
2124
2125 if (!MovDelay[x][y]) /* next animation frame */
2126 MovDelay[x][y] = frames * delay;
2127
2128 if (MovDelay[x][y]) /* wait some time before next frame */
2129 {
2130 int phase;
2131 int wall_mask = Store2[x][y];
2132 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2133
2134 MovDelay[x][y]--;
2135 phase = MovDelay[x][y] / delay;
2136
2137 if (!MovDelay[x][y])
2138 {
2139 Feld[x][y] = real_element;
2140 Store[x][y] = Store2[x][y] = 0;
2141
2142 DrawWalls(x, y, Feld[x][y]);
2143 DrawLaser(0, DL_LASER_ENABLED);
2144 }
2145 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2146 DrawWallsAnimation(x, y, real_element, phase, wall_mask);
2147 }
2148 }
2149
Explode(int x,int y,int phase,int mode)2150 void Explode(int x, int y, int phase, int mode)
2151 {
2152 int num_phase = 9, delay = 2;
2153 int last_phase = num_phase * delay;
2154 int half_phase = (num_phase / 2) * delay;
2155 #if 0
2156 int first_phase_after_start = EX_PHASE_START + 1;
2157 #endif
2158
2159 laser.redraw = TRUE;
2160
2161 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2162 {
2163 int center_element = Feld[x][y];
2164
2165 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2166 {
2167 /* put moving element to center field (and let it explode there) */
2168 center_element = MovingOrBlocked2Element(x, y);
2169 RemoveMovingField(x, y);
2170 Feld[x][y] = center_element;
2171 }
2172
2173 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2174 Store[x][y] = center_element;
2175 else
2176 Store[x][y] = EL_EMPTY;
2177 Store2[x][y] = mode;
2178 Feld[x][y] = EL_EXPLODING_OPAQUE;
2179 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2180 Frame[x][y] = 1;
2181
2182 return;
2183 }
2184
2185 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2186
2187 if (phase == half_phase)
2188 {
2189 Feld[x][y] = EL_EXPLODING_TRANSP;
2190
2191 if (x == ELX && y == ELY)
2192 ScanLaser();
2193 }
2194
2195 if (phase == last_phase)
2196 {
2197 int element;
2198
2199 if (Store[x][y] == EL_BOMB)
2200 {
2201 laser.num_damages--;
2202 DrawLaser(0, DL_LASER_DISABLED);
2203 laser.num_edges = 0;
2204
2205 Bang(laser.start_edge.x, laser.start_edge.y);
2206 Store[x][y] = EL_EMPTY;
2207 }
2208 else if (IS_MCDUFFIN(Store[x][y]))
2209 {
2210 game.game_over = TRUE;
2211 game.game_over_cause = GAME_OVER_BOMB;
2212 Store[x][y] = EL_EMPTY;
2213 }
2214
2215 element = Feld[x][y] = Store[x][y];
2216 Store[x][y] = Store2[x][y] = 0;
2217 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2218 InitField(x, y, FALSE);
2219 DrawField(x, y);
2220 }
2221 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2222 {
2223 int graphic = GFX_EXPLOSION_START;
2224 int graphic_phase = (phase / delay - 1);
2225
2226 if (Store2[x][y] == EX_KETTLE)
2227 {
2228 if (graphic_phase < 3)
2229 graphic = GFX_EXPLOSION_KETTLE;
2230 else if (graphic_phase < 5)
2231 {
2232 graphic = GFX_EXPLOSION_LAST;
2233 graphic_phase -= graphic_phase;
2234 }
2235 else
2236 {
2237 graphic = GFX_EMPTY;
2238 graphic_phase = 0;
2239 }
2240 }
2241 else if (Store2[x][y] == EX_SHORT)
2242 {
2243 if (graphic_phase < 4)
2244 graphic = GFX_EXPLOSION_SHORT;
2245 else
2246 {
2247 graphic = GFX_EMPTY;
2248 graphic_phase = 0;
2249 }
2250 }
2251
2252 DrawGraphic(x, y, graphic + graphic_phase);
2253 }
2254 }
2255
Bang(int x,int y)2256 void Bang(int x, int y)
2257 {
2258 int element = Feld[x][y];
2259 int mode = EX_NORMAL;
2260
2261 #if 0
2262 DrawLaser(0, DL_LASER_ENABLED);
2263 #endif
2264
2265 switch(element)
2266 {
2267 case EL_KETTLE:
2268 mode = EX_KETTLE;
2269 break;
2270
2271 case EL_GATE_STONE:
2272 case EL_GATE_WOOD:
2273 mode = EX_SHORT;
2274 break;
2275
2276 default:
2277 mode = EX_NORMAL;
2278 break;
2279 }
2280
2281 if (IS_PACMAN(element))
2282 PlaySoundStereo(SND_QUIEK, ST(x));
2283 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2284 PlaySoundStereo(SND_ROAAAR, ST(x));
2285 else if (element == EL_KEY)
2286 PlaySoundStereo(SND_KLING, ST(x));
2287 else
2288 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2289
2290 Explode(x, y, EX_PHASE_START, mode);
2291 }
2292
TurnRound(int x,int y)2293 void TurnRound(int x, int y)
2294 {
2295 static struct
2296 {
2297 int x, y;
2298 } move_xy[] =
2299 {
2300 { 0, 0 },
2301 {-1, 0 },
2302 {+1, 0 },
2303 { 0, 0 },
2304 { 0, -1 },
2305 { 0, 0 }, { 0, 0 }, { 0, 0 },
2306 { 0, +1 }
2307 };
2308 static struct
2309 {
2310 int left, right, back;
2311 } turn[] =
2312 {
2313 { 0, 0, 0 },
2314 { MV_DOWN, MV_UP, MV_RIGHT },
2315 { MV_UP, MV_DOWN, MV_LEFT },
2316 { 0, 0, 0 },
2317 { MV_LEFT, MV_RIGHT, MV_DOWN },
2318 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2319 { MV_RIGHT, MV_LEFT, MV_UP }
2320 };
2321
2322 int element = Feld[x][y];
2323 int old_move_dir = MovDir[x][y];
2324 int left_dir = turn[old_move_dir].left;
2325 int right_dir = turn[old_move_dir].right;
2326 int back_dir = turn[old_move_dir].back;
2327
2328 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
2329 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2330
2331 int left_x = x+left_dx, left_y = y+left_dy;
2332 int right_x = x+right_dx, right_y = y+right_dy;
2333
2334 if (element == EL_PACMAN)
2335 {
2336 boolean can_turn_left = FALSE, can_turn_right = FALSE;
2337
2338 if (IN_LEV_FIELD(left_x, left_y) &&
2339 IS_EATABLE4PACMAN(Feld[left_x][left_y]))
2340 can_turn_left = TRUE;
2341 if (IN_LEV_FIELD(right_x, right_y) &&
2342 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2343 can_turn_right = TRUE;
2344
2345 if (can_turn_right)
2346 MovDir[x][y] = right_dir;
2347 else
2348 MovDir[x][y] = back_dir;
2349
2350 MovDelay[x][y] = 0;
2351 }
2352 }
2353
StartMoving(int x,int y)2354 void StartMoving(int x, int y)
2355 {
2356 int element = Feld[x][y];
2357
2358 if (Stop[x][y])
2359 return;
2360
2361 if (CAN_MOVE(element))
2362 {
2363 int newx, newy;
2364
2365 if (MovDelay[x][y]) /* wait some time before next movement */
2366 {
2367 MovDelay[x][y]--;
2368
2369 if (MovDelay[x][y])
2370 return;
2371 }
2372
2373 /* now make next step */
2374
2375 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
2376
2377 if (element == EL_PACMAN &&
2378 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2379 !ObjHit(newx, newy, HIT_POS_CENTER))
2380 {
2381 Store[newx][newy] = Feld[newx][newy];
2382 Feld[newx][newy] = EL_EMPTY;
2383 DrawField(newx, newy);
2384 }
2385 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2386 ObjHit(newx, newy, HIT_POS_CENTER))
2387 {
2388 /* object was running against a wall */
2389
2390 TurnRound(x, y);
2391
2392 return;
2393 }
2394
2395 InitMovingField(x, y, MovDir[x][y]);
2396 }
2397
2398 if (MovDir[x][y])
2399 ContinueMoving(x, y);
2400 }
2401
ContinueMoving(int x,int y)2402 void ContinueMoving(int x, int y)
2403 {
2404 int element = Feld[x][y];
2405 int direction = MovDir[x][y];
2406 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2407 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2408 int horiz_move = (dx!=0);
2409 int newx = x + dx, newy = y + dy;
2410 int step = (horiz_move ? dx : dy) * TILEX / 8;
2411
2412 MovPos[x][y] += step;
2413
2414 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2415 {
2416 Feld[x][y] = EL_EMPTY;
2417 Feld[newx][newy] = element;
2418
2419 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2420 MovDelay[newx][newy] = 0;
2421
2422 if (!CAN_MOVE(element))
2423 MovDir[newx][newy] = 0;
2424
2425 DrawField(x, y);
2426 DrawField(newx, newy);
2427
2428 Stop[newx][newy] = TRUE;
2429
2430 if (element == EL_PACMAN)
2431 {
2432 if (Store[newx][newy] == EL_BOMB)
2433 Bang(newx, newy);
2434
2435 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2436 (LX + 2 * XS) / TILEX == newx &&
2437 (LY + 2 * YS) / TILEY == newy)
2438 {
2439 laser.num_edges--;
2440 ScanLaser();
2441 }
2442 }
2443 }
2444 else /* still moving on */
2445 DrawField(x, y);
2446
2447 laser.redraw = TRUE;
2448 }
2449
ClickElement(int mx,int my,int button)2450 void ClickElement(int mx, int my, int button)
2451 {
2452 static unsigned long click_delay = 0;
2453 static int click_delay_value = CLICK_DELAY_SHORT;
2454 static boolean new_button = TRUE;
2455 int element;
2456 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2457
2458 if (button == MB_RELEASED)
2459 {
2460 new_button = TRUE;
2461 click_delay_value = CLICK_DELAY_SHORT;
2462
2463 /* release eventually hold auto-rotating mirror */
2464 RotateMirror(x, y, MB_RELEASED);
2465
2466 return;
2467 }
2468
2469 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2470 return;
2471
2472 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2473 return;
2474
2475 if (!IN_PIX_FIELD(mx - SX, my - SY))
2476 return;
2477
2478 if (Feld[x][y] == EL_EMPTY)
2479 return;
2480
2481 element = Feld[x][y];
2482
2483 if (IS_MIRROR(element) ||
2484 IS_BEAMER(element) ||
2485 IS_POLAR(element) ||
2486 IS_POLAR_CROSS(element) ||
2487 IS_DF_MIRROR(element) ||
2488 IS_DF_MIRROR_AUTO(element))
2489 {
2490 RotateMirror(x, y, button);
2491 }
2492 else if (IS_MCDUFFIN(element))
2493 {
2494 if (!laser.fuse_off)
2495 {
2496 DrawLaser(0, DL_LASER_DISABLED);
2497 /*
2498 BackToFront();
2499 */
2500 }
2501
2502 element = get_rotated_element(element, BUTTON_ROTATION(button));
2503 laser.start_angle = get_element_angle(element);
2504
2505 InitLaser();
2506
2507 Feld[x][y] = element;
2508 DrawField(x, y);
2509 /*
2510 BackToFront();
2511 */
2512 if (!laser.fuse_off)
2513 ScanLaser();
2514 }
2515 else if (element == EL_FUSE_ON && laser.fuse_off)
2516 {
2517 if (x != laser.fuse_x || y != laser.fuse_y)
2518 return;
2519
2520 laser.fuse_off = FALSE;
2521 laser.fuse_x = laser.fuse_y = -1;
2522
2523 DrawGraphic(x, y, GFX_FUSE_ON);
2524 ScanLaser();
2525 }
2526 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2527 {
2528 laser.fuse_off = TRUE;
2529 laser.fuse_x = x;
2530 laser.fuse_y = y;
2531 laser.overloaded = FALSE;
2532
2533 DrawLaser(0, DL_LASER_DISABLED);
2534 DrawGraphic(x, y, GFX_FUSE_OFF);
2535 }
2536 else if (element == EL_LIGHTBALL)
2537 {
2538 Bang(x, y);
2539 RaiseScore(10);
2540 DrawLaser(0, DL_LASER_ENABLED);
2541 }
2542
2543 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2544 new_button = FALSE;
2545 }
2546
RotateMirror(int x,int y,int button)2547 void RotateMirror(int x, int y, int button)
2548 {
2549 static int hold_x = -1, hold_y = -1;
2550
2551 if (button == MB_RELEASED)
2552 {
2553 /* release eventually hold auto-rotating mirror */
2554 hold_x = -1;
2555 hold_y = -1;
2556
2557 return;
2558 }
2559
2560 if (IS_MIRROR(Feld[x][y]) ||
2561 IS_POLAR_CROSS(Feld[x][y]) ||
2562 IS_POLAR(Feld[x][y]) ||
2563 IS_BEAMER(Feld[x][y]) ||
2564 IS_DF_MIRROR(Feld[x][y]) ||
2565 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2566 IS_GRID_WOOD_AUTO(Feld[x][y]))
2567 {
2568 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2569 }
2570 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2571 {
2572 if (button == MB_LEFTBUTTON)
2573 {
2574 /* left mouse button only for manual adjustment, no auto-rotating;
2575 freeze mirror for until mouse button released */
2576 hold_x = x;
2577 hold_y = y;
2578 }
2579 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2580 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2581 }
2582
2583 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2584 {
2585 int edge = Hit[x][y];
2586
2587 DrawField(x, y);
2588
2589 if (edge > 0)
2590 {
2591 DrawLaser(edge - 1, DL_LASER_DISABLED);
2592 ScanLaser();
2593 }
2594 }
2595 else if (ObjHit(x, y, HIT_POS_CENTER))
2596 {
2597 int edge = Hit[x][y];
2598
2599 if (edge == 0)
2600 {
2601 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2602 edge = 1;
2603 }
2604
2605 DrawLaser(edge - 1, DL_LASER_DISABLED);
2606 ScanLaser();
2607 }
2608 else
2609 {
2610 int check = 1;
2611
2612 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2613 check = 2;
2614
2615 DrawField(x, y);
2616
2617 if ((IS_BEAMER(Feld[x][y]) ||
2618 IS_POLAR(Feld[x][y]) ||
2619 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2620 {
2621 check = 0;
2622
2623 if (IS_BEAMER(Feld[x][y]))
2624 {
2625 #if 0
2626 printf("TEST (%d, %d) [%d] [%d]\n",
2627 LX, LY,
2628 laser.beamer_edge, laser.beamer[1].num);
2629 #endif
2630
2631 laser.num_edges--;
2632 }
2633
2634 ScanLaser();
2635 }
2636
2637 if (check == 2)
2638 DrawLaser(0, DL_LASER_ENABLED);
2639 }
2640 }
2641
AutoRotateMirrors()2642 void AutoRotateMirrors()
2643 {
2644 static unsigned long rotate_delay = 0;
2645 int x, y;
2646
2647 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2648 return;
2649
2650 for (x=0; x<lev_fieldx; x++)
2651 {
2652 for (y=0; y<lev_fieldy; y++)
2653 {
2654 int element = Feld[x][y];
2655
2656 if (IS_DF_MIRROR_AUTO(element) ||
2657 IS_GRID_WOOD_AUTO(element) ||
2658 IS_GRID_STEEL_AUTO(element) ||
2659 element == EL_REFRACTOR)
2660 RotateMirror(x, y, MB_RIGHTBUTTON);
2661 }
2662 }
2663 }
2664
ObjHit(int obx,int oby,int bits)2665 boolean ObjHit(int obx, int oby, int bits)
2666 {
2667 int i;
2668
2669 obx *= TILEX;
2670 oby *= TILEY;
2671
2672 if (bits & HIT_POS_CENTER)
2673 {
2674 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2675 return TRUE;
2676 }
2677
2678 if (bits & HIT_POS_EDGE)
2679 {
2680 for(i=0; i<4; i++)
2681 if (ReadPixel(drawto,
2682 SX + obx + 31 * (i % 2),
2683 SY + oby + 31 * (i / 2)) == pen_ray)
2684 return TRUE;
2685 }
2686
2687 if (bits & HIT_POS_BETWEEN)
2688 {
2689 for(i=0; i<4; i++)
2690 if (ReadPixel(drawto,
2691 SX + 4 + obx + 22 * (i % 2),
2692 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2693 return TRUE;
2694 }
2695
2696 return FALSE;
2697 }
2698
DeletePacMan(int px,int py)2699 void DeletePacMan(int px, int py)
2700 {
2701 int i, j;
2702
2703 Bang(px, py);
2704
2705 if (game.num_pacman <= 1)
2706 {
2707 game.num_pacman = 0;
2708 return;
2709 }
2710
2711 for(i=0; i<game.num_pacman; i++)
2712 if (game.pacman[i].x == px && game.pacman[i].y == py)
2713 break;
2714
2715 game.num_pacman--;
2716
2717 for(j=i; j<game.num_pacman; j++)
2718 {
2719 game.pacman[j].x = game.pacman[j + 1].x;
2720 game.pacman[j].y = game.pacman[j + 1].y;
2721 game.pacman[j].dir = game.pacman[j + 1].dir;
2722 }
2723 }
2724
ColorCycling(void)2725 void ColorCycling(void)
2726 {
2727 static int CC, Cc = 0;
2728
2729 static int color, old = 0xF00, new = 0x010, mult = 1;
2730 static unsigned short red, green, blue;
2731
2732 if (color_status == STATIC_COLORS)
2733 return;
2734
2735 CC = Counter();
2736
2737 if (CC < Cc || CC > Cc + 50)
2738 {
2739 Cc = CC;
2740
2741 color = old + new * mult;
2742 if (mult > 0)
2743 mult++;
2744 else
2745 mult--;
2746
2747 if (ABS(mult) == 16)
2748 {
2749 mult =- mult / 16;
2750 old = color;
2751 new = new << 4;
2752 if (new > 0x100)
2753 new = 0x001;
2754 }
2755
2756 red = 0x0e00 * ((color & 0xF00) >> 8);
2757 green = 0x0e00 * ((color & 0x0F0) >> 4);
2758 blue = 0x0e00 * ((color & 0x00F));
2759 SetRGB(pen_magicolor[0], red, green, blue);
2760
2761 red = 0x1111 * ((color & 0xF00) >> 8);
2762 green = 0x1111 * ((color & 0x0F0) >> 4);
2763 blue = 0x1111 * ((color & 0x00F));
2764 SetRGB(pen_magicolor[1], red, green, blue);
2765 }
2766 }
2767
GameActions()2768 void GameActions()
2769 {
2770 static unsigned long action_delay = 0;
2771 static unsigned long pacman_delay = 0;
2772 static unsigned long energy_delay = 0;
2773 static unsigned long overload_delay = 0;
2774 #if 0
2775 unsigned short color_scale = 0xFFFF / 15;
2776 #endif
2777 int element;
2778 int x, y, i;
2779
2780 int r, d;
2781
2782 #if 1
2783 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2784 #else
2785 if (!DelayReached(&action_delay, GameFrameDelay))
2786 {
2787 if (!PendingEvent()) /* delay only if no pending events */
2788 Delay(10);
2789 return;
2790 }
2791 #endif
2792
2793 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2794 Stop[x][y] = FALSE;
2795
2796 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2797 {
2798 element = Feld[x][y];
2799
2800 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2801 StartMoving(x, y);
2802 else if (IS_MOVING(x, y))
2803 ContinueMoving(x, y);
2804 else if (IS_EXPLODING(element))
2805 Explode(x, y, Frame[x][y], EX_NORMAL);
2806 else if (element == EL_EXIT_OPENING)
2807 OpenExit(x, y);
2808 else if (element == EL_GRAY_BALL_OPENING)
2809 OpenSurpriseBall(x, y);
2810 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2811 MeltIce(x, y);
2812 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2813 GrowAmoeba(x, y);
2814 }
2815
2816 AutoRotateMirrors();
2817
2818 #if 1
2819 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2820
2821 /* redraw after Explode() ... */
2822 if (laser.redraw)
2823 DrawLaser(0, DL_LASER_ENABLED);
2824 laser.redraw = FALSE;
2825 #endif
2826
2827 CT = Counter();
2828
2829 if (game.num_pacman && DelayReached(&pacman_delay, 250))
2830 {
2831 MovePacMen();
2832
2833 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2834 {
2835 DrawLaser(0, DL_LASER_DISABLED);
2836 ScanLaser();
2837 }
2838 }
2839
2840 if (DelayReached(&energy_delay, 4000))
2841 {
2842 game.energy_left--;
2843 if (game.energy_left >= 0)
2844 {
2845 BlitBitmap(pix[PIX_DOOR], drawto,
2846 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2847 ENERGY_XSIZE, ENERGY_YSIZE - game.energy_left,
2848 DX_ENERGY, DY_ENERGY);
2849 redraw_mask |= REDRAW_DOOR_1;
2850 }
2851 else if (setup.time_limit)
2852 {
2853 int i;
2854
2855 for(i=15; i>=0; i--)
2856 {
2857 #if 0
2858 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2859 #endif
2860 pen_ray = GetPixelFromRGB(window,
2861 level.laser_red * 0x11 * i,
2862 level.laser_green * 0x11 * i,
2863 level.laser_blue * 0x11 * i);
2864 DrawLaser(0, DL_LASER_ENABLED);
2865 BackToFront();
2866 Delay(50);
2867 }
2868
2869 StopSound(SND_WARNTON);
2870 FadeMusic();
2871
2872 DrawLaser(0, DL_LASER_DISABLED);
2873 game.game_over = TRUE;
2874 game.game_over_cause = GAME_OVER_NO_ENERGY;
2875
2876 #if 0
2877 if (Request("Out of magic energy ! Play it again ?",
2878 REQ_ASK | REQ_STAY_CLOSED))
2879 {
2880 InitGame();
2881 }
2882 else
2883 {
2884 game_status = MAINMENU;
2885 DrawMainMenu();
2886 }
2887 #endif
2888
2889 return;
2890 }
2891 }
2892
2893 element = laser.dest_element;
2894
2895 #if 0
2896 if (element != Feld[ELX][ELY])
2897 {
2898 printf("element == %d, Feld[ELX][ELY] == %d\n",
2899 element, Feld[ELX][ELY]);
2900 }
2901 #endif
2902
2903 if (!laser.overloaded && laser.overload_value == 0 &&
2904 element != EL_BOMB &&
2905 element != EL_MINE &&
2906 element != EL_BALL_GRAY &&
2907 element != EL_BLOCK_STONE &&
2908 element != EL_BLOCK_WOOD &&
2909 element != EL_FUSE_ON &&
2910 element != EL_FUEL_FULL &&
2911 !IS_WALL_ICE(element) &&
2912 !IS_WALL_AMOEBA(element))
2913 return;
2914
2915 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2916 (!laser.overloaded && laser.overload_value > 0)) &&
2917 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2918 {
2919 if (laser.overloaded)
2920 laser.overload_value++;
2921 else
2922 laser.overload_value--;
2923
2924 if (game.cheat_no_overload)
2925 {
2926 laser.overloaded = FALSE;
2927 laser.overload_value = 0;
2928 }
2929
2930 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2931 {
2932 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2933 int color_down = 0xFF - color_up;
2934
2935 #if 0
2936 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2937 (15 - (laser.overload_value / 6)) * color_scale);
2938 #endif
2939 pen_ray = GetPixelFromRGB(window,
2940 (level.laser_red ? 0xFF : color_up),
2941 (level.laser_green ? color_down : 0x00),
2942 (level.laser_blue ? color_down : 0x00));
2943 DrawLaser(0, DL_LASER_ENABLED);
2944 BackToFront();
2945 }
2946
2947 if (laser.overloaded)
2948 {
2949 if (setup.sound_loops)
2950 PlaySoundExt(SND_WARNTON, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
2951 else
2952 PlaySoundStereo(SND_WARNTON, PSND_MAX_RIGHT);
2953 }
2954
2955 if (!laser.overloaded)
2956 StopSound(SND_WARNTON);
2957
2958 if (laser.overloaded)
2959 {
2960 BlitBitmap(pix[PIX_DOOR], drawto,
2961 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2962 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2963 - laser.overload_value,
2964 OVERLOAD_XSIZE, laser.overload_value,
2965 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2966 - laser.overload_value);
2967 redraw_mask |= REDRAW_DOOR_1;
2968 }
2969 else
2970 {
2971 BlitBitmap(pix[PIX_DOOR], drawto,
2972 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2973 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2974 DX_OVERLOAD, DY_OVERLOAD);
2975 redraw_mask |= REDRAW_DOOR_1;
2976 }
2977
2978 if (laser.overload_value == MAX_LASER_OVERLOAD)
2979 {
2980 int i;
2981
2982 for(i=15; i>=0; i--)
2983 {
2984 #if 0
2985 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2986 #endif
2987
2988 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2989 DrawLaser(0, DL_LASER_ENABLED);
2990 BackToFront();
2991 Delay(50);
2992 }
2993
2994 DrawLaser(0, DL_LASER_DISABLED);
2995 game.game_over = TRUE;
2996 game.game_over_cause = GAME_OVER_OVERLOADED;
2997
2998 #if 0
2999 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3000 REQ_ASK | REQ_STAY_CLOSED))
3001 {
3002 InitGame();
3003 }
3004 else
3005 {
3006 game_status = MAINMENU;
3007 DrawMainMenu();
3008 }
3009 #endif
3010
3011 return;
3012 }
3013 }
3014
3015 if (laser.fuse_off)
3016 return;
3017
3018 CT -= Ct;
3019
3020 if (element == EL_BOMB && CT > 1500)
3021 {
3022 if (game.cheat_no_explosion)
3023 return;
3024
3025 #if 0
3026 laser.num_damages--;
3027 DrawLaser(0, DL_LASER_DISABLED);
3028 laser.num_edges = 0;
3029 #endif
3030
3031 Bang(ELX, ELY);
3032
3033 laser.dest_element = EL_EXPLODING_OPAQUE;
3034
3035 #if 0
3036 Bang(ELX, ELY);
3037 laser.num_damages--;
3038 DrawLaser(0, DL_LASER_DISABLED);
3039
3040 laser.num_edges = 0;
3041 Bang(laser.start_edge.x, laser.start_edge.y);
3042
3043 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3044 REQ_ASK | REQ_STAY_CLOSED))
3045 {
3046 InitGame();
3047 }
3048 else
3049 {
3050 game_status = MAINMENU;
3051 DrawMainMenu();
3052 }
3053 #endif
3054
3055 return;
3056 }
3057
3058 if (element == EL_FUSE_ON && CT > 500)
3059 {
3060 laser.fuse_off = TRUE;
3061 laser.fuse_x = ELX;
3062 laser.fuse_y = ELY;
3063 DrawLaser(0, DL_LASER_DISABLED);
3064 DrawGraphic(ELX, ELY, GFX_FUSE_OFF);
3065 }
3066
3067 if (element == EL_BALL_GRAY && CT > 1500)
3068 {
3069 static int new_elements[] =
3070 {
3071 EL_MIRROR_START,
3072 EL_MIRROR_FIXED_START,
3073 EL_POLAR_START,
3074 EL_POLAR_CROSS_START,
3075 EL_PACMAN_START,
3076 EL_KETTLE,
3077 EL_BOMB,
3078 EL_PRISM
3079 };
3080 int num_new_elements = sizeof(new_elements) / sizeof(int);
3081 int new_element = new_elements[RND(num_new_elements)];
3082
3083 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3084 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3085
3086 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3087 ScanLaser();
3088
3089 return;
3090
3091 #if 0
3092 int graphic;
3093
3094 switch (RND(5))
3095 {
3096 case 0:
3097 element = EL_MIRROR_START + RND(16);
3098 break;
3099 case 1:
3100 {
3101 int rnd = RND(3);
3102
3103 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3104 }
3105 break;
3106 default:
3107 {
3108 int rnd = RND(3);
3109
3110 element = (rnd == 0 ? EL_FUSE_ON :
3111 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3112 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3113 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3114 EL_MIRROR_FIXED_START + rnd - 25);
3115 }
3116 break;
3117 }
3118
3119 graphic = el2gfx(element);
3120
3121 for(i=0; i<50; i++)
3122 {
3123 int x = RND(26);
3124 int y = RND(26);
3125
3126 BlitBitmap(pix[PIX_BACK], drawto,
3127 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3128 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3129 SX + ELX * TILEX + x,
3130 SY + ELY * TILEY + y);
3131 MarkTileDirty(ELX, ELY);
3132 BackToFront();
3133
3134 DrawLaser(0, DL_LASER_ENABLED);
3135
3136 Delay(50);
3137 }
3138
3139 Feld[ELX][ELY] = element;
3140 DrawField(ELX, ELY);
3141
3142 #if 0
3143 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3144 #endif
3145
3146 /* above stuff: GRAY BALL -> PRISM !!! */
3147 /*
3148 LX = ELX * TILEX + 14;
3149 LY = ELY * TILEY + 14;
3150 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3151 OK = 8;
3152 else
3153 OK = 4;
3154 LX -= OK * XS;
3155 LY -= OK * YS;
3156
3157 laser.num_edges -= 2;
3158 laser.num_damages--;
3159 */
3160
3161 #if 0
3162 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3163 if (laser.damage[i].is_mirror)
3164 break;
3165
3166 if (i > 0)
3167 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3168 else
3169 DrawLaser(0, DL_LASER_DISABLED);
3170 #else
3171 DrawLaser(0, DL_LASER_DISABLED);
3172 #endif
3173
3174 ScanLaser();
3175
3176 /*
3177 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3178 */
3179 #endif
3180
3181 return;
3182 }
3183
3184 if (IS_WALL_ICE(element) && CT > 1000)
3185 {
3186 PlaySoundStereo(SND_SLURP, ST(ELX));
3187
3188
3189
3190 {
3191 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3192 Store[ELX][ELY] = EL_WALL_ICE;
3193 Store2[ELX][ELY] = laser.wall_mask;
3194
3195 laser.dest_element = Feld[ELX][ELY];
3196
3197 return;
3198 }
3199
3200
3201
3202
3203 for(i=0; i<5; i++)
3204 {
3205 int phase = i + 1;
3206
3207 if (i == 4)
3208 {
3209 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3210 phase = 0;
3211 }
3212
3213 DrawWallsAnimation(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3214 BackToFront();
3215 Delay(100);
3216 }
3217
3218 if (Feld[ELX][ELY] == EL_WALL_ICE)
3219 Feld[ELX][ELY] = EL_EMPTY;
3220
3221 /*
3222 laser.num_edges--;
3223 LX = laser.edge[laser.num_edges].x - (SX + 2);
3224 LY = laser.edge[laser.num_edges].y - (SY + 2);
3225 */
3226
3227 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3228 if (laser.damage[i].is_mirror)
3229 break;
3230
3231 if (i > 0)
3232 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3233 else
3234 DrawLaser(0, DL_LASER_DISABLED);
3235
3236 ScanLaser();
3237
3238 return;
3239 }
3240
3241 if (IS_WALL_AMOEBA(element) && CT > 1200)
3242 {
3243 int k1, k2, k3, dx, dy, de, dm;
3244 int element2 = Feld[ELX][ELY];
3245
3246 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3247 return;
3248
3249 for (i = laser.num_damages - 1; i>=0; i--)
3250 if (laser.damage[i].is_mirror)
3251 break;
3252
3253 r = laser.num_edges;
3254 d = laser.num_damages;
3255 k1 = i;
3256
3257 if (k1 > 0)
3258 {
3259 int x, y;
3260
3261 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3262
3263 laser.num_edges++;
3264 DrawLaser(0, DL_LASER_ENABLED);
3265 laser.num_edges--;
3266
3267 x = laser.damage[k1].x;
3268 y = laser.damage[k1].y;
3269 DrawField(x, y);
3270 }
3271
3272 for(i=0; i<4; i++)
3273 {
3274 if (laser.wall_mask & (1 << i))
3275 {
3276 if (ReadPixel(drawto,
3277 SX + ELX * TILEX + 14 + (i % 2) * 2,
3278 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3279 break;
3280 if (ReadPixel(drawto,
3281 SX + ELX * TILEX + 31 * (i % 2),
3282 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3283 break;
3284 }
3285 }
3286
3287 k2 = i;
3288
3289 for(i=0; i<4; i++)
3290 {
3291 if (laser.wall_mask & (1 << i))
3292 {
3293 if (ReadPixel(drawto,
3294 SX + ELX * TILEX + 31 * (i % 2),
3295 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3296 break;
3297 }
3298 }
3299
3300 k3 = i;
3301
3302 if (laser.num_beamers > 0 ||
3303 k1 < 1 || k2 < 4 || k3 < 4 ||
3304 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3305 == pen_ray)
3306 {
3307 laser.num_edges = r;
3308 laser.num_damages = d;
3309 DrawLaser(0, DL_LASER_DISABLED);
3310 }
3311
3312 Feld[ELX][ELY] = element | laser.wall_mask;
3313 dx = ELX;
3314 dy = ELY;
3315 de = Feld[ELX][ELY];
3316 dm = laser.wall_mask;
3317
3318
3319
3320 #if 1
3321 {
3322 int x = ELX, y = ELY;
3323 int wall_mask = laser.wall_mask;
3324
3325
3326 ScanLaser();
3327 DrawLaser(0, DL_LASER_ENABLED);
3328
3329 PlaySoundStereo(SND_AMOEBE, ST(dx));
3330
3331
3332
3333 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3334 Store[x][y] = EL_WALL_AMOEBA;
3335 Store2[x][y] = wall_mask;
3336
3337 return;
3338 }
3339 #endif
3340
3341
3342
3343 DrawWallsAnimation(dx, dy, de, 4, dm);
3344 ScanLaser();
3345 DrawLaser(0, DL_LASER_ENABLED);
3346
3347 PlaySoundStereo(SND_AMOEBE, ST(dx));
3348
3349 for(i=4; i>=0; i--)
3350 {
3351 DrawWallsAnimation(dx, dy, de, i, dm);
3352 BackToFront();
3353 Delay(20);
3354 }
3355
3356 DrawLaser(0, DL_LASER_ENABLED);
3357
3358 return;
3359 }
3360
3361 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3362 laser.stops_inside_element && CT > 1500)
3363 {
3364 int x, y;
3365 int k;
3366
3367 if (ABS(XS) > ABS(YS))
3368 k = 0;
3369 else
3370 k = 1;
3371 if (XS < YS)
3372 k += 2;
3373
3374 for(i=0; i<4; i++)
3375 {
3376 if (i)
3377 k++;
3378 if (k > 3)
3379 k=0;
3380
3381 x = ELX + Step[k * 4].x;
3382 y = ELY + Step[k * 4].y;
3383
3384 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3385 continue;
3386
3387 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3388 continue;
3389
3390 break;
3391 }
3392
3393 if (i > 3)
3394 {
3395 laser.overloaded = (element == EL_BLOCK_STONE);
3396 return;
3397 }
3398
3399 PlaySoundStereo(SND_BONG, ST(ELX));
3400
3401 Feld[ELX][ELY] = 0;
3402 Feld[x][y] = element;
3403
3404 DrawGraphic(ELX, ELY, -1);
3405 DrawField(x, y);
3406
3407 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3408 {
3409 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3410 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3411 }
3412
3413 ScanLaser();
3414
3415 return;
3416 }
3417
3418 if (element == EL_FUEL_FULL && CT > 200)
3419 {
3420 for(i=game.energy_left; i<=MAX_LASER_ENERGY; i+=2)
3421 {
3422 BlitBitmap(pix[PIX_DOOR], drawto,
3423 DOOR_GFX_PAGEX4 + XX_ENERGY,
3424 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3425 ENERGY_XSIZE, i, DX_ENERGY,
3426 DY_ENERGY + ENERGY_YSIZE - i);
3427
3428 redraw_mask |= REDRAW_DOOR_1;
3429 BackToFront();
3430
3431 Delay(20);
3432 }
3433
3434 game.energy_left = MAX_LASER_ENERGY;
3435 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3436 DrawField(ELX, ELY);
3437
3438 DrawLaser(0, DL_LASER_ENABLED);
3439
3440 return;
3441 }
3442
3443 return;
3444 }
3445
MovePacMen()3446 void MovePacMen()
3447 {
3448 static int p = -1;
3449 int mx, my, ox, oy, nx, ny;
3450 int g, element;
3451 int l;
3452
3453 if (++p >= game.num_pacman)
3454 p = 0;
3455 game.pacman[p].dir--;
3456
3457 for(l=1; l<5; l++)
3458 {
3459 game.pacman[p].dir++;
3460
3461 if (game.pacman[p].dir > 4)
3462 game.pacman[p].dir = 1;
3463
3464 if (game.pacman[p].dir % 2)
3465 {
3466 mx = 0;
3467 my = game.pacman[p].dir - 2;
3468 }
3469 else
3470 {
3471 my = 0;
3472 mx = 3 - game.pacman[p].dir;
3473 }
3474
3475 ox = game.pacman[p].x;
3476 oy = game.pacman[p].y;
3477 nx = ox + mx;
3478 ny = oy + my;
3479 element = Feld[nx][ny];
3480 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3481 continue;
3482
3483 if (!IS_EATABLE4PACMAN(element))
3484 continue;
3485
3486 if (ObjHit(nx, ny, HIT_POS_CENTER))
3487 continue;
3488
3489 Feld[ox][oy] = EL_EMPTY;
3490 Feld[nx][ny] =
3491 EL_PACMAN_RIGHT - 1 +
3492 (game.pacman[p].dir - 1 +
3493 (game.pacman[p].dir % 2) * 2);
3494
3495 game.pacman[p].x = nx;
3496 game.pacman[p].y = ny;
3497 g = Feld[nx][ny] - EL_PACMAN_RIGHT;
3498 DrawGraphic(ox, oy, GFX_EMPTY);
3499
3500 if (element != EL_EMPTY)
3501 {
3502 int i;
3503
3504 CT = Counter();
3505 ox = SX + ox * TILEX;
3506 oy = SY + oy * TILEY;
3507
3508 for(i=1; i<33; i+=2)
3509 {
3510 BlitBitmap(pix[PIX_BACK], window,
3511 SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY,
3512 ox + i * mx, oy + i * my);
3513 FlushDisplay();
3514 Delay(1);
3515 }
3516 Ct = Ct + Counter() - CT;
3517 }
3518 DrawField(nx, ny);
3519 BackToFront();
3520
3521 if (!laser.fuse_off)
3522 {
3523 DrawLaser(0, DL_LASER_ENABLED);
3524
3525 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3526 {
3527 AddDamagedField(nx, ny);
3528 laser.damage[laser.num_damages - 1].edge = 0;
3529 }
3530 }
3531
3532 if (element == EL_BOMB)
3533 {
3534 DeletePacMan(nx, ny);
3535 }
3536
3537 if (IS_WALL_AMOEBA(element) &&
3538 (LX + 2 * XS) / TILEX == nx &&
3539 (LY + 2 * YS) / TILEY == ny)
3540 {
3541 laser.num_edges--;
3542 ScanLaser();
3543 }
3544 break;
3545 }
3546 }
3547
GameWon()3548 void GameWon()
3549 {
3550 int hi_pos;
3551 boolean raise_level = FALSE;
3552
3553 #if 0
3554 if (local_player->MovPos)
3555 return;
3556
3557 local_player->LevelSolved = FALSE;
3558 #endif
3559
3560 if (game.energy_left)
3561 {
3562 if (setup.sound_loops)
3563 PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
3564
3565 while(game.energy_left > 0)
3566 {
3567 if (!setup.sound_loops)
3568 PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
3569
3570 /*
3571 if (game.energy_left > 0 && !(game.energy_left % 10))
3572 RaiseScore(level.score[SC_ZEITBONUS]);
3573 */
3574
3575 RaiseScore(5);
3576
3577 game.energy_left--;
3578 if (game.energy_left >= 0)
3579 {
3580 BlitBitmap(pix[PIX_DOOR], drawto,
3581 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3582 ENERGY_XSIZE, ENERGY_YSIZE - game.energy_left,
3583 DX_ENERGY, DY_ENERGY);
3584 redraw_mask |= REDRAW_DOOR_1;
3585 }
3586
3587 BackToFront();
3588 Delay(10);
3589 }
3590
3591 if (setup.sound_loops)
3592 StopSound(SND_SIRR);
3593 }
3594 else if (level.time == 0) /* level without time limit */
3595 {
3596 if (setup.sound_loops)
3597 PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
3598
3599 while(TimePlayed < 999)
3600 {
3601 if (!setup.sound_loops)
3602 PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
3603 if (TimePlayed < 999 && !(TimePlayed % 10))
3604 RaiseScore(level.score[SC_ZEITBONUS]);
3605 if (TimePlayed < 900 && !(TimePlayed % 10))
3606 TimePlayed += 10;
3607 else
3608 TimePlayed++;
3609
3610 /*
3611 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
3612 */
3613
3614 BackToFront();
3615 Delay(10);
3616 }
3617
3618 if (setup.sound_loops)
3619 StopSound(SND_SIRR);
3620 }
3621
3622 #if 0
3623 FadeSounds();
3624 #endif
3625
3626 CloseDoor(DOOR_CLOSE_1);
3627
3628 Request("Level solved !", REQ_CONFIRM);
3629
3630 if (level_nr == leveldir_current->handicap_level)
3631 {
3632 leveldir_current->handicap_level++;
3633 SaveLevelSetup_SeriesInfo();
3634 }
3635
3636 if (level_editor_test_game)
3637 game.score = -1; /* no highscore when playing from editor */
3638 else if (level_nr < leveldir_current->last_level)
3639 raise_level = TRUE; /* advance to next level */
3640
3641 if ((hi_pos = NewHiScore()) >= 0)
3642 {
3643 game_status = HALLOFFAME;
3644 DrawHallOfFame(hi_pos);
3645 if (raise_level)
3646 level_nr++;
3647 }
3648 else
3649 {
3650 game_status = MAINMENU;
3651 if (raise_level)
3652 level_nr++;
3653 DrawMainMenu();
3654 }
3655
3656 BackToFront();
3657 }
3658
NewHiScore()3659 int NewHiScore()
3660 {
3661 int k, l;
3662 int position = -1;
3663
3664 LoadScore(level_nr);
3665
3666 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3667 game.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3668 return -1;
3669
3670 for (k=0; k<MAX_SCORE_ENTRIES; k++)
3671 {
3672 if (game.score > highscore[k].Score)
3673 {
3674 /* player has made it to the hall of fame */
3675
3676 if (k < MAX_SCORE_ENTRIES - 1)
3677 {
3678 int m = MAX_SCORE_ENTRIES - 1;
3679
3680 #ifdef ONE_PER_NAME
3681 for (l=k; l<MAX_SCORE_ENTRIES; l++)
3682 if (!strcmp(setup.player_name, highscore[l].Name))
3683 m = l;
3684 if (m == k) /* player's new highscore overwrites his old one */
3685 goto put_into_list;
3686 #endif
3687
3688 for (l=m; l>k; l--)
3689 {
3690 strcpy(highscore[l].Name, highscore[l - 1].Name);
3691 highscore[l].Score = highscore[l - 1].Score;
3692 }
3693 }
3694
3695 #ifdef ONE_PER_NAME
3696 put_into_list:
3697 #endif
3698 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3699 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3700 highscore[k].Score = game.score;
3701 position = k;
3702 break;
3703 }
3704
3705 #ifdef ONE_PER_NAME
3706 else if (!strncmp(setup.player_name, highscore[k].Name,
3707 MAX_PLAYER_NAME_LEN))
3708 break; /* player already there with a higher score */
3709 #endif
3710
3711 }
3712
3713 if (position >= 0)
3714 SaveScore(level_nr);
3715
3716 return position;
3717 }
3718
InitMovingField(int x,int y,int direction)3719 void InitMovingField(int x, int y, int direction)
3720 {
3721 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3722 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3723
3724 MovDir[x][y] = direction;
3725 MovDir[newx][newy] = direction;
3726 if (Feld[newx][newy] == EL_EMPTY)
3727 Feld[newx][newy] = EL_BLOCKED;
3728 }
3729
Moving2Blocked(int x,int y,int * goes_to_x,int * goes_to_y)3730 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3731 {
3732 int direction = MovDir[x][y];
3733 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3734 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3735
3736 *goes_to_x = newx;
3737 *goes_to_y = newy;
3738 }
3739
Blocked2Moving(int x,int y,int * comes_from_x,int * comes_from_y)3740 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3741 {
3742 int oldx = x, oldy = y;
3743 int direction = MovDir[x][y];
3744
3745 if (direction == MV_LEFT)
3746 oldx++;
3747 else if (direction == MV_RIGHT)
3748 oldx--;
3749 else if (direction == MV_UP)
3750 oldy++;
3751 else if (direction == MV_DOWN)
3752 oldy--;
3753
3754 *comes_from_x = oldx;
3755 *comes_from_y = oldy;
3756 }
3757
MovingOrBlocked2Element(int x,int y)3758 int MovingOrBlocked2Element(int x, int y)
3759 {
3760 int element = Feld[x][y];
3761
3762 if (element == EL_BLOCKED)
3763 {
3764 int oldx, oldy;
3765
3766 Blocked2Moving(x, y, &oldx, &oldy);
3767 return Feld[oldx][oldy];
3768 }
3769 else
3770 return element;
3771 }
3772
3773 #if 0
3774 static void RemoveField(int x, int y)
3775 {
3776 Feld[x][y] = EL_EMPTY;
3777 MovPos[x][y] = 0;
3778 MovDir[x][y] = 0;
3779 MovDelay[x][y] = 0;
3780 }
3781 #endif
3782
RemoveMovingField(int x,int y)3783 void RemoveMovingField(int x, int y)
3784 {
3785 int oldx = x, oldy = y, newx = x, newy = y;
3786
3787 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3788 return;
3789
3790 if (IS_MOVING(x, y))
3791 {
3792 Moving2Blocked(x, y, &newx, &newy);
3793 if (Feld[newx][newy] != EL_BLOCKED)
3794 return;
3795 }
3796 else if (Feld[x][y] == EL_BLOCKED)
3797 {
3798 Blocked2Moving(x, y, &oldx, &oldy);
3799 if (!IS_MOVING(oldx, oldy))
3800 return;
3801 }
3802
3803 Feld[oldx][oldy] = EL_EMPTY;
3804 Feld[newx][newy] = EL_EMPTY;
3805 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3806 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3807
3808 DrawLevelField(oldx, oldy);
3809 DrawLevelField(newx, newy);
3810 }
3811
PlaySoundLevel(int x,int y,int sound_nr)3812 void PlaySoundLevel(int x, int y, int sound_nr)
3813 {
3814 int sx = SCREENX(x), sy = SCREENY(y);
3815 int volume, stereo;
3816 int silence_distance = 8;
3817
3818 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3819 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3820 return;
3821
3822 if (!IN_LEV_FIELD(x, y) ||
3823 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3824 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3825 return;
3826
3827 volume = PSND_MAX_VOLUME;
3828
3829 #ifndef MSDOS
3830 stereo = (sx - SCR_FIELDX/2) * 12;
3831 #else
3832 stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3833 if (stereo > PSND_MAX_RIGHT)
3834 stereo = PSND_MAX_RIGHT;
3835 if (stereo < PSND_MAX_LEFT)
3836 stereo = PSND_MAX_LEFT;
3837 #endif
3838
3839 if (!IN_SCR_FIELD(sx, sy))
3840 {
3841 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3842 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3843
3844 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3845 }
3846
3847 PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
3848 }
3849
RaiseScore(int value)3850 void RaiseScore(int value)
3851 {
3852 game.score += value;
3853 DrawText(DX_SCORE, DY_SCORE, int2str(game.score, 4),
3854 FS_SMALL, FC_YELLOW);
3855 }
3856
RaiseScoreElement(int element)3857 void RaiseScoreElement(int element)
3858 {
3859 switch(element)
3860 {
3861 case EL_PACMAN:
3862 RaiseScore(level.score[SC_PACMAN]);
3863 break;
3864 case EL_KEY:
3865 RaiseScore(level.score[SC_KEY]);
3866 break;
3867 default:
3868 break;
3869 }
3870 }
3871
3872 /* ---------- new game button stuff ---------------------------------------- */
3873
3874 /* graphic position values for game buttons */
3875 #define GAME_BUTTON1_XSIZE 20
3876 #define GAME_BUTTON1_YSIZE 20
3877 #define GAME_BUTTON2_XSIZE 28
3878 #define GAME_BUTTON2_YSIZE 28
3879 #define GAME_BUTTON1_YPOS 128
3880 #define GAME_BUTTON2_YPOS 124
3881
3882 #define GAME_BUTTON_LEFT_XPOS 8
3883 #define GAME_BUTTON_MIDDLE_XPOS 36
3884 #define GAME_BUTTON_RIGHT_XPOS 72
3885
3886 static struct
3887 {
3888 int x, y;
3889 int gadget_id;
3890 char *infotext;
3891 } gamebutton_info[NUM_GAME_BUTTONS] =
3892 {
3893 {
3894 GAME_BUTTON_LEFT_XPOS, GAME_BUTTON1_YPOS,
3895 GAME_CTRL_ID_LEFT,
3896 "left game button"
3897 },
3898 {
3899 GAME_BUTTON_MIDDLE_XPOS, GAME_BUTTON2_YPOS,
3900 GAME_CTRL_ID_MIDDLE,
3901 "middle game button"
3902 },
3903 {
3904 GAME_BUTTON_RIGHT_XPOS, GAME_BUTTON1_YPOS,
3905 GAME_CTRL_ID_RIGHT,
3906 "right game button"
3907 },
3908 };
3909
CreateGameButtons()3910 void CreateGameButtons()
3911 {
3912 int i;
3913
3914 for (i=0; i<NUM_GAME_BUTTONS; i++)
3915 {
3916 Bitmap *gd_bitmap = pix[PIX_DOOR];
3917 struct GadgetInfo *gi;
3918 int button_type;
3919 boolean checked;
3920 unsigned long event_mask;
3921 int gd_xoffset, gd_yoffset;
3922 int gd_x1, gd_x2, gd_y1, gd_y2;
3923 int gd_xsize, gd_ysize;
3924 int id = i;
3925
3926 gd_xoffset = gamebutton_info[i].x;
3927 gd_yoffset = gamebutton_info[i].y;
3928 gd_x1 = DOOR_GFX_PAGEX5 + gd_xoffset;
3929 gd_x2 = DOOR_GFX_PAGEX4 + gd_xoffset;
3930
3931 button_type = GD_TYPE_NORMAL_BUTTON;
3932 checked = FALSE;
3933 event_mask = GD_EVENT_RELEASED;
3934 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
3935 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset;
3936
3937 if (id != GAME_CTRL_ID_MIDDLE)
3938 {
3939 gd_xsize = GAME_BUTTON1_XSIZE;
3940 gd_ysize = GAME_BUTTON1_YSIZE;
3941 }
3942 else
3943 {
3944 gd_xsize = GAME_BUTTON2_XSIZE;
3945 gd_ysize = GAME_BUTTON2_YSIZE;
3946 }
3947
3948 gi = CreateGadget(GDI_CUSTOM_ID, id,
3949 GDI_INFO_TEXT, gamebutton_info[i].infotext,
3950 GDI_X, DX + gd_xoffset,
3951 GDI_Y, DY + gd_yoffset,
3952 GDI_WIDTH, gd_xsize,
3953 GDI_HEIGHT, gd_ysize,
3954 GDI_TYPE, button_type,
3955 GDI_STATE, GD_BUTTON_UNPRESSED,
3956 GDI_CHECKED, checked,
3957 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
3958 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
3959 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
3960 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
3961 GDI_EVENT_MASK, event_mask,
3962 GDI_CALLBACK_ACTION, HandleGameButtons,
3963 GDI_END);
3964
3965 if (gi == NULL)
3966 Error(ERR_EXIT, "cannot create gadget");
3967
3968 game_gadget[id] = gi;
3969 }
3970 }
3971
MapGameButtons()3972 static void MapGameButtons()
3973 {
3974 int i;
3975
3976 for (i=0; i<NUM_GAME_BUTTONS; i++)
3977 MapGadget(game_gadget[i]);
3978 }
3979
UnmapGameButtons()3980 void UnmapGameButtons()
3981 {
3982 int i;
3983
3984 for (i=0; i<NUM_GAME_BUTTONS; i++)
3985 UnmapGadget(game_gadget[i]);
3986 }
3987
HandleGameButtons(struct GadgetInfo * gi)3988 static void HandleGameButtons(struct GadgetInfo *gi)
3989 {
3990 int id = gi->custom_id;
3991
3992 if (game_status != PLAYING)
3993 return;
3994
3995 switch (id)
3996 {
3997 case GAME_CTRL_ID_LEFT:
3998 if (setup.sound_music)
3999 {
4000 setup.sound_music = FALSE;
4001 FadeMusic();
4002 }
4003 break;
4004
4005 case GAME_CTRL_ID_MIDDLE:
4006 if (game.game_over)
4007 {
4008 CloseDoor(DOOR_CLOSE_1);
4009 game_status = MAINMENU;
4010 DrawMainMenu();
4011 break;
4012 }
4013
4014 if (level_editor_test_game ||
4015 Request("Do you really want to quit the game ?",
4016 REQ_ASK | REQ_STAY_CLOSED))
4017 {
4018 game_status = MAINMENU;
4019 DrawMainMenu();
4020 }
4021 else
4022 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4023 break;
4024
4025 case GAME_CTRL_ID_RIGHT:
4026 if (audio.loops_available)
4027 {
4028 setup.sound = setup.sound_music = TRUE;
4029 if (num_bg_loops)
4030 PlayMusic(level_nr % num_bg_loops);
4031 }
4032 break;
4033
4034 default:
4035 break;
4036 }
4037 }
4038