/* * Battle Tetris * ------------- * * Original Version (C) Iwan van Rienen, 1995, 1996 * Duoplay Version (C) Song Li, 1996 * * Iwan van Rienen http://www.bart.nl/~ivr E-Mail: ivr@bart.nl * Song Li http://www.cs.umbc.edu/~sli2 E-Mail: sli2@gl.umbc.edu * * * This is a "postcard-ware", as Iwan calls it. He hopes his cup of java * to be shared by anyone insterested with Java. Actually, this is where * I started. I am a C++ programmer for years, but one week ago (01/17/96) * I got a keen desire to learn Java, a new language for me. At first, I * wanted to begin with a Tetris program written in C developed for * OpenLook by myself years ago. After looking up Gamelan, however, I * found there were a lot of Tetrises up there already. So I gave up my * first thought and picked up Iwan's source code and tried to improve it. * * Since the program is based on Iwan's postcard-ware, I put his name * here and hope those who has a chance to get this code improve it in * any favorable way. Please let your friends know this little program * and contribute some additional functions if any of you is a programmer. * * * Song Li. * Jan 25, 1996. * * */ import java.awt.*; import java.awt.image.*; import java.applet.Applet; import java.applet.AudioClip; import java.util.Vector; import java.lang.Math; import java.lang.Thread; import java.lang.System; public class TetrisApp extends java.applet.Applet { Panel Player1, Player2 ; TetrisThread Tetris1, Tetris2 ; Choice PlayerNumChoice ; Choice LevelChoice ; Choice RandomChoice ; public void init () { setLayout (new BorderLayout ()); Panel grid = new Panel (); grid.setLayout (new GridLayout (1, 2)); add ("Center", grid); Player1 = new Panel () ; Player1.setLayout (null) ; grid.add ("Left", Player1) ; Player2 = new Panel () ; Player2.setLayout (null) ; grid.add ("Right", Player2) ; Panel control = new Panel () ; control.setLayout (new FlowLayout ()) ; add ("South", control) ; control.add (new Button ("New Game")) ; control.add (new Button ("Pause / Resume")) ; control.add (new Button ("About")) ; PlayerNumChoice = new Choice () ; PlayerNumChoice.addItem ("2 Players") ; PlayerNumChoice.addItem ("1 - Right") ; PlayerNumChoice.addItem ("1 - Left") ; control.add (PlayerNumChoice) ; LevelChoice = new Choice () ; for (int i=0; i<=9; i++) LevelChoice.addItem ("Level " + i) ; control.add (LevelChoice) ; RandomChoice = new Choice () ; for (int i=0; i<=9; i++) RandomChoice.addItem (i + " Rows") ; control.add (RandomChoice) ; TetrisThread.BlockMap = new int [TetrisThread.BlockNum][4] ; TetrisThread.BlockColor = new Color [TetrisThread.BlockNum] ; TetrisThread.BlockValue = new int [TetrisThread.BlockNum] ; TetrisThread.BlockMap[0][0] = 0x0000 ; TetrisThread.BlockMap[0][1] = 0x0FF0 ; TetrisThread.BlockMap[0][2] = 0x0FF0 ; TetrisThread.BlockMap[0][3] = 0x0000 ; TetrisThread.BlockColor[0] = Color.blue ; TetrisThread.BlockValue[0] = 3 ; TetrisThread.BlockMap[1][0] = 0x0F00 ; TetrisThread.BlockMap[1][1] = 0x0F00 ; TetrisThread.BlockMap[1][2] = 0x0FF0 ; TetrisThread.BlockMap[1][3] = 0x0000 ; TetrisThread.BlockColor[1] = Color.yellow ; TetrisThread.BlockValue[1] = 5 ; TetrisThread.BlockMap[2][0] = 0x00F0 ; TetrisThread.BlockMap[2][1] = 0x00F0 ; TetrisThread.BlockMap[2][2] = 0x0FF0 ; TetrisThread.BlockMap[2][3] = 0x0000 ; TetrisThread.BlockColor[2] = Color.pink ; TetrisThread.BlockValue[2] = 5 ; TetrisThread.BlockMap[3][0] = 0x0000 ; TetrisThread.BlockMap[3][1] = 0x0F00 ; TetrisThread.BlockMap[3][2] = 0xFFF0 ; TetrisThread.BlockMap[3][3] = 0x0000 ; TetrisThread.BlockColor[3] = Color.green ; TetrisThread.BlockValue[3] = 4 ; TetrisThread.BlockMap[4][0] = 0x0F00 ; TetrisThread.BlockMap[4][1] = 0x0F00 ; TetrisThread.BlockMap[4][2] = 0x0F00 ; TetrisThread.BlockMap[4][3] = 0x0F00 ; TetrisThread.BlockColor[4] = Color.red ; TetrisThread.BlockValue[4] = 4 ; TetrisThread.BlockMap[5][0] = 0x0F00 ; TetrisThread.BlockMap[5][1] = 0x0FF0 ; TetrisThread.BlockMap[5][2] = 0x00F0 ; TetrisThread.BlockMap[5][3] = 0x0000 ; TetrisThread.BlockColor[5] = Color.magenta ; TetrisThread.BlockValue[5] = 4 ; TetrisThread.BlockMap[6][0] = 0x00F0 ; TetrisThread.BlockMap[6][1] = 0x0FF0 ; TetrisThread.BlockMap[6][2] = 0x0F00 ; TetrisThread.BlockMap[6][3] = 0x0000 ; TetrisThread.BlockColor[6] = Color.orange ; TetrisThread.BlockValue[6] = 4 ; TetrisThread.Sound = new AudioClip [4] ; TetrisThread.Sound[0] = getAudioClip (getCodeBase (), "audio/drop.au"); TetrisThread.Sound[1] = getAudioClip (getCodeBase (), "audio/gameover.au"); TetrisThread.Sound[2] = getAudioClip (getCodeBase (), "audio/nextlevel.au"); TetrisThread.Sound[3] = getAudioClip (getCodeBase (), "audio/line.au"); } public void start() { Tetris1 = new TetrisThread (this, Player1, 'a', 's', 'd', 'z') ; Tetris2 = new TetrisThread (this, Player2, 'j', 'k', 'l', 'm') ; Tetris1.SetEnemy (Tetris2) ; Tetris2.SetEnemy (Tetris1) ; Tetris1.start () ; Tetris2.start () ; } public void stop() { Tetris1.stop () ; Tetris2.stop () ; } public int StartLevel () { return LevelChoice.getSelectedIndex () ; } public int StartRow () { return RandomChoice.getSelectedIndex () ; } private boolean CommandAction (Event event, Object arg) { if (event.target == PlayerNumChoice) switch (PlayerNumChoice.getSelectedIndex ()) { case 0 : Tetris1.Suspend (false) ; Tetris2.Suspend (false) ; break ; case 1: Tetris1.Suspend (true) ; Tetris2.Suspend (false) ; break ; case 2: Tetris1.Suspend (false) ; Tetris2.Suspend (true) ; break ; } if (event.target == LevelChoice) { Tetris1.SetLevel (LevelChoice.getSelectedIndex ()) ; Tetris2.SetLevel (LevelChoice.getSelectedIndex ()) ; } if ("New Game".equals (arg)) { Tetris1.NewGame () ; Tetris2.NewGame () ; } if ("About".equals (arg)) { Tetris1.Pause (true) ; Tetris2.Pause (true) ; AboutFrame about = new AboutFrame () ; about.show () ; } if ("Pause / Resume".equals (arg)) { Tetris1.Pause () ; Tetris2.Pause () ; } return true; } public synchronized boolean handleEvent (Event event) { switch (event.id) { case Event.ACTION_EVENT: return CommandAction (event, event.arg); case Event.KEY_ACTION: case Event.KEY_PRESS: Tetris1.KeyAction (event.key) ; Tetris2.KeyAction (event.key) ; return true ; default: return false; } } } class TetrisThread extends Thread { public static final int Cols = 10 ; public static final int Rows = 18 ; public static final int ElementSize = 15 ; public static final int MaxElement = 3 ; public static final int BlockNum = 7 ; public static int BlockMap [][] ; public static Color BlockColor [] ; public static int BlockValue [] ; public static AudioClip Sound [] ; public static final int SND_DROP = 0; public static final int SND_GAMEOVER = 1; public static final int SND_NEXTLEVEL= 2; public static final int SND_LINE = 3; char KeyLeft ; char KeyRight ; char KeyRotate ; char KeyDrop ; TetrisApp Applet ; ArenaCanvas Arena ; ReportCanvas Report ; TetrisThread Enemy ; Shape CurrentShape = null; Shape NextShape = null; Color PlayMap [][]; boolean GamePaused = false; boolean GameRestart = false ; boolean GameSuspended = false ; public TetrisThread (TetrisApp app, Panel panel, char left, char rotate, char right, char drop) { Applet = app ; Arena = new ArenaCanvas (); panel.add (Arena) ; Report = new ReportCanvas (); panel.add (Report) ; Report.move (170, 0) ; KeyLeft = left ; KeyRight = right ; KeyRotate = rotate ; KeyDrop = drop ; PlayMap = new Color [Rows] [Cols]; Arena.SetPlayMap (PlayMap); InitGame (); GetNextRandomShape (); } public void SetEnemy (TetrisThread enemy) { Enemy = enemy ; } private void InitGame () { int x, y ; for (y=0 ; y= BlockNum); return num ; } private void GetNextRandomShape () { int num ; if (CurrentShape == null) { num = RandomShapeNum () ; CurrentShape = new Shape (BlockMap[num], BlockColor[num], BlockValue[num]) ; } else CurrentShape = NextShape; CurrentShape.Init (); if (!CurrentShape.CheckFit (PlayMap, 0, 0, false)) GameOver (); num = RandomShapeNum () ; NextShape = new Shape (BlockMap[num], BlockColor[num], BlockValue[num]) ; Report.DisplayNextShape (NextShape); } private void GameOver () { Play (SND_GAMEOVER); Arena.GameOver (); Report.GameOver (); InitGame (); } public void NewGame () { GameRestart = true ; Pause (false) ; } public void Suspend (boolean needsuspend) { GameSuspended = needsuspend ; } public void Pause () { GamePaused = ! GamePaused; } public void Pause (boolean needpause) { GamePaused = needpause ; } public void run () { for (;;) { try { Thread.sleep (Report.GetGameSpeed ()); } catch (InterruptedException e) {} if (! GamePaused && ! GameSuspended) UpdateMap (false, 0, 0, false) ; } } private void CheckLines () { int Lines = 0; int x, y, yc; boolean Gap; for (y=0; y=0; yc--) for (x=0; x 0) { Play (SND_LINE); Arena.RepaintPlayMap (); if (Lines > 1) Enemy.Suffer (Lines) ; } Report.AddLines (Lines); } private void ChangeShape (int incx, int incy, boolean rotate) { while (! CurrentShape.IsReady ()) ; if (CurrentShape.CheckFit (PlayMap, incx, incy, rotate)) { CurrentShape.ChangePosition (incx, incy, rotate); Arena.repaint (CurrentShape); } } private synchronized void UpdateMap (boolean userrequest, int incx, int incy, boolean rotate) { if (userrequest) ChangeShape (incx, incy, rotate) ; else { if (GameRestart) { GameRestart = false ; GameOver () ; GetNextRandomShape () ; } else if (CurrentShape != null) if (CurrentShape.CheckFit (PlayMap, 0, 1, false)) ChangeShape (0, 1, false); else { Play (SND_DROP); CurrentShape.Place (PlayMap); Arena.RepaintPlayMap (); Report.AddScore (CurrentShape.GetValue ()); CheckLines (); GetNextRandomShape (); } Arena.repaint (CurrentShape); } } public synchronized void Suffer (int linecount) { if (GameSuspended) return ; int x, y ; for (y=0; y='A' && key<='Z') key += 'a' - 'A' ; if (key == KeyLeft) incx --; if (key == KeyRight) incx ++; if (key == KeyDrop) incy ++; if (key == KeyRotate) rotate = true ; UpdateMap (true, incx, incy, rotate) ; return true; } private void RandomRow (Color rowmap []) { Color [] colortab = { Color.blue, Color.yellow, Color.pink, Color.green, Color.red, Color.magenta, Color.orange } ; for (int col=0; col (10 * (Level+1))) AddLevel (); repaint (); } public void AddLevel () { TetrisThread.Play (TetrisThread.SND_NEXTLEVEL); if (Level < MaxLevel) Level ++ ; repaint (); } public void DisplayNextShape (Shape shape) { NextShape = shape ; repaint (); } public void paint (Graphics graph) { graph.setColor (TextColor); graph.drawString ("Level: " + Level, 0, FontHeight); graph.drawString ("Lines: " + Lines, 0, FontHeight * 3); graph.drawString ("Score: " + Score, 0, FontHeight * 5); graph.drawString ("Next:", 0, FontHeight * 7); if (NextShape != null) { NextShape.DisplayAbs (graph, 10, FontHeight * 7 + 10); } } } class Element { protected int X, Y ; int OldX, OldY; protected int XinShape, YinShape; protected int OrgX, OrgY; protected int OrgXinShape, OrgYinShape; protected Color ElementColor; protected boolean ErasePossible; public Element (int xpos, int ypos, Color color) { XinShape = OrgXinShape = xpos; YinShape = OrgYinShape = ypos; X = OrgX = xpos + TetrisThread.Cols/2 - (TetrisThread.MaxElement+1)/2; Y = OrgY = ypos; ElementColor = color; ErasePossible = false; } public void Init () { ErasePossible = false; X = OrgX; Y = OrgY; XinShape = OrgXinShape; YinShape = OrgYinShape; } public void Hide (Graphics graph, int xoff, int yoff) { if (ErasePossible) { int size = TetrisThread.ElementSize; graph.setColor (Color.black); graph.fillRect (xoff + OldX * size, yoff + OldY * size, size, size); ErasePossible = false; } } public void Display (Graphics graph, int xoff, int yoff) { int size = TetrisThread.ElementSize; graph.setColor (ElementColor); graph.fill3DRect (xoff + X*size + 1, yoff + Y*size + 1, size-2, size-2, true); OldX = X; OldY = Y; ErasePossible = true; } public void DisplayAbs (Graphics graph, int xoff, int yoff) { int size = TetrisThread.ElementSize; graph.setColor (ElementColor); graph.fill3DRect (xoff + OrgXinShape*size + 1, yoff + OrgYinShape*size + 1, size-2, size-2, true); graph.setColor (Color.white); graph.drawRect (xoff + OrgXinShape*size, yoff + OrgYinShape*size, size-1, size-1); } public boolean CheckFit (Color playmap [][], int xoff, int yoff, boolean rotate) { if (rotate) { xoff += TetrisThread.MaxElement - YinShape - XinShape; yoff += XinShape - YinShape; } if (X + xoff < 0 || X + xoff >= TetrisThread.Cols) return false; if (Y + yoff >= TetrisThread.Rows ) return false; if (playmap[Y + yoff][X + xoff] != Color.black) return false; return true; } public void ChangePostion (int xoff, int yoff, boolean rotate) { if (rotate) { xoff += TetrisThread.MaxElement - YinShape - XinShape; yoff += XinShape - YinShape; int tempXinShape = XinShape; XinShape = TetrisThread.MaxElement - YinShape; YinShape = tempXinShape; } X += xoff; Y += yoff; } public int GetXPos () { return X; } public int GetYPos () { return Y; } public Color GetColor () { return ElementColor; } } class Shape { protected Vector Elements; protected int Value; protected boolean DrawReady = true; public Shape () { DrawReady = true; } public void Init () { DrawReady = true; for (int ix=0; ix 0) Elements.addElement (new Element (0, row, color)); if ((map & 0x0f00) > 0) Elements.addElement (new Element (1, row, color)); if ((map & 0x00f0) > 0) Elements.addElement (new Element (2, row, color)); if ((map & 0x000f) > 0) Elements.addElement (new Element (3, row, color)); } public void Hide (Graphics graph, int xoff) { for (int ix=0; ix