forked from: ぷよぷよ
おじゃまぷよ実装、サイズ変更などなど
本家の とことんぷよぷよ っぽくしてみた
連鎖しすぎると死ぬというのはいかがなものか・・・
[←][↓][→]: 移動
[Z]: 左回転
[X]: 右回転
[C]: 下まで一気に落下
/**
* Copyright ic_yas ( http://wonderfl.net/user/ic_yas )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/gklx
*/
/**
* GAME :: ぷよぷよ
*
* [←][↓][→]: 移動
* [Z]: 左回転
* [X]: 右回転
* [C]: 下まで一気に落下
*
* できたらいいな、300連鎖♪
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.ShaderParameter;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.utils.getTimer;
import frocessing.math.Random;
[SWF(backgroundColor = "0x555555", frameRate = "30", width = "465", height = "465")]
public class Puyopuyo extends Sprite
{
private const NUM_W:uint = 6; // ヨコ 個数
private const NUM_H:uint = 13; // タテ 個数
private const BLOCK_SIZE:uint = 15; // 1ブロックの大きさ
// 0, 1, 2, 3, 4, 5, 6,
//白、赤、緑、青、黄、紫、灰
private const COLOR1:Vector.<uint> = Vector.<uint>([0xffffff, 0xff3300, 0x33dd00, 0x3366dd, 0xffcc00, 0xbb00cc, 0x333344]);
private const COLOR2:Vector.<uint> = Vector.<uint>([0xffffff, 0xff9966, 0x99ff55, 0x88aaff, 0xffee66, 0xee44ff, 0xbbbbcc]);
private const OJAMA:int = 6;
/*落下中ぷよの回転状態を表す配列
0:ぷよ無し
1:中心(回転軸)となるぷよ
2:回転するぷよ
*/
private const BLOCK :Array =
[
[[0, 2, 0],
[0, 1, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 1, 2],
[0, 0, 0]],
[[0, 0, 0],
[0, 1, 0],
[0, 2, 0]],
[[0, 0, 0],
[2, 1, 0],
[0, 0, 0]]
];
private var phase:int;
private var map :Vector.<Vector.<Puyo>>;
private var bmd:BitmapData;
private var bmp:Bitmap;
private var nowX:int = int((NUM_W - 3) / 2);
private var nowY:int = 0;
private var num :int = 0; // BLOCK 番号
private var maxH:Vector.<int> = new Vector.<int>(NUM_W); //列ごとのぷよを積める高さ
private var tmpH:Vector.<int> = new Vector.<int>(NUM_W); //
private var ojamaH:Vector.<int> = new Vector.<int>(NUM_W); //おじゃまぷよデータ
private var numColor :int = 4; //出現する色の数
private var nowCols :Vector.<int> = Vector.<int>([rand(numColor), rand(numColor)]);
private var nextCols:Vector.<int> = Vector.<int>([rand(numColor), rand(numColor)]);
private var que :Vector.<Puyo> = new Vector.<Puyo>();
private var ojamaQue :Vector.<Puyo> = new Vector.<Puyo>();
private var isChain :Boolean = false;
private var interval :int = 30;
private var intervalCt :int = 0;
private var time :int = 0;
private var ojamaInterval :int =10;
private var ojamaIntervalCt :int =0;
private var ojamaStack :int =0;
private var ojamaFlag :Boolean = false;
private var chainCt :uint;
private var status :Status;
private var mapRect :Rectangle = new Rectangle(0, 1, NUM_W, NUM_H);
private var keyFlags :Array = new Array();
private var keyCounts :Array = new Array();
private var tf :TextField = new TextField();
private var DebugTime:int=0;
public function Puyopuyo():void
{
bmd = new BitmapData(NUM_W + 5, NUM_H, false, 0x555555);
bmp = new Bitmap(bmd);
bmp.scaleX = bmp.scaleY = BLOCK_SIZE;
addChild(bmp);
// map 初期化
map = new Vector.<Vector.<Puyo>>(NUM_H);
for (var i :int = 0; i < NUM_H; i ++)
{
map[i] = new Vector.<Puyo>(NUM_W);
for (var j :int = 0; j < NUM_W; j ++)
{
var puyo :Puyo = new Puyo();
puyo.x = j;
puyo.y = i;
map[i][j] = puyo;
}
}
status = new Status();
//status.addScore(0);
//status.setChain(0);
addChild(status);
status.x = status.y = bmp.x = 15;
bmp.y = int(status.y + status.height + 5);
tf.defaultTextFormat = new TextFormat("_sans", 10, 0xffffff); tf.autoSize = "left"; tf.selectable = false;
tf.text = "CLICK TO START"; tf.x = (465 - tf.width) / 2; tf.y = (465 - tf.height) / 2;
addChild(tf);
stage.addEventListener(MouseEvent.CLICK, clickHandler);
}
private function clickHandler(e:MouseEvent = null):void
{
tf.visible = false;
// 次手表示
bmd.fillRect(new Rectangle(NUM_W + 1, 1, 3, 4), 0xffffff);
bmd.setPixel(NUM_W + 2, 2, COLOR1[nextCols[1]]);
bmd.setPixel(NUM_W + 2, 3, COLOR1[nextCols[0]]);
// 配列初期化
for (var i :int = 0; i < NUM_H; i ++)
for (var j :int = 0; j < NUM_W; j ++)
map[i][j].type = 0;
for (i = 0; i < NUM_W; i ++) { maxH[i] = NUM_H - 1; tmpH[i] = -1; }
status.reset();
interval = 30;
phase = 1;
render();
stage.removeEventListener(MouseEvent.CLICK, clickHandler);
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
/* ENTER FRAME */
private function enterFrameHandler(e:Event):void
{
if (phase == 1) loopPhase1(); /* phase1 (操作) */
else if(phase == 2) {loopPhase2();} /* phase2 (探索, 落下) */
}
private function loopPhase1():void
{
intervalCt ++;
time++;
// スコアによって落下速度アップ
interval = 31 - status.level;
// 落下速度アップ(とりあえずなし)
// time ++; if (time % 1800 == 0) interval = (2 <= interval)? interval - 1:1;
//var tmp:int = (nowY != 0)? interval:Math.max(NUM_W, interval);
var tmp:int = interval;
if (keyFlags[37]) if (keyCounts[37]++ == 0 || 4 < keyCounts[37]) move(-1); //左移動
if (keyFlags[39]) if (keyCounts[39]++ == 0 || 4 < keyCounts[39]) move(1); //右移動
if (keyFlags[90]) if (keyCounts[90]++ == 0 || 4 < keyCounts[90]) rotate(); //左回転
if (keyFlags[88]) if (keyCounts[88]++ == 0 || 4 < keyCounts[88]) rotate(true); //右回転
if (keyFlags[40]) move(1, true, true); //落下
if (keyFlags[67]) while(phase==1) move(1, true, true); //高速落下
if (tmp <= intervalCt) { move(1, true); intervalCt = 0; } //自然落下
}
private function loopPhase2():void
{
intervalCt ++;
switch ( intervalCt )
{
case 3: fallPuyo(); break;
case 9: searchStep(); break;
case 18: fallStep(); break;
}
}
/* 移動 */
private function move ( n :int, isY :Boolean = false, isScore :Boolean = false ) :void
{
if ( ! isY ) /* x 方向 */
{
if ( ! check(BLOCK[num], nowX + n, nowY) ) nowX += n;
}
else /* y 方向 */
{
DebugTime = getTimer();
if (isScore) status.addScore(1);
if ( ! check(BLOCK[num], nowX, nowY + n) ){
nowY += n;
}
else {
intervalCt = 0;
phase = 2;
return;
}
}
render();
}
/* 回転 */
private function rotate ( isRight :Boolean = false ) :void
{
var nn :int;
if (isRight) nn = (num + 1 < 4)? num + 1:0;
else nn = (0 <= num - 1)? num - 1:3;
var tmp :int = num; num = nn;
if ( check(BLOCK[nn], nowX, nowY) )
{
if ( nn == 2 ) nowY --;
else if ( nn == 1 || nn == 3 )
{
if ( ! check(BLOCK[nn], nowX + (nn - 2), nowY) ) nowX += (nn - 2);
else
{//1ぷよ分のスキマに入ったとき
num = - (tmp - 2); //回転可
//num = tmp ; //こうすると回転不可
if ( check(BLOCK[num], nowX, nowY) ) nowY --; //ぷよ最下段時で、nowY-- しないと下部画面外に出てしまう
}
}
}
render();
}
/* ぷよ接地ステップ */
private function fallPuyo () :void
{
var px :int, py :int;
var ar :Array = BLOCK[num];
chainCt = 0;
for (var i :int = 2; 0 <= i; i --)
{
py = i + nowY;
for (var j :int = 0; j < 3; j ++)
{
px = j + nowX;
if ( 0 < ar[i][j] )
{
map[py][px].type = nowCols[ar[i][j] - 1];
fall( px, py );
if (tmpH[px] < maxH[px] + 1) tmpH[px] = maxH[px] + 1;
}
}
}
render();
}
/* 探索ステップ */
private function searchStep ():void
{
var i :int, j :int;
var tH :Vector.<int> = Vector.<int>(tmpH.concat()); // Vector コピー
for (i = 0; i < NUM_W; i ++) tmpH[i] = -1; // 初期化
for (i = 0; i < NUM_W; i ++)
{
if (tH[i] == -1) continue;
for (j = tH[i]; 0 <= j; j --) search(i, j);
}
render();
}
/* 落下ステップ */
private function fallStep ():void
{
var i :int, j :int;
if (isChain) // 1 箇所でも消えた
{
chainCt ++;
for (i = 0; i < NUM_W; i ++)
{
if (tmpH[i] == -1) continue;
for (j = tmpH[i]; 0 <= j; j --) fall(i, j);
}
isChain = false;
intervalCt = 4;
}
else
{
//おじゃまぷよ落下処理
if(ojamaFlag){
setOjama();
ojamaFlag=false;
status.removeOjamaCaution();
}
else ojamaIntervalCt++;
if(ojamaIntervalCt >= ojamaInterval && ojamaStack >0){
ojamaFlag =true;
status.addOjamaCaution(ojamaStack);
}
nowX = int((NUM_W - 3) / 2);
nowY = 0;
num = 0;
intervalCt = 0;
//setNumColor();
nowCols[0] = nextCols[0]; nextCols[0] = rand(numColor);
nowCols[1] = nextCols[1]; nextCols[1] = rand(numColor);
bmd.setPixel(NUM_W + 2, 2, COLOR1[nextCols[1]]);
bmd.setPixel(NUM_W + 2, 3, COLOR1[nextCols[0]]);
if (!check(BLOCK[num], nowX, nowY)) phase = 1;
else gameover();
}
render();
status.setChain(chainCt);
}
private function gameover ():void
{
tf.visible = true;
stage.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(MouseEvent.CLICK, clickHandler);
}
/* 落下 */
private function fall (nx:int, ny:int) :void
{
var nn :int = maxH[nx];
if (0 <= nn && 0 < map[ny][nx].type)
{
if (nn != ny)
{
map[nn][nx].type = map[ny][nx].type;
map[ny][nx].type = 0;
}
maxH[nx] = nn - 1;
}
}
/* 描画 */
private function render ( ) :void
{
bmd.lock();
bmd.fillRect(mapRect, COLOR1[0]);
for (var i :int = 1; i < NUM_H; i ++)
for (var j :int = 0; j < NUM_W; j ++)
{
if(map[i][j].type == 0 && (i+j)%2 == 0)
bmd.setPixel(j, i, 0xEFEFEF); //空白の色を1マスごとに変える
else
bmd.setPixel(j, i, COLOR1[map[i][j].type]);
}
if (phase == 1)
{
var px:int, py:int;
var ar :Array = BLOCK[num];
for (i = 0; i < 3; i ++)
{
py = i + nowY;
for (j = 0; j < 3; j ++)
{
px = j + nowX;
if ( 0 < ar[i][j] )
{
var n:int = ((num == 0 && ar[i][j] == 2) || (num == 2 && ar[i][j] == 1))? 1:0; //一番高い落下中ぷよ
if(maxH[px]- n > 0) //(上から)0段目は描画しない
bmd.setPixel(px, maxH[px] - n, COLOR2[nowCols[ar[i][j] - 1]]); //落下地点表示
if(py != 0) //(上から)0段目は描画しない
bmd.setPixel(px, py, COLOR1[nowCols[ar[i][j] - 1]]); //落下中ぷよ表示
/*
// 落下中ぷよの中心ぷよを光らせてみたかった
var tmpColor:uint = (ar[i][j] == 1 && (time%60)<=30 )? COLOR2[nowCols[ar[i][j] - 1]]:COLOR1[nowCols[ar[i][j] - 1]];
bmd.setPixel(px, py, tmpColor); //落下中ぷよ表示
*/
}
}
}
}
bmd.unlock();
}
/* 衝突チェック */
private function check(ar:Array, nx:int, ny:int):Boolean
{
var px :int, py :int;
for (var i :int = 0; i < 3; i ++)
{
py = i + ny;
for (var j :int = 0; j < 3; j ++)
{
px = j + nx;
if ( 0 < ar[i][j] )
{
if ((px < 0 || NUM_W <= px) || (py < 0 || NUM_H <= py)) return true; // 壁
if (0 < map[py][px].type) return true; // ぷよ
}
}
}
return false;
}
/* ぷよ消し 探索開始 */
private function search(nx:int, ny:int):void
{
if (map[ny][nx].type == 0) return;
searchLoop(map[ny][nx].type, nx, ny); /* 再帰探索 */
if (que.length >=4) // 4 連結以上
{
// score 加算
status.addScore( generateScore(que.length, chainCt) );
//おじゃまぷよ加算
generateOjama(que.length, chainCt);
deletePuyo(que)
deletePuyo(ojamaQue)
/*
while (que.length != 0)
{
var p:Puyo = que.pop();
p.flag = false;
p.type = 0;
tmpH[p.x] = maxH[p.x] = Math.max(maxH[p.x], p.y);
}
isChain = true;
*/
}
else
{
while (que.length != 0) que.pop().flag = false;
while (ojamaQue.length != 0) ojamaQue.pop().flag = false;
}
}
/*ぷよ削除*/
private function deletePuyo(q:Vector.<Puyo>):void{
while (q.length != 0)
{
var p:Puyo = q.pop();
p.flag = false;
p.type = 0;
tmpH[p.x] = maxH[p.x] = Math.max(maxH[p.x], p.y);
}
isChain = true;
}
/* 再起探索 */
private function searchLoop(n:int, nx:int, ny:int):void
{
// 通常ぷよ and (探索済み or 不一致の場合)
if ( map[ny][nx].type!=6 && ( map[ny][nx].flag || n != map[ny][nx].type ) ) return;
// (上から)0段目の場合 (幽霊連鎖用の処理)
if (ny == 0) return;
map[ny][nx].flag = true;
//おじゃまぷよの場合、再帰探索しない
if (map[ny][nx].type == 6) {
ojamaQue.push(map[ny][nx]);
return;
}else{
que.push(map[ny][nx]);
}
if (0 <= nx - 1) searchLoop(n, nx - 1, ny);
if (nx + 1 < NUM_W) searchLoop(n, nx + 1, ny);
if (0 <= ny - 1) searchLoop(n, nx, ny - 1);
if (ny + 1 < NUM_H) searchLoop(n, nx, ny + 1);
}
private function keyDownHandler(e:KeyboardEvent):void
{
if (phase != 1) return;
keyCounts[e.keyCode] = 0;
keyFlags[e.keyCode] = true;
}
private function keyUpHandler(e:KeyboardEvent):void
{
keyFlags[e.keyCode] = false;
}
private function generateScore(len:uint, chain:uint):uint
{
var k:uint;
if (chain == 0) k = 1;
else if (chain < 3) k = chain * 8;
else k = (chain - 2) * 32;
if (5 <= len) k += (len - 4) + 1;
return k * (len * 10);
}
/*色数指定 (非使用 現在は4色固定)*/
private function setNumColor():void
{
numColor = 4;
/*
//色数をレベル10ごとに増やす
numColor = int(3+status.level/10);
numColor = (numColor>5)? 5:numColor;
*/
}
private function rand(max:int = 4, min:int = 1):int
{
return int(Math.random() * (max - min + 1)) + min;
}
/*おじゃまぷよ数計算、蓄積*/
private function generateOjama(len:uint, chain:uint):void{
var k:uint;
k= (chain+1) *( len-3 );
ojamaStack+=k;
status.ojama = ojamaStack;
/*
if (chain <= 1) k = 1;
else k = (chain - 1) * 6;
if (5 <= len) k += (len - 4) + 1;
*/
}
/*おじゃまぷよセット*/
private function setOjama():void{
var tmpRowNum:Array = Random.shakedIntegers(NUM_W);
for(var i:int=0;i<ojamaStack;i++){
ojamaH[tmpRowNum[i%NUM_W]]++;
if(i%NUM_W == NUM_W-1) tmpRowNum=Random.shake(tmpRowNum);
}
fallOjama();
}
/*おじゃまぷよ落下*/
private function fallOjama():void{
for(var i:int=0;i<NUM_W;i++){
if(ojamaH[i]>0){
for (var j:int=ojamaH[i];j>0;j--){
if(maxH[i] <0) break;
map[maxH[i]][i].type = OJAMA;
maxH[i]--
}
}
ojamaH[i] = 0;
}
ojamaIntervalCt = 0;
ojamaStack = 0;
status.ojama = ojamaStack;
}
}
}
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
class Status extends Sprite
{
private var _score :uint = 0;
private var _chain :uint = 0;
private var _level :uint = 1;
private var _tfMaxChain :TextField;
private var _tfScore :TextField;
private var _tfLevel :TextField;
private var _tfOjama :TextField;
private var _tfOjamaCaution:TextField;
public function get score():uint{
return _score;
}
public function get level():uint{
return _level;
}
public function set ojama(n:int):void{
_tfOjama.text = "ojama: " + String(n);
}
public function Status()
{
_tfMaxChain = createTextField(); _tfMaxChain.y = 13;
_tfScore = createTextField();
_tfLevel = createTextField(); _tfLevel.x = 80;
_tfOjama = createTextField(); _tfOjama.x = 80; _tfOjama.y = 13;
_tfOjamaCaution= createTextField(); _tfOjamaCaution.y = 26;
addChild(_tfMaxChain);
addChild(_tfScore);
addChild(_tfLevel);
addChild(_tfOjama);
addChild(_tfOjamaCaution);
}
public function reset():void
{
_score = 0; _tfScore.text = "score: " + _score;
_chain = 0; _tfMaxChain.text = "chain: " + _chain;
_level = 1; _tfLevel.text = "level: " + _level;
_tfOjama.text = "ojama: 0";
_tfOjamaCaution.text = "Next Ojama: 0";
_tfOjamaCaution.visible =false;
}
public function addScore(n:uint):void
{
_score += n;
_tfScore.text = "score: " + _score;
levelup();
}
public function setChain(n:uint):void
{
if (n < _chain) return;
_chain = n;
_tfMaxChain.text = "chain: " + _chain;
}
public function addOjamaCaution(n:int):void
{
_tfOjamaCaution.text = "Next, "+String(n)+" ojama fall !!";
_tfOjamaCaution.visible =true;
}
public function removeOjamaCaution():void
{
_tfOjamaCaution.visible =false;
}
private function levelup():void{
//1500点ごとにレベル上昇
_level = uint(_score/1500)+1;
_tfLevel.text = "level: " + _level;
}
private function createTextField():TextField
{
var format :TextFormat = new TextFormat("_等幅", 10, 0xffffff);
format.letterSpacing = 1;
var tf :TextField = new TextField();
tf.autoSize = "left";
tf.defaultTextFormat = format;
return tf;
}
}
class Puyo
{
public var type:int = 0;
public var flag:Boolean = false;
public var x :int;
public var y :int;
}