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