⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tetrislib.c

📁 compiled for AtMega32 The source, to compile with AVR-G
💻 C
📖 第 1 页 / 共 2 页
字号:
  unsigned char i;
  unsigned char moving = 0;
  
  // First, check if bricks are currently moving in animation
  for (i=0; i<active_bricks; i++) {
    if (active_brick[i].x & 0xF0) {
      active_brick[i].x -= 1 << 4;
      moving = 1;
    }
  }
  
  // Test all bricks if they are able to move one cube down
  if (!moving) {
    for (i=0; i<active_bricks; i++) {
      if (active_brick[i].y > 0) {
        // Remove brick from occupancy grid
        occupancy_xor_brick(&active_brick[i]);
        // Test if brick is ok moving one cube down
        active_brick[i].y--;
        if (test_if_brick_fits(&active_brick[i])) {
          // brick did fit, keep new y coordinate
          // add brick in occupancy grid at new position 
          occupancy_xor_brick(&active_brick[i]);
          // Start animation
          active_brick[i].x |= 10 << 4 ;
          moving = 1;
        }
        else {
          // didn't fit, restore old y coordinate
          active_brick[i].y++;
          // add brick at old position
          occupancy_xor_brick(&active_brick[i]);          
        }
      }
    }
  }
  
  return moving;
}

// Zap row with coordinate y. 
// Changes both active_brick array and occupancy grid
void zap_row(signed char y)
{
  unsigned char i;
  signed char dy;
  struct Brick_Type tmp1,tmp2;
  
  for (i=0; i<active_bricks; i++) {
    dy = y - active_brick[i].y;
    if ((dy >=0) && (dy < 4)) {
      // this brick must be split
      split_brick(&tmp1, &tmp2, &active_brick[i], dy);
      if (fix_brick(&tmp1)) {
        // lower brick is ok, copy this brick on top of original
        copy_brick(&active_brick[i], &tmp1);
        
        if (fix_brick(&tmp2)) {
          // higher brick is also ok, add this brick to end of array
          new_brick(&tmp2);
        } 
      }
      else if (fix_brick(&tmp2)) {
        // only higher brick is ok, copy this brick on top of original 
        copy_brick(&active_brick[i], &tmp2);
      }
      else {
        // nothing left of the brick!
        // delete entry in array and re-check it
        delete_brick(i);
        i--;
      }
    }
  }
  
  // The effect on occupancy grid is to remove one row completely
  occupancy[y] = OCCUPANCY_FREE;
  
}

// returns coordinate of row to zap, or 0xFF if no row to zap
//
unsigned char find_zap_row(void)
{
  unsigned char i;
  
  for (i=0; i<MATRIX_HEIGHT; i++) {
    if (occupancy[i] == OCCUPANCY_NONE) 
      return i;
  }
  
  return 0xFF;
  
}

////////////////////////////////////////////////////////////////////////////////
// GRAPHICS RENDERING
////////////////////////////////////////////////////////////////////////////////

unsigned char two_powers[4] = {1,2,4,8};

unsigned char inline brick_cube(struct Brick_Type *brick, 
                                  signed char x, signed char y)
{
//if ((x>=0) && (x<4) && (y>=0) && (y<4))  // below is equivalent
  if (((unsigned char)x<4) && ((unsigned char)y<4))
    return brick->brick[y] & two_powers[x];
  else
    return 0;
}


// This macro convinces compiler that its ok to use unsigned 8 bit
#define UCHAR_MUL(x,y) ((unsigned char)((unsigned char)(x)*(unsigned char)(y)))

//
// Draws the brick by tracing the edge all the way around.
// The brick position is basically defined by its x and y coordinate, but it 
// can be offset a number of pixels by y_offset and high nibble of x coordinate 
// 
// y_offset = 0xFF is a special case for drawing the pending brick left of the
// playground.
void draw_brick(struct Brick_Type *brick, unsigned char y_offset)
{
  signed char dx, dy;
  signed char x, y, xs;
  unsigned char tmp;
  
  // Find leftmost brick in lower row
  tmp = brick->brick[0];
  
  for (xs=0; xs<4; xs++) {
    if (tmp & two_powers[xs])
      break;
  }
  
  // Start pen
  if (y_offset == 0xFF) {
    // this special case is used for drawing the pending brick on the left
    draw_set_xy(UCHAR_MUL(xs, BRICK_SIZE) + 8 + BRICK_INSET, 180);    
  }
  else {
    x = (brick->x & 0x0F) + xs;
    y_offset += brick->x >> 4;
    y = brick->y;
    
    draw_set_xy(UCHAR_MUL(x, BRICK_SIZE) + X_OFFSET + BRICK_INSET, 
                UCHAR_MUL(y, BRICK_SIZE) + Y_OFFSET + BRICK_INSET+y_offset);
  }
              
  // Trace brick edge, start seeking right
  x = xs;
  y = 0;
  dx = 1;
  dy = 0;
  
  do {
    if (brick_cube(brick, x+dy, y-dx)) {
      // turn downwards
      tmp = -dx;
      dx = dy;
      dy = tmp;
      
      // move inset*2
      draw_pen_dxdy(dx, dy, BRICK_INSET * 2);
      
      // one pixel down the new direction
      x += dx;
      y += dy;
    }
    else if (brick_cube(brick, x+dx, y+dy)) {
      // Continue one pixel this direction
      draw_pen_dxdy(dx, dy, BRICK_SIZE);
      
      // update pixelpos
      x += dx;
      y += dy;
    }
    else {
      // stay in this pixel and turn upwards
      draw_pen_dxdy(dx, dy, BRICK_SIZE - BRICK_INSET * 2);
      
      tmp = dx;
      dx = -dy;
      dy = tmp;
      
    }
    
    // check if we are back facing right on start pixel
  } while (!((dx == 1) && (dy == 0) && (y == 0) && (x == xs)) );
  
}

