/**
* Copyright hacker_johiroshi ( http://wonderfl.net/user/hacker_johiroshi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/wGdJ
*/
package
{
import flash.display.Sprite;
import flash.display.Shape;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.ui.Keyboard;
import flash.utils.Timer;
import flash.text.TextField;
import flash.text.TextFormat;
/**
* AS_TETRiS08
* @author necoEngine
*/
public class Tetris extends Sprite
{
private const FIELD_WIDTH:uint = 14;
private const FIELD_HEIGHT:uint = 22;
private const CELL_WIDTH:uint = 20;
private const CELL_HEIGHT:uint = 20;
private const TET_WIDTH:uint = 4;
private const TET_HEIGHT:uint = 4;
private const EMPTY:uint = 0;
private const WALL:uint = 9;
private const LOCKED:uint = 10;
private const WALL_COLOR:uint = 0x7A5B52;
private const GRID_COLOR:uint = 0xC0C0C0;
private const TET_COLORS:Array = [
0xE9EE11, 0x4DE6E1, 0xEA68E7, 0xE49E1B, 0x2746D8, 0x46DF20, 0xED2212
];
private const tetPattern01:Array = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]];
private const tetPattern02:Array = [[0, 0, 0, 0], [2, 2, 2, 2], [0, 0, 0, 0], [0, 0, 0, 0]];
private const tetPattern03:Array = [[0, 0, 0, 0], [0, 0, 3, 0], [0, 3, 3, 3], [0, 0, 0, 0]];
private const tetPattern04:Array = [[0, 0, 0, 0], [0, 0, 0, 4], [0, 4, 4, 4], [0, 0, 0, 0]];
private const tetPattern05:Array = [[0, 0, 0, 0], [0, 5, 0, 0], [0, 5, 5, 5], [0, 0, 0, 0]];
private const tetPattern06:Array = [[0, 0, 0, 0], [0, 0, 6, 6], [0, 6, 6, 0], [0, 0, 0, 0]];
private const tetPattern07:Array = [[0, 0, 0, 0], [0, 7, 7, 0], [0, 0, 7, 7], [0, 0, 0, 0]];
private const TETROMINOS:Array = [
tetPattern01, tetPattern02, tetPattern03, tetPattern04, tetPattern05, tetPattern06, tetPattern07
];
private const MAP_BLANK:Array = [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9];
private const DELAY_RANGE:Number = 1000;
private const DELAY_MIN:Number = 100;
private const DELETE_RAG:Number = 150; // 追加
private var fieldMap:Array = [
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
];
private var fieldMapColor:Array = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
private var gameField:Shape;
private var location:Point;
private var currentTetromino:Array;
private var currentIndex:uint;
private var nextIndex:uint;
private var deleteLineIndex:Array;
private var gameover:Boolean;
private var dropTimer:Timer;
private var ragTimer:Timer; // 追加
private var nextBorad:NextTetrrominoDisplay;
private var score:int; //スコア
private var scoreboard:Sprite; //スコアボード
private var tscb:TextField = new TextField(); //スコア用テキストフィールド
private var tsc:TextField = new TextField(); //
public function Tetris():void
{
init();
}
private function init():void
{
//テトリスゲームフィールド
gameField = new Shape();
addChild(gameField);
gameField.x = CELL_WIDTH;
gameField.y = CELL_HEIGHT;
TETROMINOS.push(0);
//nextテトリミノ表示ボード
nextBorad = new NextTetrrominoDisplay();
addChild(nextBorad);
nextBorad.x = stage.stageWidth - nextBorad.width - CELL_WIDTH;
nextBorad.y = CELL_HEIGHT * 2;
location = new Point(0, 0);
nextTetromino(true);
fullDrawing();
//timer設定
dropTimer = new Timer(DELAY_RANGE);
dropTimer.addEventListener(TimerEvent.TIMER, onDropTetromino);
dropTimer.start();
ragTimer = new Timer(DELETE_RAG, 1); // 追加
ragTimer.addEventListener(TimerEvent.TIMER, fillMapBlank);
//スコアの表示
scoreboard = new Sprite();
addChild(scoreboard);
scoreboard.graphics.beginFill(0x7A5B52);
scoreboard.graphics.drawRoundRect(305, 300, 150, 100, 50);
scoreboard.graphics.endFill();
//ステージイベント
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
/**
* イベント
** ** ** ** ** ** ** ** ** ** ** **/
private function onKeyDown(event:KeyboardEvent):void
{
switch(event.keyCode) {
case Keyboard.LEFT:
tetrominoAction(Keyboard.LEFT);
break;
case Keyboard.RIGHT:
tetrominoAction(Keyboard.RIGHT);
break;
case Keyboard.DOWN:
tetrominoAction(Keyboard.DOWN);
dropTimer.stop();
dropTimer.start();
break;
case Keyboard.UP:
tetrominoAction(Keyboard.UP);
break;
}
}
private function onDropTetromino(event:TimerEvent):void
{
/**
* 一定時間ごとに自動で落下させる
*/
tetrominoAction(Keyboard.DOWN);
}
private function fillMapBlank(event:TimerEvent):void
{
/**
* 揃ったラインを削除した後に、空白を詰める処理
* 少し時間のおいてから実行される
*/
tetrominoClear();
for (var y:int = deleteLineIndex.length - 1; y >= 0; y--) {
fieldMap.splice(deleteLineIndex[y], 1);
fieldMapColor.splice(deleteLineIndex[y], 1);
fieldMap.splice(0, 0, MAP_BLANK.concat());
fieldMapColor.splice(0, 0, MAP_BLANK.concat());
}
fullDrawing();
}
/**
* テトリミノ生成
** ** ** ** ** ** ** ** ** ** ** **/
private function createTetromino():uint {
/**
* 出現するテトリミノをランダムで選ぶ。
* 今のところただランダム値を返すだけ。
*/
return Math.floor(Math.random() * 7);
}
private function nextTetromino(first:Boolean = false):void
{
/**
* createTetromino()で選択しておいたテトリミノパターンを現在のテトリミノとしてコピー。
* その後、次のテトリミノパターンを選択しておく。
* 開始直後の「次のテトリミノがない」状態の時はテトリミノを作成してから同様の処理を行う
*/
currentTetromino = new Array();
currentIndex = (first)? createTetromino(): nextIndex;
for (var y:int = 0; y < TET_HEIGHT; y++) {
currentTetromino[y] = TETROMINOS[currentIndex][y].concat();
}
//落下テトリミノの初期配置位置
location.x = 5;
location.y = 0;
gameover = overlapCheck(currentTetromino);
if (gameover) {
dropTimer.stop();
stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
nextBorad.tetrominoClear();
fullDrawing();
} else {
nextIndex = createTetromino();
nextBorad.drawNextTetromino(TETROMINOS[nextIndex], TET_COLORS[nextIndex]);
}
}
/**
* テトリミノの操作関連処理
** ** ** ** ** ** ** ** ** ** ** **/
private function tetrominoAction(key:int):void {
/**
* テトリミノの移動判定
* 引数:方向キーのキーコード
*/
switch(key) {
case Keyboard.LEFT:
if (getLeftHit()) break;
tetrominoClear();
location.x--;
fullDrawing();
break;
case Keyboard.RIGHT:
if (getRightHit()) break;
tetrominoClear();
location.x++;
fullDrawing();
break;
case Keyboard.DOWN:
if (getBottomHit()) {
lockTetromino();
nextTetromino();
fullDrawing();
break;
}
tetrominoClear();
location.y++;
fullDrawing();
break;
case Keyboard.UP:
if (turnTetromino()) break;
fullDrawing();
break;
}
}
private function adjacentCheck(tx:int, ty:int, direct:uint):Boolean {
/**
* 渡されたゲームフィールド座標に隣接するセルが空白であるかどうかをチェックして真偽値を返す
* 引数:[tx ty] 現在のテトリミノのセルが存在しているゲームフィールド上の座標
* [direct] 調べたいのはtx tyのどの方角なのか
* 戻り値:隣にブロック、テトリミノがあるなら真を返す
*/
var mx:int, my:int;
switch(direct) {
case Keyboard.LEFT:
mx = location.x + tx -1; //一つ左隣を調べたい
my = location.y + ty;
if (fieldMap[my][mx]) return true;
else return false;
case Keyboard.RIGHT:
mx = location.x + tx +1; //一つ右隣を調べたい
my = location.y + ty;
if (fieldMap[my][mx]) return true;
else return false;
case Keyboard.DOWN:
mx = location.x + tx;
my = location.y + ty + 1; //一つ下を調べたい
if (fieldMap[my][mx]) return true;
else return false;
}
return true;
}
private function getBottomHit():Boolean
{
/**
* 現在のテトリミノ(currentTetromino:Array)
* 下方向に障害物がないかどうかをチェックして真偽値を返す。
* 戻り値:障害物があった場合に真を返す
*/
var result:Boolean;
for (var x:int = 0; x < TET_WIDTH; x++) {
for (var y:int = TET_HEIGHT - 1; y >= 0; y--) {
if (currentTetromino[y][x]) {
//テトリミノがあればチェック
result = adjacentCheck(x, y, Keyboard.DOWN);
break;
}
}
if (result) break;
}
return result;
}
private function getLeftHit():Boolean {
/**
* 現在のテトリミノ(currentTetromino:Array)
* 左方向に障害物がないかどうかをチェックして真偽値を返す。
* 戻り値:障害物があった場合に真を返す
*/
var result:Boolean;
for (var y:int = 0; y < TET_HEIGHT; y++) {
for (var x:int = 0; x < TET_WIDTH; x++) {
if (currentTetromino[y][x]) {
//テトリミノがあればチェック
result = adjacentCheck(x, y, Keyboard.LEFT);
break;
}
}
if (result) break;
}
return result;
}
private function getRightHit():Boolean
{
/**
* 現在のテトリミノ(currentTetromino:Array)
* 右方向に障害物がないかどうかをチェックして真偽値を返す。
* 戻り値:障害物があった場合に真を返す
*/
var result:Boolean;
for (var y:int = 0; y < TET_HEIGHT; y++) {
for (var x:int = TET_WIDTH - 1; x >= 0; x--) {
if (currentTetromino[y][x]) {
//テトリミノがあればチェック
result = adjacentCheck(x, y, Keyboard.RIGHT);
break;
}
}
if (result) break;
}
return result;
}
private function turnTetromino():Boolean
{
/**
* テトリミノを回転させる。
* 一度仮説で回転させて、実際のマップ上でその回転が出来るかどうかを調べる。
* 回転可能なら回転させ、不可なら回転させない。
* 戻り値は利用していない。
*/
var x:int, y:int;
var myValue:int;
var pTurn:Array = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
//currentTetromino:Arrayを一旦コピーする
var tetTemp:Array = new Array();
for (y = 0; y < TET_HEIGHT; y++) tetTemp[y] = currentTetromino[y].concat();
//currentTetromino:Arrayを一旦コピーしたテトリミノを時計回りに90度回転させる
for (y = 0; y < TET_HEIGHT; y++) {
for (x = 0; x < TET_WIDTH; x++) pTurn[x][TET_HEIGHT - 1 - y] = tetTemp[y][x];
}
//回転した際に、障害物に重なっていないかどうかを調べる。障害がある場合は回転しない。
if (overlapCheck(pTurn)) return true;
//問題なければ回転後のマップを確定する。ただし確定する前に描画されている元のテトリミノをクリアする
tetrominoClear();
for (y = 0; y < TET_HEIGHT; y++) currentTetromino[y] = pTurn[y].concat();
return false;
}
private function overlapCheck(pTurn:Array):Boolean
{
/**
* テトリミノを回転させた時に、他の障害物と重なっていないかをチェックして真偽値を返す
* 引数:[pTurn] 一旦回転させたテトリミノのマップ情報
* 戻り値:障害物に重なっていた場合に真を返す
*/
for (var y:int = 0; y < TET_HEIGHT; y++) {
for (var x:int = 0; x < TET_WIDTH; x++) {
if (pTurn[y][x]) {
var mx:int = location.x + x;
var my:int = location.y + y;
if(fieldMap[my][mx] && fieldMap[my][mx] != currentIndex + 1) return true;
}
}
}
return false;
}
/**
* テトリミノの固定処理など
** ** ** ** ** ** ** ** ** ** ** **/
private function lockTetromino():void
{
/**
* 下方向に移動できなくなったテトリミノをマップデータ上に固定する。
*/
for (var y:int = 0; y < TET_HEIGHT; y++) {
for (var x:int = 0; x < TET_WIDTH; x++) {
if (currentTetromino[y][x]) {
var mx:int = location.x + x;
var my:int = location.y + y;
fieldMap[my][mx] = LOCKED; // 固定を表す定数を代入
fieldMapColor[y][x] += TET_COLORS[currentIndex];
}
}
}
lineCheck();
scoreboard.addChild(tscb);
ScoreCheck();
//落下速度を少しだけ早める
if (dropTimer.delay > DELAY_MIN) dropTimer.delay--;
}
//スコアの表示
private function ScoreCheck():void
{
scoreboard.removeChild(tscb)
tscb = createTextField(330, 360, 50, 50);
tscb.text = "score: " + score;
//textscb.x = 330;
//textscb.y = 360;
}
private function createTextField(x:Number, y:Number, width:Number, height:Number):TextField
{
var result:TextField = new TextField();
result.x = x;
result.y = y;
result.width = width;
result.height = height;
addChild(result);
return result;
}
private function lineCheck():void
{
/**
* 揃っているラインがないかどうかをチェックして、あった場合は削除する。
*/
deleteLineIndex = new Array();
var end:Boolean;
var x:int, y:int;
for (y = FIELD_HEIGHT - 2; y >= 0 && !end; y--) {
var zero:Boolean = false;
for (x = 0; x < FIELD_WIDTH && !zero; x++) {
if (fieldMap[y][x] <= 0) zero = true;
}
//揃った行を削除する
if (!zero) {
fieldMap.splice(y, 1, MAP_BLANK.concat());
fieldMapColor.splice(y, 1, MAP_BLANK.concat());
//削除する行の配列インデックスを保存しておく
deleteLineIndex.push(y);
//点数加算
score++;
}
}
// 削除した行を詰めるのはちょっと遅らせる
if (deleteLineIndex.length > 0) ragTimer.start();
}
/**
* 描画関連
** ** ** ** ** ** ** ** ** ** ** **/
private function tetrominoClear():void
{
/**
* マッピングデータ上から現在のテトリミノデータを消去する。
* テトリミノを移動する時に必要な処理。
*/
for (var y:int = location.y; y < location.y + TET_HEIGHT; y++) {
var py:int = y - location.y;
for (var x:int = location.x; x < location.x + TET_WIDTH; x++) {
var px:int = x - location.x;
if (currentTetromino[py][px]) {
fieldMap[y][x] = EMPTY;
fieldMapColor[y][x] = EMPTY;
}
}
}
}
private function fullDrawing():void
{
colorMapping();
drawField();
}
private function colorMapping():void
{
/**
* ゲームフィールドを描画するためのマッピングデータを作成する
*/
for (var y:int = 0; y < FIELD_HEIGHT; y++) {
for (var x:int = 0; x < FIELD_WIDTH; x++) {
//壁のマッピング
if (fieldMap[y][x] == WALL) fieldMapColor[y][x] = WALL_COLOR;
//テトリミノのマッピング
if (y >= location.y && y < location.y + TET_HEIGHT &&
x >= location.x && x < location.x + TET_WIDTH) {
var px:int = x - location.x;
var py:int = y - location.y;
if(currentTetromino[py][px] && (fieldMap[y][x] == EMPTY || gameover)) {
fieldMap[y][x] = currentTetromino[py][px];
fieldMapColor[y][x] = TET_COLORS[currentIndex];
}
}
}
}
}
private function drawField():void
{
/**
* ゲームフィールド描画
*/
var py:Number = 0;
gameField.graphics.clear();
gameField.graphics.lineStyle(1, GRID_COLOR);
for (var y:int = 0; y < fieldMap.length; y++) {
var px:Number = 0;
for (var x:int = 0; x < fieldMap[y].length; x++) {
if (fieldMap[y][x] > EMPTY) {
gameField.graphics.beginFill(fieldMapColor[y][x]);
gameField.graphics.drawRect(px, py, CELL_WIDTH, CELL_HEIGHT);
}
px += CELL_WIDTH;
}
py += CELL_HEIGHT;
}
gameField.graphics.endFill();
}
}
}
/*
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
*/
import flash.display.Sprite;
import flash.display.Shape;
/**
* ...
* @author necoEngine
*/
class NextTetrrominoDisplay extends Sprite {
private const FRAME_WIDTH:uint = 120;
private const FRAME_HEIGHT:uint = 120;
private const CENTER_X:uint = FRAME_WIDTH / 2;
private const CENTER_Y:uint = FRAME_HEIGHT / 2;
private const CELL_WIDTH:uint = 20;
private const CELL_HEIGHT:uint = 20;
private const FRAME_COLOR:uint = 0xFFCC00;
private const GRID_COLOR:uint = 0xC0C0C0;
private var baseFrame:Shape;
private var tetrominoShape:Shape;
public function NextTetrrominoDisplay():void
{
init();
}
private function init():void
{
baseFrame = new Shape();
addChild(baseFrame);
baseFrame.graphics.lineStyle(2, FRAME_COLOR, 1);
baseFrame.graphics.drawRoundRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT, 10, 10);
tetrominoShape = new Shape();
addChild(tetrominoShape);
}
public function drawNextTetromino(mapData:Array, color:int):void
{
/**
* 次に落下させるテトリミノを描画する。
*/
var py:Number = 0;
tetrominoShape.graphics.clear();
tetrominoShape.graphics.lineStyle(1, GRID_COLOR);
for (var y:int = 0; y < mapData.length; y++)
{
var px:Number = 0;
var hit:Boolean;
for (var x:int = 0; x < mapData[y].length; x++) {
if (mapData[y][x]) {
tetrominoShape.graphics.beginFill(color);
tetrominoShape.graphics.drawRect(px, py, CELL_WIDTH, CELL_HEIGHT);
hit = true;
}
px += CELL_WIDTH;
}
if (hit) py += CELL_HEIGHT;
}
tetrominoShape.graphics.endFill();
tetrominoShape.x = -(CELL_WIDTH * 2 - CENTER_X);
//幅が違うものを少し補正
if (tetrominoShape.width > 50 && tetrominoShape.width < 70) tetrominoShape.x -= 10;
tetrominoShape.y = -(tetrominoShape.height / 2 - CENTER_Y);
}
public function tetrominoClear():void { tetrominoShape.graphics.clear(); }
}
/*
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
*/