某テトロミノゲーム
某テトロミノゲーム
数年前に自分用に作った ezアプリを wonderfl に移植してみました。
コードはきれいではないですが、某テトロミノゲームを作りたい人の参考になれば嬉しいです。
- 機能 -
ホールド
回転入れ
ネクスト
ゴースト
出現ミノの偏り補正
- 設定メニュー -
DROP LEVEL
- DROP TIME 経過時にどれだけミノが落ちるか
DROP TIME
- ミノが落ちる間隔
SLIDE TIME
- 横タメ
FIX TIME
- 設置してから固定するまでの時間
CLEAR LINE TIME
- ライン消去時間
ARE
- 固定してから次のミノが出現するまでの時間
KEY SETTING
1 2
右移動 左カーソル sキー
左移動 右カーソル eキー
ハードドロップ 上カーソル fキー
ソフトドロップ 下カーソル dキー
左回転(決定) zキー jキー
右回転 xキー kキー
左回転2 cキー lキー
ホールド シフトキー スペースキー
@author tkinjo
/**
* Copyright tkinjo ( http://wonderfl.net/user/tkinjo )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/mp3f
*/
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
[SWF(width="465", height="465", backgroundColor="0xffffff", frameRate="60")]
/**
* 某テトロミノゲーム
*
* 数年前に自分用に作った ezアプリを wonderfl に移植してみました。
* コードはきれいではないですが、某テトロミノゲームを作りたい人の参考になれば嬉しいです。
*
* - 機能 -
* ホールド
* 回転入れ
* ネクスト
* ゴースト
* 出現ミノの偏り補正
*
* - 設定メニュー -
* DROP LEVEL
* - DROP TIME 経過時にどれだけミノが落ちるか
*
* DROP TIME
* - ミノが落ちる間隔
*
* SLIDE TIME
* - 横タメ
*
* FIX TIME
* - 設置してから固定するまでの時間
*
* CLEAR LINE TIME
* - ライン消去時間
*
* ARE
* - 固定してから次のミノが出現するまでの時間
*
* KEY SETTING
* 1 2
*
* 右移動 左カーソル sキー
* 左移動 右カーソル eキー
* ハードドロップ 上カーソル fキー
* ソフトドロップ 下カーソル dキー
* 左回転(決定) zキー jキー
* 右回転 xキー kキー
* 左回転2 cキー lキー
* ホールド シフトキー スペースキー
*
* @author tkinjo
*/
public class Main extends Sprite
{
public static const APPLICATION_NAME:String = "Test";
private var sceneManager:SceneManager;
public function Main()
{
Setting.stageWidth = stage.stageWidth;
Setting.stageHeight = stage.stageHeight;
sceneManager = SceneManager.instance;
SceneManager.scene = GameSettingScene.instance;
//SceneManager.scene = GameScene.instance;
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
private function enterFrameHandler( event:Event ):void {
sceneManager.run( graphics );
}
private function keyDownHandler( event:KeyboardEvent ):void {
KeyState.pressAtKeyCode( event.keyCode );
}
private function keyUpHandler( event:KeyboardEvent ):void {
KeyState.releaseAtKeyCode( event.keyCode );
}
}
}
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.BitmapData;
import flash.display.CapsStyle;
import flash.display.JointStyle;
import flash.display.LineScaleMode;
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.net.SharedObject;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.errors.IllegalOperationError;
import flash.events.Event;
import flash.events.EventDispatcher;
interface IScene
{
function init():void;
function run():void;
function paint( graphics:Graphics ):void;
}
class SceneManager
{
/** --------------------------------------------------
*
*/
private static var _scene:IScene;
public static function get scene():IScene { return _scene; }
public static function set scene(value:IScene):void
{
_scene = value;
scene.init();
}
private static var _previousScene:IScene;
public static function get previousScene():IScene { return _previousScene; }
/** ==================================================
*
*/
public function SceneManager()
{
if ( _instance )
throw new IllegalOperationError ("Use instance property to get the instance");
}
public function run( graphics:Graphics ):void {
scene.run();
scene.paint( graphics );
}
/** --------------------------------------------------
*
*/
private static var _instance:SceneManager;
public static function get instance():SceneManager {
if ( _instance == null )
_instance = new SceneManager();
return _instance;
}
}
class GameSettingScene implements IScene
{
private var textField:TextField;
private const MENU_ITEM_SIZE:Number = 30;
private const MENU_ITEM_Y:Number = 100;
private const MENU_ITEM_NAME_X:Number = 100;
private const MENU_ITEM_VALUE_X:Number = 300;
private const MENU_FONT:String = "_sans";
private const MENU_FONT_SIZE:Number = 20;
private const MENU_FONT_BOLD:Boolean = true;
private const SELECT_MARKER_RADIUS:Number = 5;
private const SELECT_MARKER_OFFSET_X:Number = -10;
private const SELECT_MARKER_OFFSET_Y:Number = MENU_FONT_SIZE / 2 + 4;
private const KEY_FIRST_REPEAT_INTERVAL:Number = 20;
private const KEY_REPEAT_INTERVAL:Number = 2;
private var selectedIndex:uint;
private var keyRepeatedCounter:Number;
public function GameSettingScene():void {
if ( _instance )
throw new IllegalOperationError ("Use instance property to get the instance");
textField = new TextField();
textField.autoSize = TextFieldAutoSize.LEFT;
textField.defaultTextFormat = new TextFormat( MENU_FONT, MENU_FONT_SIZE, null, MENU_FONT_BOLD );
}
public function init():void {
}
public function run():void {
var keyRepeatCount:uint;
if ( isRepeatInterval( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_UP_INDEX ] ) && selectedIndex > 0 ) {
selectedIndex--;
} else if ( isRepeatInterval( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_DOWN_INDEX ] ) && selectedIndex < Setting.GAME_SETTINGS.length - 1 ) {
selectedIndex++;
} else if ( isRepeatInterval( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_LEFT_INDEX ] ) &&
Setting.gameSettingValues[ selectedIndex ] > Setting.GAME_SETTINGS[ selectedIndex ][ Setting.GAME_SETTING_ITEM_MIN_VALUE_INDEX ] ) {
Setting.gameSettingValues[ selectedIndex ]--;
} else if ( isRepeatInterval( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_RIGHT_INDEX ] ) &&
Setting.gameSettingValues[ selectedIndex ] < Setting.GAME_SETTINGS[ selectedIndex ][ Setting.GAME_SETTING_ITEM_MAX_VALUE_INDEX ] ) {
Setting.gameSettingValues[ selectedIndex ]++;
} else if ( KeyState.getReleased( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_A_INDEX ] ) ) {
if ( Setting.gameSettingValues[ Setting.GAME_SETTING_VALUE_KEY_SETTING_INDEX ] == 1 )
settingKeys( Setting.KEY_SETTINGS );
else
settingKeys( Setting.KEY_SETTINGS_2 );
Setting.flush();
SceneManager.scene = GameScene.instance;
}
}
private function settingKeys( keySetting:Array ):void {
for ( var i:uint = 0; i < keySetting.length; i++ )
Setting.keySettingValues[ i ] = keySetting[ i ][ Setting.KEY_SETTING_ITEM_DEFAULT_VALUE_INDEX ];
}
private function isRepeatInterval( keyString:String ):Boolean {
return KeyState.isRepeatInterval( keyString, KEY_FIRST_REPEAT_INTERVAL, KEY_REPEAT_INTERVAL );
}
public function paint( graphics:Graphics ):void {
graphics.clear();
for ( var i:uint = 0; i < Setting.GAME_SETTINGS.length; i++ )
drawString( graphics, textField, MENU_ITEM_NAME_X, MENU_ITEM_Y + MENU_ITEM_SIZE * i, Setting.GAME_SETTINGS[ i ][ Setting.GAME_SETTING_ITEM_NAME_INDEX ] );
for ( i = 0; i < Setting.GAME_SETTINGS.length; i++ )
drawString( graphics, textField, MENU_ITEM_VALUE_X, MENU_ITEM_Y + MENU_ITEM_SIZE * i, Setting.gameSettingValues[ i ].toString() );
graphics.beginFill( 0 );
graphics.drawCircle( MENU_ITEM_NAME_X + SELECT_MARKER_OFFSET_X, MENU_ITEM_Y + MENU_ITEM_SIZE * selectedIndex + SELECT_MARKER_OFFSET_Y, SELECT_MARKER_RADIUS );
graphics.endFill();
}
private static var _instance:GameSettingScene;
public static function get instance():GameSettingScene {
if ( _instance == null )
_instance = new GameSettingScene();
return _instance;
}
}
class KeyState
{
private static var repeated:Vector.<uint> = new Vector.<uint>( 255 );
private static var pressed :Vector.<Boolean> = new Vector.<Boolean>( 255 );
private static var released:Vector.<Boolean> = new Vector.<Boolean>( 255 );
private static var manualRepeatedCounter:Vector.<uint> = new Vector.<uint>( 255 );
/** --------------------------------------------------
*
*/
public static const SHIFT:String = "shift";
public static const CTRL :String = "ctrl";
public static const ALT :String = "alt";
public static const SPACE:String = "space";
public static const LEFT :String = "left";
public static const UP :String = "up";
public static const RIGHT:String = "right";
public static const DOWN :String = "down";
/** --------------------------------------------------
*
*/
public static const KEY_SHIFT:uint = 16;
public static const KEY_CTRL :uint = 17;
public static const KEY_ALT :uint = 18;
public static const KEY_SPACE:uint = 32;
public static const KEY_LEFT :uint = 37;
public static const KEY_UP :uint = 38;
public static const KEY_RIGHT:uint = 39;
public static const KEY_DOWN :uint = 40;
public static const CHAR_LARGE_A:uint = 65;
public static const CHAR_SMALL_A:uint = 97;
public static const KEY_A :uint = 65;
public static const NUM_ALPHABET:uint = 26;
public static const CHAR_0 :uint = 48;
public static const KEY_0 :uint = 0;
public static const KEY_NUMPAD_0:uint = 96;
public static const NUM_NUMBER:uint = 10;
/** ==================================================
*
*/
public static function reset():void {
repeated = new Vector.<uint>( 255 );
pressed = new Vector.<Boolean>( 255 );
released = new Vector.<Boolean>( 255 );
manualRepeatedCounter = new Vector.<uint>( 255 );
}
public static function press( string:String ):void {
var keyCode:Number = getKeyCode( string );
if ( keyCode > 0 )
pressAtKeyCode( keyCode );
}
public static function release( string:String ):void {
var keyCode:Number = getKeyCode( string );
if ( keyCode > 0 )
releaseAtKeyCode( keyCode );
}
public static function manualRepeatCount( string:String ):void {
var keyCode:Number = getKeyCode( string );
if ( keyCode > 0 )
manualRepeatCountAtKeyCode( keyCode );
}
/** --------------------------------------------------
*
*/
public static function pressAtKeyCode( keyCode:uint ):void {
if ( repeated[ keyCode ] ) {
repeated[ keyCode ]++;
return;
}
repeated[ keyCode ] = 1;
pressed[ keyCode ] = true;
released[ keyCode ] = false;
manualRepeatedCounter[ keyCode ] = 1;
}
public static function releaseAtKeyCode( keyCode:uint ):void {
repeated[ keyCode ] = 0;
pressed[ keyCode ] = false;
released[ keyCode ] = true;
manualRepeatedCounter[ keyCode ] = 0;
}
public static function manualRepeatCountAtKeyCode( keyCode:uint ):void {
manualRepeatedCounter[ keyCode ]++;
}
/** --------------------------------------------------
*
*/
public static function getRepeated( string:String ):Boolean {
var keyCode:Number = getKeyCode( string );
if( keyCode > 0 )
return getRepeatedAtKeyCode( keyCode );
return false;
}
public static function getRepeatedCount( string:String ):uint {
var keyCode:Number = getKeyCode( string );
if( keyCode > 0 )
return getRepeatedCountAtKeyCode( keyCode );
return 0;
}
public static function getReleased( string:String ):Boolean {
var keyCode:Number = getKeyCode( string );
if( keyCode > 0 )
return getReleasedAtKeyCode( keyCode );
return false;
}
public static function getPressed( string:String ):Boolean {
var keyCode:Number = getKeyCode( string );
if( keyCode > 0 )
return getPressedAtKeyCode( keyCode );
return false;
}
public static function getManualRepeatedCount( string:String ):uint {
var keyCode:Number = getKeyCode( string );
if( keyCode > 0 )
return getManualRepeatedCountAtKeyCode( keyCode );
return 0;
}
/** --------------------------------------------------
*
*/
public static function getRepeatedAtKeyCode( keyCode:uint ):Boolean {
return ( repeated[ keyCode ] > 0 ) ? true : false;
}
public static function getRepeatedCountAtKeyCode( keyCode:uint ):uint {
return repeated[ keyCode ];
}
public static function getReleasedAtKeyCode( keyCode:uint ):Boolean {
var result:Boolean = released[ keyCode ];
released[ keyCode ] = false;
return result;
}
public static function getPressedAtKeyCode( keyCode:uint ):Boolean {
var result:Boolean = pressed[ keyCode ];
pressed[ keyCode ] = false;
return result;
}
public static function getManualRepeatedCountAtKeyCode( keyCode:uint ):uint {
return manualRepeatedCounter[ keyCode ];
}
/** --------------------------------------------------
*
*/
public static function getKeyCode( string:String ):Number {
if( string.length == 1 ) {
return charCodeToKeyCode( string.charCodeAt() );
}
switch ( string ) {
case SHIFT:
return KEY_SHIFT;
case CTRL:
return KEY_CTRL;
case ALT:
return KEY_ALT;
case SPACE:
return KEY_SPACE;
case LEFT:
return KEY_LEFT;
case UP:
return KEY_UP;
case RIGHT:
return KEY_RIGHT;
case DOWN:
return KEY_DOWN;
}
return -1;
}
public static function charCodeToKeyCode( charCode:uint ):Number {
// A-Z
if ( CHAR_LARGE_A <= charCode && ( charCode - CHAR_LARGE_A ) < NUM_ALPHABET ) {
return charCode;
// a-z
} else if ( CHAR_SMALL_A <= charCode && ( charCode - CHAR_SMALL_A ) < NUM_ALPHABET ) {
return KEY_A + ( charCode - CHAR_SMALL_A );
// 0-9
} else if ( CHAR_0 <= charCode && ( charCode - CHAR_0 ) < NUM_NUMBER ) {
return charCode;
}
return -1;
}
public static function isRepeatInterval( keyString:String, keyFirstRepeatInterval:uint, keyRepeatInterval:uint ):Boolean {
if ( !KeyState.getRepeated( keyString ) )
return false;
if ( KeyState.getPressed( keyString ) )
return true;
KeyState.manualRepeatCount( keyString );
var keyRepeatCount:uint = KeyState.getManualRepeatedCount( keyString );
if (
keyRepeatCount == keyFirstRepeatInterval ||
(
keyRepeatCount > keyFirstRepeatInterval &&
( keyRepeatCount - keyFirstRepeatInterval ) % keyRepeatInterval == 0
)
) {
return true;
}
return false;
}
}
class Setting
{
public static const GAME_SETTING_ITEM_NAME_INDEX:uint = 0;
public static const GAME_SETTING_ITEM_DEFAULT_VALUE_INDEX:uint = 1;
public static const GAME_SETTING_ITEM_MIN_VALUE_INDEX:uint = 2;
public static const GAME_SETTING_ITEM_MAX_VALUE_INDEX:uint = 3;
public static const GAME_SETTINGS:Array = [
[ "DROP LEVEL" , 1 , 0, 20 ],
[ "DROP TIME" , 100, 0, 100 ],
[ "SLIDE TIME" , 10, 0, 100 ],
[ "FIX TIME" , 100, 0, 100 ],
[ "CLEAR LINE TIME" , 10, 0, 100 ],
[ "ARE" , 10, 0, 100 ],
[ "KEY SETTING", 1, 1, 2 ]
];
public static const GAME_SETTING_VALUE_DROP_LEVEL_INDEX:uint = 0;
public static const GAME_SETTING_VALUE_DROP_TIME_INDEX:uint = 1;
public static const GAME_SETTING_VALUE_SLIDE_TIME_INDEX:uint = 2;
public static const GAME_SETTING_VALUE_FIX_TIME_INDEX:uint = 3;
public static const GAME_SETTING_VALUE_CLEAR_TIME_INDEX:uint = 4;
public static const GAME_SETTING_VALUE_ARE_INDEX:uint = 5;
public static const GAME_SETTING_VALUE_KEY_SETTING_INDEX:uint = 6;
/** --------------------------------------------------
*
*/
public static const KEY_SETTING_ITEM_NAME_INDEX:uint = 0;
public static const KEY_SETTING_ITEM_DEFAULT_VALUE_INDEX:uint = 1;
public static const KEY_SETTINGS:Array = [
[ "LEFT" , KeyState.LEFT ],
[ "UP" , KeyState.UP ],
[ "RIGHT", KeyState.RIGHT ],
[ "DOWN" , KeyState.DOWN ],
[ "A", "z" ],
[ "B", "x" ],
[ "C", "c" ],
[ "D", KeyState.SHIFT ]
];
public static const KEY_SETTINGS_2:Array = [ // temp
[ "LEFT" , "s" ],
[ "UP" , "e" ],
[ "RIGHT", "f" ],
[ "DOWN" , "d" ],
[ "A", "j" ],
[ "B", "k" ],
[ "C", "l" ],
[ "D", KeyState.SPACE ]
];
public static const KEY_SETTING_VALUE_LEFT_INDEX:uint = 0;
public static const KEY_SETTING_VALUE_UP_INDEX:uint = 1;
public static const KEY_SETTING_VALUE_RIGHT_INDEX:uint = 2;
public static const KEY_SETTING_VALUE_DOWN_INDEX:uint = 3;
public static const KEY_SETTING_VALUE_A_INDEX:uint = 4;
public static const KEY_SETTING_VALUE_B_INDEX:uint = 5;
public static const KEY_SETTING_VALUE_C_INDEX:uint = 6;
public static const KEY_SETTING_VALUE_D_INDEX:uint = 7;
/** ==================================================
*
*/
private static var sharedObject:SharedObject;
public static var gameSettingValues:Vector.<Number>;
public static var keySettingValues:Vector.<String>;
public static var stageWidth:Number;
public static var stageHeight:Number;
/** ==================================================
*
*/
public function Setting():void {
if ( _instance )
throw new IllegalOperationError ("Use instance property to get the instance");
// load
sharedObject = SharedObject.getLocal( Main.APPLICATION_NAME );
var i:uint;
//gameSettingValues
var tempGameSettingValues:Vector.<Number> = sharedObject.data.gameSettingValues as Vector.<Number>;
if ( !tempGameSettingValues ) {
gameSettingValues = new Vector.<Number>( GAME_SETTINGS.length )
for ( i = 0; i < GAME_SETTINGS.length; i++ )
gameSettingValues[ i ] = GAME_SETTINGS[ i ][ GAME_SETTING_ITEM_DEFAULT_VALUE_INDEX ];
} else {
gameSettingValues = tempGameSettingValues.concat();
}
//keySettingValues
//keySettingValues = sharedObject.data.keySettingValues; //cast error
var tempKeySettingValues:Vector.<Object> = sharedObject.data.keySettingValues as Vector.<Object>;
if ( tempKeySettingValues ) {
keySettingValues = new Vector.<String>( KEY_SETTINGS.length );
for ( var key:String in tempKeySettingValues )
keySettingValues[ key ] = tempKeySettingValues[ key ] as String;
}
if ( !keySettingValues ) {
keySettingValues = new Vector.<String>( KEY_SETTINGS.length );
for ( i = 0; i < KEY_SETTINGS.length; i++ )
keySettingValues[ i ] = KEY_SETTINGS[ i ][ KEY_SETTING_ITEM_DEFAULT_VALUE_INDEX ];
}
}
public static function flush():void {
sharedObject.data.gameSettingValues = gameSettingValues;
sharedObject.data.keySettingValues = keySettingValues;
sharedObject.flush();
}
public static function flushGameSettingValues():void {
sharedObject.data.gameSettingValues = gameSettingValues;
sharedObject.flush();
}
public static function flushKeySettingValues():void {
sharedObject.data.keySettingValues = keySettingValues;
sharedObject.flush();
}
public static function clear():void {
sharedObject.clear();
}
private static var _instance:Setting = instance;
public static function get instance():Setting {
if ( _instance == null )
_instance = new Setting();
return _instance;
}
}
class GameSystem extends EventDispatcher
{
/**
* ...
* @eventType com.tkinjo.tetromino.system.Field.GAME_OVER
*/
[Event(name = "gameOver", type = "com.tkinjo.tetromino.system.GameSystem")]
public static const GAME_OVER:String = "gameOver";
/**
* ...
* @eventType com.tkinjo.tetromino.system.Field.GAME_OVER
*/
[Event(name = "entry", type = "com.tkinjo.tetromino.system.GameSystem")]
public static const ENTRY:String = "entry";
/**
* ...
* @eventType com.tkinjo.tetromino.event.ClearLineEvent.CLEAR_LINE
*/
[Event(name = "clearLine", type = "com.tkinjo.tetromino.event.ClearLineEvent")]
/** ==================================================
*
*/
public static const FIELD_HORIZONTAL_LENGTH:Number = 10;
public static const FIELD_VERTICAL_LENGTH:Number = 20;
private var _data:Array;
public function get data():Array { return _data; }
public function get drawData():Array {
var drawData:Array = new Array( FIELD_HORIZONTAL_LENGTH );
for ( var i:uint = 0; i < drawData.length; i++ ) {
drawData[ i ] = new Array( FIELD_VERTICAL_LENGTH );
for ( var j:uint = 0; j < drawData[ i ].length; j++ ) {
drawData[ i ][ j ] = data[ i + 4 ][ j + 4 ];
}
}
if( currentMino && !( areTimerRunning || clearLineTimerRunning ) ) {
for ( i = 0; i < currentMino.length; i++ ) {
for ( j = 0; j < currentMino.length; j++ ) {
if ( currentMino.data[ i ][ j ] == 0 || currentMinoY + j - 4 < 0 )
continue
drawData[ currentMinoX + i - 4 ][ currentMinoY + j - 4 ] = currentMino.data[ i ][ j ];
}
}
}
return drawData;
}
private var minoManager:MinoManager;
private var _currentMino:Mino;
public function get currentMino():Mino { return _currentMino; }
private var currentMinoX:uint;
public function get ghostMinoX():uint { return currentMinoX - 4; }
private var currentMinoY:uint;
public function get ghostMinoY():uint { return dropY - 4; }
private var maxDroppedY:uint;
private var rotationSystem:IRotationSystem;
private var held:Boolean;
public var dropLevel:uint;
public var dropTime:uint;
private var dropTimer:int;
public var fixTime:uint;
private var fixTimer:int;
private var _fixTimerRunning:Boolean;
public function get fixTimerRunning():Boolean { return _fixTimerRunning; }
public var clearLineTime:uint;
private var clearLineTimer:int;
private var _clearLineTimerRunning:Boolean;
public function get clearLineTimerRunning():Boolean { return _clearLineTimerRunning; }
private var _clearLineNums:Vector.<uint>;
public function get clearLineNums():Vector.<uint> { return _clearLineNums; }
public var are:uint;
private var areTimer:int;
private var _areTimerRunning:Boolean;
public function get areTimerRunning():Boolean { return _areTimerRunning; }
private var time:uint;
private var _running:Boolean;
public function get running():Boolean { return _running; }
/** ==================================================
*
*/
public function GameSystem( minoManager:MinoManager, rotationSystem:IRotationSystem )
{
this.minoManager = minoManager;
this.rotationSystem = rotationSystem;
createField();
}
private function createField():void {
var i:uint, j:uint;
_data = new Array( FIELD_HORIZONTAL_LENGTH + 4 * 2 );
for ( i = 0; i < FIELD_HORIZONTAL_LENGTH + 4 * 2; i++ ) {
_data[ i ] = new Array( FIELD_VERTICAL_LENGTH + 4 * 2 );
for ( j = 0; j < data[ 0 ].length; j++ ) {
if ( i < 4 || i >= data.length - 4 || j >= data[ 0 ].length - 4 ) {
data[ i ][ j ] = -1;
} else {
data[ i ][ j ] = 0;
}
}
}
}
public function start():void {
_running = true;
next();
}
/** --------------------------------------------------
* run
*/
public function run():void {
//*
if ( !running )
return;
if ( dropTimer <= 0 ) {
for ( var i:uint = 0; i < dropLevel; i++ ) {
if ( !softDrop() )
break;
}
dropTimer = dropTime;
}
if ( fixTimerRunning && !areTimerRunning && !clearLineTimerRunning ) {
if ( fixTimer <= 0 ) {
fix();
_fixTimerRunning = false;
setClearLineNums();
if ( isClearLine() ) {
_clearLineTimerRunning = true;
dispatchEvent( new ClearLineEvent( ClearLineEvent.CLEAR, clearLineNums ) );
} else {
_areTimerRunning = true;
}
}
fixTimer--;
}
if ( clearLineTimerRunning ) {
if ( clearLineTimer <= 0 ) {
_clearLineTimerRunning = false;
clearLine();
dispatchEvent( new ClearLineEvent( ClearLineEvent.CLEARED, clearLineNums ) );
_areTimerRunning = true;
}
clearLineTimer--;
}
if ( areTimerRunning ) {
if ( areTimer <= 0 ) {
_areTimerRunning = false;
next();
dispatchEvent( new Event( ENTRY ) );
if ( isGameOver() ) {
dispatchEvent( new Event( GAME_OVER ) );
_running = false;
return;
}
}
areTimer--;
}
if( !( clearLineTimerRunning || areTimerRunning ) )
dropTimer--;
time++;//*/
}
/** --------------------------------------------------
* control
*/
public function moveLeft():Boolean {
if ( !isOverlap( -1, 0 ) ) {
currentMinoX--;
return true;
}
return false;
}
public function moveRight():Boolean {
if ( !isOverlap( 1, 0 ) ) {
currentMinoX++;
return true;
}
return false;
}
public function softDrop():Boolean {
if ( !isOverlap( 0, 1 ) ) {
currentMinoY++;
if ( currentMinoY > maxDroppedY ) {
maxDroppedY = currentMinoY;
fixTimer = fixTime;
_fixTimerRunning = false;
}
return true;
}
_fixTimerRunning = true;
return false;
}
public function hardDrop():void {
_fixTimerRunning = true;
fixTimer = 0;
}
private function get dropY():uint {
for ( var i:uint = 0; i < FIELD_VERTICAL_LENGTH + 4; i++ ) {
if ( isOverlap( 0, i ) )
return i - 1 + currentMinoY;
}
return 0;
}
public function hold():Boolean {
if ( held || !running )
return false;
held = true;
minoManager.hold();
reset();
return true;
}
public function spinLeft():Boolean {
return trySpin( SpinDirection.LEFT );
}
public function spinRight():Boolean {
return trySpin( SpinDirection.RIGHT );
}
private function trySpin( direction:String ):Boolean {
spinCurrentMino( direction );
var spunPoint:Point = rotationSystem.getSpunPoint( this, direction );
if ( spunPoint ) {
currentMinoX += spunPoint.x;
currentMinoY += spunPoint.y;
return true;
}
spinCurrentMino( SpinDirection.reverse( direction ) );
return false;
}
private function spinCurrentMino( direction:String ):void {
if( !currentMino )
return;
if( direction == SpinDirection.LEFT )
currentMino.spinLeft();
else if( direction == SpinDirection.RIGHT )
currentMino.spinRight();
}
/** --------------------------------------------------
*
* @param dx
* @param dy
* @return
*/
public function isOverlap( dx:int, dy:int ):Boolean {
if ( !currentMino )
return true;
for ( var i:uint = 0; i < currentMino.length; i++ ) {
for ( var j:uint = 0; j < currentMino.length; j++ ) {
if( currentMino.data[ i ][ j ] > 0 && data[ currentMinoX + i + dx ][ currentMinoY + j + dy ] != 0 )
return true;
}
}
return false;
}
/** --------------------------------------------------
* fix
*/
public function fix():void {
if ( !currentMino ) {
next();
return;
}
currentMinoY = dropY;
for ( var i:uint = 0; i < currentMino.length; i++ ) {
for ( var j:uint = 0; j < currentMino.length; j++ ) {
if ( currentMino.data[ i ][ j ] == 0 )
continue;
data[ currentMinoX + i ][ currentMinoY + j ] = currentMino.data[ i ][ j ];
}
}
}
/** --------------------------------------------------
* clearLine
*/
private function isClearLine():Boolean {
if ( clearLineNums.length > 0 )
return true;
return false;
}
private function clearLine():void {
var clearLineCounter:uint;
for ( var i:int = FIELD_VERTICAL_LENGTH + 4 - 1; i >= 0; i-- ) {
if ( isClearLineAt( i ) ) {
clearLineAt( i );
clearLineCounter++;
} else {
moveLine( i, clearLineCounter );
}
}
}
private function setClearLineNums():Vector.<uint> {
_clearLineNums = new Vector.<uint>();
for ( var i:int = FIELD_VERTICAL_LENGTH + 4 - 1; i >= 0; i-- ) {
if ( isClearLineAt( i ) )
clearLineNums.push( i );
}
if ( clearLineNums.length > 0 )
return clearLineNums;
return null;
}
private function clearLineAt( lineNum:uint ):void {
for ( var i:uint = 0; i < FIELD_HORIZONTAL_LENGTH; i++ )
data[ i + 4 ][ lineNum ] = 0;
}
private function moveLine( lineNum:uint, how:uint ):void {
for ( var i:uint = 0; i < FIELD_HORIZONTAL_LENGTH; i++ )
data[ i + 4 ][ lineNum + how ] = data[ i + 4 ][ lineNum ];
}
private function isClearLineAt( lineNum:uint ):Boolean {
var blockCounter:uint;
for ( var i:uint = 0; i < FIELD_HORIZONTAL_LENGTH; i++ ) {
if ( data[ i + 4 ][ lineNum ] > 0 )
blockCounter++;
}
if ( blockCounter == FIELD_HORIZONTAL_LENGTH )
return true;
return false;
}
/** --------------------------------------------------
* next
*/
public function next():void {
held = false;
minoManager.next();
reset();
}
private function reset():void {
_currentMino = minoManager.currentMino;
currentMinoX = 7;
currentMinoY = currentMino.length == 3 ? 2 : 1;
dropTimer = dropTime;
areTimer = are;
_areTimerRunning = false;
fixTimer = fixTime;
_fixTimerRunning = false;
clearLineTimer = clearLineTime;
_clearLineTimerRunning = false;
}
/** --------------------------------------------------
* gameOver
*/
private function isGameOver():Boolean {
for ( var i:uint = 0; i < currentMino.length; i++ ) {
for ( var j:uint = 0; j < currentMino.length; j++ ) {
if ( currentMino.data[ i ][ j ] > 0 && data[ currentMinoX + i ][ currentMinoY + j ] > 0 )
return true;
}
}
return false;
}
}
class ClearLineEvent extends Event
{
public static const CLEAR:String = "clearLine";
public static const CLEARED:String = "cleared";
private var _lineNums:Vector.<uint>;
public function get lineNums():Vector.<uint> { return _lineNums; }
public function ClearLineEvent( type:String, lineNums:Vector.<uint>, bubbles:Boolean = false, cancelable:Boolean = false )
{
_lineNums = lineNums;
super( type, bubbles, cancelable );
}
}
class MinoManager
{
public static const MINO_I:String = "minoI";
public static const MINO_I_COLOR:uint = 0x0087ceeb;
public static const MINO_I_DATA:Array = [
/*[ 0, 0, 0, 0 ],
[ 1, 1, 1, 1 ],
[ 0, 0, 0, 0 ],
[ 0, 0, 0, 0 ]//*/
[ 0, 1, 0, 0 ],
[ 0, 1, 0, 0 ],
[ 0, 1, 0, 0 ],
[ 0, 1, 0, 0 ]
];
public static const MINO_O:String = "minoO";
public static const MINO_O_COLOR:uint = 0x00ffff00;
public static const MINO_O_DATA:Array = [
[ 0, 0, 0, 0 ],
[ 0, 2, 2, 0 ],
[ 0, 2, 2, 0 ],
[ 0, 0, 0, 0 ]
];
public static const MINO_S:String = "minoS";
public static const MINO_S_COLOR:uint = 0x0000ff00;
public static const MINO_S_DATA:Array = [
/*[ 0, 3, 3 ],
[ 3, 3, 0 ],
[ 0, 0, 0 ]//*/
[ 0, 3, 0 ],
[ 3, 3, 0 ],
[ 3, 0, 0 ]
];
public static const MINO_Z:String = "minoZ";
public static const MINO_Z_COLOR:uint = 0x00ff0000;
public static const MINO_Z_DATA:Array = [
/*[ 4, 4, 0 ],
[ 0, 4, 4 ],
[ 0, 0, 0 ]//*/
[ 4, 0, 0 ],
[ 4, 4, 0 ],
[ 0, 4, 0 ]
];
public static const MINO_J:String = "minoJ";
public static const MINO_J_COLOR:uint = 0x000000ff;
public static const MINO_J_DATA:Array = [
/*[ 5, 0, 0 ],
[ 5, 5, 5 ],
[ 0, 0, 0 ]//*/
[ 5, 5, 0 ],
[ 0, 5, 0 ],
[ 0, 5 ,0 ]
];
public static const MINO_L:String = "minoL";
public static const MINO_L_COLOR:uint = 0x00ffa500;
public static const MINO_L_DATA:Array = [
/*[ 0, 0, 6 ],
[ 6, 6, 6 ],
[ 0, 0, 0 ]//*/
[ 0, 6, 0 ],
[ 0, 6, 0 ],
[ 6, 6, 0 ]
];
public static const MINO_T:String = "minoT";
public static const MINO_T_COLOR:uint = 0x00800080;
public static const MINO_T_DATA:Array = [
/*[ 0, 7, 0 ],
[ 7, 7, 7 ],
[ 0, 0, 0 ]//*/
[ 0, 7, 0 ],
[ 7, 7, 0 ],
[ 0, 7, 0 ]
];
public static const NUM_NEXT_MINO:Number = 3;
public static const MINO_TYPES:Vector.<String> = Vector.<String>( [ MINO_I, MINO_O, MINO_S, MINO_Z, MINO_J, MINO_L, MINO_T ] );
public static const MINO_DATAS:Vector.<Array> = Vector.<Array>( [ MINO_I_DATA, MINO_O_DATA, MINO_S_DATA, MINO_Z_DATA, MINO_J_DATA, MINO_L_DATA, MINO_T_DATA ] );
public static const MINO_COLORS:Vector.<uint> = Vector.<uint>( [ MINO_I_COLOR, MINO_O_COLOR, MINO_S_COLOR, MINO_Z_COLOR, MINO_J_COLOR, MINO_L_COLOR, MINO_T_COLOR ] );
/** --------------------------------------------------
*
*/
private var _currentMino:Mino;
public function get currentMino():Mino { return _currentMino; }
private var _holdMino:Mino;
public function get holdMino():Mino { return _holdMino; }
private var _nextMinos:Vector.<Mino>;
public function get nextMinos():Vector.<Mino> { return _nextMinos; }
private var nextMinoStock:Vector.<uint>;
/** ==================================================
*
*/
public function MinoManager()
{
resetNextMinoStock();
_nextMinos = new Vector.<Mino>( NUM_NEXT_MINO );
for ( var i:uint = 0; i < NUM_NEXT_MINO; i++ )
next();
}
public function next():void {
var nextMinoStockIndex:uint = Math.random() * nextMinoStock.length;
var nextMinoIndex:uint = nextMinoStock[ nextMinoStockIndex ];
nextMinoStock.splice( nextMinoStockIndex, 1 );
if ( nextMinoStock.length == 0 )
resetNextMinoStock();
// set currentMino
_currentMino = _nextMinos[ 0 ];
// set nextMinos
for ( var i:uint = 0; i < nextMinos.length - 1; i++ )
_nextMinos[ i ] = nextMinos[ i + 1 ];
_nextMinos[ nextMinos.length - 1 ] = new Mino( MINO_TYPES[ nextMinoIndex ] );
}
private function resetNextMinoStock():void {
nextMinoStock = new Vector.<uint>( MINO_TYPES.length );
for ( var i:uint = 0; i < nextMinoStock.length; i++ )
nextMinoStock[ i ] = i;
}
public function hold():void {
if ( holdMino ) {
var heldMino:Mino = holdMino;
_holdMino = new Mino( currentMino.type );
_currentMino = heldMino;
} else {
_holdMino = currentMino;
next();
}
}
public static function getMinoData( type:String ):Array {
switch( type ) {
case MINO_I:
return MINO_I_DATA;
case MINO_O:
return MINO_O_DATA;
case MINO_S:
return MINO_S_DATA;
case MINO_Z:
return MINO_Z_DATA;
case MINO_J:
return MINO_J_DATA;
case MINO_L:
return MINO_L_DATA;
case MINO_T:
return MINO_T_DATA;
}
return null;
}
public static function getMinoColor( type:String ):uint {
switch( type ) {
case MINO_I:
return MINO_I_COLOR;
case MINO_O:
return MINO_O_COLOR;
case MINO_S:
return MINO_S_COLOR;
case MINO_Z:
return MINO_Z_COLOR;
case MINO_J:
return MINO_J_COLOR;
case MINO_L:
return MINO_L_COLOR;
case MINO_T:
return MINO_T_COLOR;
}
return 0;
}
public static function getMinoColorAtDataValue( index:uint ):uint {
return MINO_COLORS[ index - 1 ];
}
}
class Mino
{
public static const ROTATION_0:uint = 0;
public static const ROTATION_90:uint = 1;
public static const ROTATION_180:uint = 2;
public static const ROTATION_270:uint = 3;
private var _type:String;
public function get type():String { return _type; }
private var _rotationState:uint;
public function get rotationState():uint { return _rotationState; }
private var _data:Array;
public function get data():Array { return _data; }
private var _length:uint;
public function get length():uint { return _length; }
private var _color:uint;
public function get color():uint { return _color; }
public function Mino( type:String )
{
_type = type;
_color = MinoManager.getMinoColor( type );
_data = MinoManager.getMinoData( type );
_length = data.length;
}
public function spinLeft():void {
if( rotationState - 1 >= ROTATION_0 )
_rotationState--;
else
_rotationState = ROTATION_270;
spin( SpinDirection.LEFT );
}
public function spinRight():void {
if( rotationState + 1 <= ROTATION_270 )
_rotationState++;
else
_rotationState = ROTATION_0;
spin( SpinDirection.RIGHT );
}
private function spin( direction:String ):void {
var i:uint, j:uint;
var spunData:Array = new Array( length );
for ( i = 0; i < length; i++ )
spunData[ i ] = new Array( length );
for ( i = 0; i < length; i++ ) {
for ( j = 0; j < length; j++ ) {
if ( direction == SpinDirection.LEFT ) {
if( length == 3 )
spunData[ j ][ 2 - i ] = data[ i ][ j ];
if( length == 4 )
spunData[ j ][ 3 - i ] = data[ i ][ j ];
} else if ( direction == SpinDirection.RIGHT ) {
if( length == 3 )
spunData[ 2 - j ][ i ] = data[ i ][ j ];
if( length == 4 )
spunData[ 3 - j ][ i ] = data[ i ][ j ];
}
}
}
_data = spunData;
}
}
interface IRotationSystem
{
function getSpunPoint( gameSystem:GameSystem, direction:String ):Point;
}
class SpinDirection
{
public static const LEFT:String = "left";
public static const RIGHT:String = "right";
public static function reverse( direction:String ):String {
if ( direction == LEFT )
return RIGHT;
else ( direction == RIGHT )
return LEFT;
}
}
/**
* --- 参考 ---
* [SONIC KONTROLL] TETRiS-Side(Category-TOP - 小ネタ - テトラミノ回転法則(WORLDルール)
* http://www.din.or.jp/~koryan/tetris/index_cj.htm
*/
class World implements IRotationSystem
{
private static const X_INDEX:uint = 0;
private static const Y_INDEX:uint = 1;
/*
private static const MINO_SIZE_3_LEFT_SPIN_RULE:Array = [
[ [ 1, 0 ], [ 1, -1 ], [ 0, 2 ], [ 1, 2 ] ], // L0 > L1
[ [ -1, 0 ], [ -1, 1 ], [ 0, -2 ], [ -1, -2 ] ], // L1 > L2
[ [ -1, 0 ], [ -1, -1 ], [ 0, 2 ], [ -1, 2 ] ], // L2 > L3
[ [ 1, 0 ], [ 1, 1 ], [ 0, -2 ], [ 1, -2 ] ] // L3 > L0
];
private static const MINO_SIZE_3_RIGHT_SPIN_RULE:Array = [
[ [ -1, 0 ], [ -1, 1 ], [ 0, -2 ], [ -1, -2 ] ], // R3 > R0
[ [ 1, 0 ], [ 1, -1 ], [ 0, 2 ], [ 1, 2 ] ], // R2 > R3
[ [ 1, 0 ], [ 1, 1 ], [ 0, -2 ], [ 1, -2 ] ], // R1 > R2
[ [ -1, 0 ], [ -1, -1 ], [ 0, 2 ], [ -1, 2 ] ] // R0 > R1
];
private static const MINO_SIZE_4_LEFT_SPIN_RULE:Array = [
[ [ -1, 0 ], [ 2, 0 ], [ -1, -2 ], [ 2, 1 ] ], // 0 > 1
[ [ -2, 0 ], [ 1, 0 ], [ 1, -2 ], [ -2, 1 ] ], // 1 > 2
[ [ -2, 0 ], [ 1, 0 ], [ -2, -1 ], [ 1, 1 ] ], // 2 > 3
[ [ 2, 0 ], [ -1, 0 ], [ 2, -1 ], [ -1, 2 ] ] // 3 > 0
];
private static const MINO_SIZE_4_RIGHT_SPIN_RULE:Array = [
[ [ -2, 0 ], [ 1, 0 ], [ 1, -2 ], [ -2, 1 ] ], // 0 > 3
[ [ 2, 0 ], [ -1, 0 ], [ -1, -2 ], [ 2, 1 ] ], // 3 > 2
[ [ 2, 0 ], [ -1, 0 ], [ 2, -1 ], [ -1, 1 ] ], // 2 > 1
[ [ -2, 0 ], [ 1, 0 ], [ -2, -1 ], [ 1, 2 ] ] // 1 > 0
];
//*/
private static const MINO_SIZE_3_LEFT_SPIN_RULE:Array = [
[ [ 1, 0 ], [ 1, 1 ], [ 0, -2 ], [ 1, -2 ] ], // L3 > L0
[ [ -1, 0 ], [ -1, -1 ], [ 0, 2 ], [ -1, 2 ] ], // L2 > L3
[ [ -1, 0 ], [ -1, 1 ], [ 0, -2 ], [ -1, -2 ] ], // L1 > L2
[ [ 1, 0 ], [ 1, -1 ], [ 0, 2 ], [ 1, 2 ] ] // L0 > L1
];
private static const MINO_SIZE_3_RIGHT_SPIN_RULE:Array = [
[ [ -1, 0 ], [ -1, 1 ], [ 0, -2 ], [ -1, -2 ] ], // R3 > R0
[ [ -1, 0 ], [ -1, -1 ], [ 0, 2 ], [ -1, 2 ] ], // R0 > R1
[ [ 1, 0 ], [ 1, 1 ], [ 0, -2 ], [ 1, -2 ] ], // R1 > R2
[ [ 1, 0 ], [ 1, -1 ], [ 0, 2 ], [ 1, 2 ] ] // R2 > R3
];
private static const MINO_SIZE_4_LEFT_SPIN_RULE:Array = [
[ [ 2, 0 ], [ -1, 0 ], [ 2, -1 ], [ -1, 2 ] ], // 3 > 0
[ [ -2, 0 ], [ 1, 0 ], [ -2, -1 ], [ 1, 1 ] ], // 2 > 3
[ [ -2, 0 ], [ 1, 0 ], [ 1, -2 ], [ -2, 1 ] ], // 1 > 2
[ [ -1, 0 ], [ 2, 0 ], [ -1, -2 ], [ 2, 1 ] ] // 0 > 1
];
private static const MINO_SIZE_4_RIGHT_SPIN_RULE:Array = [
[ [ -2, 0 ], [ 1, 0 ], [ -2, -1 ], [ 1, 2 ] ], // 1 > 0
[ [ -2, 0 ], [ 1, 0 ], [ 1, -2 ], [ -2, 1 ] ], // 0 > 3
[ [ 2, 0 ], [ -1, 0 ], [ -1, -2 ], [ 2, 1 ] ], // 3 > 2
[ [ 2, 0 ], [ -1, 0 ], [ 2, -1 ], [ -1, 1 ] ] // 2 > 1
];
public function World()
{
if ( _instance )
throw new IllegalOperationError ("Use instance property to get the instance");
}
public function getSpunPoint( gameSystem:GameSystem, direction:String ):Point {
if( !gameSystem.isOverlap( 0, 0 ) )
return new Point( 0, 0 );
var rule:Array;
if ( !gameSystem.currentMino )
return null;
if ( gameSystem.currentMino.length == 3 ) {
if ( direction == SpinDirection.LEFT )
rule = MINO_SIZE_3_LEFT_SPIN_RULE;
else
rule = MINO_SIZE_3_RIGHT_SPIN_RULE;
} else {
if ( direction == SpinDirection.LEFT )
rule = MINO_SIZE_4_LEFT_SPIN_RULE;
else
rule = MINO_SIZE_4_RIGHT_SPIN_RULE;
}
var xSign:int = 1;// ( gameSystem.currentMino.type == MinoManager.MINO_L || gameSystem.currentMino.type == MinoManager.MINO_S ) ? -1 : 1;
for ( var i:uint = 0; i < rule[ gameSystem.currentMino.rotationState ].length; i++ ) {
if( !gameSystem.isOverlap( xSign * rule[ gameSystem.currentMino.rotationState ][ i ][ X_INDEX ], rule[ gameSystem.currentMino.rotationState ][ i ][ Y_INDEX ] ) )
return new Point( xSign * rule[ gameSystem.currentMino.rotationState ][ i ][ X_INDEX ], rule[ gameSystem.currentMino.rotationState ][ i ][ Y_INDEX ] );
}
return null;
}
private static var _instance:World;
public static function get instance():World {
if ( _instance == null )
_instance = new World();
return _instance;
}
}
class GameScene implements IScene
{
public static const NEXT_FRAME_BORDER_THICKNESS:Number = 4;
public static const NEXT_FRAME_BORDER_COLOR:uint = 0xcccccc;
public static const NEXT_FRAME_WIDTH:Number = 70;
public static const NEXT_FRAME_HEIGHT:Number = 70;
public static const NEXT_FRAME_X:Number = 370;
public static const NEXT_FRAME_Y:Number = 50;
public static const NEXT_FRAME_OFFSET_Y:Number = 10;
public static const HOLD_FRAME_BORDER_THICKNESS:Number = 4;
public static const HOLD_FRAME_BORDER_COLOR:uint = 0xcccccc;
public static const HOLD_FRAME_WIDTH:Number = 70;
public static const HOLD_FRAME_HEIGHT:Number = 70;
public static const HOLD_FRAME_X:Number = 25;
public static const HOLD_FRAME_Y:Number = 50;
public static const FIELD_BORDER_THICKNESS:Number = 2;
public static const FIELD_BORDER_COLOR:uint = 0xdddddd;
public static const BLOCK_WIDTH:Number = 20;
public static const FIELD_WIDTH:Number = GameSystem.FIELD_HORIZONTAL_LENGTH * ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) + FIELD_BORDER_THICKNESS;
public static const FIELD_HEIGHT:Number = GameSystem.FIELD_VERTICAL_LENGTH * ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) + FIELD_BORDER_THICKNESS;
private static const GAME_OVER_BLOCK_COLOR:uint = 0xcccccc;
private static const GAME_OVER_MESSAGE_FONT:String = "_sans";
private static const GAME_OVER_MESSAGE_FONT_SIZE:Number = 50;
private static const GAME_OVER_MESSAGE_FONT_BOLD:Boolean = true;
private static const SCORE_X:uint = 10;
private static const SCORE_Y:uint = 200;
private static const SCORE_FONT:String = "_sans";
private static const SCORE_FONT_SIZE:Number = 20;
private static const SCORE_FONT_BOLD:Boolean = true;
private const KEY_FIRST_REPEAT_INTERVAL:Number = 20;
private const KEY_REPEAT_INTERVAL:Number = 2;
/** --------------------------------------------------
*
*/
private var backgroundBitmapData:BitmapData;
private var scoreTextField:TextField;
private var gameOverTextField:TextField;
private var minoManager:MinoManager;
private var gameSystem:GameSystem;
private var gameOver:Boolean;
private var clearLineCounter:uint;
/** ==================================================
*
*/
public function GameScene()
{
if ( _instance )
throw new IllegalOperationError ("Use instance property to get the instance");
scoreTextField = new TextField();
scoreTextField.autoSize = TextFieldAutoSize.LEFT;
scoreTextField.defaultTextFormat = new TextFormat( SCORE_FONT, SCORE_FONT_SIZE, null, SCORE_FONT_BOLD );
gameOverTextField = new TextField();
gameOverTextField.autoSize = TextFieldAutoSize.LEFT;
gameOverTextField.defaultTextFormat = new TextFormat( GAME_OVER_MESSAGE_FONT, GAME_OVER_MESSAGE_FONT_SIZE, null, GAME_OVER_MESSAGE_FONT_BOLD );
createBackground();
}
public function init():void {
gameOver = false;
clearLineCounter = 0;
minoManager = new MinoManager();
gameSystem = new GameSystem( minoManager, World.instance );
gameSystem.dropLevel = Setting.gameSettingValues[ Setting.GAME_SETTING_VALUE_DROP_LEVEL_INDEX ];
gameSystem.dropTime = Setting.gameSettingValues[ Setting.GAME_SETTING_VALUE_DROP_TIME_INDEX ];
gameSystem.fixTime = Setting.gameSettingValues[ Setting.GAME_SETTING_VALUE_FIX_TIME_INDEX ];
gameSystem.clearLineTime = Setting.gameSettingValues[ Setting.GAME_SETTING_VALUE_CLEAR_TIME_INDEX ];
gameSystem.are = Setting.gameSettingValues[ Setting.GAME_SETTING_VALUE_ARE_INDEX ];
gameSystem.start();
gameSystem.addEventListener( GameSystem.GAME_OVER, gameOverHandler );
gameSystem.addEventListener( ClearLineEvent.CLEARED, clearLineHandler );
gameSystem.addEventListener( GameSystem.ENTRY, entryHandler );
}
private function gameOverHandler( event:Event ):void {
gameOver = true;
KeyState.reset();
}
private function clearLineHandler( event:ClearLineEvent ):void {
clearLineCounter += event.lineNums.length;
}
private function entryHandler( event:Event ):void {
if ( KeyState.getRepeated( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_A_INDEX ] ) )
gameSystem.spinLeft();
if ( KeyState.getRepeated( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_B_INDEX ] ) )
gameSystem.spinRight();
if ( KeyState.getRepeated( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_C_INDEX ] ) )
gameSystem.spinLeft();
}
private function get fieldX():uint { return ( Setting.stageWidth - FIELD_WIDTH ) / 2; }
private function get fieldY():uint { return ( Setting.stageHeight - FIELD_HEIGHT ) / 2; }
private function createBackground():void {
var backgroundCanvas:Sprite = new Sprite();
var graphics:Graphics = backgroundCanvas.graphics;
backgroundBitmapData = new BitmapData( Setting.stageWidth, Setting.stageHeight );
var i:uint;
graphics.lineStyle( FIELD_BORDER_THICKNESS, FIELD_BORDER_COLOR, 1, false, LineScaleMode.NONE, CapsStyle.NONE, JointStyle.MITER );
/** --------------------------------------------------
* gameSystem frame
*/
// vertical line
for ( i = 0; i < GameSystem.FIELD_HORIZONTAL_LENGTH + 1; i++ ) {
graphics.moveTo( ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * i + FIELD_BORDER_THICKNESS / 2 + fieldX, fieldY );
graphics.lineTo( ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * i + FIELD_BORDER_THICKNESS / 2 + fieldX, FIELD_HEIGHT + fieldY );
}
// horizontal line
for ( i = 0; i < GameSystem.FIELD_VERTICAL_LENGTH + 1; i++ ) {
graphics.moveTo( fieldX, ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * i + FIELD_BORDER_THICKNESS / 2 + fieldY );
graphics.lineTo( FIELD_WIDTH + fieldX, ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * i + FIELD_BORDER_THICKNESS / 2 + fieldY );
}
/** --------------------------------------------------
* next frame
*/
/*
graphics.lineStyle( NEXT_FRAME_BORDER_THICKNESS, NEXT_FRAME_BORDER_COLOR, 1, false, LineScaleMode.NONE, CapsStyle.NONE, JointStyle.MITER );
for ( i = 0; i < MinoManager.NUM_NEXT; i++ )
graphics.drawRect( NEXT_FRAME_X, NEXT_FRAME_Y + ( NEXT_FRAME_HEIGHT + NEXT_FRAME_OFFSET_Y ) * i, NEXT_FRAME_WIDTH, NEXT_FRAME_HEIGHT );
//*/
/** --------------------------------------------------
* hold frame
*/
/*
graphics.lineStyle( HOLD_FRAME_BORDER_THICKNESS, HOLD_FRAME_BORDER_COLOR, 1, false, LineScaleMode.NONE, CapsStyle.NONE, JointStyle.MITER );
graphics.drawRect( HOLD_FRAME_X, HOLD_FRAME_Y, HOLD_FRAME_WIDTH, HOLD_FRAME_HEIGHT );
//*/
/** --------------------------------------------------
*
*/
backgroundBitmapData.draw( backgroundCanvas );
}
private function isRepeatInterval( keyString:String ):Boolean {
return KeyState.isRepeatInterval( keyString, Setting.gameSettingValues[ Setting.GAME_SETTING_VALUE_SLIDE_TIME_INDEX ], KEY_REPEAT_INTERVAL );
}
public function run():void {
if ( gameOver ) {
if ( KeyState.getReleased( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_A_INDEX ] ) )
SceneManager.scene = GameSettingScene.instance;
return;
}
if ( isRepeatInterval( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_LEFT_INDEX ] ) )
gameSystem.moveLeft();
if ( isRepeatInterval( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_RIGHT_INDEX ] ) )
gameSystem.moveRight();
if ( isRepeatInterval( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_DOWN_INDEX ] ) )
gameSystem.softDrop();
if ( KeyState.getPressed( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_UP_INDEX ] ) )
gameSystem.hardDrop();
if ( KeyState.getPressed( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_A_INDEX ] ) )
gameSystem.spinLeft();
//Setting.clear();
if ( KeyState.getPressed( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_B_INDEX ] ) )
gameSystem.spinRight();
if ( KeyState.getPressed( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_C_INDEX ] ) )
gameSystem.spinLeft();
if ( KeyState.getPressed( Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_D_INDEX ] ) )
gameSystem.hold();
gameSystem.run();
}
public function paint( graphics:Graphics ):void {
graphics.clear();
// background
graphics.beginBitmapFill( backgroundBitmapData );
graphics.drawRect( 0, 0, backgroundBitmapData.width, backgroundBitmapData.height );
graphics.endFill();
// score
drawString( graphics, scoreTextField, SCORE_X, SCORE_Y, clearLineCounter.toString() );
var i:uint, j:uint, k:uint;
// ghost
if ( minoManager.currentMino && !( gameSystem.areTimerRunning || gameSystem.clearLineTimerRunning ) ) {
drawMino( graphics, gameSystem.currentMino,
( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * gameSystem.ghostMinoX + FIELD_BORDER_THICKNESS + fieldX,
( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * gameSystem.ghostMinoY + FIELD_BORDER_THICKNESS + fieldY, true );
}
// nexts
for ( i = 0; i < minoManager.nextMinos.length; i++ ) {
if( minoManager.nextMinos[ i ] == null )
break;
var nextMinoX:uint = NEXT_FRAME_X;
var nextMinoY:uint = NEXT_FRAME_Y + ( NEXT_FRAME_HEIGHT + NEXT_FRAME_OFFSET_Y ) * i;
drawMino( graphics, minoManager.nextMinos[ i ], nextMinoX, nextMinoY );
}
// hold
if ( minoManager.holdMino )
drawMino( graphics, minoManager.holdMino, HOLD_FRAME_X, HOLD_FRAME_Y );
// gameSystem
var drawData:Array = gameSystem.drawData;
for ( i = 0; i < drawData.length; i++ ) {
for ( j = 0; j < drawData[ i ].length; j++ ) {
if ( drawData[ i ][ j ] > 0 ) {
if( !gameOver )
graphics.beginFill( MinoManager.getMinoColorAtDataValue( drawData[ i ][ j ] ) );
else
graphics.beginFill( GAME_OVER_BLOCK_COLOR );
graphics.drawRect( ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * i + FIELD_BORDER_THICKNESS + fieldX, ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) * j + FIELD_BORDER_THICKNESS + fieldY, BLOCK_WIDTH, BLOCK_WIDTH );
}
}
}
// gameOver
if( gameOver )
drawString( graphics, gameOverTextField, ( Setting.stageWidth - gameOverTextField.width ) / 2, ( Setting.stageHeight - gameOverTextField.height ) / 2, "Game Over\nPress '" + Setting.keySettingValues[ Setting.KEY_SETTING_VALUE_A_INDEX ] + "' key." );
graphics.endFill();
}
private function drawMino( graphics:Graphics, mino:Mino, x:uint, y:uint, ghost:Boolean = false ):void {
for ( var i:uint = 0; i < mino.length; i++ ) {
for ( var j:uint = 0; j < mino.length; j++ ) {
// no block
if ( mino.data[ i ][ j ] == 0 )
continue;
var blockX:uint = x + i * ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) + ( ( mino.length == 3 || ghost ) ? 0 : - BLOCK_WIDTH / 2 );
var blockY:uint = y + j * ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) + ( ( mino.length == 3 || ghost ) ? 0 : - ( BLOCK_WIDTH + FIELD_BORDER_THICKNESS ) );
graphics.beginFill( mino.color, ( ghost ) ? 0.5 : 1 );
graphics.drawRect( blockX, blockY, BLOCK_WIDTH, BLOCK_WIDTH );
}
}
//graphics.endFill();
}
private static var _instance:GameScene;
public static function get instance():GameScene {
if ( _instance == null )
_instance = new GameScene();
return _instance;
}
}
function drawString( graphics:Graphics, textField:TextField, x:Number, y:Number, text:String = "" ):void {
if ( text )
textField.text = text;
var bitmapData:BitmapData = new BitmapData( textField.width, textField.height, true, 0 );
bitmapData.draw( textField );
drawStringMatrix.identity();
drawStringMatrix.translate( x, y );
graphics.beginBitmapFill( bitmapData, drawStringMatrix );
graphics.drawRect( x, y, textField.width, textField.height );
graphics.endFill();
}
var drawStringMatrix:Matrix = new Matrix();