// Play3D.java // Part of Tetris3D.java, V1.0 // Sunday, Oct. 29, 2000, by Yamin Li, yamin@ieee.org // Free to use, modify, and distribute. import java.awt.*; class Play3D extends Canvas { // // | // +------+------+ // | | // O-----+-------------+-------> Z // eye | | // +------+------+ // 5 | 5 5 // view // plane // private static final int rows = 5; private static final int cols = 5; private static final int deps = 10; private static final int ssize = 30; private static final int lsize = 90; // = ssize * 3; focus = deps/2 = 5 private static final int window = 450; // = lsize * 5 private static final int half = 225; // = window / 2 private static final int thirdth = 150; // = window / 3 private static final Color mark = Color.lightGray; private static final Color shapeColor[] = { Color.green, Color.blue, Color.red, Color.magenta, Color.pink, Color.orange, Color.cyan, Color.yellow, Color.green, Color.red}; private static final int empty = 0; private static final int occupied = 1; private static final int moving = -1; private boolean leftOutSide = false, rightOutSide = false; private boolean upOutSide = false, downOutSide = false, frontOutSide = false; private boolean noPlace = false, drop = false, gameOver = false; private int score = 0, level = 0, layers = 0, delay = 2000, height = 0; private int shapeType = 0; private int tryPositionX = 2, tryPositionY = 1, tryPositionZ = 0; private int tryX[], tryY[], tryZ[]; private int currentPositionX = 2, currentPositionY = 1, currentPositionZ = 0; private int currentX[], currentY[], currentZ[]; private int tryStateX[], tryStateY[], tryStateZ[]; private int currentStateX[], currentStateY[], currentStateZ[]; private Image displayBuffer; private Graphics drawingBuffer; private int playArea[][][]; private Color myColor; private int rectPoint[]; private int rectLength[]; private int px[]; private int py[]; public void init(){ rectPoint = new int[deps + 1]; rectLength = new int[deps + 1]; for (int i = 0; i <= deps; i++){ rectPoint[i] = half - half * window / (window + lsize * i); rectLength[i] = window * window / (window + lsize * i); } setSize(window + 100, window + 30); displayBuffer = createImage(window + 100, window + 30); drawingBuffer = displayBuffer.getGraphics(); playArea = new int[cols][rows][deps + 1]; // XYZ tryX = new int[Shape3D.pieces]; tryY = new int[Shape3D.pieces]; tryZ = new int[Shape3D.pieces]; currentX = new int[Shape3D.pieces]; currentY = new int[Shape3D.pieces]; currentZ = new int[Shape3D.pieces]; tryStateX = new int[Shape3D.pieces]; tryStateY = new int[Shape3D.pieces]; tryStateZ = new int[Shape3D.pieces]; currentStateX = new int[Shape3D.pieces]; currentStateY = new int[Shape3D.pieces]; currentStateZ = new int[Shape3D.pieces]; drop = false; score = 0; level = 0; layers = 0; height = 0; clearPlayArea(); myColor = new Color(0x1f, 0xbf, 0xaf); px = new int[4]; py = new int[4]; generateNewBlock(); } public void paint(Graphics g){ drawBack(); drawBlocks(); drawMsg(); g.drawImage(displayBuffer, 0, 0, this); } public void drawBack(){ int i; displayBuffer = createImage(window + 100, window + 30); drawingBuffer = displayBuffer.getGraphics(); drawingBuffer.setColor(Color.darkGray); drawingBuffer.fillRect(0, 0, window + 1, window + 1); drawingBuffer.setColor(mark); for (i = 1; i < rows; i++){ drawingBuffer.drawLine(thirdth, thirdth + ssize * i, thirdth + thirdth, thirdth + ssize * i); drawingBuffer.drawLine(thirdth + ssize * i, thirdth, thirdth + ssize * i, thirdth + thirdth); } for (i = 0; i < rows; i++){ drawingBuffer.drawLine(lsize * i, 0, thirdth + ssize * i, thirdth); //up lines drawingBuffer.drawLine(window, lsize * i, window - thirdth, thirdth + ssize * i); //right lines drawingBuffer.drawLine(window - lsize * i, window, window - (thirdth + ssize * i), window - thirdth); //buttom lines drawingBuffer.drawLine(0, window - lsize * i, thirdth, window - (thirdth + ssize * i)); //left lines } for (i = 0; i <= deps; i++){ // rects drawingBuffer.drawRect(rectPoint[i], rectPoint[i], rectLength[i], rectLength[i]); } } public void drawBlocks(){ int i, j, k; for (k = deps - 1; k >= 0; k--){ for (i = 0; i < cols; i++){ for (j = 0; j < rows; j++){ if (playArea[i][j][k] != empty){ if (i > 2) if (playArea[i - 1][j][k] == empty) drawLeft(i, j, k); if (i < 2) if (playArea[i + 1][j][k] == empty) drawRight(i, j, k); if (j > 2) if (playArea[i][j - 1][k] == empty) drawTop(i, j, k); if (j < 2) if (playArea[i][j + 1][k] == empty) drawBottom(i, j, k); } } } for (i = 0; i < cols; i++){ for (j = 0; j < rows; j++){ if (playArea[i][j][k] != empty){ if (k == 0) drawFront(i, j, k); else if (playArea[i][j][k - 1] == empty) drawFront(i, j, k); } } } } } public void drawMsg(){ drawingBuffer.setColor(myColor); drawingBuffer.fillRect(window, 0, 100, window); drawingBuffer.fillRect(0, window, window + 100, 30); drawingBuffer.setColor(Color.yellow); drawingBuffer.setFont(new Font("Sansserif", Font.BOLD, 18)); drawingBuffer.drawString("TETRIS3D", window + 8, 30); drawingBuffer.setColor(Color.blue); drawingBuffer.setFont(new Font("Sansserif", Font.BOLD, 12)); drawingBuffer.drawString("I,J,K,L: Shift", window + 8, 80); drawingBuffer.drawString("F,T,H: Rotate", window + 8, 100); drawingBuffer.drawString("SPACE: Drop", window + 8, 120); drawingBuffer.setFont(new Font("Sansserif", Font.BOLD, 14)); drawingBuffer.drawString("LEVEL: " + Integer.toString(level), window + 8, window - 80); drawingBuffer.drawString("LINES: " + Integer.toString(layers), window + 8, window - 60); drawingBuffer.drawString("SCORE: " + Integer.toString(score), window + 8, window - 40); for (int h = 1; h <= height; h++){ drawingBuffer.setColor(shapeColor[h - 1]); drawingBuffer.fill3DRect((h - 1) * 45, window + 5, 40, 20, true); } } public void drawLeft(int i, int j, int k){ px[0] = half - (half - (i + 0) * lsize) * window / (window + (k + 0) * lsize); py[0] = half - (half - (j + 0) * lsize) * window / (window + (k + 0) * lsize); px[1] = half - (half - (i + 0) * lsize) * window / (window + (k + 1) * lsize); py[1] = half - (half - (j + 0) * lsize) * window / (window + (k + 1) * lsize); px[2] = half - (half - (i + 0) * lsize) * window / (window + (k + 1) * lsize); py[2] = half - (half - (j + 1) * lsize) * window / (window + (k + 1) * lsize); px[3] = half - (half - (i + 0) * lsize) * window / (window + (k + 0) * lsize); py[3] = half - (half - (j + 1) * lsize) * window / (window + (k + 0) * lsize); drawSurface(i, j, k); } public void drawRight(int i, int j, int k){ px[0] = half - (half - (i + 1) * lsize) * window / (window + (k + 1) * lsize); py[0] = half - (half - (j + 0) * lsize) * window / (window + (k + 1) * lsize); px[1] = half - (half - (i + 1) * lsize) * window / (window + (k + 0) * lsize); py[1] = half - (half - (j + 0) * lsize) * window / (window + (k + 0) * lsize); px[2] = half - (half - (i + 1) * lsize) * window / (window + (k + 0) * lsize); py[2] = half - (half - (j + 1) * lsize) * window / (window + (k + 0) * lsize); px[3] = half - (half - (i + 1) * lsize) * window / (window + (k + 1) * lsize); py[3] = half - (half - (j + 1) * lsize) * window / (window + (k + 1) * lsize); drawSurface(i, j, k); } public void drawTop(int i, int j, int k){ px[0] = half - (half - (i + 0) * lsize) * window / (window + (k + 0) * lsize); py[0] = half - (half - (j + 0) * lsize) * window / (window + (k + 0) * lsize); px[1] = half - (half - (i + 1) * lsize) * window / (window + (k + 0) * lsize); py[1] = half - (half - (j + 0) * lsize) * window / (window + (k + 0) * lsize); px[2] = half - (half - (i + 1) * lsize) * window / (window + (k + 1) * lsize); py[2] = half - (half - (j + 0) * lsize) * window / (window + (k + 1) * lsize); px[3] = half - (half - (i + 0) * lsize) * window / (window + (k + 1) * lsize); py[3] = half - (half - (j + 0) * lsize) * window / (window + (k + 1) * lsize); drawSurface(i, j, k); } public void drawBottom(int i, int j, int k){ px[0] = half - (half - (i + 0) * lsize) * window / (window + (k + 1) * lsize); py[0] = half - (half - (j + 1) * lsize) * window / (window + (k + 1) * lsize); px[1] = half - (half - (i + 1) * lsize) * window / (window + (k + 1) * lsize); py[1] = half - (half - (j + 1) * lsize) * window / (window + (k + 1) * lsize); px[2] = half - (half - (i + 1) * lsize) * window / (window + (k + 0) * lsize); py[2] = half - (half - (j + 1) * lsize) * window / (window + (k + 0) * lsize); px[3] = half - (half - (i + 0) * lsize) * window / (window + (k + 0) * lsize); py[3] = half - (half - (j + 1) * lsize) * window / (window + (k + 0) * lsize); drawSurface(i, j, k); } public void drawFront(int i, int j, int k){ px[0] = half - (half - (i + 0) * lsize) * window / (window + k * lsize); py[0] = half - (half - (j + 0) * lsize) * window / (window + k * lsize); px[1] = half - (half - (i + 1) * lsize) * window / (window + k * lsize); py[1] = half - (half - (j + 0) * lsize) * window / (window + k * lsize); px[2] = half - (half - (i + 1) * lsize) * window / (window + k * lsize); py[2] = half - (half - (j + 1) * lsize) * window / (window + k * lsize); px[3] = half - (half - (i + 0) * lsize) * window / (window + k * lsize); py[3] = half - (half - (j + 1) * lsize) * window / (window + k * lsize); drawSurface(i, j, k); } public void drawSurface(int i, int j, int k){ if (playArea[i][j][k] == moving) drawingBuffer.setColor(Color.lightGray); else drawingBuffer.setColor(shapeColor[deps - 1 - k]); drawingBuffer.fillPolygon(px, py, 4); drawingBuffer.setColor(Color.white); drawingBuffer.drawPolygon(px, py, 4); } public void update(Graphics g){ paint(g); } public void game(int c){ int i; if (gameOver){ if (c == 83){ // S clearPlayArea(); score = 0; level = 0; layers = 0; delay = 1000; height = 0; gameOver = false; } return; } if (!drop) for (i = 0; i < Shape3D.pieces; i++){ playArea[currentX[i]][currentY[i]][currentZ[i]] = empty; } else { for (i = 0; i < Shape3D.pieces; i++){ playArea[currentX[i]][currentY[i]][currentZ[i]] = occupied; if ((deps - currentZ[i]) > height) height = deps - currentZ[i]; } if (removeLayers()) repaint(); generateNewBlock(); drop = false; repaint(); return; } tryPositionX = currentPositionX; tryPositionY = currentPositionY; tryPositionZ = currentPositionZ; for (i = 0; i < Shape3D.pieces; i++){ tryStateX[i] = currentStateX[i]; tryStateY[i] = currentStateY[i]; tryStateZ[i] = currentStateZ[i]; } if ((c == 68) || (c == 72)){ // D or H for (i = 0; i < Shape3D.pieces; i++){ tryStateX[i] = -currentStateZ[i]; tryStateZ[i] = currentStateX[i]; } checkPlace(); } else if ((c == 87) || (c == 84)){ // W or T for (i = 0; i < Shape3D.pieces; i++){ tryStateY[i] = currentStateZ[i]; tryStateZ[i] = -currentStateY[i]; } checkPlace(); } else if ((c == 65) || (c == 70)){ // A or F for (i = 0; i < Shape3D.pieces; i++){ tryStateX[i] = currentStateY[i]; tryStateY[i] = -currentStateX[i]; } checkPlace(); } else if ((c == 37) || (c == 74)){ // Left arrow or J tryPositionX = currentPositionX - 1; checkPlace(); } else if ((c == 39) || (c == 76)){ // Right arrow or L tryPositionX = currentPositionX + 1; checkPlace(); } else if ((c == 38) || (c == 73)){ // Up arrow or I tryPositionY = currentPositionY - 1; checkPlace(); } else if ((c == 40) || (c == 75)){ // Down arrow or K tryPositionY = currentPositionY + 1; checkPlace(); } else if (c == 32){ // SPACE while(!drop){ tryPositionX = currentPositionX; tryPositionY = currentPositionY; tryPositionZ = currentPositionZ + 1; for (i = 0; i < Shape3D.pieces; i++){ tryStateX[i] = currentStateX[i]; tryStateY[i] = currentStateY[i]; tryStateZ[i] = currentStateZ[i]; } checkPlace(); checkDrop(); } } else { tryPositionZ = currentPositionZ + 1; checkPlace(); checkDrop(); } for (i = 0; i < Shape3D.pieces; i++){ playArea[currentX[i]][currentY[i]][currentZ[i]] = moving; } repaint(); } public void checkPlace(){ int i; for (i = 0; i < Shape3D.pieces; i++){ tryX[i] = tryPositionX + tryStateX[i]; tryY[i] = tryPositionY + tryStateY[i]; tryZ[i] = tryPositionZ + tryStateZ[i]; } int d = 0; leftOutSide = false; for (i = 0; i < Shape3D.pieces; i++) if (tryX[i] < 0){ leftOutSide = true; if (-tryX[i] > d) d = -tryX[i]; } if (leftOutSide){ for (i = 0; i < Shape3D.pieces; i++) tryX[i] += d; tryPositionX += d; } d = 0; rightOutSide = false; for (i = 0; i < Shape3D.pieces; i ++) if (tryX[i] > (cols - 1)){ rightOutSide = true; if ((tryX[i] - (cols - 1)) > d) d = tryX[i] - (cols - 1); } if (rightOutSide){ for (i = 0; i < Shape3D.pieces; i++) tryX[i] -= d; tryPositionX -= d; } d = 0; upOutSide = false; for (i = 0; i < Shape3D.pieces; i++) if (tryY[i] < 0){ upOutSide = true; if (-tryY[i] > d) d = -tryY[i]; } if (upOutSide){ for (i = 0; i < Shape3D.pieces; i++) tryY[i] += d; tryPositionY += d; } d = 0; downOutSide = false; for (i = 0; i < Shape3D.pieces; i++) if (tryY[i] > (rows - 1)){ downOutSide = true; if ((tryY[i] - (rows - 1)) > d) d = tryY[i] - (rows - 1); } if (downOutSide){ for (i = 0; i < Shape3D.pieces; i++) tryY[i] -= d; tryPositionY -= d; } d = 0; frontOutSide = false; for (i = 0; i < Shape3D.pieces; i++) if (tryZ[i] < 0){ frontOutSide = true; if (-tryZ[i] > d) d = -tryZ[i]; } if (frontOutSide){ for (i = 0; i < Shape3D.pieces; i++) tryZ[i] += d; tryPositionZ += d; } noPlace = false; for (i = 0; i < Shape3D.pieces; i++){ if (playArea[tryX[i]][tryY[i]][tryZ[i]] != empty){ noPlace = true; } } if (!noPlace){ for (i = 0; i < Shape3D.pieces; i++){ currentX[i] = tryX[i]; currentY[i] = tryY[i]; currentZ[i] = tryZ[i]; currentStateX[i] = tryStateX[i]; currentStateY[i] = tryStateY[i]; currentStateZ[i] = tryStateZ[i]; } currentPositionX = tryPositionX; currentPositionY = tryPositionY; currentPositionZ = tryPositionZ; } } public void checkDrop(){ int i; drop = false; for (i = 0; i < Shape3D.pieces; i++){ if (playArea[tryX[i]][tryY[i]][tryZ[i]] != empty){ drop = true; } } } public void generateNewBlock(){ int i; shapeType = (int)(Math.random() * Shape3D.numbers); currentPositionX = tryPositionX = 2; currentPositionY = tryPositionY = 1; currentPositionZ = tryPositionZ = 0; gameOver = false; for (i = 0; i < Shape3D.pieces; i++){ currentStateX[i] = Shape3D.X[shapeType][i]; currentStateY[i] = Shape3D.Y[shapeType][i]; currentStateZ[i] = Shape3D.Z[shapeType][i]; currentX[i] = currentPositionX + currentStateX[i]; currentY[i] = currentPositionY + currentStateY[i]; currentZ[i] = currentPositionZ + currentStateZ[i]; if (playArea[currentX[i]][currentY[i]][currentZ[i]] != empty){ gameOver = true; } } } public boolean removeLayers(){ int i, j, k, h; int dropLayers = 0; boolean removed = false; for (k = deps - 1; k > 0; k--){ boolean removeThisLayer = true; for (i = 0; i < cols; i++) for (j = 0; j < rows; j++) if (playArea[i][j][k] == empty) removeThisLayer = false; if (removeThisLayer){ dropLayers++; removed = true; for (h = k; h > 0; h--) for (i = 0; i < cols; i++) for (j = 0; j < rows; j++){ playArea[i][j][h] = playArea[i][j][h - 1]; if ((h - 1) == 0) playArea[i][j][h - 1] = empty; } k++; } } switch(dropLayers){ case 1: score += 1; break; case 2: score += 3; break; case 3: score += 7; break; case 4: score += 15; break; case 5: score += 31; break; } height -= dropLayers; layers += dropLayers; level = layers >> 4; return(removed); } public void clearPlayArea(){ int i, j, k; for (i = 0; i < cols; i++){ for (j = 0; j < rows; j++){ for (k = 0; k < deps; k++){ playArea[i][j][k] = empty; } } } for (i = 0; i < cols; i++){ for (j = 0; j < rows; j++){ playArea[i][j][deps] = occupied; } } } public boolean getOver(){ return(gameOver); } public int getSleepTime(){ return(delay - level * 20); } }