home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / games / volume11 / blockbuster / part01 / balls_pallet.c next >
C/C++ Source or Header  |  1990-08-10  |  16KB  |  603 lines

  1. /*
  2.  * File:       balls_pallet.c
  3.  * Author:     Eric Van Gestel
  4.  *
  5.  * For:                blockbuster
  6.  *
  7.  * Implementation:
  8.  *     Drawing is to be done twice with the same coordinates such that
  9.  *     the second removes the first while restoring the context.
  10.  *     The procedures move_pallet and move_balls are to be called on
  11.  *     mouse and timeout events respectively.
  12.  *     The auxiliary functions return a boolean value indicating whether
  13.  *     the hit might have increased the score.
  14.  */
  15. /*** WORKAROUND for a Horizontal Crosshair ***/
  16.  
  17. #include "blockbuster.h"
  18. #include "bricks.h"
  19.  
  20. short           ball_image[] = {
  21. #include "icons/ball.pr"
  22. };
  23.  
  24. mpr_static( ball_pr, 16, 16, 1, ball_image );
  25.  
  26.  
  27. /* Macro to draw a ball */
  28.  
  29. #define draw_ball( ball )      \
  30.     /* struct Ball *ball */    \
  31.     pw_rop( stage_win, (int)( (ball)->x ) - 8, (int)( (ball)->y ) - 8, 16, 16, \
  32.             PIX_XOR, &ball_pr, 0, 0 )
  33.  
  34.  
  35. /* Procedure to draw the pallet */
  36. void
  37. draw_pallet(  )
  38. {
  39.     pw_vector( stage_win, pallet_xI - pallet_lengthI + 2, pallet_yI,
  40.            pallet_xI + pallet_lengthI - 2, pallet_yI,
  41.            PIX_XOR, 1 );
  42.     pw_vector( stage_win, pallet_xI - pallet_lengthI + 1, pallet_yI + 1,
  43.            pallet_xI + pallet_lengthI - 1, pallet_yI + 1,
  44.            PIX_XOR, 1 );
  45.     pw_vector( stage_win, pallet_xI - pallet_lengthI, pallet_yI + 2,
  46.            pallet_xI + pallet_lengthI, pallet_yI + 2,
  47.            PIX_XOR, 1 );
  48.     pw_vector( stage_win, pallet_xI - 1, pallet_yI + 3,
  49.            pallet_xI - 1, pallet_yI + 6,
  50.            PIX_XOR, 1 );
  51.     pw_vector( stage_win, pallet_xI - 1, pallet_yI + 6,
  52.            pallet_xI + 1, pallet_yI + 6,
  53.            PIX_XOR, 1 );
  54.     pw_vector( stage_win, pallet_xI + 1, pallet_yI + 6,
  55.            pallet_xI + 1, pallet_yI + 3,
  56.            PIX_XOR, 1 );
  57.     pw_vector( stage_win, 0, mouse_yI - 1,    /* <HC> */
  58.            10, mouse_yI - 1,    /* <HC> */
  59.            PIX_XOR, 1 );/* <HC> */
  60.     pw_vector( stage_win, 0, mouse_yI,
  61.            10, mouse_yI,/* <HC> */
  62.            PIX_XOR, 1 );/* <HC> */
  63.     pw_vector( stage_win, 0, mouse_yI + 1,    /* <HC> */
  64.            10, mouse_yI + 1,    /* <HC> */
  65.            PIX_XOR, 1 );/* <HC> */
  66.     pw_vector( stage_win, STAGE_WIDTH_IN_PIXELS - 1, mouse_yI - 1,    /* <HC> */
  67.            STAGE_WIDTH_IN_PIXELS - 11, mouse_yI - 1,    /* <HC> */
  68.            PIX_XOR, 1 );/* <HC> */
  69.     pw_vector( stage_win, STAGE_WIDTH_IN_PIXELS - 1, mouse_yI,
  70.            STAGE_WIDTH_IN_PIXELS - 11, mouse_yI,    /* <HC> */
  71.            PIX_XOR, 1 );/* <HC> */
  72.     pw_vector( stage_win, STAGE_WIDTH_IN_PIXELS - 1, mouse_yI + 1,    /* <HC> */
  73.            STAGE_WIDTH_IN_PIXELS - 11, mouse_yI + 1,    /* <HC> */
  74.            PIX_XOR, 1 );/* <HC> */
  75. }
  76.  
  77.  
  78. /* Procedure to show the speeds */
  79. #define SX     OFFSET_SPEED + 70
  80. #define SY     FONT_HEIGHT
  81. void
  82. show_speeds(  )
  83. {
  84.     int             sp;
  85.  
  86.     msg0( OFFSET_SPEED, "Speed:%10c", ' ' );
  87.  
  88.     /* scale line */
  89.     pw_vector( msg_win, SX, SY - 1, SX + SPEED_RESOLUTION - 1, SY - 1, PIX_SRC, 1 );
  90.     pw_vector( msg_win, SX, SY, SX + SPEED_RESOLUTION, SY, PIX_SRC, 1 );
  91.     pw_vector( msg_win, SX, SY + 1, SX + SPEED_RESOLUTION - 1, SY + 1, PIX_SRC, 1 );
  92.  
  93.     /* base bar */
  94.     pw_vector( msg_win, SX, SY - 12, SX, SY + 3, PIX_SRC, 1 );
  95.     pw_vector( msg_win, SX + 1, SY - 12, SX + 1, SY + 3, PIX_SRC, 1 );
  96.  
  97.     /* launch speed bar */
  98.     sp = ( int ) ( launch_speed * SPEED_RESOLUTION_FACTOR );
  99.     if ( launch_speed < MAX_SPEED )
  100.         pw_vector( msg_win, SX + sp, SY - 2, SX + sp, SY + 2, PIX_XOR, 1 );
  101.     else
  102.         pw_vector( msg_win, SX + sp, SY - 2, SX + sp, SY + 2, PIX_SRC, 1 );
  103.  
  104.     /* ball lines */
  105.     if ( ball1.quadrant ) {
  106.         sp = ( int ) ( ball1.speed * SPEED_RESOLUTION_FACTOR );
  107.         pw_vector( msg_win, SX, SY - 4, SX + sp, SY - 4, PIX_SRC, 1 );
  108.     }
  109.     if ( ball2.quadrant ) {
  110.         sp = ( int ) ( ball2.speed * SPEED_RESOLUTION_FACTOR );
  111.         pw_vector( msg_win, SX, SY - 7, SX + sp, SY - 7, PIX_SRC, 1 );
  112.     }
  113.     if ( ball3.quadrant ) {
  114.         sp = ( int ) ( ball3.speed * SPEED_RESOLUTION_FACTOR );
  115.         pw_vector( msg_win, SX, SY - 10, SX + sp, SY - 10, PIX_SRC, 1 );
  116.     }
  117. }
  118.  
  119.  
  120.  
  121. /* auxiliary procedures */
  122. void
  123. new_ball( ball )
  124.     register struct Ball *ball;
  125. {
  126.     if ( balls_left-- ) {
  127.         ball->quadrant = launch_quadrant;
  128.         ball->angle = 0.0;
  129.         ball->row = launch_row;
  130.         ball->col = launch_col;
  131.         ball->x = launch_x;
  132.         ball->y = launch_y;
  133.         ball->speed = launch_speed;
  134.         ball->x_speed = launch_speed * ( ( ball->quadrant == NE ) ? M_SQRT2_2
  135.                           /* NW */ : -M_SQRT2_2 );
  136.         ball->y_speed = launch_speed * -M_SQRT2_2;
  137.         /* initial ball image */
  138.         draw_ball( ball );
  139.         /* show balls left */
  140.         msg0( OFFSET_BALLS, "Balls left:  %d ", balls_left );
  141.         /* show speeds */
  142.         show_speeds(  );
  143.     } else {
  144.         balls_left = 0;    /* kludge */
  145.         msg( 0, "Game Over." );
  146.         sleep( 2 );
  147.         show_score_board(  );    /* BYE !! */
  148.     }
  149. }
  150.  
  151.  
  152. void
  153. blow_up( row, col )
  154.     register int    row, col;
  155. {
  156.     if ( stage[row][col].code == ' ' )
  157.         return;        /* nothing there */
  158.     if ( IS_HIT_BRICK( stage[row][col].code ) )
  159.         nbricks--;
  160.     stage[row][col].code = 'R';
  161.     draw_brick( row, col );
  162. }
  163.  
  164.  
  165. int                /* boolean */
  166. hit_brick( hit, ball )
  167.     register int    hit;    /* enumeration { HORIZONTAL, VERTICAL } */
  168.     register struct Ball *ball;
  169. {
  170.     register struct Brick *brick = &stage[ball->row][ball->col];
  171.     register int    busted = FALSE;
  172.     register int    redraw = FALSE;
  173.     register int    score_hit = FALSE;
  174.  
  175.     /* has the ball left the stage vertically ? */
  176.     if ( ball->row < 0 || ball->row > MAX_ROW ) {
  177.         ball->quadrant = NO_BALL;    /* so much for this ball */
  178.         return ( score_hit );
  179.     }
  180.     /* check for looping */
  181.     switch ( brick->code ) {
  182.     case ' ':        /* no hit */
  183.         break;
  184.     case '#':
  185.     case '/':
  186.     case '\\':
  187.     case '^':
  188.     case '0':
  189.     case 'A':
  190.     case 'R':
  191.     case 'S':
  192.     case 'U':        /* because it may undo another one */
  193.     case 'W':
  194.     case '%':
  195.         if ( !( ++loop_nhits % LOOP_MAX ) )
  196.             ball->x -=
  197.                 ball->x_speed * ( double ) ( loop_nhits / LOOP_MAX ) + 1;
  198.         /* horizontal shift, trying to get out of a bounce loop */
  199.         /* negative to try to avoid leaving the stage */
  200.         break;
  201.     default:        /* non-solid brick */
  202.         loop_nhits = 0;
  203.     }
  204.  
  205.     /* advance score taking special action if needed */
  206.     switch ( brick->code ) {
  207.     case ' ':        /* clear space */
  208.         /* has the ball left the stage horizontally ? */
  209.         if ( ball->col <= 0 || ball->col >= MAX_COL ) {
  210.             ball->quadrant = NO_BALL;    /* so much for this ball */
  211.         }
  212.         return ( score_hit );    /* no hit */
  213.  
  214.     case '#':        /* solid wall */
  215.     case '/':        /* launchpad NE */
  216.     case '\\':        /* launchpad NW */
  217.     case '^':        /* emitter */
  218.         break;
  219.  
  220.     case '0':        /* solid brick */
  221.         score += score_incr;
  222.         score_hit = TRUE;
  223.         break;
  224.  
  225.     case 'A':        /* absorber */
  226.         ball->x += ( double ) ( emit_col - ball->col ) * 64;
  227.         ball->y += ( double ) ( emit_row - ball->row ) * 16;
  228.         break;
  229.     case 'C':        /* clipper */
  230.         if ( ++( brick->nhits ) == 2 ) {
  231.             pallet_lengthI -= pallet_lengthI / 5;
  232.             if ( pallet_lengthI < MIN_PALLET_LENGTH )
  233.                 pallet_lengthI = MIN_PALLET_LENGTH;
  234.             pallet_length = ( double ) pallet_lengthI;
  235.             busted = TRUE;
  236.         }
  237.         break;
  238.     case 'D':        /* double */
  239.         if ( ++( brick->nhits ) == 2 ) {
  240.             score_incr *= 2;
  241.             busted = TRUE;
  242.         }
  243.         break;
  244.     case 'E':        /* extra ball */
  245.         if ( ++( brick->nhits ) == 2 ) {
  246.             balls_left++;
  247.             msg0( OFFSET_BALLS, "Balls left:  %d ", balls_left );
  248.             busted = TRUE;
  249.         }
  250.         break;
  251.     case 'G':        /* gap */
  252.         if ( ++( brick->nhits ) == 2 ) {
  253.             ball->quadrant = NO_BALL;    /* so much for this ball */
  254.             busted = TRUE;
  255.         }
  256.         break;
  257.     case 'H':        /* halt */
  258.         if ( ++( brick->nhits ) == 3 )
  259.             busted = TRUE;
  260.         {
  261.             double          pause = 0.1 * ( double ) ( 10 - brick->nhits );
  262.  
  263.             ball->speed *= pause;
  264.             ball->x_speed *= pause;
  265.             ball->y_speed *= pause;
  266.         }
  267.         /* approximative; will be corrected on next pallet deflection */
  268.         show_speeds(  );
  269.         break;
  270.     case 'I':        /* invisible brick */
  271.         score += score_incr;
  272.         brick->code = '1';
  273.         nbricks++;
  274.         score_hit = redraw = TRUE;
  275.         break;
  276.     case 'L':        /* launch ball */
  277.         if ( ++( brick->nhits ) == 2 ) {
  278.             balls_left++;    /* kludge to avoid consuming a ball */
  279.             if ( !ball1.quadrant )
  280.                 new_ball( &ball1 );
  281.             else if ( !ball2.quadrant )
  282.                 new_ball( &ball2 );
  283.             else if ( !ball3.quadrant )
  284.                 new_ball( &ball3 );
  285.             else
  286.                 msg0( OFFSET_BALLS, "Balls left:  %d ", balls_left );
  287.             show_speeds(  );
  288.             busted = TRUE;
  289.         }
  290.         break;
  291.     case 'M':        /* mine */
  292.         if ( ++( brick->nhits ) == 3 ) {
  293.             blow_up( ball->row - 1, ball->col - 1 );
  294.             blow_up( ball->row - 1, ball->col );
  295.             blow_up( ball->row - 1, ball->col + 1 );
  296.             blow_up( ball->row, ball->col - 1 );
  297.             blow_up( ball->row, ball->col + 1 );
  298.             blow_up( ball->row + 1, ball->col - 1 );
  299.             blow_up( ball->row + 1, ball->col );
  300.             blow_up( ball->row + 1, ball->col + 1 );
  301.             busted = TRUE;
  302.         }
  303.         break;
  304.     case 'P':        /* pause */
  305.         if ( ++( brick->nhits ) == 8 ) {
  306.             launch_speed -= ( launch_speed - INIT_SPEED ) * 0.3;
  307.             busted = TRUE;
  308.         }
  309.         show_speeds(  );
  310.         break;
  311.     case 'R':        /* refractor */
  312.         ball->angle = -( ball->angle );
  313.         {
  314.             register int    sign = ( ball->x_speed * ball->y_speed ) < 0;
  315.             register double tmp = ball->x_speed;
  316.  
  317.             ball->x_speed = sign ? -( ball->y_speed ) : ball->y_speed;
  318.             ball->y_speed = sign ? -tmp : tmp;
  319.             /*
  320.              * note no check for NEAR_HORIZONTAL and none needed
  321.              * since,
  322.              */
  323.             /*
  324.              * if it gets too horizontal, it probably will hit it
  325.              * again.
  326.              */
  327.         }
  328.         return ( FALSE );    /* no deflection */
  329.     case 'S':        /* speeder */
  330.         if ( ball->speed < SPEED_LIMIT ) {
  331.             ball->speed += SPEED_INCR;
  332.             ball->x_speed += ( ball->x_speed < 0 ) ? -SPEED_INCR_2
  333.                 : SPEED_INCR_2;
  334.             ball->y_speed += ( ball->y_speed < 0 ) ? -SPEED_INCR_2
  335.                 : SPEED_INCR_2;
  336.             /*
  337.              * approximative; will be corrected on next pallet
  338.              * deflection
  339.              */
  340.             show_speeds(  );
  341.         } else
  342.             pallet_modif++;
  343.         break;
  344.     case 'T':        /* triple */
  345.         if ( ++( brick->nhits ) == 3 ) {
  346.             score_incr *= 3;
  347.             busted = TRUE;
  348.         }
  349.         break;
  350.     case 'U': /* undo */ ;
  351.         /* effective only after something has been busted */
  352.         if ( last_busted_brick ) {
  353.             last_busted_brick->code = last_busted_code;
  354.             last_busted_brick->nhits = 0;
  355.             if ( IS_HIT_BRICK( last_busted_code ) )
  356.                 nbricks++;
  357.             draw_brick( last_busted_row, last_busted_col );
  358.             busted = TRUE;
  359.         }
  360.         break;
  361.     case 'W': /* open window */ ;
  362.         brick->code = '%';
  363.          /* redraw = TRUE */ draw_brick( ball->row, ball->col );
  364.         return ( score_hit );    /* no deflection */
  365.     case '%': /* closed window */ ;
  366.         brick->code = 'W';
  367.         redraw = TRUE;
  368.         break;
  369.     case 'X':        /* expander */
  370.         if ( ++( brick->nhits ) == 4 ) {
  371.             pallet_modif -= 2 * PALLET_INCR;
  372.             busted = TRUE;
  373.         }
  374.         break;
  375.  
  376.     default:
  377.         if ( brick->code >= '1' && brick->code <= '9' ) {
  378.             /* hit bricks */
  379.             score += ++( brick->nhits ) * score_incr;
  380.             score_hit = TRUE;
  381.             if ( brick->nhits == brick->code - '0' )
  382.                 busted = TRUE;
  383.             else
  384.                 redraw = TRUE;
  385.         } else {    /* 'a' .. 'e' & 'j' */
  386.             /* bonus bricks */
  387.             if ( ++( brick->nhits ) > brick->code - 'a' + 1 ) {
  388.                 score += ( brick->code - 'a' + 1 ) * 10 * score_incr;
  389.                 score_hit = busted = TRUE;
  390.             }
  391.         }
  392.     }
  393.     if ( busted ) {
  394.         last_busted_brick = brick;
  395.         last_busted_code = brick->code;
  396.         last_busted_row = ball->row;
  397.         last_busted_col = ball->col;
  398.         if ( IS_HIT_BRICK( brick->code ) )
  399.             nbricks--;
  400.         brick->code = ' ';
  401.         redraw = TRUE;
  402.     }
  403.     /* redraw brick (never on the sides) */
  404.     if ( redraw ) {
  405.         if ( pallet_row == ball->row )
  406.             draw_pallet(  );    /* avoid shadow */
  407.         draw_brick( ball->row, ball->col );
  408.         if ( pallet_row == ball->row )
  409.             draw_pallet(  );    /* restore */
  410.     }
  411.     /* deflection */
  412.     if ( ball->col <= 0 || ball->col >= MAX_COL ) {
  413.         /*
  414.          * kludge to avoid tunnelling out through the side (or
  415.          * corner)
  416.          */
  417.         if ( ( ball->col <= 0 &&
  418.                ( ball->quadrant == NW || ball->quadrant == SW ) ) ||
  419.              ( ball->col >= MAX_COL &&
  420.                ( ball->quadrant == NE || ball->quadrant == SE ) ) )
  421.             brick_deflection( VERTICAL, ball );
  422.         if ( ( ball->row == 0 &&
  423.                ( ball->quadrant == NE || ball->quadrant == NW ) ) ||
  424.              ( ball->row == MAX_ROW &&
  425.                ( ball->quadrant == SE || ball->quadrant == SW ) ) )
  426.             brick_deflection( HORIZONTAL, ball );
  427.     } else
  428.         brick_deflection( hit, ball );
  429.  
  430.     return ( score_hit );
  431. }
  432.  
  433. int                /* boolean */
  434. move_ball( ball )
  435.     register struct Ball *ball;
  436. {
  437.     register int    tmp;    /* tmp row or col designation */
  438.     register int    hit = FALSE;    /* enumeration { FALSE, HORIZONTAL,
  439.                      * VERTICAL } */
  440.     register int    score_hit;    /* boolean */
  441.  
  442.     /* erase ball image */
  443.     draw_ball( ball );
  444.  
  445.     /* move ball */
  446.     ball->x += ball->x_speed;
  447.     ball->y += ball->y_speed;
  448.  
  449.     /* might it have hit a brick ? */
  450.     if ( ( tmp = X_COL( ball->x ) ) != ball->col ) {
  451.         ball->col = tmp;
  452.         hit = VERTICAL;
  453.     }
  454.     if ( ( tmp = Y_ROW( ball->y ) ) != ball->row ) {
  455.         ball->row = tmp;
  456.         hit = HORIZONTAL;    /* HORIZONTAL takes precedence over
  457.                      * VERTICAL */
  458.     }
  459.     if ( hit )
  460.         score_hit = hit_brick( hit, ball );
  461.     if ( !ball->quadrant ) {
  462.         /* so much for this ball */
  463.         show_speeds(  );
  464.         return ( score_hit );
  465.     }
  466.     /* might it have hit the pallet ? */
  467.     if ( ball->y >= pallet_y - 0.1 &&    /* round of protection */
  468.          ball->y <= pallet_y + ball->y_speed &&
  469.          ball->x >= pallet_x - pallet_length &&
  470.          ball->x <= pallet_x + pallet_length ) {
  471.         loop_nhits = 0;
  472.         pallet_deflection( ball );
  473.     }
  474.     /* redraw ball image */
  475.     draw_ball( ball );
  476.  
  477.     return ( score_hit );
  478. }
  479.  
  480.  
  481. /*** on timeout event ***/
  482. Notify_value
  483. move_balls( frm, which_itimer )
  484.     Frame           frm;    /* ignored */
  485.     int             which_itimer;    /* enumeration { ITIMER_REAL } **
  486.                      * ignored */
  487. {
  488.     register int    score_hit1 = FALSE, score_hit2 = FALSE, score_hit3 = FALSE;
  489.  
  490.     /* halt cycle */
  491.     stop_timer(  );
  492.  
  493.     /* start new ball if none left */
  494.     if ( !ball1.quadrant && !ball2.quadrant && !ball3.quadrant )
  495.         new_ball( &ball1 );
  496.  
  497.     /* move balls */
  498.     if ( ball1.quadrant )
  499.         score_hit1 = move_ball( &ball1 );
  500.     if ( ball2.quadrant )
  501.         score_hit2 = move_ball( &ball2 );
  502.     if ( ball3.quadrant )
  503.         score_hit3 = move_ball( &ball3 );
  504.  
  505.     /* start new stage if no more bricks to bust */
  506.     if ( nbricks <= 0 ) {
  507.  
  508.         /* add stage bonus */
  509.         score += 100;
  510.         msg( 0, "Stage bonus: 100" );
  511.  
  512.         /* erase pallet image */
  513.         draw_pallet(  );
  514.  
  515.         /* erase ball images */
  516.         if ( ball1.quadrant ) {
  517.             ball1.quadrant = NO_BALL;
  518.             balls_left++;    /* kludge to avoid consuming the ball */
  519.             draw_ball( &ball1 );
  520.         }
  521.         if ( ball2.quadrant ) {
  522.             ball2.quadrant = NO_BALL;
  523.             balls_left++;    /* kludge to avoid consuming the ball */
  524.             draw_ball( &ball2 );
  525.         }
  526.         if ( ball3.quadrant ) {
  527.             ball3.quadrant = NO_BALL;
  528.             balls_left++;    /* kludge to avoid consuming the ball */
  529.             draw_ball( &ball3 );
  530.         }
  531.         /* update score */
  532.         msg0( OFFSET_SCORE, "Score:  %d ", score );
  533.  
  534.         /* off we go again */
  535.         new_stage(  );
  536.  
  537.     } else {
  538.  
  539.         /* update score */
  540.         if ( score_hit1 || score_hit2 || score_hit3 )
  541.             msg0( OFFSET_SCORE, "Score:  %d ", score );
  542.  
  543.         /* next cycle */
  544.         start_timer(  );
  545.     }
  546.  
  547.     return ( NOTIFY_DONE );
  548. }
  549.  
  550.  
  551. /*** on mouse events ***/
  552. int
  553. move_pallet( cvs, event, arg )
  554.     Canvas          cvs;    /* ignored */
  555.     Event          *event;
  556.     caddr_t         arg;    /* ignored */
  557. {
  558.     register int    tmp;
  559.  
  560.     /* erase pallet image */
  561.     draw_pallet(  );
  562.  
  563.     switch ( event_id( event ) ) {
  564.     case MS_LEFT:
  565.         if ( pallet_yI > PALLET_MIN_Y ) {
  566.             pallet_y = ( double ) ( pallet_yI -= 16 );
  567.             pallet_row--;
  568.         }
  569.         break;
  570.     case MS_MIDDLE:
  571.         pallet_y = ( double ) ( pallet_yI = PALLET_MAX_Y + 4 );
  572.         pallet_row = MAX_ROW - 1;
  573.         break;
  574.     case MS_RIGHT:
  575.         /* clear msg */
  576.         for ( tmp = 0; tmp < MSG_HEIGHT; tmp++ )
  577.             pw_vector( msg_win, 0, tmp, STAGE_WIDTH_IN_PIXELS - 1, tmp,
  578.                    PIX_CLR, 1 );
  579.         /* redo all messages */
  580.         msg1( OFFSET_SPEED, stage_name );
  581.         msg0( OFFSET_BALLS, "Balls left:  %d ", balls_left );
  582.         msg0( OFFSET_SCORE, "Score:  %d ", score );
  583.         show_speeds(  );
  584.         /* lets go ! */
  585.         move_balls(  );    /* starts the timer */
  586.         break;
  587.     case KEY_RIGHT( 1 ):
  588.         if ( !ball1.quadrant && !ball2.quadrant && !ball3.quadrant ) {
  589.             msg0( OFFSET_BALLS, "Saving...%46c", ' ' );
  590.             save(  );    /* BYE !! */
  591.         } else
  592.             msg0( OFFSET_BALLS, "Not saved...%43c", ' ' );
  593.         break;
  594.     default:        /* LOC_MOVE, LOC_STILL */
  595.         /* move pallet */
  596.         pallet_x = ( double ) ( pallet_xI = event_x( event ) );
  597.         mouse_yI = event_y( event );    /* <HC> */
  598.     }
  599.  
  600.     /* redraw pallet image */
  601.     draw_pallet(  );
  602. }
  603.