Forked from: fokhei's Tetris diff:1 forked from: Tetris sinclairc5 forked:0favorite:0lines:616license : MIT License modified : 2009-10-14 07:07:52 Embed Tweet // forked from fokhei's Tetris package { import flash.display.Sprite; public class FlashTest extends Sprite { private var config:Config; private var game:Game; private var renderer:Renderer; public function FlashTest(){ config = new Config(); game = new Game(); game.init(config); renderer = new Renderer(); renderer.init(config, game); renderer.build(); addChild(renderer); game.start(); } } } /********************************************/ class Config { public var spaceColumn:uint = 23; public var spaceRow:uint = 23; public var blockSize:uint = 20; } /********************************************/ import flash.events.Event; class GameEvent extends Event { static public const GAME_START:String = "GAME_START"; static public const GAME_OVER:String = "GAME_OVER"; static public const GAME_RESET:String = "GAME_RESET"; static public const MAP_UPDATE:String = "MAP_UPDATE"; public function GameEvent(type:String){ super(type); } } /********************************************/ import flash.display.DisplayObject; import flash.display.Graphics; import flash.display.Sprite; class Block { static public var TYPE_NONE:String = "0"; static public var TYPE_I:String = "I"; static public var TYPE_J:String = "J"; static public var TYPE_L:String = "L"; static public var TYPE_Q:String = "Q"; static public var TYPE_S:String = "S"; static public var TYPE_T:String = "T"; static public var TYPE_Z:String = "Z"; static public var TYPES:Array = [Block.TYPE_I, Block.TYPE_J, Block.TYPE_L, Block.TYPE_Q, Block.TYPE_S, Block.TYPE_T, Block.TYPE_Z]; public var type:String = Block.TYPE_NONE; public function Block(type:String = "0"){ this.type = type; } static public function getRandomType():String { var len:uint = Block.TYPES.length - 1; var r:Number = Math.round(Math.random() * len); return Block.TYPES[r]; } public function getView(cfg:Config):DisplayObject { var size:uint = cfg.blockSize; var fillColor:uint; var fillAlpha:Number = 1; switch (type){ case Block.TYPE_I: fillColor = 0x00F0F0; break; case Block.TYPE_J: fillColor = 0x0000F0; break; case Block.TYPE_L: fillColor = 0xF0A000; break; case Block.TYPE_Q: fillColor = 0xF0F000; break; case Block.TYPE_S: fillColor = 0x00F000; break; case Block.TYPE_T: fillColor = 0xA000F0; break; case Block.TYPE_Z: fillColor = 0xD80000; break; default: case Block.TYPE_NONE: return null; break; } var s:Sprite = new Sprite; var g:Graphics = s.graphics; g.beginFill(fillColor, fillAlpha); g.lineStyle(1, 0x666666, fillAlpha); g.drawRect(0, 0, size, size); g.endFill(); return s; } public function toString():String { return this.type; } } /********************************************/ import flash.display.DisplayObject; import flash.display.Sprite; import flash.geom.Point; class Puzzle { public var type:String; public var blocks:Array; public var position:Point; public function Puzzle(blockType:String = null){ position = new Point(0, 0); if (blockType == null){ blockType = Block.getRandomType(); } changeType(blockType); } public function rotateArray2D(arr:Array):Array { var d1:Number = arr.length; var d2:Number = arr[0].length; var r:Array = new Array(d2); for (var i:Number = 0; i < d2; i++){ var t:Array = new Array(d1); for (var j:Number = 0; j < d1; j++){ t[j] = arr[d1 - 1 - j][i]; } r[i] = t; } return r; } private function changeType(blockType:String):void { this.type = blockType; switch (type){ case Block.TYPE_I: blocks = [[new Block(Block.TYPE_I), new Block(Block.TYPE_I), new Block(Block.TYPE_I), new Block(Block.TYPE_I)]] break; case Block.TYPE_J: blocks = [[new Block(Block.TYPE_J), new Block(Block.TYPE_NONE), new Block(Block.TYPE_NONE)], [new Block(Block.TYPE_J), new Block(Block.TYPE_J), new Block(Block.TYPE_J)]] break; case Block.TYPE_L: blocks = [[new Block(Block.TYPE_L), new Block(Block.TYPE_L), new Block(Block.TYPE_L)], [new Block(Block.TYPE_L), new Block(Block.TYPE_NONE), new Block(Block.TYPE_NONE)] ] break; case Block.TYPE_Q: blocks = [[new Block(Block.TYPE_Q), new Block(Block.TYPE_Q)], [new Block(Block.TYPE_Q), new Block(Block.TYPE_Q)] ] break; case Block.TYPE_S: blocks = [[new Block(Block.TYPE_NONE), new Block(Block.TYPE_S), new Block(Block.TYPE_S)], [new Block(Block.TYPE_S), new Block(Block.TYPE_S), new Block(Block.TYPE_NONE)] ] break; case Block.TYPE_T: blocks = [[new Block(Block.TYPE_NONE), new Block(Block.TYPE_T), new Block(Block.TYPE_NONE)], [new Block(Block.TYPE_T), new Block(Block.TYPE_T), new Block(Block.TYPE_T)] ] break; case Block.TYPE_Z: blocks = [[new Block(Block.TYPE_Z), new Block(Block.TYPE_Z), new Block(Block.TYPE_NONE)], [new Block(Block.TYPE_NONE), new Block(Block.TYPE_Z), new Block(Block.TYPE_Z)] ] break; default: case Block.TYPE_NONE: blocks = [] break; } } public function getView(cfg:Config):DisplayObject { var s:Sprite = new Sprite(); var block:Block; var blockClip:DisplayObject; var h:uint = blocks.length; var w:uint = blocks[0].length; var ty:uint, tx:uint; for (ty = 0; ty < h; ty++){ for (tx = 0; tx < w; tx++){ block = blocks[ty][tx] as Block; if (block != null){ blockClip = block.getView(cfg); if (blockClip != null){ blockClip.x = tx * cfg.blockSize; blockClip.y = ty * cfg.blockSize; s.addChild(blockClip); } } } } return s; } public function rotateRight():void { blocks = rotateArray2D(blocks); } public function moveDown():void { position.y++; } public function moveRight():void { position.x++; } public function moveLeft():void { position.x--; } public function allowRotateRight(game:Game):Boolean { var clone:Array = rotateArray2D(blocks); var h:uint = clone.length; var w:uint = clone[0].length; var chkBlock:Block; var ty:int, tx:int; var cx:int, cy:int; for (ty = 0; ty < h; ty++){ for (tx = 0; tx < w; tx++){ if (clone[ty][tx].type == Block.TYPE_NONE){ continue; } else { cx = this.position.x + tx; cy = this.position.y + ty; if (cx < 0 || cx >= game.cfg.spaceColumn || cy < 0 || cy >= game.cfg.spaceRow - 1){ return false; } chkBlock = game.getBlock(cx, cy); if (chkBlock.type != Block.TYPE_NONE){ return false; } } } } return true; } public function allowMoveDown(game:Game):Boolean { if ((position.y + blocks.length) >= game.cfg.spaceRow){ return false; } var h:uint = blocks.length - 1; var w:uint = blocks[0].length; var ty:int, tx:int; var chkBlock:Block; var cx:int, cy:int; for (ty = h; ty > -1; ty--){ for (tx = 0; tx < w; tx++){ if (blocks[ty][tx].type == Block.TYPE_NONE){ continue; } else { cx = this.position.x + tx; cy = this.position.y + ty + 1; chkBlock = game.getBlock(cx, cy); if (chkBlock.type != Block.TYPE_NONE){ return false; } } } } return true; } public function allowMoveRight(game:Game):Boolean { if ((position.x + blocks[0].length) >= game.cfg.spaceColumn){ return false; } var h:uint = blocks.length - 1; var w:uint = blocks[0].length - 1; var ty:int, tx:int; var chkBlock:Block; var cx:int, cy:int; for (ty = h; ty > -1; ty--){ for (tx = w; tx > -1; tx--){ if (blocks[ty][tx].type == Block.TYPE_NONE){ continue; } else { cx = this.position.x + tx + 1; cy = this.position.y + ty; chkBlock = game.getBlock(cx, cy); if (chkBlock.type != Block.TYPE_NONE){ return false; } } } } return true; } public function allowMoveLeft(game:Game):Boolean { if (position.x <= 0){ return false; } var h:uint = blocks.length - 1; var w:uint = blocks[0].length; var ty:int, tx:int; var chkBlock:Block; var cx:int, cy:int; for (ty = h; ty > -1; ty--){ for (tx = 0; tx < w; tx++){ if (blocks[ty][tx].type == Block.TYPE_NONE){ continue; } else { cx = this.position.x + tx - 1; cy = this.position.y + ty; chkBlock = game.getBlock(cx, cy); if (chkBlock.type != Block.TYPE_NONE){ return false; } } } } return true; } public function appendToMap(game:Game):void { var h:uint = blocks.length; var w:uint = blocks[0].length; var ty:int, tx:int; var cx:int, cy:int; var block:Block; for (ty = 0; ty < h; ty++){ for (tx = 0; tx < w; tx++){ if (blocks[ty][tx].type == Block.TYPE_NONE){ continue; } else { cx = this.position.x + tx; cy = this.position.y + ty; if (cy >= 0){ block = game.getBlock(cx, cy) as Block; if (block != null){ block.type = blocks[ty][tx].type; } } } } } } public function allowDropDown(game:Game):Boolean { var h:uint = blocks.length - 1; var w:uint = blocks[0].length; var ty:int, tx:int; var chkBlock:Block; var cx:int, cy:int; for (ty = h; ty > 0; ty--){ for (tx = 0; tx < w; tx++){ if (blocks[ty][tx].type == Block.TYPE_NONE){ continue; } else { cx = this.position.x + tx; cy = this.position.y + ty; chkBlock = game.getBlock(cx, cy); if (chkBlock.type != Block.TYPE_NONE){ return false; } } } } return true; } } /**********************************************************/ import flash.events.EventDispatcher; import flash.events.TimerEvent; import flash.geom.Point; import flash.utils.Timer; class Game extends EventDispatcher { public var cfg:Config; private var map:Array; public var activePuzzle:Puzzle; public var nextPuzzle:Puzzle; private var updateTimer:Timer; public function init(config:Config):void { cfg = config; } public function reset():void { updateTimer.stop(); resetMap(); activePuzzle = null; dispatchEvent(new GameEvent(GameEvent.GAME_RESET)); start(); } private function resetMap():void { map = new Array(); var h:uint = cfg.spaceRow; var w:uint = cfg.spaceColumn; var ty:int, tx:int; for (ty = 0; ty < h; ty++){ map[ty] = []; for (tx = 0; tx < w; tx++){ map[ty][tx] = new Block(Block.TYPE_NONE); } } for (ty = 0; ty <= -1; ty--){ for (tx = 0; tx < w; tx++){ map[ty][tx] = new Block(Block.TYPE_NONE); } } } private function newRow():Array { var row:Array = new Array(); var w:uint = cfg.spaceColumn; var tx:int; for (tx = 0; tx < w; tx++){ row[tx] = new Block(Block.TYPE_NONE); } return row; } public function start():void { resetMap(); if (hasEventListener(GameEvent.GAME_START)){ dispatchEvent(new GameEvent(GameEvent.GAME_START)); } nextPuzzle = new Puzzle(Block.getRandomType()); updateTimer = new Timer(500); updateTimer.addEventListener(TimerEvent.TIMER, onUpdate); updateTimer.start() } public function getBlock(tx:int, ty:int):Block { return map[ty][tx] as Block; } private function allowDropPuzzle():Boolean { var testPuzzle:Puzzle = new Puzzle(nextPuzzle.type); testPuzzle.position = new Point(Math.floor((cfg.spaceColumn - testPuzzle.blocks[0].length) * 0.5), -1); return testPuzzle.allowDropDown(this); } private function dropPuzzle():void { activePuzzle = new Puzzle(nextPuzzle.type); activePuzzle.position = new Point(Math.floor((cfg.spaceColumn - activePuzzle.blocks[0].length) * 0.5), -1); nextPuzzle = new Puzzle(Block.getRandomType()); } private function onUpdate(e:TimerEvent):void { update(); } private function update():void { if (activePuzzle == null){ if (allowDropPuzzle()){ dropPuzzle(); } else { updateTimer.stop(); dispatchEvent(new GameEvent(GameEvent.GAME_OVER)); reset(); } } else { if (activePuzzle.allowMoveDown(this)){ activePuzzle.moveDown(); } else { activePuzzle.appendToMap(this); activePuzzle = null; var h:int = map.length; var w:int = map[0].length; var ty:int, tx:int; var cellCount:int; var removeIdx:Array = new Array(); for (ty = 0; ty < h; ty++){ cellCount = 0; for (tx = 0; tx < w; tx++){ if (getBlock(tx, ty).type != Block.TYPE_NONE){ cellCount++; } } if (cellCount == w){ removeIdx.push(ty) } } var len:int = removeIdx.length; var removedRow:Array if (len > 0){ var i:int = len - 1; for (i; i > -1; i--){ removedRow = map.splice(removeIdx[i], 1); } } for (i = 0; i < len; i++){ map.unshift(newRow()); } if (hasEventListener(GameEvent.MAP_UPDATE)){ dispatchEvent(new GameEvent(GameEvent.MAP_UPDATE)); } } } } } /*******************************************************/ import flash.display.DisplayObject; import flash.display.Graphics; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.geom.Rectangle; class Renderer extends Sprite { private var cfg:Config; private var game:Game; private var bg:Sprite; private var grid:Sprite; private var layerBlocks:Sprite; private var layerPuzzle:Sprite; private var layerMask:Rectangle; public function init(config:Config, game:Game):void { this.game = game; this.cfg = config; game.addEventListener(GameEvent.GAME_START, onGameStart); game.addEventListener(GameEvent.GAME_OVER, onGameOver); game.addEventListener(GameEvent.GAME_RESET, onGameReset); game.addEventListener(GameEvent.MAP_UPDATE, onMapUpdate); addEventListener(Event.ENTER_FRAME, onEnterFrame); } public function build():void { initBG(); initGrid(); initLayerBlocks(); initLayerPuzzle(); initMask(); } private function initBG():void { bg = new Sprite(); var g:Graphics = bg.graphics; g.beginFill(0xFFFFFF); g.drawRect(0, 0, cfg.spaceColumn * cfg.blockSize, cfg.spaceRow * cfg.blockSize); g.endFill(); bg.mouseEnabled = false; addChild(bg); } private function initGrid():void { grid = new Sprite(); var g:Graphics = grid.graphics; g.lineStyle(1, 0xCCCCCC, 0.5); var ty:int, tx:int; var w:int = cfg.spaceColumn * cfg.blockSize; var h:int = cfg.spaceRow * cfg.blockSize for (ty = 1; ty < cfg.spaceRow; ty++){ g.moveTo(0, ty * cfg.blockSize); g.lineTo(w, ty * cfg.blockSize) } for (tx = 1; tx < cfg.spaceColumn; tx++){ g.moveTo(tx * cfg.blockSize, 0); g.lineTo(tx * cfg.blockSize, h); } g.drawRect(0, 0, cfg.spaceColumn * cfg.blockSize, cfg.spaceRow * cfg.blockSize); grid.mouseEnabled = false; addChild(grid); } private function initMask():void { layerMask = new Rectangle(0, 0, cfg.spaceColumn * cfg.blockSize + 1, cfg.spaceRow * cfg.blockSize + 1); this.scrollRect = layerMask; } private function initLayerBlocks():void { layerBlocks = new Sprite(); addChild(layerBlocks); } private function drawBlocks():void { var block:Block; var clip:DisplayObject; var ty:uint, tx:uint; while (layerBlocks.numChildren){ layerBlocks.removeChildAt(0); } for (ty = 0; ty < cfg.spaceRow; ty++){ for (tx = 0; tx < cfg.spaceColumn; tx++){ block = game.getBlock(tx, ty) as Block; if (block != null && block.type != Block.TYPE_NONE){ clip = block.getView(cfg) as DisplayObject; if (clip != null){ clip.x = tx * cfg.blockSize; clip.y = ty * cfg.blockSize; layerBlocks.addChild(clip); } } } } } private function initLayerPuzzle():void { layerPuzzle = new Sprite(); addChild(layerPuzzle); } private function updateLayerPuzzle():void { drawPuzzle(game.activePuzzle); } private function drawPuzzle(puzzle:Puzzle = null):void { while (layerPuzzle.numChildren){ layerPuzzle.removeChildAt(0); } if (puzzle != null){ var puzzleClip:DisplayObject = puzzle.getView(cfg) as DisplayObject; if (puzzleClip != null){ puzzleClip.x = puzzle.position.x * cfg.blockSize; puzzleClip.y = puzzle.position.y * cfg.blockSize; layerPuzzle.addChild(puzzleClip); } } } private function onEnterFrame(e:Event):void { update(); } private function update():void { updateLayerPuzzle(); } private function onGameStart(e:Event):void { //trace("onGameStart"); drawBlocks(); stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); } private function onKeyDown(e:KeyboardEvent):void { //trace("onKeyDown:", e.keyCode); var p:Puzzle = game.activePuzzle; if (p != null){ //Arrow Key or A,S,D,W if (e.keyCode == 37 || e.keyCode == 65){ if (p.allowMoveLeft(game)){ p.moveLeft(); } } if (e.keyCode == 38 || e.keyCode == 87){ if (p.allowRotateRight(game)){ p.rotateRight(); } } if (e.keyCode == 39 || e.keyCode == 68){ if (p.allowMoveRight(game)){ p.moveRight(); } } if (e.keyCode == 40 || e.keyCode == 83){ if (p.allowMoveDown(game)){ p.moveDown(); } } } } private function onMapUpdate(e:Event = null):void { updateLayerPuzzle(); drawBlocks(); } private function onGameOver(e:Event = null):void { trace("onGameOver"); } private function onGameReset(e:Event = null):void { trace("onGameReset"); build(); } } Code Fullscreen Preview Fullscreen