// This function draws the playground square
void draw_playground(void)
{
  unsigned char x1,x2,y1,y2;
  
  x1 = X_OFFSET-1;
  x2 = X_OFFSET + BRICK_SIZE*MATRIX_WIDTH;
  y1 = Y_OFFSET-1;
  y2 = Y_OFFSET + BRICK_SIZE*MATRIX_HEIGHT;
  
  draw_set_xy(x1, y2);   
  draw_pen_dx(2,  (x2-x1)>>1);   // draw the playground square extra fast
  draw_pen_dy(-2, (y2-y1)>>1);
  draw_pen_dx(-2, (x2-x1)>>1);   
  draw_pen_dy(2,  (y2-y1)>>1);

}


void tetris_draw_frame(void)
{
  unsigned char i;

  DRAW_PIXEL_SPEED = 20;
  
  for (i=0; i<active_bricks; i++) {
    draw_brick(&active_brick[i],0);
  }
  
  if (graphics_status & SHOW_FALLING_BRICK) {
    draw_brick(&falling_brick, (falling_pixels & 0xFF00) >> 8);
  }
    
  if (graphics_status & SHOW_ZAPPED_ROWS) {
    DRAW_PIXEL_SPEED = 50;
    for (i=0; i<MATRIX_HEIGHT; i++) {
      if (occupancy[i] & 0x8000) {
        draw_box(X_OFFSET + 3, 
          Y_OFFSET + 3 + UCHAR_MUL(i, BRICK_SIZE),
          X_OFFSET + MATRIX_WIDTH*BRICK_SIZE - BRICK_INSET,
          Y_OFFSET + BRICK_SIZE - 3 + UCHAR_MUL(i, BRICK_SIZE));
      } 
    }
    DRAW_PIXEL_SPEED = 20;
  }

  draw_brick(&pending_brick, 0xFF);  

  if (graphics_status & SHOW_ZAP_COUNT) {
    vfont_number(X_OFFSET+MATRIX_WIDTH*0.5*BRICK_SIZE - (animation_frame>>2), 
                 Y_OFFSET+MATRIX_HEIGHT*0.75*BRICK_SIZE - (animation_frame>>1), 
                 animation_frame>>2, lines_zapped);
    if (--animation_frame == 5) {
      graphics_status &= ~SHOW_ZAP_COUNT;
    }
  }
  
  vfont_number(10,155,4,tetris_stats_lines);
  vfont_number(10,135,4,tetris_stats_zapcount);
  
  DRAW_PIXEL_SPEED = 10;
  draw_playground();
  
}

////////////////////////////////////////////////////////////////////////////////
// GAME PLAY IMPLEMENTATION
////////////////////////////////////////////////////////////////////////////////


// alter the falling brick
void tetris_modify_brick(unsigned char dx, unsigned char rot)
{
  struct Brick_Type tmp;

  if (dx) {
    // try to move brick and see if its ok
    falling_brick.x += dx;
    if (!test_if_brick_fits(&falling_brick)) {
      // not ok, move back
      falling_brick.x -= dx;
    }
    
  }
  
  if (rot) {
    // try rotate and see if its ok
    rotate_brick(&tmp, &falling_brick, 0);
    
    if (test_if_brick_fits(&tmp)) {
      // its ok!
      copy_brick(&falling_brick, &tmp);
    }
    else {
      // Not ok, this might be because brick is hitting right edge
      // try moving brick left
      if (tmp.x>2) {
        if (tmp.x--, test_if_brick_fits(&tmp)) {
          copy_brick(&falling_brick, &tmp);
        }
        else if (tmp.x--, test_if_brick_fits(&tmp)) {
          copy_brick(&falling_brick, &tmp);
        }
        else if (tmp.x--, test_if_brick_fits(&tmp)) {
          copy_brick(&falling_brick, &tmp);
        }        
      }
    }
  }
  
}

