SlotChain
操作方法(How To Play)
・タッチ(Touch)
・スロットを止める(Stop)
ルール
・スロットを止めて、縦に3つアイコンを並べてスコアを稼ぐ
・前回並べたところにさらに並べると+1されていく
比較用
・Android版(Air)(あとで追加予定)
・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
/**
* Copyright o_healer ( http://wonderfl.net/user/o_healer )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/a0fI
*/
/*
「スロット・チェイン」
・スロットを止めまくり、チェインをつなげまくるゲーム
・爽快感を目指したものの、実際には微妙な先読みを行う思考ゲーになった
操作方法(How To Play)
・タッチ(Touch)
・スロットを止める(Stop)
ルール
・スロットを止めて、縦に3つアイコンを並べてスコアを稼ぐ
・前回並べたところにさらに並べると+1されていく
比較用
・Android版(Air)(あとで追加予定)
・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
*/
package {
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import net.wonderfl.utils.WonderflAPI;
[SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")]
public class SlotChain extends Sprite {
//==Const==
//画面の大きさ
static public const VIEW_W:int = 465;
static public const VIEW_H:int = 465;
//行数(何段積み重ねるか)
//- 下2行は並べるために必須。上は2行以上あると先のことが考えられて良い。
static public const LINE_Y_NUM_D:int = 2;
static public const LINE_Y_NUM_U:int = 3;
static public const LINE_Y_NUM:int = LINE_Y_NUM_D + LINE_Y_NUM_U;
//列数
//-1スロットあたりの要素数を兼ねる
static public const LINE_X_NUM:int = 4;
//アイコンの種類
//- 列数よりも1つ大きくすることで、必ず1つ何かが欠ける=どこかで並べるアイコンを別種に切り替えるのを誘発
static public const ICON_TYPE_NUM:int = LINE_X_NUM+1;
//パネルの大きさ
static public const PANEL_W:int = 64;
//State
static public var s_StateIter:int = 0;
static public const STATE_MOVE:int = s_StateIter++;
//制限時間
static public const TIME_LIMIT:int = 60;
//==Var==
//Pseudo Singleton
// static public var Instance:TouchTennis;
//レイヤー(ペイン)
public var m_Layer_Game:Sprite = new Sprite();
public var m_Layer_UI:Sprite = new Sprite();
//スロット表示
//- 下にズラす際に行数+1の表示が必要になるので、その分だけ確保
public var m_Slot:Vector.<SlotOneLine> = new Vector.<SlotOneLine>(LINE_Y_NUM + 1);
//止まっているスロットの一番下のIndex
public var m_BottomSlotIndex:int = 0;
//テキスト
public var m_Text_Time:TextField = new TextField();
public var m_Text_Score:TextField = new TextField();
public var m_Text_ScorePop:Vector.<TextField> = new Vector.<TextField>(LINE_X_NUM);
public var m_Text_Debug:TextField = new TextField();
//State
public var m_State:int = 0;//最初から
//Score
public var m_Score:int = 0;
//コンボ数の保持
public var m_Combo:Vector.<int> = new Vector.<int>(LINE_X_NUM);
//残り時間
public var m_RestTime:Number = TIME_LIMIT;
public var m_RestTimeGauge:Bitmap = new Bitmap(new BitmapData(1, 1, false, 0xFFFFFF));
//==Function==
//Init
public function SlotChain():void{
//stageを参照できるようになってから初期化する(イベントリスナの追加や画面の大きさの取得のため)
if(stage != null){
Init();
}else{
addEventListener(
Event.ADDED_TO_STAGE,//ステージに追加されたら
function(e:Event):void{
Init();
}
);
}
}
public function Init():void{
var i:int;
//StaticInit
{
SlotOneLine.StaticInit();
ScoreWindowLoader.init(this, new WonderflAPI(loaderInfo.parameters));
}
//BG
{
//主にwonderfl用
addChild(new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000)));
}
//Layer
{
addChild(m_Layer_Game);
addChild(m_Layer_UI);
}
//Clipping
{
var clipX:int = 0;
var clipY:int = 0;
var clipW:int = PANEL_W * LINE_X_NUM;
var clipH:int = PANEL_W * LINE_Y_NUM;
m_Layer_Game.scrollRect = new Rectangle(clipX,clipY,clipW,clipH);
}
//Param
{
for(i = 0; i < LINE_X_NUM; ++i){
m_Combo[i] = 0;
}
}
//Slot
{
for(i = 0; i < LINE_Y_NUM + 1; ++i){
var slot:SlotOneLine = new SlotOneLine();
slot.y = (LINE_Y_NUM-1 - i) * PANEL_W;//下から並べる
if(i < LINE_Y_NUM_D){
//下2列は使わないタイプを同じにして止めておく
slot.Reset(0);
slot.Stop();
}else{
//それ以降はランダムに設定して回す
slot.Reset(int(SlotChain.ICON_TYPE_NUM * Math.random()));
}
m_Layer_Game.addChild(slot);
m_Slot[i] = slot;
}
}
//Gauge
{
m_RestTimeGauge.x = LINE_X_NUM * PANEL_W;
m_RestTimeGauge.y = LINE_Y_NUM * PANEL_W;
m_RestTimeGauge.scaleX = 16;
//m_RestTimeGauge.scaleX = VIEW_W - LINE_Y_NUM * PANEL_W;
m_RestTimeGauge.scaleY = -1 * LINE_Y_NUM * PANEL_W;
addChild(m_RestTimeGauge);
}
//Text
//*
{
m_Text_Time.selectable = false;
m_Text_Time.autoSize = TextFieldAutoSize.LEFT;
m_Text_Time.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true);
m_Text_Time.text = "Time : ";
//m_Text_Time.filters = [new GlowFilter(0x00FFFF,1.0, 8,8)];
m_Text_Time.x = PANEL_W * (LINE_X_NUM + 0.5);
m_Text_Time.y = VIEW_H/2;
addChild(m_Text_Time);
}
//*/
{
m_Text_Score.selectable = false;
m_Text_Score.autoSize = TextFieldAutoSize.LEFT;
m_Text_Score.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true);
m_Text_Score.text = "Score : 0";
//m_Text_Score.filters = [new GlowFilter(0xFFFF00,1.0, 4,4)];
m_Text_Score.x = PANEL_W * (LINE_X_NUM + 0.5);
m_Text_Score.y = 0;
addChild(m_Text_Score);
}
{
for(i = 0; i < LINE_X_NUM; ++i){
m_Text_ScorePop[i] = new TextField();
m_Text_ScorePop[i].selectable = false;
m_Text_ScorePop[i].autoSize = TextFieldAutoSize.CENTER;
m_Text_ScorePop[i].defaultTextFormat = new TextFormat('Verdana', PANEL_W/2, 0x000000, true);
m_Text_ScorePop[i].text = "";
m_Text_ScorePop[i].filters = [new GlowFilter(0xFFFFFF,1.0, 4,4)];
m_Text_ScorePop[i].x = PANEL_W * (i + 0.5);
m_Text_ScorePop[i].y = PANEL_W * LINE_Y_NUM_U;
addChild(m_Text_ScorePop[i]);
}
}
{
m_Text_Debug.selectable = false;
m_Text_Debug.autoSize = TextFieldAutoSize.LEFT;
m_Text_Debug.defaultTextFormat = new TextFormat('Verdana', 12, 0xFFFFFF, true);
m_Text_Debug.text = "";
m_Text_Debug.filters = [new GlowFilter(0xFFFF00,1.0, 4,4)];
m_Text_Debug.x = 0;
m_Text_Debug.y = VIEW_H - 24;
addChild(m_Text_Debug);
}
//Touch
{
stage.addEventListener(MouseEvent.MOUSE_DOWN, OnMouseDown);
}
//Update
{
addEventListener(Event.ENTER_FRAME, Update);
}
//OnEnd
{
addEventListener(Event.REMOVED_FROM_STAGE, Finish);
}
}
//Finish
public function Finish(e:Event):void{
removeEventListener(Event.ADDED_TO_STAGE, Init);
removeEventListener(Event.ENTER_FRAME, Update);
removeEventListener(Event.REMOVED_FROM_STAGE, Finish);
}
//Update
public function Update(e:Event=null):void{
var DeltaTime:Number = 1.0 / stage.frameRate;
var i:int;
//*
//Time
{
if(m_RestTime <= 0){
return;
}
m_RestTime -= DeltaTime;
if(m_RestTime <= 0){
//TimeUp
m_Text_Time.text = "Time : 0";
m_RestTimeGauge.scaleY = 0;
//
ScoreWindowLoader.show(m_Score);
return;
}
m_Text_Time.text = "Time : " + CeilInt(m_RestTime);
m_RestTimeGauge.scaleY = -m_RestTime/TIME_LIMIT * LINE_Y_NUM * PANEL_W;
var c:uint = 0xFF * m_RestTime/TIME_LIMIT;
var color:uint = (0xFF << 16) | (c << 8) | (c << 0);
m_RestTimeGauge.bitmapData.setPixel(0, 0, color);
}
//*/
switch(m_State){
case STATE_MOVE:
Update_Move(DeltaTime);
break;
}
for(i = 0; i < LINE_X_NUM; ++i){
m_Text_ScorePop[i].alpha -= 1 * DeltaTime;
if(m_Text_ScorePop[i].alpha <= 0){
m_Text_ScorePop[i].alpha = 0;
}
}
}
//Update : Move
public function Update_Move(DeltaTime:Number):void{
var i:int;
//リールの回転
for(i = 0; i < LINE_Y_NUM + 1; ++i){
m_Slot[i].Update_Move(DeltaTime);
}
//タッチ後のリールの下への移動(&上へのループ)
{
//基準となるY(このリールが一番下に来るようにして、上下のリールの位置もこれに合わせる)
var OldY:Number = m_Slot[m_BottomSlotIndex].y;
//まずは基準となるリールの移動
var NewY:Number = Lerp(OldY, (LINE_Y_NUM-1) * PANEL_W, 0.1);
m_Slot[m_BottomSlotIndex].y = NewY;
//上下のリールをこれに合わせて移動
for(i = 0; i < LINE_Y_NUM + 1; ++i){
//基準リールはすでに移動したのでスキップ
if(i == m_BottomSlotIndex){
continue;
}
//基準リールより下にあるか(下にあるなら上にループさせる必要がある)
var IsDown:Boolean = (OldY < m_Slot[i].y);
var OffsetIndex:int = (i - m_BottomSlotIndex + LINE_Y_NUM+1) % (LINE_Y_NUM+1);
if(IsDown){
OffsetIndex -= (LINE_Y_NUM + 1);
m_Slot[i].y = NewY - OffsetIndex * PANEL_W;
//下に入りきったら再び上に戻して回す
if(LINE_Y_NUM * PANEL_W <= m_Slot[i].y){
OffsetIndex += (LINE_Y_NUM + 1);
m_Slot[i].y = NewY - OffsetIndex * PANEL_W;
m_Slot[i].Reset(int(SlotChain.ICON_TYPE_NUM * Math.random()));
}
}else{
m_Slot[i].y = NewY - OffsetIndex * PANEL_W;
}
}
}
}
//Touch
private function OnMouseDown(e:MouseEvent):void{
//Check
{
if(m_RestTime <= 0){
return;
}
}
//Stop
{
var stop_index:int = (m_BottomSlotIndex + LINE_Y_NUM_D) % (LINE_Y_NUM + 1);
m_Slot[stop_index].Stop();
}
//Check Score
{
CheckScore();
}
//++
{
m_BottomSlotIndex++;
if(LINE_Y_NUM + 1 <= m_BottomSlotIndex){
m_BottomSlotIndex = 0;
}
}
}
//Check Score
private function CheckScore():void{
m_Text_Debug.text = "";
for(var x:int = 0; x < LINE_X_NUM; ++x){
//縦に揃ったか
var flag:Boolean = true;
{
var bottom_type:int = m_Slot[m_BottomSlotIndex].GetType(x);
for(var y:int = 1; y < 3; ++y){
var index:int = (m_BottomSlotIndex + y) % (LINE_Y_NUM + 1);
if(bottom_type != m_Slot[index].GetType(x)){
flag = false;
break;
}
}
}
if(flag){
//縦に揃ったなら並べた数を記録
if(m_Combo[x] <= 0){//並んでいなかったら
m_Combo[x] = 3;//新規登録
}else{//並んでいたら
++m_Combo[x];//一つ追加
}
}else{
//並ばなかったらリセット
m_Combo[x] = 0;
}
m_Score += m_Combo[x];
if(0 < m_Combo[x]){
m_Text_ScorePop[x].alpha = 1;
m_Text_ScorePop[x].text = "+" + m_Combo[x];
}
}
//m_Text_Debug.appendText("" + m_Slot[m_BottomSlotIndex].m_UnselectedType + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 1)].m_UnselectedType + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 2)].m_UnselectedType);
//m_Text_Debug.appendText(";" + m_Slot[m_BottomSlotIndex].m_IndexOffset + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 1)].m_IndexOffset + ", " + m_Slot[(m_BottomSlotIndex + 1) % (LINE_Y_NUM + 2)].m_IndexOffset);
m_Text_Score.text = 'Score : ' + m_Score;
}
//Util : Lerp
public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
}
//Util : Ceil
public function CeilInt(in_Number:Number):int{
var result:int = int(in_Number);
if(result < in_Number){
result += 1;
}
return result;
}
}
}
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import net.wonderfl.utils.WonderflAPI;
//スロット1行分の管理用クラス
class SlotOneLine extends Sprite
{
//==Const==
//パネル数
//- 1周するのを見せるため、同じパターンを2回並べて表示する
static public const PANEL_NUM:int = 2 * SlotChain.LINE_X_NUM;
//移動速度
static public const VX_MIN:Number = SlotChain.PANEL_W * 3.0;
static public const VX_MAX:Number = SlotChain.PANEL_W * 4.0;
//State
static public var s_StateIter:int = 0;
static public const STATE_MOVE:int = s_StateIter++;
static public const STATE_STOP:int = s_StateIter++;
//==Var==
//アイコンの元画像
static public var s_BitmapData:Vector.<BitmapData> = new Vector.<BitmapData>(SlotChain.ICON_TYPE_NUM);
//選択されていないType
//- これを一つだけ指定することで、残りを指定したことにする
public var m_UnselectedType:int = 0;
//移動速度
public var m_VX:Number = 0;
//State
public var m_State:int = 0;//最初から
//止めた際の左端のアイコンを求めるためのオフセット
public var m_IndexOffset:int = 0;
//==Function==
//Static Init
static public function StaticInit():void{
/*
for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
//!!仮
var color:uint = 0x000000;
if((i & 1) != 0){color |= 0x0000FF;}
if((i & 2) != 0){color |= 0x00FF00;}
if((i & 4) != 0){color |= 0xFF0000;}
s_BitmapData[i] = new BitmapData(SlotChain.PANEL_W, SlotChain.PANEL_W, false, color);
}
/*/
//外枠
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.lineStyle(2, 0x000000, 1.0);
g.drawRect(0, 0, SlotChain.PANEL_W, SlotChain.PANEL_W);
//中の模様
const TEXT_W:int = SlotChain.PANEL_W * 0.8;
var tf:TextField = new TextField();
tf.selectable = false;
tf.autoSize = TextFieldAutoSize.LEFT;
var mtx:Matrix = new Matrix();
for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
s_BitmapData[i] = new BitmapData(SlotChain.PANEL_W, SlotChain.PANEL_W, false, 0xF0F0F0);
switch(i){
case 0:
tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0xFF0000, true);
tf.text = "○";
break;
case 1:
tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x00FF00, true);
tf.text = "□";
break;
case 2:
tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x0000FF, true);
tf.text = "△";
break;
case 3:
tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x888800, true);
tf.text = "×";
break;
case 4:
tf.defaultTextFormat = new TextFormat('Verdana', TEXT_W, 0x008888, true);
tf.text = "☆";
break;
}
mtx.tx = (SlotChain.PANEL_W - tf.textWidth) / 2;
mtx.ty = (SlotChain.PANEL_W - tf.textHeight) / 2;
s_BitmapData[i].draw(shape);
s_BitmapData[i].draw(tf, mtx);
}
//*/
}
//Init
public function SlotOneLine(){
}
//Reset
public function Reset(in_UnselectedType:int):void{
//m_UnselectedType
{
m_UnselectedType = in_UnselectedType;
}
//m_VX
{
m_VX = Lerp(VX_MIN, VX_MAX, Math.random());
}
//Panel
{
var bmp:Bitmap;
var offsetX:int = 0;
while(0 < numChildren){
removeChildAt(0);
}
for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
//選ばれなかったものはスキップ
if(i == m_UnselectedType){
continue;
}
bmp = new Bitmap(s_BitmapData[i % SlotChain.ICON_TYPE_NUM]);
bmp.x = offsetX;
addChild(bmp);
bmp = new Bitmap(s_BitmapData[i % SlotChain.ICON_TYPE_NUM]);
bmp.x = offsetX + SlotChain.PANEL_W * SlotChain.LINE_X_NUM;
addChild(bmp);
offsetX += SlotChain.PANEL_W;
}
}
//m_State
{
m_State = STATE_MOVE;
}
}
//Update : Move
public function Update_Move(DeltaTime:Number):void{
switch(m_State){
case STATE_MOVE:
this.x -= m_VX * DeltaTime;
if(this.x <= -SlotChain.PANEL_W * SlotChain.LINE_X_NUM){
this.x += SlotChain.PANEL_W * SlotChain.LINE_X_NUM;
}
break;
case STATE_STOP:
break;
}
}
//Stop : Move
public function Stop():void{
m_IndexOffset = int(-this.x / SlotChain.PANEL_W + 0.5);
// if(SlotChain.LINE_X_NUM <= m_IndexOffset){m_IndexOffset -= SlotChain.LINE_X_NUM;}
this.x = -m_IndexOffset * SlotChain.PANEL_W;
m_State = STATE_STOP;
}
//Get Type
public function GetType(in_IndexX:int):int{
/*
var index:int = in_IndexX + m_IndexOffset;
var result:int = 0;
var i:int = 0;
for(;;){
//未使用分は1つ余計に飛ばす
if((i % SlotChain.ICON_TYPE_NUM) == m_UnselectedType){
++result;
}
if(i == index){
break;
}
++i;
++result;
}
return (result % SlotChain.ICON_TYPE_NUM);
/*/
var index:int = (in_IndexX + m_IndexOffset) % SlotChain.LINE_X_NUM;
var result:int = 0;
for(var i:int = 0; i < SlotChain.ICON_TYPE_NUM; ++i){
//未使用分は1つ余計に飛ばす
if(i == m_UnselectedType){
++result;
}
if(i == index){
break;
}
++result;
}
return result;
//*/
}
//Util : Lerp
public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
}
}
//*
//bkzenさんのコードを利用
//@see http://wonderfl.net/c/cuY4
//@see http://wonderfl.net/c/kYyY
class ScoreWindowLoader
{
private static var _top: DisplayObjectContainer;
private static var _api: WonderflAPI;
private static var _content: Object;
//private static const URL: String = "wonderflScore.swf";
private static const URL: String = "http://swf.wonderfl.net/swf/usercode/5/57/579a/579a46e1306b5770d429a3738349291f05fec4f3.swf";
private static const TWEET: String = "Playing Slot Chain [score: %SCORE%] #wonderfl";
public static function init(top: DisplayObjectContainer, api: WonderflAPI): void
{
_top = top, _api = api;
var loader: Loader = new Loader();
var comp: Function = function(e: Event): void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, comp);
_content = loader.content;
// handler();
}
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, comp);
loader.load(new URLRequest(URL), new LoaderContext(true));
}
public static function show( score: int): void
{
var window: DisplayObject = _content.makeScoreWindow(_api, score, "Slot Chain", 1, TWEET);
// var close: Function = function(e: Event): void
// {
// window.removeEventListener(Event.CLOSE, close);
// closeHandler();
// }
// window.addEventListener(Event.CLOSE, close);
_top.addChild(window);
}
}
//*/