/*********************************************************** * Mirror Magic -- McDuffin's Revenge * *----------------------------------------------------------* * (c) 1994-2001 Artsoft Entertainment * * Holger Schemel * * Detmolder Strasse 189 * * 33604 Bielefeld * * Germany * * e-mail: info@artsoft.org * *----------------------------------------------------------* * game.c * ***********************************************************/ #include "libgame/libgame.h" #include "game.h" #include "tools.h" #include "screens.h" #include "init.h" #include "files.h" /* graphic position values for game controls */ #define ENERGY_XSIZE 32 #define ENERGY_YSIZE MAX_LASER_ENERGY #define OVERLOAD_XSIZE ENERGY_XSIZE #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD /* values for Explode() */ #define EX_PHASE_START 0 #define EX_NORMAL 0 #define EX_KETTLE 1 #define EX_SHORT 2 /* special positions in the game control window (relative to control window) */ #define XX_LEVEL 36 #define YY_LEVEL 23 #define XX_KETTLES 29 #define YY_KETTLES 63 #define XX_SCORE 22 #define YY_SCORE 101 #define XX_ENERGY 8 #define YY_ENERGY 158 #define XX_OVERLOAD 60 #define YY_OVERLOAD YY_ENERGY /* special positions in the game control window (relative to main window) */ #define DX_LEVEL (DX + XX_LEVEL) #define DY_LEVEL (DY + YY_LEVEL) #define DX_KETTLES (DX + XX_KETTLES) #define DY_KETTLES (DY + YY_KETTLES) #define DX_SCORE (DX + XX_SCORE) #define DY_SCORE (DY + YY_SCORE) #define DX_ENERGY (DX + XX_ENERGY) #define DY_ENERGY (DY + YY_ENERGY) #define DX_OVERLOAD (DX + XX_OVERLOAD) #define DY_OVERLOAD (DY + YY_OVERLOAD) #define IS_LOOP_SOUND(s) ((s) == SND_FUEL) #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER) /* game button identifiers */ #define GAME_CTRL_ID_LEFT 0 #define GAME_CTRL_ID_MIDDLE 1 #define GAME_CTRL_ID_RIGHT 2 #define NUM_GAME_BUTTONS 3 /* values for DrawLaser() */ #define DL_LASER_DISABLED 0 #define DL_LASER_ENABLED 1 /* values for 'click_delay_value' in ClickElement() */ #define CLICK_DELAY_SHORT 125 #define CLICK_DELAY_LONG 250 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT /* forward declaration for internal use */ static void MapGameButtons(); static void HandleGameButtons(struct GadgetInfo *); static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS]; void GetPlayerConfig() { if (!audio.sound_available) setup.sound = FALSE; if (!audio.loops_available) { setup.sound_loops = FALSE; setup.sound_music = FALSE; } if (!video.fullscreen_available) setup.fullscreen = FALSE; setup.sound_simple = setup.sound; SetAudioMode(setup.sound); } static int get_element_angle(int element) { int element_phase = get_element_phase(element); if (IS_MIRROR_FIXED(element) || IS_MCDUFFIN(element) || IS_LASER(element) || IS_RECEIVER(element)) return 4 * element_phase; else return element_phase; } static int get_opposite_angle(int angle) { int opposite_angle = angle + ANG_RAY_180; /* make sure "opposite_angle" is in valid interval [0, 15] */ return (opposite_angle + 16) % 16; } static int get_mirrored_angle(int laser_angle, int mirror_angle) { int reflected_angle = 16 - laser_angle + mirror_angle; /* make sure "reflected_angle" is in valid interval [0, 15] */ return (reflected_angle + 16) % 16; } void InitMovDir(int x, int y) { int element = Feld[x][y]; static int direction[3][4] = { { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN }, { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP }, { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN } }; switch(element) { case EL_PACMAN_RIGHT: case EL_PACMAN_UP: case EL_PACMAN_LEFT: case EL_PACMAN_DOWN: Feld[x][y] = EL_PACMAN; MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT]; break; default: break; } } static void InitField(int x, int y, boolean init_game) { int element = Feld[x][y]; switch (element) { case EL_DF_EMPTY: Feld[x][y] = EL_EMPTY; break; case EL_KETTLE: case EL_CELL: if (level.auto_count_kettles) game.kettles_still_needed++; break; case EL_LIGHTBULB_OFF: game.lights_still_needed++; break; default: if (IS_MIRROR(element) || IS_BEAMER_OLD(element) || IS_BEAMER(element) || IS_POLAR(element) || IS_POLAR_CROSS(element) || IS_DF_MIRROR(element) || IS_DF_MIRROR_AUTO(element) || IS_GRID_STEEL_AUTO(element) || IS_GRID_WOOD_AUTO(element) || IS_FIBRE_OPTIC(element)) { if (IS_BEAMER_OLD(element)) { Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START); element = Feld[x][y]; } if (!IS_FIBRE_OPTIC(element)) { static int steps_grid_auto = 0; if (game.num_cycle == 0) /* initialize cycle steps for grids */ steps_grid_auto = RND(16) * (RND(2) ? -1 : +1); if (IS_GRID_STEEL_AUTO(element) || IS_GRID_WOOD_AUTO(element)) game.cycle[game.num_cycle].steps = steps_grid_auto; else game.cycle[game.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1); game.cycle[game.num_cycle].x = x; game.cycle[game.num_cycle].y = y; game.num_cycle++; } if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) { int beamer_nr = BEAMER_NR(element); int nr = laser.beamer[beamer_nr][0].num; laser.beamer[beamer_nr][nr].x = x; laser.beamer[beamer_nr][nr].y = y; laser.beamer[beamer_nr][nr].num = 1; } } else if (IS_PACMAN(element)) { #if 0 int phase = element - EL_PACMAN_RIGHT; game.pacman[game.num_pacman].x = x; game.pacman[game.num_pacman].y = y; game.pacman[game.num_pacman].dir = phase + ((phase + 1) % 2) * 2; game.num_pacman++; #else InitMovDir(x, y); #endif } else if (IS_MCDUFFIN(element) || IS_LASER(element)) { laser.start_edge.x = x; laser.start_edge.y = y; laser.start_angle = get_element_angle(element); } break; } } static void InitCycleElements() { int i, j; if (game.num_cycle == 0) /* no elements to cycle */ return; for(i=0; i<16; i++) { for(j=0; jchecked = setup.sound_music; game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops; game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple; */ MapGameButtons(); /* copy actual game door content to door double buffer for OpenDoor() */ BlitBitmap(drawto, pix[PIX_DB_DOOR], DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1); OpenDoor(DOOR_OPEN_ALL); if (setup.sound_loops) PlaySoundExt(SND_FUEL, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP); for(i=0; i<=game.energy_left; i+=2) { if (!setup.sound_loops) PlaySoundStereo(SND_FUEL, PSND_MAX_RIGHT); BlitBitmap(pix[PIX_DOOR], drawto, DOOR_GFX_PAGEX4 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i, ENERGY_XSIZE, i, DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i); redraw_mask |= REDRAW_DOOR_1; BackToFront(); ColorCycling(); #ifdef DEBUG if (setup.quick_doors) continue; #endif Delay(20); } if (setup.sound_loops) StopSound(SND_FUEL); if (setup.sound_music && num_bg_loops) PlayMusic(level_nr % num_bg_loops); ScanLaser(); } void AddLaserEdge(int lx, int ly) { if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2) { Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly); return; } laser.edge[laser.num_edges].x = SX + 2 + lx; laser.edge[laser.num_edges].y = SY + 2 + ly; laser.num_edges++; laser.redraw = TRUE; } void AddDamagedField(int ex, int ey) { laser.damage[laser.num_damages].is_mirror = FALSE; laser.damage[laser.num_damages].angle = laser.current_angle; laser.damage[laser.num_damages].edge = laser.num_edges; laser.damage[laser.num_damages].x = ex; laser.damage[laser.num_damages].y = ey; laser.num_damages++; } boolean StepBehind() { if (laser.num_edges) { int x = LX - XS; int y = LY - YS; int last_x = laser.edge[laser.num_edges - 1].x - SX - 2; int last_y = laser.edge[laser.num_edges - 1].y - SY - 2; return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0); } else return FALSE; } static int getMaskFromElement(int element) { if (IS_GRID(element)) return GFX_MASK_GRID_00 + get_element_phase(element); else if (IS_MCDUFFIN(element)) return GFX_MASK_MCDUFFIN_00 + get_element_phase(element); else if (IS_RECTANGLE(element) || IS_DF_GRID(element)) return GFX_MASK_RECTANGLE; else return GFX_MASK_CIRCLE; } int ScanPixel() { int hit_mask = 0; #if 0 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n", LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY); #endif /* follow laser beam until it hits something (at least the screen border) */ while (hit_mask == HIT_MASK_NO_HIT) { int i; #if 0 /* for security */ if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE || SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE) { printf("ScanPixel: touched screen border!\n"); return HIT_MASK_ALL; } #endif for (i=0; i<4; i++) { Pixel pixel; int px, py, lx, ly; px = SX + LX + (i % 2) * 2; py = SY + LY + (i / 2) * 2; lx = (px - SX + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */ ly = (py - SY + TILEY) / TILEY - 1; /* negative values! */ if (IN_LEV_FIELD(lx, ly)) { int element = Feld[lx][ly]; if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP) pixel = 0; else if (IS_WALL(element) || IS_WALL_CHANGING(element)) { int pos = ((py - SY - ly * TILEY) / MINI_TILEX) * 2 + (px - SX - lx * TILEX) / MINI_TILEY; pixel = ((element & (1 << pos)) ? 1 : 0); } else { int graphic_mask = getMaskFromElement(element); int mask_x, mask_y; int dx = px - lx * TILEX; int dy = py - ly * TILEY; mask_x = (graphic_mask % GFX_PER_LINE) * TILEX + dx; mask_y = (graphic_mask / GFX_PER_LINE) * TILEY + dy; pixel = (ReadPixel(pix[PIX_BACK], mask_x, mask_y) ? 1 : 0); } } else { if (px < REAL_SX || px >= REAL_SX + FULL_SXSIZE || py < REAL_SY || py >= REAL_SY + FULL_SYSIZE) pixel = 1; else pixel = 0; } if ((Sign[laser.current_angle] & (1 << i)) && pixel) hit_mask |= (1 << i); } if (hit_mask == HIT_MASK_NO_HIT) { /* hit nothing -- go on with another step */ LX += XS; LY += YS; } } return hit_mask; } void ScanLaser() { int element; int end = 0, rf = laser.num_edges; #if 0 unsigned short color_scale = 0xFFFF / 15; #endif int testx, testy; laser.overloaded = FALSE; laser.stops_inside_element = FALSE; #if 0 if (laser.overload_value < MAX_LASER_OVERLOAD - 8) SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000, (15 - (laser.overload_value / 6)) * color_scale); #endif DrawLaser(0, DL_LASER_ENABLED); #if 0 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n", LX, LY, XS, YS); #endif while (1) { int hit_mask; if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN) { end = 1; laser.overloaded = TRUE; break; } hit_mask = ScanPixel(); #if 0 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n", LX, LY, XS, YS); #endif /* hit something -- check out what it was */ ELX = (LX + XS) / TILEX; ELY = (LY + YS) / TILEY; #if 0 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n", hit_mask, LX, LY, ELX, ELY); #endif if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY)) { element = EL_EMPTY; laser.dest_element = element; break; } if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT)) { /* we have hit the top-right and bottom-left element -- choose the bottom-left one */ /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */ ELX = (LX - 2) / TILEX; ELY = (LY + 2) / TILEY; } if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT)) { /* we have hit the top-left and bottom-right element -- choose the top-left one */ /* !!! SEE ABOVE !!! */ ELX = (LX - 2) / TILEX; ELY = (LY - 2) / TILEY; } #if 0 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n", hit_mask, LX, LY, ELX, ELY); #endif element = Feld[ELX][ELY]; laser.dest_element = element; #if 0 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n", element, ELX, ELY, LX, LY, LX % TILEX, LY % TILEY, hit_mask); #endif #if 0 if (!IN_LEV_FIELD(ELX, ELY)) printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element); #endif testx = ELX; testy = ELY; if (element == EL_EMPTY) { if (!HitOnlyAnEdge(element, hit_mask)) break; } else if (element == EL_FUSE_ON) { if (HitPolarizer(element, hit_mask)) break; } else if (IS_GRID(element) || IS_DF_GRID(element)) { if (HitPolarizer(element, hit_mask)) break; } else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD || element == EL_GATE_STONE || element == EL_GATE_WOOD) { if (HitBlock(element, hit_mask)) { rf = 1; break; } } else if (IS_MCDUFFIN(element)) { if (HitLaserSource(element, hit_mask)) break; } else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) || IS_RECEIVER(element)) { if (HitLaserDestination(element, hit_mask)) break; } else if (IS_WALL(element)) { if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element)) { if (HitReflectingWalls(element, hit_mask)) break; } else { if (HitAbsorbingWalls(element, hit_mask)) break; } } else { if (HitElement(element, hit_mask)) break; } if (rf) DrawLaser(rf - 1, DL_LASER_ENABLED); rf = laser.num_edges; } /* element = Feld[ELX][ELY]; laser.dest_element = element; */ #if 0 if (laser.dest_element != Feld[ELX][ELY]) { printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n", laser.dest_element, Feld[ELX][ELY]); } #endif if (!end && !laser.stops_inside_element && !StepBehind()) { #if 0 printf("ScanLaser: Go one step back\n"); #endif LX -= XS; LY -= YS; AddLaserEdge(LX, LY); } if (rf) DrawLaser(rf - 1, DL_LASER_ENABLED); Ct = CT = Counter(); #if 0 if (!IN_LEV_FIELD(ELX, ELY)) printf("WARNING! (2) %d, %d\n", ELX, ELY); #endif #if 0 printf("(%d, %d) == %d [(%d, %d) == %d]\n", testx, testy, laser.dest_element, ELX, ELY, (IN_SCR_FIELD(ELX,ELY) ? Feld[ELX][ELY] : -1)); #endif } void DrawLaserExt(int start_edge, int num_edges, int mode) { int element; int elx, ely; #if 0 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n", start_edge, num_edges, mode); #endif if (start_edge < 0) { Error(ERR_WARN, "DrawLaserExt: start_edge < 0"); return; } if (num_edges < 0) { Error(ERR_WARN, "DrawLaserExt: num_edges < 0"); return; } #if 0 if (mode == DL_LASER_DISABLED) { printf("DrawLaser: Delete laser from edge %d\n", start_edge); } #endif /* now draw the laser to the backbuffer and (if enabled) to the screen */ DrawLines(drawto, &laser.edge[start_edge], num_edges, (mode == DL_LASER_ENABLED ? pen_ray : pen_bg)); redraw_mask |= REDRAW_FIELD; if (mode == DL_LASER_ENABLED) return; /* after the laser was deleted, the "damaged" graphics must be restored */ if (laser.num_damages) { int damage_start = 0; int i; /* determine the starting edge, from which graphics need to be restored */ if (start_edge > 0) { for(i=0; i %d\n", laser.beamer_edge[i]); printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n", mode, elx, ely, Hit[elx][ely], start_edge); printf("DrawLaserExt: IS_BEAMER: %d / %d\n", get_element_angle(element), laser.damage[damage_start].angle); } #endif if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) && laser.num_beamers > 0 && start_edge == laser.beamer_edge[laser.num_beamers - 1]) { /* element is outgoing beamer */ laser.num_damages = damage_start + 1; if (IS_BEAMER(element)) laser.current_angle = get_element_angle(element); } else { /* element is incoming beamer or other element */ laser.num_damages = damage_start; laser.current_angle = laser.damage[laser.num_damages].angle; } } else { /* no damages but McDuffin himself (who needs to be redrawn anyway) */ elx = laser.start_edge.x; ely = laser.start_edge.y; element = Feld[elx][ely]; } laser.num_edges = start_edge + 1; if (start_edge == 0) laser.current_angle = laser.start_angle; LX = laser.edge[start_edge].x - (SX + 2); LY = laser.edge[start_edge].y - (SY + 2); XS = 2 * Step[laser.current_angle].x; YS = 2 * Step[laser.current_angle].y; #if 0 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n", LX, LY, element); #endif if (start_edge > 0) { if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element) || IS_PACMAN(element) || IS_POLAR(element) || IS_POLAR_CROSS(element) || element == EL_FUSE_ON) { int step_size; #if 0 printf("element == %d\n", element); #endif if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */ step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3); else step_size = 8; if (IS_POLAR(element) || IS_POLAR_CROSS(element) || ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) && (laser.num_beamers == 0 || start_edge != laser.beamer_edge[laser.num_beamers - 1]))) { /* element is incoming beamer or other element */ step_size = -step_size; laser.num_edges--; } #if 0 if (IS_BEAMER(element)) { printf("start_edge == %d, laser.beamer_edge == %d\n", start_edge, laser.beamer_edge); } #endif LX += step_size * XS; LY += step_size * YS; } else if (element != EL_EMPTY) { LX -= 3 * XS; LY -= 3 * YS; laser.num_edges--; } } #if 0 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n", LX, LY, element); #endif } void DrawLaser(int start_edge, int mode) { if (laser.num_edges - start_edge < 0) { Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0"); return; } /* check if laser is interrupted by beamer element */ if (laser.num_beamers > 0 && start_edge < laser.beamer_edge[laser.num_beamers - 1]) { if (mode == DL_LASER_ENABLED) { int i; int tmp_start_edge = start_edge; /* draw laser segments forward from the start to the last beamer */ for (i=0; i=0; i--) { int tmp_num_edges = last_num_edges - laser.beamer_edge[i]; if (laser.beamer_edge[i] - start_edge <= 0) break; DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED); last_num_edges = laser.beamer_edge[i]; laser.num_beamers--; } #if 0 if (last_num_edges - start_edge <= 0) printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n", last_num_edges, start_edge); #endif /* delete first segment from start to the first beamer */ DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED); } } else DrawLaserExt(start_edge, laser.num_edges - start_edge, mode); } boolean HitElement(int element, int hit_mask) { if (HitOnlyAnEdge(element, hit_mask)) return FALSE; if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY)) element = MovingOrBlocked2Element(ELX, ELY); #if 0 printf("HitElement (1): element == %d\n", element); #endif #if 0 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS) printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY); else printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY); #endif AddDamagedField(ELX, ELY); #if 0 if (ELX != (LX + 5 * XS) / TILEX || ELY != (LY + 5 * YS) / TILEY) { LX += 2 * XS; LY += 2 * YS; return FALSE; } #else /* this is more precise: check if laser would go through the center */ if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS) { /* skip the whole element before continuing the scan */ do { LX += XS; LY += YS; } while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0); if (LX/TILEX > ELX || LY/TILEY > ELY) { /* skipping scan positions to the right and down skips one scan position too much, because this is only the top left scan position of totally four scan positions (plus one to the right, one to the bottom and one to the bottom right) */ LX -= XS; LY -= YS; } return FALSE; } #endif #if 0 printf("HitElement (2): element == %d\n", element); #endif if (LX + 5 * XS < 0 || LY + 5 * YS < 0) { LX += 2 * XS; LY += 2 * YS; return FALSE; } #if 0 printf("HitElement (3): element == %d\n", element); #endif if (IS_POLAR(element) && ((element - EL_POLAR_START) % 2 || (element - EL_POLAR_START) / 2 != laser.current_angle % 8)) { PlaySoundStereo(SND_KINK, ST(ELX)); laser.num_damages--; return TRUE; } if (IS_POLAR_CROSS(element) && (element - EL_POLAR_CROSS_START) != laser.current_angle % 4) { PlaySoundStereo(SND_KINK, ST(ELX)); laser.num_damages--; return TRUE; } if (!IS_BEAMER(element) && !IS_FIBRE_OPTIC(element) && !IS_GRID_WOOD(element) && element != EL_FUEL_EMPTY) { #if 0 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS) printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY); else printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY); #endif LX = ELX * TILEX + 14; LY = ELY * TILEY + 14; AddLaserEdge(LX, LY); } if (IS_MIRROR(element) || IS_MIRROR_FIXED(element) || IS_POLAR(element) || IS_POLAR_CROSS(element) || IS_DF_MIRROR(element) || IS_DF_MIRROR_AUTO(element) || element == EL_PRISM || element == EL_REFRACTOR) { int current_angle = laser.current_angle; int step_size; laser.num_damages--; AddDamagedField(ELX, ELY); laser.damage[laser.num_damages - 1].is_mirror = TRUE; if (!Hit[ELX][ELY]) Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge; if (IS_MIRROR(element) || IS_MIRROR_FIXED(element) || IS_DF_MIRROR(element) || IS_DF_MIRROR_AUTO(element)) laser.current_angle = get_mirrored_angle(laser.current_angle, get_element_angle(element)); if (element == EL_PRISM || element == EL_REFRACTOR) laser.current_angle = RND(16); XS = 2 * Step[laser.current_angle].x; YS = 2 * Step[laser.current_angle].y; if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */ step_size = 8; else step_size = 4; LX += step_size * XS; LY += step_size * YS; #if 0 /* draw sparkles on mirror */ if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) && current_angle != laser.current_angle) { MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1); } #endif if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) && current_angle != laser.current_angle) PlaySoundStereo(SND_LASER, ST(ELX)); laser.overloaded = (get_opposite_angle(laser.current_angle) == laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE); return (laser.overloaded ? TRUE : FALSE); } if (element == EL_FUEL_FULL) { laser.stops_inside_element = TRUE; return TRUE; } if (element == EL_BOMB || element == EL_MINE) { PlaySoundStereo(SND_KINK, ST(ELX)); if (element == EL_MINE) laser.overloaded = TRUE; } if (element == EL_KETTLE || element == EL_CELL || element == EL_KEY || element == EL_LIGHTBALL || element == EL_PACMAN || IS_PACMAN(element)) { if (!IS_PACMAN(element)) Bang(ELX, ELY); if (element == EL_PACMAN) Bang(ELX, ELY); if (element == EL_KETTLE || element == EL_CELL) { if (game.kettles_still_needed) DrawText(DX_KETTLES, DY_KETTLES, int2str(--game.kettles_still_needed, 3), FS_SMALL, FC_YELLOW); RaiseScore(10); if (game.kettles_still_needed == 0) { int x, y; static int xy[4][2] = { { +1, 0 }, { 0, -1 }, { -1, 0 }, { 0, +1 } }; PlaySoundStereo(SND_KLING, ST(ELX)); for(y=0; y> 1) << 1) step_size = 8; else step_size = 4; LX += step_size * XS; LY += step_size * YS; laser.num_beamers++; return FALSE; } } return TRUE; } boolean HitOnlyAnEdge(int element, int hit_mask) { /* check if the laser hit only the edge of an element and, if so, go on */ #if 0 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask); #endif if ((hit_mask == HIT_MASK_TOPLEFT || hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT || hit_mask == HIT_MASK_BOTTOMRIGHT) && laser.current_angle % 4) /* angle is not 90° */ { int dx, dy; if (hit_mask == HIT_MASK_TOPLEFT) { dx = -1; dy = -1; } else if (hit_mask == HIT_MASK_TOPRIGHT) { dx = +1; dy = -1; } else if (hit_mask == HIT_MASK_BOTTOMLEFT) { dx = -1; dy = +1; } else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */ { dx = +1; dy = +1; } AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY); LX += XS; LY += YS; #if 0 printf("[HitOnlyAnEdge() == TRUE]\n"); #endif return TRUE; } #if 0 printf("[HitOnlyAnEdge() == FALSE]\n"); #endif return FALSE; } boolean HitPolarizer(int element, int hit_mask) { if (HitOnlyAnEdge(element, hit_mask)) return FALSE; if (IS_DF_GRID(element)) { int grid_angle = get_element_angle(element); #if 0 printf("HitPolarizer: angle: grid == %d, laser == %d\n", grid_angle, laser.current_angle); #endif AddLaserEdge(LX, LY); AddDamagedField(ELX, ELY); if (!Hit[ELX][ELY]) Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge; if (laser.current_angle == grid_angle || laser.current_angle == get_opposite_angle(grid_angle)) { #if 0 int step_size; if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */ step_size = 8; else step_size = 4; LX += step_size * XS; LY += step_size * YS; #else /* skip the whole element before continuing the scan */ do { LX += XS; LY += YS; } while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0); if (LX/TILEX > ELX || LY/TILEY > ELY) { /* skipping scan positions to the right and down skips one scan position too much, because this is only the top left scan position of totally four scan positions (plus one to the right, one to the bottom and one to the bottom right) */ LX -= XS; LY -= YS; } #endif AddLaserEdge(LX, LY); LX += XS; LY += YS; #if 0 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n", LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY); #endif return FALSE; } else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element)) return HitReflectingWalls(element, hit_mask); else return HitAbsorbingWalls(element, hit_mask); } else if (IS_GRID_STEEL(element)) return HitReflectingWalls(element, hit_mask); else /* IS_GRID_WOOD */ return HitAbsorbingWalls(element, hit_mask); return TRUE; } boolean HitBlock(int element, int hit_mask) { boolean check = FALSE; if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) && game.num_keys == 0) check = TRUE; if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD) { int i, x, y; int ex = ELX * TILEX + 14; int ey = ELY * TILEY + 14; check = TRUE; for(i=1; i<32; i++) { x = LX + i * XS; y = LY + i * YS; if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1)) check = FALSE; } } if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD)) return HitAbsorbingWalls(element, hit_mask); if (check) { AddLaserEdge(LX - XS, LY - YS); AddDamagedField(ELX, ELY); if (!Box[ELX][ELY]) Box[ELX][ELY] = laser.num_edges; return HitReflectingWalls(element, hit_mask); } if (element == EL_GATE_STONE || element == EL_GATE_WOOD) { int xs = XS / 2, ys = YS / 2; int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT; int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT; if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 || (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2) { laser.overloaded = (element == EL_GATE_STONE); return TRUE; } if (ABS(xs) == 1 && ABS(ys) == 1 && (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT || hit_mask == HIT_MASK_BOTTOM)) AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM), ELY - ys * (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT)); AddLaserEdge(LX, LY); Bang(ELX, ELY); game.num_keys--; if (element == EL_GATE_STONE && Box[ELX][ELY]) { DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED); /* BackToFront(); */ ScanLaser(); return TRUE; } return FALSE; } if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD) { int xs = XS / 2, ys = YS / 2; int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT; int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT; if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 || (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2) { laser.overloaded = (element == EL_BLOCK_STONE); return TRUE; } if (ABS(xs) == 1 && ABS(ys) == 1 && (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT || hit_mask == HIT_MASK_BOTTOM)) AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM), ELY - ys * (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT)); AddDamagedField(ELX, ELY); LX = ELX * TILEX + 14; LY = ELY * TILEY + 14; AddLaserEdge(LX, LY); laser.stops_inside_element = TRUE; return TRUE; } return TRUE; } boolean HitLaserSource(int element, int hit_mask) { if (HitOnlyAnEdge(element, hit_mask)) return FALSE; PlaySoundStereo(SND_AUTSCH, ST(ELX)); laser.overloaded = TRUE; return TRUE; } boolean HitLaserDestination(int element, int hit_mask) { if (HitOnlyAnEdge(element, hit_mask)) return FALSE; if (element != EL_EXIT_OPEN && !(IS_RECEIVER(element) && game.kettles_still_needed == 0 && laser.current_angle == get_opposite_angle(get_element_angle(element)))) { PlaySoundStereo(SND_HOLZ, ST(ELX)); return TRUE; } if (IS_RECEIVER(element) || (IS_22_5_ANGLE(laser.current_angle) && (ELX != (LX + 6 * XS) / TILEX || ELY != (LY + 6 * YS) / TILEY || LX + 6 * XS < 0 || LY + 6 * YS < 0))) { LX -= XS; LY -= YS; } else { LX = ELX * TILEX + 14; LY = ELY * TILEY + 14; laser.stops_inside_element = TRUE; } AddLaserEdge(LX, LY); AddDamagedField(ELX, ELY); if (game.lights_still_needed == 0) game.level_solved = TRUE; return TRUE; } boolean HitReflectingWalls(int element, int hit_mask) { /* check if laser hits side of a wall with an angle that is not 90° */ if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT || hit_mask == HIT_MASK_BOTTOM)) { PlaySoundStereo(SND_HUI, ST(ELX)); LX -= XS; LY -= YS; if (!IS_DF_GRID(element)) AddLaserEdge(LX, LY); /* check if laser hits wall with an angle of 45° */ if (!IS_22_5_ANGLE(laser.current_angle)) { if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM) { LX += 2 * XS; laser.current_angle = get_mirrored_angle(laser.current_angle, ANG_MIRROR_0); } else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */ { LY += 2 * YS; laser.current_angle = get_mirrored_angle(laser.current_angle, ANG_MIRROR_90); } AddLaserEdge(LX, LY); XS = 2 * Step[laser.current_angle].x; YS = 2 * Step[laser.current_angle].y; return FALSE; } else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM) { laser.current_angle = get_mirrored_angle(laser.current_angle, ANG_MIRROR_0); if (ABS(XS) == 4) { LX += 2 * XS; if (!IS_DF_GRID(element)) AddLaserEdge(LX, LY); } else { LX += XS; if (!IS_DF_GRID(element)) AddLaserEdge(LX, LY + YS / 2); LX += XS; if (!IS_DF_GRID(element)) AddLaserEdge(LX, LY); } YS = 2 * Step[laser.current_angle].y; return FALSE; } else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */ { laser.current_angle = get_mirrored_angle(laser.current_angle, ANG_MIRROR_90); if (ABS(YS) == 4) { LY += 2 * YS; if (!IS_DF_GRID(element)) AddLaserEdge(LX, LY); } else { LY += YS; if (!IS_DF_GRID(element)) AddLaserEdge(LX + XS / 2, LY); LY += YS; if (!IS_DF_GRID(element)) AddLaserEdge(LX, LY); } XS = 2 * Step[laser.current_angle].x; return FALSE; } } /* reflection at the edge of reflecting DF style wall */ if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle)) { if (((laser.current_angle == 1 || laser.current_angle == 3) && hit_mask == HIT_MASK_TOPRIGHT) || ((laser.current_angle == 5 || laser.current_angle == 7) && hit_mask == HIT_MASK_TOPLEFT) || ((laser.current_angle == 9 || laser.current_angle == 11) && hit_mask == HIT_MASK_BOTTOMLEFT) || ((laser.current_angle == 13 || laser.current_angle == 15) && hit_mask == HIT_MASK_BOTTOMRIGHT)) { int mirror_angle = (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ? ANG_MIRROR_135 : ANG_MIRROR_45); PlaySoundStereo(SND_HUI, ST(ELX)); AddDamagedField(ELX, ELY); AddLaserEdge(LX, LY); laser.current_angle = get_mirrored_angle(laser.current_angle, mirror_angle); XS = 8 / -XS; YS = 8 / -YS; LX += XS; LY += YS; AddLaserEdge(LX, LY); return FALSE; } } /* reflection inside an edge of reflecting DF style wall */ if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle)) { if (((laser.current_angle == 1 || laser.current_angle == 3) && hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) || ((laser.current_angle == 5 || laser.current_angle == 7) && hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) || ((laser.current_angle == 9 || laser.current_angle == 11) && hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) || ((laser.current_angle == 13 || laser.current_angle == 15) && hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT))) { int mirror_angle = (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) || hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ? ANG_MIRROR_135 : ANG_MIRROR_45); PlaySoundStereo(SND_HUI, ST(ELX)); /* AddDamagedField(ELX, ELY); */ AddLaserEdge(LX - XS, LY - YS); AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0), LY - YS + (ABS(YS) == 4 ? YS/2 : 0)); laser.current_angle = get_mirrored_angle(laser.current_angle, mirror_angle); XS = 8 / -XS; YS = 8 / -YS; LX += XS; LY += YS; AddLaserEdge(LX, LY); return FALSE; } } /* check if laser hits DF style wall with an angle of 90° */ if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle)) { if ((IS_HORIZ_ANGLE(laser.current_angle) && (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) || (IS_VERT_ANGLE(laser.current_angle) && (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT)))) { static int last_LX = 0, last_LY = 0, last_hit_mask = 0; /* laser at last step touched nothing or the same side of the wall */ if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask) { AddDamagedField(ELX, ELY); LX += 8 * XS; LY += 8 * YS; last_LX = LX; last_LY = LY; last_hit_mask = hit_mask; return FALSE; } } } if (!HitOnlyAnEdge(element, hit_mask)) { laser.overloaded = TRUE; return TRUE; } return FALSE; } boolean HitAbsorbingWalls(int element, int hit_mask) { if (HitOnlyAnEdge(element, hit_mask)) return FALSE; if (ABS(XS) == 4 && (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT)) { AddLaserEdge(LX - XS, LY - YS); LX = LX + XS / 2; LY = LY + YS; } if (ABS(YS) == 4 && (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)) { AddLaserEdge(LX - XS, LY - YS); LX = LX + XS; LY = LY + YS / 2; } if (IS_WALL_WOOD(element) || IS_DF_WALL_WOOD(element) || IS_GRID_WOOD(element) || IS_GRID_WOOD_FIXED(element) || IS_GRID_WOOD_AUTO(element) || element == EL_FUSE_ON || element == EL_BLOCK_WOOD || element == EL_GATE_WOOD) { PlaySoundStereo(SND_HOLZ, ST(ELX)); return TRUE; } if (IS_WALL_ICE(element)) { int mask; mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */ mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */ /* check if laser hits wall with an angle of 90° */ if (IS_90_ANGLE(laser.current_angle)) mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2); if (mask == 1 || mask == 2 || mask == 4 || mask == 8) { int i; for(i=0; i<4; i++) { if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2)) mask = 15 - (8 >> i); else if (ABS(XS) == 4 && mask == (1 << i) && (XS > 0) == (i % 2) && (YS < 0) == (i / 2)) mask = 3 + (i / 2) * 9; else if (ABS(YS) == 4 && mask == (1 << i) && (XS < 0) == (i % 2) && (YS > 0) == (i / 2)) mask = 5 + (i % 2) * 5; } } laser.wall_mask = mask; } else if (IS_WALL_AMOEBA(element)) { int elx = (LX - 2 * XS) / TILEX; int ely = (LY - 2 * YS) / TILEY; int element2 = Feld[elx][ely]; int mask; if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2)) { laser.dest_element = EL_EMPTY; return TRUE; } ELX = elx; ELY = ely; mask = (LX - 2 * XS) / 16 - ELX * 2 + 1; mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2; if (IS_90_ANGLE(laser.current_angle)) mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2); laser.dest_element = element2 | EL_WALL_AMOEBA; laser.wall_mask = mask; } return TRUE; } void OpenExit(int x, int y) { int delay = 6; if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = 4 * delay; if (MovDelay[x][y]) /* wait some time before next frame */ { int phase; MovDelay[x][y]--; phase = MovDelay[x][y] / delay; if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y)) DrawGraphic(x, y, EL_EXIT_OPEN - phase); if (!MovDelay[x][y]) { Feld[x][y] = EL_EXIT_OPEN; DrawField(x, y); } } } void OpenSurpriseBall(int x, int y) { int delay = 2; if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = 50 * delay; if (MovDelay[x][y]) /* wait some time before next frame */ { int phase; MovDelay[x][y]--; phase = MovDelay[x][y] / delay; if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y)) { int graphic = el2gfx(Store[x][y]); int bitmap_nr, gx, gy; int dx = RND(26), dy = RND(26); getGraphicSource(graphic, &bitmap_nr, &gx, &gy); BlitBitmap(pix[bitmap_nr], drawto, gx + dx, gy + dy, 6, 6, SX + x * TILEX + dx, SY + y * TILEY + dy); MarkTileDirty(x, y); } if (!MovDelay[x][y]) { Feld[x][y] = Store[x][y]; Store[x][y] = 0; DrawField(x, y); ScanLaser(); } } } void MeltIce(int x, int y) { int frames = 5; int delay = 5; if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = frames * delay; if (MovDelay[x][y]) /* wait some time before next frame */ { int phase; int wall_mask = Store2[x][y]; int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE; MovDelay[x][y]--; phase = frames - MovDelay[x][y] / delay - 1; if (!MovDelay[x][y]) { int i; Feld[x][y] = real_element & (wall_mask ^ 0xFF); Store[x][y] = Store2[x][y] = 0; DrawWalls(x, y, Feld[x][y]); if (Feld[x][y] == EL_WALL_ICE) Feld[x][y] = EL_EMPTY; for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--) if (laser.damage[i].is_mirror) break; if (i > 0) DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED); else DrawLaser(0, DL_LASER_DISABLED); ScanLaser(); } else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y)) { DrawWallsAnimation(x, y, real_element, phase, wall_mask); laser.redraw = TRUE; } } } void GrowAmoeba(int x, int y) { int frames = 5; int delay = 1; if (!MovDelay[x][y]) /* next animation frame */ MovDelay[x][y] = frames * delay; if (MovDelay[x][y]) /* wait some time before next frame */ { int phase; int wall_mask = Store2[x][y]; int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA; MovDelay[x][y]--; phase = MovDelay[x][y] / delay; if (!MovDelay[x][y]) { Feld[x][y] = real_element; Store[x][y] = Store2[x][y] = 0; DrawWalls(x, y, Feld[x][y]); DrawLaser(0, DL_LASER_ENABLED); } else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y)) DrawWallsAnimation(x, y, real_element, phase, wall_mask); } } void Explode(int x, int y, int phase, int mode) { int num_phase = 9, delay = 2; int last_phase = num_phase * delay; int half_phase = (num_phase / 2) * delay; #if 0 int first_phase_after_start = EX_PHASE_START + 1; #endif laser.redraw = TRUE; if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */ { int center_element = Feld[x][y]; if (IS_MOVING(x, y) || IS_BLOCKED(x, y)) { /* put moving element to center field (and let it explode there) */ center_element = MovingOrBlocked2Element(x, y); RemoveMovingField(x, y); Feld[x][y] = center_element; } if (center_element == EL_BOMB || IS_MCDUFFIN(center_element)) Store[x][y] = center_element; else Store[x][y] = EL_EMPTY; Store2[x][y] = mode; Feld[x][y] = EL_EXPLODING_OPAQUE; MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0; Frame[x][y] = 1; return; } Frame[x][y] = (phase < last_phase ? phase + 1 : 0); if (phase == half_phase) { Feld[x][y] = EL_EXPLODING_TRANSP; if (x == ELX && y == ELY) ScanLaser(); } if (phase == last_phase) { int element; if (Store[x][y] == EL_BOMB) { laser.num_damages--; DrawLaser(0, DL_LASER_DISABLED); laser.num_edges = 0; Bang(laser.start_edge.x, laser.start_edge.y); Store[x][y] = EL_EMPTY; } else if (IS_MCDUFFIN(Store[x][y])) { game.game_over = TRUE; game.game_over_cause = GAME_OVER_BOMB; Store[x][y] = EL_EMPTY; } element = Feld[x][y] = Store[x][y]; Store[x][y] = Store2[x][y] = 0; MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0; InitField(x, y, FALSE); DrawField(x, y); } else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) { int graphic = GFX_EXPLOSION_START; int graphic_phase = (phase / delay - 1); if (Store2[x][y] == EX_KETTLE) { if (graphic_phase < 3) graphic = GFX_EXPLOSION_KETTLE; else if (graphic_phase < 5) { graphic = GFX_EXPLOSION_LAST; graphic_phase -= graphic_phase; } else { graphic = GFX_EMPTY; graphic_phase = 0; } } else if (Store2[x][y] == EX_SHORT) { if (graphic_phase < 4) graphic = GFX_EXPLOSION_SHORT; else { graphic = GFX_EMPTY; graphic_phase = 0; } } DrawGraphic(x, y, graphic + graphic_phase); } } void Bang(int x, int y) { int element = Feld[x][y]; int mode = EX_NORMAL; #if 0 DrawLaser(0, DL_LASER_ENABLED); #endif switch(element) { case EL_KETTLE: mode = EX_KETTLE; break; case EL_GATE_STONE: case EL_GATE_WOOD: mode = EX_SHORT; break; default: mode = EX_NORMAL; break; } if (IS_PACMAN(element)) PlaySoundStereo(SND_QUIEK, ST(x)); else if (element == EL_BOMB || IS_MCDUFFIN(element)) PlaySoundStereo(SND_ROAAAR, ST(x)); else if (element == EL_KEY) PlaySoundStereo(SND_KLING, ST(x)); else PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x)); Explode(x, y, EX_PHASE_START, mode); } void TurnRound(int x, int y) { static struct { int x, y; } move_xy[] = { { 0, 0 }, {-1, 0 }, {+1, 0 }, { 0, 0 }, { 0, -1 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, +1 } }; static struct { int left, right, back; } turn[] = { { 0, 0, 0 }, { MV_DOWN, MV_UP, MV_RIGHT }, { MV_UP, MV_DOWN, MV_LEFT }, { 0, 0, 0 }, { MV_LEFT, MV_RIGHT, MV_DOWN }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { MV_RIGHT, MV_LEFT, MV_UP } }; int element = Feld[x][y]; int old_move_dir = MovDir[x][y]; int left_dir = turn[old_move_dir].left; int right_dir = turn[old_move_dir].right; int back_dir = turn[old_move_dir].back; int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y; int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y; int left_x = x+left_dx, left_y = y+left_dy; int right_x = x+right_dx, right_y = y+right_dy; if (element == EL_PACMAN) { boolean can_turn_left = FALSE, can_turn_right = FALSE; if (IN_LEV_FIELD(left_x, left_y) && IS_EATABLE4PACMAN(Feld[left_x][left_y])) can_turn_left = TRUE; if (IN_LEV_FIELD(right_x, right_y) && IS_EATABLE4PACMAN(Feld[right_x][right_y])) can_turn_right = TRUE; if (can_turn_right) MovDir[x][y] = right_dir; else MovDir[x][y] = back_dir; MovDelay[x][y] = 0; } } void StartMoving(int x, int y) { int element = Feld[x][y]; if (Stop[x][y]) return; if (CAN_MOVE(element)) { int newx, newy; if (MovDelay[x][y]) /* wait some time before next movement */ { MovDelay[x][y]--; if (MovDelay[x][y]) return; } /* now make next step */ Moving2Blocked(x, y, &newx, &newy); /* get next screen position */ if (element == EL_PACMAN && IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) && !ObjHit(newx, newy, HIT_POS_CENTER)) { Store[newx][newy] = Feld[newx][newy]; Feld[newx][newy] = EL_EMPTY; DrawField(newx, newy); } else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) || ObjHit(newx, newy, HIT_POS_CENTER)) { /* object was running against a wall */ TurnRound(x, y); return; } InitMovingField(x, y, MovDir[x][y]); } if (MovDir[x][y]) ContinueMoving(x, y); } void ContinueMoving(int x, int y) { int element = Feld[x][y]; int direction = MovDir[x][y]; int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); int horiz_move = (dx!=0); int newx = x + dx, newy = y + dy; int step = (horiz_move ? dx : dy) * TILEX / 8; MovPos[x][y] += step; if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */ { Feld[x][y] = EL_EMPTY; Feld[newx][newy] = element; MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; MovDelay[newx][newy] = 0; if (!CAN_MOVE(element)) MovDir[newx][newy] = 0; DrawField(x, y); DrawField(newx, newy); Stop[newx][newy] = TRUE; if (element == EL_PACMAN) { if (Store[newx][newy] == EL_BOMB) Bang(newx, newy); if (IS_WALL_AMOEBA(Store[newx][newy]) && (LX + 2 * XS) / TILEX == newx && (LY + 2 * YS) / TILEY == newy) { laser.num_edges--; ScanLaser(); } } } else /* still moving on */ DrawField(x, y); laser.redraw = TRUE; } void ClickElement(int mx, int my, int button) { static unsigned long click_delay = 0; static int click_delay_value = CLICK_DELAY_SHORT; static boolean new_button = TRUE; int element; int x = (mx - SX) / TILEX, y = (my - SY) / TILEY; if (button == MB_RELEASED) { new_button = TRUE; click_delay_value = CLICK_DELAY_SHORT; /* release eventually hold auto-rotating mirror */ RotateMirror(x, y, MB_RELEASED); return; } if (!DelayReached(&click_delay, click_delay_value) && !new_button) return; if (button == MB_MIDDLEBUTTON) /* middle button has no function */ return; if (!IN_PIX_FIELD(mx - SX, my - SY)) return; if (Feld[x][y] == EL_EMPTY) return; element = Feld[x][y]; if (IS_MIRROR(element) || IS_BEAMER(element) || IS_POLAR(element) || IS_POLAR_CROSS(element) || IS_DF_MIRROR(element) || IS_DF_MIRROR_AUTO(element)) { RotateMirror(x, y, button); } else if (IS_MCDUFFIN(element)) { if (!laser.fuse_off) { DrawLaser(0, DL_LASER_DISABLED); /* BackToFront(); */ } element = get_rotated_element(element, BUTTON_ROTATION(button)); laser.start_angle = get_element_angle(element); InitLaser(); Feld[x][y] = element; DrawField(x, y); /* BackToFront(); */ if (!laser.fuse_off) ScanLaser(); } else if (element == EL_FUSE_ON && laser.fuse_off) { if (x != laser.fuse_x || y != laser.fuse_y) return; laser.fuse_off = FALSE; laser.fuse_x = laser.fuse_y = -1; DrawGraphic(x, y, GFX_FUSE_ON); ScanLaser(); } else if (element == EL_FUSE_ON && !laser.fuse_off && new_button) { laser.fuse_off = TRUE; laser.fuse_x = x; laser.fuse_y = y; laser.overloaded = FALSE; DrawLaser(0, DL_LASER_DISABLED); DrawGraphic(x, y, GFX_FUSE_OFF); } else if (element == EL_LIGHTBALL) { Bang(x, y); RaiseScore(10); DrawLaser(0, DL_LASER_ENABLED); } click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT); new_button = FALSE; } void RotateMirror(int x, int y, int button) { static int hold_x = -1, hold_y = -1; if (button == MB_RELEASED) { /* release eventually hold auto-rotating mirror */ hold_x = -1; hold_y = -1; return; } if (IS_MIRROR(Feld[x][y]) || IS_POLAR_CROSS(Feld[x][y]) || IS_POLAR(Feld[x][y]) || IS_BEAMER(Feld[x][y]) || IS_DF_MIRROR(Feld[x][y]) || IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y])) { Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button)); } else if (IS_DF_MIRROR_AUTO(Feld[x][y])) { if (button == MB_LEFTBUTTON) { /* left mouse button only for manual adjustment, no auto-rotating; freeze mirror for until mouse button released */ hold_x = x; hold_y = y; } else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y)) Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT); } if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y])) { int edge = Hit[x][y]; DrawField(x, y); if (edge > 0) { DrawLaser(edge - 1, DL_LASER_DISABLED); ScanLaser(); } } else if (ObjHit(x, y, HIT_POS_CENTER)) { int edge = Hit[x][y]; if (edge == 0) { Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n"); edge = 1; } DrawLaser(edge - 1, DL_LASER_DISABLED); ScanLaser(); } else { int check = 1; if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN)) check = 2; DrawField(x, y); if ((IS_BEAMER(Feld[x][y]) || IS_POLAR(Feld[x][y]) || IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY) { check = 0; if (IS_BEAMER(Feld[x][y])) { #if 0 printf("TEST (%d, %d) [%d] [%d]\n", LX, LY, laser.beamer_edge, laser.beamer[1].num); #endif laser.num_edges--; } ScanLaser(); } if (check == 2) DrawLaser(0, DL_LASER_ENABLED); } } void AutoRotateMirrors() { static unsigned long rotate_delay = 0; int x, y; if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY)) return; for (x=0; x Cc + 50) { Cc = CC; color = old + new * mult; if (mult > 0) mult++; else mult--; if (ABS(mult) == 16) { mult =- mult / 16; old = color; new = new << 4; if (new > 0x100) new = 0x001; } red = 0x0e00 * ((color & 0xF00) >> 8); green = 0x0e00 * ((color & 0x0F0) >> 4); blue = 0x0e00 * ((color & 0x00F)); SetRGB(pen_magicolor[0], red, green, blue); red = 0x1111 * ((color & 0xF00) >> 8); green = 0x1111 * ((color & 0x0F0) >> 4); blue = 0x1111 * ((color & 0x00F)); SetRGB(pen_magicolor[1], red, green, blue); } } void GameActions() { static unsigned long action_delay = 0; static unsigned long pacman_delay = 0; static unsigned long energy_delay = 0; static unsigned long overload_delay = 0; #if 0 unsigned short color_scale = 0xFFFF / 15; #endif int element; int x, y, i; int r, d; #if 1 WaitUntilDelayReached(&action_delay, GameFrameDelay); #else if (!DelayReached(&action_delay, GameFrameDelay)) { if (!PendingEvent()) /* delay only if no pending events */ Delay(10); return; } #endif for (y=0; y MAX_LASER_LEN && !laser.fuse_off) { DrawLaser(0, DL_LASER_DISABLED); ScanLaser(); } } if (DelayReached(&energy_delay, 4000)) { game.energy_left--; if (game.energy_left >= 0) { BlitBitmap(pix[PIX_DOOR], drawto, DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY, ENERGY_XSIZE, ENERGY_YSIZE - game.energy_left, DX_ENERGY, DY_ENERGY); redraw_mask |= REDRAW_DOOR_1; } else if (setup.time_limit) { int i; for(i=15; i>=0; i--) { #if 0 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale); #endif pen_ray = GetPixelFromRGB(window, level.laser_red * 0x11 * i, level.laser_green * 0x11 * i, level.laser_blue * 0x11 * i); DrawLaser(0, DL_LASER_ENABLED); BackToFront(); Delay(50); } StopSound(SND_WARNTON); FadeMusic(); DrawLaser(0, DL_LASER_DISABLED); game.game_over = TRUE; game.game_over_cause = GAME_OVER_NO_ENERGY; #if 0 if (Request("Out of magic energy ! Play it again ?", REQ_ASK | REQ_STAY_CLOSED)) { InitGame(); } else { game_status = MAINMENU; DrawMainMenu(); } #endif return; } } element = laser.dest_element; #if 0 if (element != Feld[ELX][ELY]) { printf("element == %d, Feld[ELX][ELY] == %d\n", element, Feld[ELX][ELY]); } #endif if (!laser.overloaded && laser.overload_value == 0 && element != EL_BOMB && element != EL_MINE && element != EL_BALL_GRAY && element != EL_BLOCK_STONE && element != EL_BLOCK_WOOD && element != EL_FUSE_ON && element != EL_FUEL_FULL && !IS_WALL_ICE(element) && !IS_WALL_AMOEBA(element)) return; if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) || (!laser.overloaded && laser.overload_value > 0)) && DelayReached(&overload_delay, 60 + !laser.overloaded * 120)) { if (laser.overloaded) laser.overload_value++; else laser.overload_value--; if (game.cheat_no_overload) { laser.overloaded = FALSE; laser.overload_value = 0; } if (laser.overload_value < MAX_LASER_OVERLOAD - 8) { int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD; int color_down = 0xFF - color_up; #if 0 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000, (15 - (laser.overload_value / 6)) * color_scale); #endif pen_ray = GetPixelFromRGB(window, (level.laser_red ? 0xFF : color_up), (level.laser_green ? color_down : 0x00), (level.laser_blue ? color_down : 0x00)); DrawLaser(0, DL_LASER_ENABLED); BackToFront(); } if (laser.overloaded) { if (setup.sound_loops) PlaySoundExt(SND_WARNTON, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP); else PlaySoundStereo(SND_WARNTON, PSND_MAX_RIGHT); } if (!laser.overloaded) StopSound(SND_WARNTON); if (laser.overloaded) { BlitBitmap(pix[PIX_DOOR], drawto, DOOR_GFX_PAGEX4 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE - laser.overload_value, OVERLOAD_XSIZE, laser.overload_value, DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE - laser.overload_value); redraw_mask |= REDRAW_DOOR_1; } else { BlitBitmap(pix[PIX_DOOR], drawto, DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD, OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value, DX_OVERLOAD, DY_OVERLOAD); redraw_mask |= REDRAW_DOOR_1; } if (laser.overload_value == MAX_LASER_OVERLOAD) { int i; for(i=15; i>=0; i--) { #if 0 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000); #endif pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00); DrawLaser(0, DL_LASER_ENABLED); BackToFront(); Delay(50); } DrawLaser(0, DL_LASER_DISABLED); game.game_over = TRUE; game.game_over_cause = GAME_OVER_OVERLOADED; #if 0 if (Request("Magic spell hit Mc Duffin ! Play it again ?", REQ_ASK | REQ_STAY_CLOSED)) { InitGame(); } else { game_status = MAINMENU; DrawMainMenu(); } #endif return; } } if (laser.fuse_off) return; CT -= Ct; if (element == EL_BOMB && CT > 1500) { if (game.cheat_no_explosion) return; #if 0 laser.num_damages--; DrawLaser(0, DL_LASER_DISABLED); laser.num_edges = 0; #endif Bang(ELX, ELY); laser.dest_element = EL_EXPLODING_OPAQUE; #if 0 Bang(ELX, ELY); laser.num_damages--; DrawLaser(0, DL_LASER_DISABLED); laser.num_edges = 0; Bang(laser.start_edge.x, laser.start_edge.y); if (Request("Bomb killed Mc Duffin ! Play it again ?", REQ_ASK | REQ_STAY_CLOSED)) { InitGame(); } else { game_status = MAINMENU; DrawMainMenu(); } #endif return; } if (element == EL_FUSE_ON && CT > 500) { laser.fuse_off = TRUE; laser.fuse_x = ELX; laser.fuse_y = ELY; DrawLaser(0, DL_LASER_DISABLED); DrawGraphic(ELX, ELY, GFX_FUSE_OFF); } if (element == EL_BALL_GRAY && CT > 1500) { static int new_elements[] = { EL_MIRROR_START, EL_MIRROR_FIXED_START, EL_POLAR_START, EL_POLAR_CROSS_START, EL_PACMAN_START, EL_KETTLE, EL_BOMB, EL_PRISM }; int num_new_elements = sizeof(new_elements) / sizeof(int); int new_element = new_elements[RND(num_new_elements)]; Store[ELX][ELY] = new_element + RND(get_num_elements(new_element)); Feld[ELX][ELY] = EL_GRAY_BALL_OPENING; /* !!! CHECK AGAIN: Laser on Polarizer !!! */ ScanLaser(); return; #if 0 int graphic; switch (RND(5)) { case 0: element = EL_MIRROR_START + RND(16); break; case 1: { int rnd = RND(3); element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM); } break; default: { int rnd = RND(3); element = (rnd == 0 ? EL_FUSE_ON : rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 : rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 : rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 : EL_MIRROR_FIXED_START + rnd - 25); } break; } graphic = el2gfx(element); for(i=0; i<50; i++) { int x = RND(26); int y = RND(26); BlitBitmap(pix[PIX_BACK], drawto, SX + (graphic % GFX_PER_LINE) * TILEX + x, SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6, SX + ELX * TILEX + x, SY + ELY * TILEY + y); MarkTileDirty(ELX, ELY); BackToFront(); DrawLaser(0, DL_LASER_ENABLED); Delay(50); } Feld[ELX][ELY] = element; DrawField(ELX, ELY); #if 0 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY); #endif /* above stuff: GRAY BALL -> PRISM !!! */ /* LX = ELX * TILEX + 14; LY = ELY * TILEY + 14; if (laser.current_angle == (laser.current_angle >> 1) << 1) OK = 8; else OK = 4; LX -= OK * XS; LY -= OK * YS; laser.num_edges -= 2; laser.num_damages--; */ #if 0 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--) if (laser.damage[i].is_mirror) break; if (i > 0) DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED); else DrawLaser(0, DL_LASER_DISABLED); #else DrawLaser(0, DL_LASER_DISABLED); #endif ScanLaser(); /* printf("TEST ELEMENT: %d\n", Feld[0][0]); */ #endif return; } if (IS_WALL_ICE(element) && CT > 1000) { PlaySoundStereo(SND_SLURP, ST(ELX)); { Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING; Store[ELX][ELY] = EL_WALL_ICE; Store2[ELX][ELY] = laser.wall_mask; laser.dest_element = Feld[ELX][ELY]; return; } for(i=0; i<5; i++) { int phase = i + 1; if (i == 4) { Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF); phase = 0; } DrawWallsAnimation(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask); BackToFront(); Delay(100); } if (Feld[ELX][ELY] == EL_WALL_ICE) Feld[ELX][ELY] = EL_EMPTY; /* laser.num_edges--; LX = laser.edge[laser.num_edges].x - (SX + 2); LY = laser.edge[laser.num_edges].y - (SY + 2); */ for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--) if (laser.damage[i].is_mirror) break; if (i > 0) DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED); else DrawLaser(0, DL_LASER_DISABLED); ScanLaser(); return; } if (IS_WALL_AMOEBA(element) && CT > 1200) { int k1, k2, k3, dx, dy, de, dm; int element2 = Feld[ELX][ELY]; if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element)) return; for (i = laser.num_damages - 1; i>=0; i--) if (laser.damage[i].is_mirror) break; r = laser.num_edges; d = laser.num_damages; k1 = i; if (k1 > 0) { int x, y; DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED); laser.num_edges++; DrawLaser(0, DL_LASER_ENABLED); laser.num_edges--; x = laser.damage[k1].x; y = laser.damage[k1].y; DrawField(x, y); } for(i=0; i<4; i++) { if (laser.wall_mask & (1 << i)) { if (ReadPixel(drawto, SX + ELX * TILEX + 14 + (i % 2) * 2, SY + ELY * TILEY + 31 * (i / 2)) == pen_ray) break; if (ReadPixel(drawto, SX + ELX * TILEX + 31 * (i % 2), SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray) break; } } k2 = i; for(i=0; i<4; i++) { if (laser.wall_mask & (1 << i)) { if (ReadPixel(drawto, SX + ELX * TILEX + 31 * (i % 2), SY + ELY * TILEY + 31 * (i / 2)) == pen_ray) break; } } k3 = i; if (laser.num_beamers > 0 || k1 < 1 || k2 < 4 || k3 < 4 || ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14) == pen_ray) { laser.num_edges = r; laser.num_damages = d; DrawLaser(0, DL_LASER_DISABLED); } Feld[ELX][ELY] = element | laser.wall_mask; dx = ELX; dy = ELY; de = Feld[ELX][ELY]; dm = laser.wall_mask; #if 1 { int x = ELX, y = ELY; int wall_mask = laser.wall_mask; ScanLaser(); DrawLaser(0, DL_LASER_ENABLED); PlaySoundStereo(SND_AMOEBE, ST(dx)); Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING; Store[x][y] = EL_WALL_AMOEBA; Store2[x][y] = wall_mask; return; } #endif DrawWallsAnimation(dx, dy, de, 4, dm); ScanLaser(); DrawLaser(0, DL_LASER_ENABLED); PlaySoundStereo(SND_AMOEBE, ST(dx)); for(i=4; i>=0; i--) { DrawWallsAnimation(dx, dy, de, i, dm); BackToFront(); Delay(20); } DrawLaser(0, DL_LASER_ENABLED); return; } if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) && laser.stops_inside_element && CT > 1500) { int x, y; int k; if (ABS(XS) > ABS(YS)) k = 0; else k = 1; if (XS < YS) k += 2; for(i=0; i<4; i++) { if (i) k++; if (k > 3) k=0; x = ELX + Step[k * 4].x; y = ELY + Step[k * 4].y; if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY) continue; if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN)) continue; break; } if (i > 3) { laser.overloaded = (element == EL_BLOCK_STONE); return; } PlaySoundStereo(SND_BONG, ST(ELX)); Feld[ELX][ELY] = 0; Feld[x][y] = element; DrawGraphic(ELX, ELY, -1); DrawField(x, y); if (element == EL_BLOCK_STONE && Box[ELX][ELY]) { DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED); DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED); } ScanLaser(); return; } if (element == EL_FUEL_FULL && CT > 200) { for(i=game.energy_left; i<=MAX_LASER_ENERGY; i+=2) { BlitBitmap(pix[PIX_DOOR], drawto, DOOR_GFX_PAGEX4 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i, ENERGY_XSIZE, i, DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i); redraw_mask |= REDRAW_DOOR_1; BackToFront(); Delay(20); } game.energy_left = MAX_LASER_ENERGY; Feld[ELX][ELY] = EL_FUEL_EMPTY; DrawField(ELX, ELY); DrawLaser(0, DL_LASER_ENABLED); return; } return; } void MovePacMen() { static int p = -1; int mx, my, ox, oy, nx, ny; int g, element; int l; if (++p >= game.num_pacman) p = 0; game.pacman[p].dir--; for(l=1; l<5; l++) { game.pacman[p].dir++; if (game.pacman[p].dir > 4) game.pacman[p].dir = 1; if (game.pacman[p].dir % 2) { mx = 0; my = game.pacman[p].dir - 2; } else { my = 0; mx = 3 - game.pacman[p].dir; } ox = game.pacman[p].x; oy = game.pacman[p].y; nx = ox + mx; ny = oy + my; element = Feld[nx][ny]; if (nx < 0 || nx > 15 || ny < 0 || ny > 11) continue; if (!IS_EATABLE4PACMAN(element)) continue; if (ObjHit(nx, ny, HIT_POS_CENTER)) continue; Feld[ox][oy] = EL_EMPTY; Feld[nx][ny] = EL_PACMAN_RIGHT - 1 + (game.pacman[p].dir - 1 + (game.pacman[p].dir % 2) * 2); game.pacman[p].x = nx; game.pacman[p].y = ny; g = Feld[nx][ny] - EL_PACMAN_RIGHT; DrawGraphic(ox, oy, GFX_EMPTY); if (element != EL_EMPTY) { int i; CT = Counter(); ox = SX + ox * TILEX; oy = SY + oy * TILEY; for(i=1; i<33; i+=2) { BlitBitmap(pix[PIX_BACK], window, SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY, ox + i * mx, oy + i * my); FlushDisplay(); Delay(1); } Ct = Ct + Counter() - CT; } DrawField(nx, ny); BackToFront(); if (!laser.fuse_off) { DrawLaser(0, DL_LASER_ENABLED); if (ObjHit(nx, ny, HIT_POS_BETWEEN)) { AddDamagedField(nx, ny); laser.damage[laser.num_damages - 1].edge = 0; } } if (element == EL_BOMB) { DeletePacMan(nx, ny); } if (IS_WALL_AMOEBA(element) && (LX + 2 * XS) / TILEX == nx && (LY + 2 * YS) / TILEY == ny) { laser.num_edges--; ScanLaser(); } break; } } void GameWon() { int hi_pos; boolean raise_level = FALSE; #if 0 if (local_player->MovPos) return; local_player->LevelSolved = FALSE; #endif if (game.energy_left) { if (setup.sound_loops) PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP); while(game.energy_left > 0) { if (!setup.sound_loops) PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT); /* if (game.energy_left > 0 && !(game.energy_left % 10)) RaiseScore(level.score[SC_ZEITBONUS]); */ RaiseScore(5); game.energy_left--; if (game.energy_left >= 0) { BlitBitmap(pix[PIX_DOOR], drawto, DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY, ENERGY_XSIZE, ENERGY_YSIZE - game.energy_left, DX_ENERGY, DY_ENERGY); redraw_mask |= REDRAW_DOOR_1; } BackToFront(); Delay(10); } if (setup.sound_loops) StopSound(SND_SIRR); } else if (level.time == 0) /* level without time limit */ { if (setup.sound_loops) PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP); while(TimePlayed < 999) { if (!setup.sound_loops) PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT); if (TimePlayed < 999 && !(TimePlayed % 10)) RaiseScore(level.score[SC_ZEITBONUS]); if (TimePlayed < 900 && !(TimePlayed % 10)) TimePlayed += 10; else TimePlayed++; /* DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW); */ BackToFront(); Delay(10); } if (setup.sound_loops) StopSound(SND_SIRR); } #if 0 FadeSounds(); #endif CloseDoor(DOOR_CLOSE_1); Request("Level solved !", REQ_CONFIRM); if (level_nr == leveldir_current->handicap_level) { leveldir_current->handicap_level++; SaveLevelSetup_SeriesInfo(); } if (level_editor_test_game) game.score = -1; /* no highscore when playing from editor */ else if (level_nr < leveldir_current->last_level) raise_level = TRUE; /* advance to next level */ if ((hi_pos = NewHiScore()) >= 0) { game_status = HALLOFFAME; DrawHallOfFame(hi_pos); if (raise_level) level_nr++; } else { game_status = MAINMENU; if (raise_level) level_nr++; DrawMainMenu(); } BackToFront(); } int NewHiScore() { int k, l; int position = -1; LoadScore(level_nr); if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 || game.score < highscore[MAX_SCORE_ENTRIES - 1].Score) return -1; for (k=0; k highscore[k].Score) { /* player has made it to the hall of fame */ if (k < MAX_SCORE_ENTRIES - 1) { int m = MAX_SCORE_ENTRIES - 1; #ifdef ONE_PER_NAME for (l=k; lk; l--) { strcpy(highscore[l].Name, highscore[l - 1].Name); highscore[l].Score = highscore[l - 1].Score; } } #ifdef ONE_PER_NAME put_into_list: #endif strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN); highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0'; highscore[k].Score = game.score; position = k; break; } #ifdef ONE_PER_NAME else if (!strncmp(setup.player_name, highscore[k].Name, MAX_PLAYER_NAME_LEN)) break; /* player already there with a higher score */ #endif } if (position >= 0) SaveScore(level_nr); return position; } void InitMovingField(int x, int y, int direction) { int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); MovDir[x][y] = direction; MovDir[newx][newy] = direction; if (Feld[newx][newy] == EL_EMPTY) Feld[newx][newy] = EL_BLOCKED; } void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y) { int direction = MovDir[x][y]; int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); *goes_to_x = newx; *goes_to_y = newy; } void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y) { int oldx = x, oldy = y; int direction = MovDir[x][y]; if (direction == MV_LEFT) oldx++; else if (direction == MV_RIGHT) oldx--; else if (direction == MV_UP) oldy++; else if (direction == MV_DOWN) oldy--; *comes_from_x = oldx; *comes_from_y = oldy; } int MovingOrBlocked2Element(int x, int y) { int element = Feld[x][y]; if (element == EL_BLOCKED) { int oldx, oldy; Blocked2Moving(x, y, &oldx, &oldy); return Feld[oldx][oldy]; } else return element; } #if 0 static void RemoveField(int x, int y) { Feld[x][y] = EL_EMPTY; MovPos[x][y] = 0; MovDir[x][y] = 0; MovDelay[x][y] = 0; } #endif void RemoveMovingField(int x, int y) { int oldx = x, oldy = y, newx = x, newy = y; if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y)) return; if (IS_MOVING(x, y)) { Moving2Blocked(x, y, &newx, &newy); if (Feld[newx][newy] != EL_BLOCKED) return; } else if (Feld[x][y] == EL_BLOCKED) { Blocked2Moving(x, y, &oldx, &oldy); if (!IS_MOVING(oldx, oldy)) return; } Feld[oldx][oldy] = EL_EMPTY; Feld[newx][newy] = EL_EMPTY; MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0; MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0; DrawLevelField(oldx, oldy); DrawLevelField(newx, newy); } void PlaySoundLevel(int x, int y, int sound_nr) { int sx = SCREENX(x), sy = SCREENY(y); int volume, stereo; int silence_distance = 8; if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) || (!setup.sound_loops && IS_LOOP_SOUND(sound_nr))) return; if (!IN_LEV_FIELD(x, y) || sx < -silence_distance || sx >= SCR_FIELDX+silence_distance || sy < -silence_distance || sy >= SCR_FIELDY+silence_distance) return; volume = PSND_MAX_VOLUME; #ifndef MSDOS stereo = (sx - SCR_FIELDX/2) * 12; #else stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5; if (stereo > PSND_MAX_RIGHT) stereo = PSND_MAX_RIGHT; if (stereo < PSND_MAX_LEFT) stereo = PSND_MAX_LEFT; #endif if (!IN_SCR_FIELD(sx, sy)) { int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2; int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2; volume -= volume * (dx > dy ? dx : dy) / silence_distance; } PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP); } void RaiseScore(int value) { game.score += value; DrawText(DX_SCORE, DY_SCORE, int2str(game.score, 4), FS_SMALL, FC_YELLOW); } void RaiseScoreElement(int element) { switch(element) { case EL_PACMAN: RaiseScore(level.score[SC_PACMAN]); break; case EL_KEY: RaiseScore(level.score[SC_KEY]); break; default: break; } } /* ---------- new game button stuff ---------------------------------------- */ /* graphic position values for game buttons */ #define GAME_BUTTON1_XSIZE 20 #define GAME_BUTTON1_YSIZE 20 #define GAME_BUTTON2_XSIZE 28 #define GAME_BUTTON2_YSIZE 28 #define GAME_BUTTON1_YPOS 128 #define GAME_BUTTON2_YPOS 124 #define GAME_BUTTON_LEFT_XPOS 8 #define GAME_BUTTON_MIDDLE_XPOS 36 #define GAME_BUTTON_RIGHT_XPOS 72 static struct { int x, y; int gadget_id; char *infotext; } gamebutton_info[NUM_GAME_BUTTONS] = { { GAME_BUTTON_LEFT_XPOS, GAME_BUTTON1_YPOS, GAME_CTRL_ID_LEFT, "left game button" }, { GAME_BUTTON_MIDDLE_XPOS, GAME_BUTTON2_YPOS, GAME_CTRL_ID_MIDDLE, "middle game button" }, { GAME_BUTTON_RIGHT_XPOS, GAME_BUTTON1_YPOS, GAME_CTRL_ID_RIGHT, "right game button" }, }; void CreateGameButtons() { int i; for (i=0; icustom_id; if (game_status != PLAYING) return; switch (id) { case GAME_CTRL_ID_LEFT: if (setup.sound_music) { setup.sound_music = FALSE; FadeMusic(); } break; case GAME_CTRL_ID_MIDDLE: if (game.game_over) { CloseDoor(DOOR_CLOSE_1); game_status = MAINMENU; DrawMainMenu(); break; } if (level_editor_test_game || Request("Do you really want to quit the game ?", REQ_ASK | REQ_STAY_CLOSED)) { game_status = MAINMENU; DrawMainMenu(); } else OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK); break; case GAME_CTRL_ID_RIGHT: if (audio.loops_available) { setup.sound = setup.sound_music = TRUE; if (num_bg_loops) PlayMusic(level_nr % num_bg_loops); } break; default: break; } }