void get_new_brick(void)
{
  unsigned char c;
  
  // Promote pending brick to falling brick
  copy_brick(&falling_brick, &pending_brick);
  
  do {
    c = rand() & 7;
  } while (c == 7);
 
  // Make new pending brick from template
  pending_brick.brick[0] = pgm_read_byte_near(&brick_template[c][0]);
  pending_brick.brick[1] = pgm_read_byte_near(&brick_template[c][1]);
  pending_brick.brick[2] = 0;
  pending_brick.brick[3] = 0;
  pending_brick.x = MATRIX_WIDTH/2-1;
  pending_brick.y = MATRIX_HEIGHT-2; 
  
}

// Game statemachine implementation
void tetris_tick(void)
{
  unsigned char c;
  
  if (tetris_state == TETRIS_NEW_BRICK) {
    // add new falling brick
    get_new_brick();
    falling_pixels = BRICK_SIZE<<8;  // reset animation  
    
    if (test_if_brick_fits(&falling_brick)) {
      graphics_status |= SHOW_FALLING_BRICK;
      // go to next state
      tetris_state = TETRIS_BRICK_FALLING; 
    }
    else {
      // brick doesn't fit... game over
      tetris_state = TETRIS_GAME_OVER;
    }
     
  }
  else if (tetris_state == TETRIS_BRICK_FALLING) {
    
    if (tetris_mode & TETRIS_MODE_SPEEDUP) 
      falling_pixels -= ((unsigned short)SPEEDUP_SPEED) << 2;
    else
      falling_pixels -= ((unsigned short)tetris_speed) << 2;
    
      
    if (falling_pixels < 0) {
      falling_pixels += BRICK_SIZE<<8;
      
      // move brick down one step
      falling_brick.y--;
    
      if ((falling_brick.y>=0) && test_if_brick_fits(&falling_brick)) {
        // still fits 
      }
      else {
        // it hit the ground, add brick from previous position
        graphics_status &= ~SHOW_FALLING_BRICK;
        falling_brick.y++;
        new_brick(&falling_brick);
        // update occupancy grid
        occupancy_xor_brick(&falling_brick);
   
        lines_zapped = 0;
        
        // test zapping
        tetris_state = TETRIS_TRY_ZAP;
        
      }  
    }  
  }
  else if (tetris_state == TETRIS_TRY_ZAP) {
    
    // we need a new brick...
    tetris_state = TETRIS_NEW_BRICK;
    
    // ...unless there are lines to zap
    while ((c=find_zap_row()) != 0xFF) {
      zap_row(c);
      occupancy[c] |= 0x8000; // use MSB in occupancy to indicate zapped row
      lines_zapped++;
      tetris_state = TETRIS_SHOW_ZAPPED;
      animation_frame = 20;
    }
    
    if ((tetris_state == TETRIS_NEW_BRICK) && (lines_zapped > 0)) {
      // We are done zapping, update score
      tetris_stats_lines += lines_zapped;
      tetris_stats_zapcount++;
      
      // Brag with result if its good enough
      if (lines_zapped > 3) {
        graphics_status |= SHOW_ZAP_COUNT; 
        animation_frame = 100;
      }
    }
    
  }
  else if (tetris_state == TETRIS_DO_GRAVITY) {
    
    if (tetris_mode & TETRIS_MODE_GRAVITY) 
      c = apply_gravity();
    else      
      c = apply_naive_gravity();
      
    if (c)
      tetris_state = TETRIS_DO_GRAVITY; // something still moving
    else  
      tetris_state = TETRIS_TRY_ZAP;    // try zapping again
    
  }
  else if (tetris_state == TETRIS_SHOW_ZAPPED) {
    animation_frame--;
    
    if (animation_frame == 0) {
        // Animation is done, remove MSB in occupancy grid
        calc_occupancy_grid();
        // Apply gravity
        tetris_state = TETRIS_DO_GRAVITY;
        graphics_status &= ~SHOW_ZAPPED_ROWS;
    } 
    else {
      // Show zapped rows
      graphics_status |= SHOW_ZAPPED_ROWS;
    }
    
  }
  
}

//
// Must be called with 100 Hz
//
void tetris_handler(void)
{
  // draw frame
  tetris_draw_frame();
  
  // update game
  tetris_tick();
  
}

// Must be called at startup
void tetris_init(void)
{
  active_bricks = 0; 
  graphics_status = 0;

  tetris_stats_lines = 0;
  tetris_stats_zapcount = 0;
  
  tetris_state = TETRIS_NEW_BRICK;
  tetris_mode = 0;
  tetris_speed = 16;
  calc_occupancy_grid();
  get_new_brick();
}


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -