import { BonusShootAni, stepPosition } from '../something/anis/BonusShootAni';
import { BoomAni } from '../something/anis/BoomAni';
import { CrossAni } from '../something/anis/CrossAni';
import { EleDisAni } from '../something/anis/EleDisAni';
import { FlyTargetAni } from '../something/anis/FlyTargetAni';
import { HorizontalAni } from '../something/anis/HorizontalAni';
import { IceAni } from '../something/anis/IceAni';
import { MagicLionAni } from '../something/anis/MagicLionAni';
import { MagicRotateAni } from '../something/anis/MagicRotateAni';
import { RockAni } from '../something/anis/RockAni';
import { ScoreAni } from '../something/anis/ScoreAni';
import { ThreeCrossAni } from '../something/anis/ThreeCrossAni';
import { VerticalAni } from '../something/anis/VerticalAni';
import { Element } from '../something/class/Element';
import { Ice } from '../something/class/Ice';
import { Lattice } from '../something/class/Lattice';
import { EffectType } from '../something/enum/EffectType';
import { ElementType, FiveBaseElementTypes } from '../something/enum/ElementType';
import { PassType } from '../something/enum/PassType';
import { RecoverName } from '../something/enum/RecoverName';
import { ChapterData } from '../something/interface/ChapterData';
import { ElementTargetData } from '../something/interface/ElementTargetData';
import { MagicRotateData } from '../something/interface/MagicRotateData';
import { drawBg, drawShape, generateMapBg } from '../something/logic/aaa';
import { Pool } from '../something/Pool';
import { Tool } from '../something/Tool';
import { ElementTargets } from '../something/uis/ElementTargets';
import { PropGuide } from '../something/uis/PropGuide';
import { ScoreProgress } from '../something/uis/ScoreProgress';
import { StepAni } from '../something/anis/StepAni';
import { AiControl, fesELeSmallNum } from '../something/logic/AiControl';
import { JellyDisAni } from '../something/anis/JellyDisAni';
import { EggBrokenAni } from '../something/anis/EggBrokenAni';
import { FallAniData, FallType } from '../something/interface/FallAniData';
import { EleMaskAni } from '../something/anis/EleMaskAni';
import { getChapterData } from '../something/chapters/getChapter';
import { GenerateLatData } from '../something/interface/GenerateLatData';
import { StateType } from '../something/enum/StateType';
import { HairballBlackState } from '../something/states/HairballBlackState';
import { HairballGreyDisAni } from '../something/anis/HairballGreyDisAni';
import { HairballBrownState } from '../something/states/HairballBrownState';
import { EleDownAni } from '../something/anis/EleDownAni';
import { GameGuide } from '../something/uis/GameGuide';
import { PropBtnCon } from '../something/uis/PropBtnCon';
import { FestivalTarget } from '../something/uis/FestivalTarget';
import { FesRedBombShowAni } from '../something/anisCall/FesRedBombShowAni';
import { RectsWaveAni } from '../something/anisCall/RectsWaveAni';
import { Door } from '../something/class/Door';
import { Scene } from '../../module/views/Scene';
import { GTool } from '../../module/tools/GTool';
import { Tools } from '../Tools';
import { PropType } from '../something/enum/PropType';
import { RES } from '../../module/RES';
import { showToast, showWaiting, showPanel, hideWaiting, wait, changeScene } from '../../module/ctrls';
import { layers } from '../../module/views/layers';
import { Lottie } from '../Lottie';
import { GameToast } from '../something/uis/GameToast';
import { sendTbNet, TbNetName } from '../TaoBaoNet';
import { MapScene } from './map/MapScene';
import { NoStepPanel } from '../panels/NoStepPanel';
import { FirstPropGift } from '../something/uis/FirstPropGift';
import { bonus } from '../lotties/bonus';
import { GDispatcher } from '../Main';
import { G_EVENT } from '../common/G_EVENT';
import Tween = FYGE.Tween;
import { BitmapRecycle } from "../something/class/BitmapRecycle";
import { FesRedPrizePanel } from "../panels/FesRedPrizePanel";
import FrameAni = FYGE.FrameAni;
import Texture = FYGE.Texture;
import Container = FYGE.Container;
import Ease = FYGE.Ease;
import Graphics = FYGE.Graphics;
import Sprite = FYGE.Sprite;

const aniClass = {
    "BoomAni": BoomAni,
    "IceAni": IceAni,
    "RockAni": RockAni,
    "HorizontalAni": HorizontalAni,
    "VerticalAni": VerticalAni,
    "CrossAni": CrossAni,
    "ThreeCrossAni": ThreeCrossAni,
    // "EleDisAni": EleDisAni,
    "MagicLionAni": MagicLionAni,
    "StepAni": StepAni,
    "JellyDisAni": JellyDisAni,
    "EggBrokenAni": EggBrokenAni,
    "HairballGreyDisAni": HairballGreyDisAni,
    // "HairballBlackDisAni": HairballBlackDisAni,
}
export const baseScore = 20;
/**
 * 直线特效(4) 1.5倍
 * 爆炸特效(L) 2.5倍
 * 魔力鸟(5) 5倍
 * 直线+直线(4+4) 10倍
 * 直线+炸弹(4+L) 15倍
 * 炸弹+炸弹(L+L) 25倍
 * 魔力鸟+魔力鸟(5+5) 50倍
 */
export const effectBaseTimes = {
    0: 2.5,
    1: 2.5,
    2: 5,
    3: 15, //         //前四个就是特效类型
    LL: 10,
    EL: 20,
    EE: 25,
    MM: 50
}
//加载器
let svgaParser;
//三个toast和bonusTime
const movieClips = {
    wonderful: null,
    amazing: null,
    great: null,
    bonusTime: null
}

//红包炸弹弹框名字
const redBombPanelName = "redBombPanelName";
//所有可能被添加节日红包的关卡；如果没有就不放
// const fesChapterData: number[] = []
const fesChapterData: number[] = new Array(...new Array(117)).map((a, i) => i + 4);
//节日大红包格子索引 及索引,默认索引值都是40
const festivalIndex: number = 40

export class PlayScene extends Scene {
    get groupNames() {
        return [
            "playScene",
            "playFrameAni",
            "playerFont",
            "fiveVerHorDis",
            'festival',
            'MagicAni'
        ]
    }

    //关卡数据
    chapterData: ChapterData;
    //关数
    chapter: number
    //所有的格子数组
    lattices: Lattice[];
    //生成口，索引就是列数，元素式该列的生成口数据,能不止一个
    generateIndexs: GenerateLatData[][];
    //回收口的格子索引
    recycleIndexs: number[];
    //元素的容器，为了移动事件简单
    elementContainer: FYGE.Container;
    //选中框
    choosed: FYGE.Sprite;
    //消除后生成空格的索引
    emptys: number[] = [];
    //消除队列的索引
    eliminatedElements: number[] = [];
    //棒棒糖生成标志
    lollipopMark: boolean;
    //地图背景容器
    map: FYGE.Container;
    //是否手动触发特效，用于两个组合后的特效
    isManual: boolean;
    //是否手动触发消除，不是ai触动的，暂时只针对石门翻转，
    isMouseAction: boolean
    //选中的元素
    SELECTED: Element;
    //提示交换对象
    warningCop: Element[];
    //未操作的计时，点击元素时置0，点击道具时置0，动画也要去掉，在有些引导，道具操作时去掉计时侦听，使用完后加回
    noActionCount: number = 0;
    isShowWarning: boolean = false;
    //记录旋转的信息
    magicRotateData: MagicRotateData[] = [];
    //普通连消次数
    commonContinuityTimes: number = 0;
    //特效连消次数
    effectContinuityTimes: number = 0;
    //棒棒糖数量
    lollipopCount: number = 0;
    //是否已通关
    hasPassed: boolean = false;
    //是否结算时间
    isCountingTime: boolean = false;
    //通关的目标元素的计数,下标和元素类型ElementType枚举一致
    passElements: number[];
    elementTargets: ElementTargets;
    //节日元素目标
    festivalTarget: FestivalTarget;
    //节日小元素需要个数
    festivalTargetNum: number = fesELeSmallNum;
    //所有元素的消耗数量，需要传给后端
    hasEliminatedElements: number[];
    //得分的动画，首先特效组合的单独算分，然后把所有符合的放入，在波及的特效触发时不算分
    scoreAnis: ScoreAni[] = [];
    //每一步生成的总分数
    oneStepScore: number;
    //果冻有消除的标记；
    jellyBrokenMark: boolean;
    /**
     * 判断连通的状态
     * 1表示存在小列连通到大列，从右往左遍历
     * 2表示存在大列连通到小烈，从左往右遍历
     * 3表示都有，遍历两边，down比up大，从右先
     * 4表示都有，遍历两边，down比up小，从左先
     */
    connectState: number = 2;
    //皮肤上的 //注意都需要重写
    public musicBtn: MusicBtn;
    /**
     * 分数为目标时的文案
     */
    targetScoreTxt: FYGE.BitmapText;
    /**
     * 剩余步数
     */
    private _steps: number;
    private stepNumber: FYGE.BitmapText;

    get steps(): number {
        return this._steps
    }

    set steps(value: number) {
        if (value == this._steps) return
        this._steps = value;
        this.stepNumber.text = "" + value;
    }

    scoreProgress: ScoreProgress
    /**
     * 分数
     */
    private _score: number;
    get score(): number {
        return this._score
    }

    set score(value: number) {
        if (value == this._score) return;
        this._score = value;
        FYGE.Tween.removeTweens(this.scoreProgress);
        FYGE.Tween.get(this.scoreProgress, {}, null, true)
            .to({score: value}, 300)
    }

    //所有道具容器
    propBtnCon: PropBtnCon;
    //道具使用引导
    propGuide: PropGuide;
    //步数道具引导时的圆
    stepCircle: FYGE.Graphics//Shape;
    //bonus Time 提示
    // bonusTime: BonusTime;
    //游戏操作引导
    gameGuide: GameGuide;
    //记录是否有红包炸弹弹框
    redBombPanelMark: boolean;

    //记录本局是否提示过加步数
    addedStepWarning: boolean = false;
    //记录三种道具，及失败步数增加hash
    recordOnce = {
        boom: false,
        hammer: false,
        step: false,
        failStep: false
    }

    initUi() {
        //适配，整体置顶，
        //背景图居中
        this.addChild(FYGE.Sprite.fromFrame("playBg.jpg")).position.set(0, -layers.stageOffsetY);
        //排版
        //底部背景
        this.addChild(FYGE.Sprite.fromFrame("topBg.png")).position.set(50, 9);

        var commonTextArr = {};
        for (var i = 0; i < 10; i++) commonTextArr[i] = RES.getRes("scoreNum" + i + ".png");
        ["通", "关", "分", "数"].forEach((e) => {
            commonTextArr[e] = RES.getRes("scoreNum" + e + ".png");
        })
        commonTextArr["："] = RES.getRes("scoreNum冒号.png");
        //通关目标分数
        this.targetScoreTxt = this.addChild(new FYGE.BitmapText(commonTextArr));
        this.targetScoreTxt.position.set(375, 193);
        //设置
        this.musicBtn = this.addChild(new MusicBtn())
        this.musicBtn.position.set(618, 16)
    }

    data: { chapter: number, isGiftPrizeLevel: boolean }

    async start(data: { chapter: number, isGiftPrizeLevel: boolean }) {
        super.start();
        // data = { chapter: 1 }
        //第几关
        this.chapter = (data && data.chapter) ? data.chapter : 1;
        // this.chapter = 6;
        //关卡数据
        this.chapterData = getChapterData(this.chapter);
        // this.chapterData.stepCount = 4
        //初始化目标信息
        this.initTarget();
        //初始化索引信息，包括10*10的，
        Tool.init(Tool.getColOddEven(this.chapterData.map.lattices), !!(this.passElements && this.passElements[ElementType.LOLLIPOP]));
        this.initScene();
        //先禁掉事件
        this.enableMouseEvt(false);

        //初始话地图格子数据
        this.initLattices();
        //初始化回收口,如果是棒棒糖关卡，那么在地图底部增加棒棒糖洞，用到map
        this.initRecycle();
        //初始化元素
        this.initElement();
        //重置基础元素类型及特效
        this.initBaseElement();
        //设置栏置顶
        //先判断是否添加节日元素,如果加节日元素，就按initFestivalEle里的来
        if (!await this.initFestivalEle()) {
            //检查一次三消
            if (this.threeMatch()) {
                this.eliminate();
            } else {
                //先检测死图
                this.warningCop = Tool.dieMapCheck(this.lattices);
                if (!this.warningCop) {
                    //替换顺序
                    this.upsetElement();
                } else {
                    this.enableMouseEvt(true);
                    //首次送道具，的动画先出，然后再出引导
                    if (this.chapter == 1 && Tools.globalData.isFirstLogin) {
                        this.addChild(new FirstPropGift())
                            .once(FYGE.MouseEvent.CLICK, (e: FYGE.MouseEvent) => {
                                Tools.globalData.isFirstLogin = false;
                                //移除自己
                                this.removeChild(e.target)
                                //游戏引导
                                this.initGuide();
                            }, this)
                    } else {
                        //游戏引导
                        this.initGuide();
                    }

                }
            }
        }
        //最后加载svga,为了判断是否要加大红包的动效
        this.initSvgas();
        //AiControl初始化，
        AiControl.ins.init(this.lattices);

        //测试波纹
        // let fun = ()=>{
        //     RectsWaveAni(this.lattices,this.map,fun);
        //     console.log(1)
        // }
        // fun()
    }

    /**
     * 加载所有用到的svga
     * 重写，变成其他动画
     */
    async initSvgas() {
        //这里加载资源吧
        await RES.loadGroup("bonus");
        const bonusTime = new Lottie(bonus);
        bonusTime.position.set(
            (750 - bonusTime.videoWidth) / 2,
            (1624 - bonusTime.videoHeight) / 2 - layers.stageOffsetY
        )
        movieClips.bonusTime = bonusTime;
        ["great", "wonderful", "amazing"].forEach((e) => {
            const g = new GameToast(e + ".png");
            g.position.set(375, 1624 / 2 - layers.stageOffsetY);
            movieClips[e] = g
        })
    }

    //初始化界面ui,,道具弄在这里s
    initScene() {

        //第几关
        const chapterTex = Tools.getNumTextures("chapterNum");
        ["第", "关"].forEach((e) => {
            chapterTex[e] = RES.getRes("chapterNum" + e + ".png")
        })
        const chapterTxt = this.addChild(new FYGE.BitmapText(chapterTex));
        chapterTxt.text = "第" + (this.chapter >> 0) + "关";
        chapterTxt.position.set(535, 58);
        //步数
        this.stepNumber = this.addChild(new FYGE.BitmapText(Tools.getNumTextures("stepNum")));
        this.stepNumber.position.set(375, 20);
        //步数初始化
        this.steps = this.chapterData.stepCount;
        //分数进度条，托管
        this.scoreProgress = this.addChild(new ScoreProgress(this.chapterData.starScores));
        this.scoreProgress.position.set(175, 113)
        //分数置0
        this.score = 0;
        //地图生成
        const path = generateMapBg(this.chapterData.map.lattices);
        const bg = drawBg(path, this.chapterData.map.lattices);
        this.addChild(bg);
        this.map = bg;
        //容器
        this.elementContainer = new FYGE.Container();
        this.addChild(this.elementContainer);
        //加遮罩,大遮罩一直都加，小遮罩时时判断
        const mask = drawShape(path);
        this.elementContainer.mask = mask;
        mask.updateShape();//执行一边吧，不然又bug，不更新_localBoundSelf
        this.addChild(mask)
        //容器事件
        this.elementContainer.addEventListener(FYGE.MouseEvent.MOUSE_DOWN, this.mouseDownE, this);
        this.elementContainer.addEventListener(FYGE.MouseEvent.MOUSE_MOVE, this.mouseMoveE, this);

        //初始换道具容器
        this.propBtnCon = new PropBtnCon();
        this.propBtnCon.y = 238 + Tool.gameAreaHeight + 10;
        this.addChild(this.propBtnCon);
        //按钮的事件
        this.propBtnCon.boomBtn.addEventListener(FYGE.MouseEvent.CLICK, this.onTap_boomBtn, this)
        this.propBtnCon.hammerBtn.addEventListener(FYGE.MouseEvent.CLICK, this.onTap_hammerBtn, this)
        this.propBtnCon.stepBtn.addEventListener(FYGE.MouseEvent.CLICK, this.onTap_stepBtn, this)
        //初始化道具信息
        this.updateScene();
        //选择框 无需加入场景
        const textureChoosed: FYGE.Texture = RES.getRes("chooseRect.png");
        this.choosed = new FYGE.Sprite(textureChoosed);
        this.choosed.anchorTexture.set(0.5, 0.5)
        //道具使用时蒙层 无需加入场景，带入propBtnCo的位置数据
        this.propGuide = new PropGuide(this.propBtnCon.y + 2);
        // this.addChild(this.propGuide);
        //bonusTime引导，备用的，无需加入场景
        // this.bonusTime = new BonusTime();
        // this.bonusTime.x = 375
        // this.bonusTime.y = 1624 / 2 - layers.stageOffsetY;
    }

    initTarget() {
        //通关目标,如果通关目标是元素时，赋值this.passElements，否则
        if (this.chapterData.passTarget.type == PassType.ELEMENT_TARGET) {
            this.hasEliminatedElements = [];
            this.passElements = [];
            for (let m = 0; m < this.chapterData.passTarget.elements.length; m++) {
                const element: ElementTargetData = this.chapterData.passTarget.elements[m];
                this.passElements[element.type] = element.count;
                this.hasEliminatedElements[element.type] = 0;
            }
            this.elementTargets = new ElementTargets(this.passElements);
            this.elementTargets.x = 375;
            this.elementTargets.y = 180;
            this.addChild(this.elementTargets);
            this.targetScoreTxt.visible = false;
        } else {
            this.passElements = null;
            this.targetScoreTxt.visible = true;
            this.targetScoreTxt.text = "目标分数：" + this.chapterData.passTarget.score;
        }
    }

    initRecycle() {
        //以防万一，初始化下。不排除，目标元素不是棒棒糖的，但是元素里有棒棒糖的情况；
        this.recycleIndexs = [];
        //通关目标是消除元素，并且有棒棒糖
        //不考虑0，初始的时候目标是0？
        if (this.passElements && this.passElements[ElementType.LOLLIPOP]) {
            //初始化recycleIndexs，
            const recycles = this.chapterData.map.recycles;
            //如果没有就取最下层的，用地图数据格子初始化
            if (!recycles || !recycles.length) {
                let endRowNumHash = {}, endRowNum: number = 0;
                for (let i = 0; i < this.chapterData.map.lattices.length; i++) {
                    const d = this.chapterData.map.lattices[i];
                    if (!d) continue;
                    const rc = Tool.indexToRc(i);
                    const [row, col] = rc;
                    //不用考虑0
                    if (!endRowNumHash[col]) {
                        endRowNumHash[col] = {row: row, index: i};
                    }
                    if (row > endRowNumHash[col].row) {
                        endRowNumHash[col] = {row: row, index: i};
                    }
                    //取大行数
                    endRowNum = Math.max(rc[0], endRowNum)
                }
                this.recycleIndexs = [];
                for (const key in endRowNumHash) {
                    const {index, row} = endRowNumHash[key];
                    if (endRowNum == row) this.recycleIndexs.push(index)
                }
            }
            //如果有数据，直接用数据，
            else {
                this.recycleIndexs = recycles.slice();
            }
            //画回收口
            const texture: FYGE.Texture = RES.getRes('main_mapbottom.png');
            const imgs = new FYGE.Container;
            for (let m = 0; m < this.recycleIndexs.length; m++) {
                const p = Tool.getPositionByIndex(this.recycleIndexs[m]);
                const img = new FYGE.Sprite(texture);
                img.x = p[0];
                img.y = p[1];
                imgs.addChild(img);
            }
            imgs.x = -texture.width / 2;
            imgs.y = Tool.height / 2 - 2;
            // imgs.cacheAsBitmap = true;//考虑如何缓存到和map一起
            this.map.addChildAt(imgs, 0);
        }
    }

    //初始化地图格子数据，包括生成口，联通口
    initLattices() {
        const latticesD = this.chapterData.map.lattices;
        //先格子
        this.lattices = [];
        for (let i = 0; i < latticesD.length; i++) {
            if (!latticesD[i]) continue;
            this.lattices[i] = Pool.takeOut(RecoverName.LATTICE);
            if (!this.lattices[i]) {
                this.lattices[i] = new Lattice(i);
            } else {
                this.lattices[i].reset(i);
            }
            //位置
            const p = Tool.getPositionByIndex(i);
            //石门
            const dooreData = [4, 5, 6, 51, 61];
            if (dooreData.indexOf(latticesD[i]) >= 0) {
                let door: Door = Pool.takeOut(RecoverName.DOOR);
                const closed: boolean = (latticesD[i] + "").length != 2;
                if (!door) {
                    door = new Door(closed);
                } else {
                    door.reset(closed);
                }
                this.map.addChild(door);
                this.lattices[i].door = door;
                door.x = p[0];
                door.y = p[1];
            }
            //冰块数据  2 3 5 6 或者51 61
            const iceData = [2, 3, 5, 6, 51, 61];
            if (iceData.indexOf(latticesD[i]) >= 0) {
                const iceCount = (function(n) {
                    if (n == 2 || n == 3) return n - 1;
                    if (n == 5 || n == 6) return n - 4;
                    return parseInt((n + "").split("")[0]) - 4;
                })(latticesD[i]);
                let ice = Pool.takeOut(RecoverName.ICE);
                if (!ice) {
                    ice = new Ice(iceCount);
                } else {
                    ice.reset(iceCount);
                }
                this.map.addChild(ice);
                this.lattices[i].ice = ice;
                ice.x = p[0];
                ice.y = p[1];
                //带石门时根据关闭隐藏
                if (latticesD[i] != 2 && latticesD[i] != 3 && (latticesD[i] + "").length == 1) {
                    ice.visible = false;
                }
            }
        }
        //生成口数据
        const genLatDatas = this.chapterData.map.generateLats || [];
        //设置生成口，
        this.generateIndexs = Tool.setGenerateLats(this.lattices, genLatDatas)


        let downMax: number = 0;
        let upMax: number = 0;
        let hasSmallToBig: boolean;
        let hasBigToSmall: boolean;
        //联通口
        const connectedLats = this.chapterData.map.connectedLats || [];
        for (let i = 0; i < connectedLats.length; i++) {
            //0是上格子，1是下格子
            const connectedLat = connectedLats[i];
            if (!connectedLat || !connectedLat.length) continue;
            this.lattices[connectedLat[0]].down = connectedLat[1];
            this.lattices[connectedLat[1]].up = connectedLat[0];

            const colUp = Tool.indexToRc(connectedLat[0])[1];
            const colDown = Tool.indexToRc(connectedLat[1])[1];
            downMax = Math.max(downMax, colDown);
            upMax = Math.max(upMax, colUp);
            if (colUp > colDown) hasBigToSmall = true;
            else if (colUp < colDown) hasSmallToBig = true;
        }
        //1表示存在小列连通到大列，从右往左遍历
        //2表示存在大列连通到小烈，从左往右遍历
        //3表示都有，遍历两边，down比up大，从右先
        //4表示都有，遍历两边，down比up小，从左先
        if (hasBigToSmall && hasSmallToBig) {
            if (downMax > upMax) {
                this.connectState = 3;
            } else {
                this.connectState = 4;
            }
        } else if (hasBigToSmall) {
            this.connectState = 2;
        } else if (hasSmallToBig) {
            this.connectState = 1;
        }//否则默认的2
    }

    //初始化地图元素
    initElement() {
        //没有就根据地图格子随机元素
        var elements = this.chapterData.map.elements || Tool.setNumber01(this.chapterData.map.lattices.slice());
        for (let i = 0; i < elements.length; i++) {
            //如果对应格子为空，直接跳过，
            if (!this.lattices[i]) continue;
            let ele: Element;
            switch (elements[i]) {
                //如果为0记录空格
                case 0:
                    this.emptys.push(i);
                    break;
                //基础元素,枷锁，气泡,灰色毛球，褐色毛球，黑色毛球
                case 1:
                case 4:
                case 7:
                case 8:
                case 9:
                case 10:
                    const type = Tool.returnType(i, this.lattices, this.chapterData.baseElementTypes);
                    ele = Tool.getElement(type)
                    if (elements[i] == 4) {
                        ele.setState(StateType.LOCK, true)
                    }
                    //气泡
                    else if (elements[i] == 7) {
                        //气泡要设定type
                        ele.setState(StateType.BUBBLE, true, type)
                    }
                    //灰色毛球
                    else if (elements[i] == 8) {
                        ele.setState(StateType.HAIRBALLGREY, true)
                    }
                    //褐色毛球
                    else if (elements[i] == 9) {
                        ele.setState(StateType.HAIRBALLBROWN, true)
                    }
                    //黑色毛球
                    else if (elements[i] == 10) {
                        ele.setState(StateType.HAIRBALLBLACK, true)
                    }
                    break;
                //棒棒糖
                case 3:
                    this.lollipopCount++;
                    ele = Tool.getElement(ElementType.LOLLIPOP)
                    break;
                //石头
                case 2:
                    ele = Tool.getElement(ElementType.ROCK)
                    break;
                //果冻
                case 5:
                    ele = Tool.getElement(ElementType.JELLY)
                    break;
                //鸡蛋
                case 6:
                    ele = Tool.getElement(ElementType.CHICKEN_EGG)
                    break;
                default:
                    //其他标识按空元素记
                    this.emptys.push(i);
                    break
            }
            //有元素操作
            if (ele) {
                var p = Tool.getPositionByIndex(i);
                ele.x = p[0];
                ele.y = p[1];
                this.elementContainer.addChild(ele);
                this.lattices[i].element = ele;
            }
            //针对石门修改
            if (this.lattices[i].doorClosed) {
                if (this.lattices[i].element) {
                    this.lattices[i].element.visible = false;
                }
                //无元素就是空格，空格去掉
                else {
                    Tool.removeEle(i, this.emptys);
                }
            }
        }
        //针对如果地图没有棒棒糖0，但是通关目标有，需要标记生成
        if (this.passElements &&
            this.passElements[ElementType.LOLLIPOP] &&
            !this.lollipopCount) {
            this.lollipopMark = true
        }
    }

    //重置基础元素及特效类型
    initBaseElement() {
        const elements = this.chapterData.map.baseElements;
        if (!elements || !elements.length) return;
        for (let i = 0; i < elements.length; i++) {
            //不是基础元素,跳过，
            if (!Tool.judgeBaseEle(this.lattices[i])) continue;
            //是0，跳过
            if (!elements[i]) continue
            //解析elements[i];
            const arr: number[] = Tool.returnTO(elements[i]);
            //基础类型，没有就是null，有就-1，对ElementType对应
            const baseType = arr[1] ? arr[1] - 1 : null;
            //baseType不是基础类型，且不是null，跳出
            if (FiveBaseElementTypes.indexOf(baseType) == -1 && baseType != null) continue;
            let ele: Element = this.lattices[i].element;
            //原先元素的类型不是基础元素
            if (FiveBaseElementTypes.indexOf(ele.type) == -1) continue;
            //如果baseType不为null，重置类型
            if (baseType != null) ele.resetType(baseType);

            //特效类型,有气炮的不能加特效
            if (!ele.hasState(StateType.BUBBLE)) ele.effectType = arr[0] ? arr[0] - 1 : null;
        }
    }

    //初始化引导，游戏和道具
    initGuide() {
        //游戏引导
        const gameGuideChapterNum = [1, 2, 3, 5, 8, 9, 10, 12, 17, 25, 41, 61, 101, 121]
        if (gameGuideChapterNum.indexOf(this.chapter >> 0) > -1) {
            if (!GTool.readCache(Tools.cacheKey + this.chapter)) {
                this.gameGuide = new GameGuide(this);
                this.addChild(this.gameGuide);
                this.gameGuide.show();
            }
        }
        // 234是道具引导,先不加
        // else if (this.chapter == 2 || this.chapter == 3 || this.chapter == 4) {
        //     if (!readCache(getCacheKey() + this.chapter + "prop")) {
        //         this.enableMouseEvt(false);
        //         var pgi = new PropGuideInt(this)
        //         this.addChild(pgi);
        //         pgi.init(this.chapter >> 0)
        //         this.lattices[41].element.effectType = EffectType.EXPLOSIVE
        //     }
        // }
    }

    startLoading() {
        return new Promise((resolve, reject) => {
            const node: Container = new Container();

            node.addChild(Tools.getSprite('playBg.jpg'));

            const tip = Tools.getSprite('festivalBg.png');
            tip.position.set(94, 246);
            node.addChild(tip);

            const loading: Sprite = node.addChild(Tools.getSprite('festivalLoading.png'));
            loading.position.set(-457, 966);

            const g: Graphics = this.addChild(new Graphics());
            g.beginFill(0xffffff);
            g.drawRoundedRect(98, 966, 556, 40, 40);
            g.endFill();
            loading.mask = g;

            node.addChild(loading);
            node.addChild(g);
            this.addChild(node);

            Tween.get(loading)
                .set({x: -457})
                .to({x: 98}, 2500, Ease.quadInOut)
                .wait(300)
                .call(() => {
                    this.removeChild(node);
                    resolve(true);
                });
        });
    }

    //初始化节日元素，暂时不用
    async initFestivalEle() {
        //添加节日元素，条件可能会变
        if (fesChapterData.indexOf(this.chapter >> 0) > -1
            // && getRedBombTimes()
            // && Math.random() > 0.5
            && this.data.isGiftPrizeLevel   // 接口说这是礼盒关卡
            && this.lattices[festivalIndex] &&   //格子必须有
            (Tool.judgeSetFesEle(this.lattices[festivalIndex]) || !this.lattices[festivalIndex].element)  //没有元素也行
        ) {

            await this.startLoading();

            const p = Tool.getPositionByIndex(festivalIndex);
            //添加节日红包目标
            this.festivalTarget = new FestivalTarget(this.festivalTargetNum);
            this.festivalTarget.x = 580;
            this.festivalTarget.y = 115;
            this.addChildAt(this.festivalTarget, 1);
            this.festivalTarget.visible = false;    /// TODO 关了这个功能先
            if (this.lattices[festivalIndex].element) {
                this.lattices[festivalIndex].element.reset(ElementType.FESTIVALELE_BIG)
            } else {
                let ele: Element = Tool.getElement(ElementType.FESTIVALELE_BIG)
                ele.x = p[0];
                ele.y = p[1];
                this.elementContainer.addChild(ele);
                this.lattices[festivalIndex].element = ele;
                Tool.removeEle(festivalIndex, this.emptys)
            }
            //动画，然后恢复this.enableMouseEvt(true);
            let ani: FesRedBombShowAni = Pool.takeOut(RecoverName.FESREDBOMBSHOW_ANI)
            if (!ani) ani = new FesRedBombShowAni();
            this.addChild(ani);
            this.lattices[festivalIndex].element.visible = false;
            ani.play(p, () => {
                this.lattices[festivalIndex].element.visible = true;
                RectsWaveAni(this.lattices, this.map)
                //检查三消
                if (this.threeMatch()) {
                    this.eliminate();
                } else {
                    //先检测死图
                    this.warningCop = Tool.dieMapCheck(this.lattices);
                    if (!this.warningCop) {
                        //替换顺序
                        this.upsetElement();
                    } else {
                        this.enableMouseEvt(true);
                    }
                }
            })
            return true
        }
        return false
    }

    /**
     * 更新三个道具
     * 绝对别修改，其他地方有调用的
     */
    updateScene() {
        this.propBtnCon.boomCount = Tools.propInfo.booms;
        this.propBtnCon.hammerCount = Tools.propInfo.hammers;
        this.propBtnCon.stepCount = Tools.propInfo.steps;
    }

    //侦听事件
    initEvents() {
        this.addEventListener(FYGE.Event.ENTER_FRAME, this.onEnterFrame, this);
    }

    removeEvents() {
        this.removeEventListener(FYGE.Event.ENTER_FRAME, this.onEnterFrame, this);

        this.propBtnCon.boomBtn.removeEventListener(FYGE.MouseEvent.CLICK, this.onTap_boomBtn, this)
        this.propBtnCon.hammerBtn.removeEventListener(FYGE.MouseEvent.CLICK, this.onTap_hammerBtn, this)
        this.propBtnCon.stepBtn.removeEventListener(FYGE.MouseEvent.CLICK, this.onTap_stepBtn, this)

        this.elementContainer.removeEventListener(FYGE.MouseEvent.MOUSE_DOWN, this.mouseDownE, this);
        this.elementContainer.removeEventListener(FYGE.MouseEvent.MOUSE_MOVE, this.mouseMoveE, this);
    }

    onEnterFrame() {
        if (this.mouseEnable && !this.gameGuide) {
            this.noActionCount++;
        } else {
            this.noActionCount = 0;
        }
        if (this.noActionCount >= 6 * 60 && !this.isShowWarning) {
            this.noActionCount = 0;
            this.isShowWarning = true;
            //出现动画
            this.warningAni();
        }
    }

    //出现交换提示
    warningAni() {
        const a = this.warningCop[0];
        const b = this.warningCop[1];
        const ax = a.x;
        const ay = a.y;
        const bx = b.x;
        const by = b.y;
        FYGE.Tween.get(a, {loop: true})
            .to({x: (bx - ax) / 10 + ax, y: (by - ay) / 10 + ay}, 100)
            .to({x: ax, y: ay}, 100)
            .wait(200)
            .to({x: (bx - ax) / 10 + ax, y: (by - ay) / 10 + ay}, 100)
            .to({x: ax, y: ay}, 100)
            .wait(400)
        FYGE.Tween.get(b, {loop: true})
            .to({x: (ax - bx) / 10 + bx, y: (ay - by) / 10 + by}, 100)
            .to({x: bx, y: by}, 100)
            .wait(200)
            .to({x: (ax - bx) / 10 + bx, y: (ay - by) / 10 + by}, 100)
            .to({x: bx, y: by}, 100)
            .wait(400)
    }

    //重置移动提示
    resetWarning() {
        if (!this.warningCop) return
        FYGE.Tween.removeTweens(this.warningCop[0]);
        FYGE.Tween.removeTweens(this.warningCop[1]);
        //还原位置
        const a = this.warningCop[0];
        const b = this.warningCop[1];
        const aIndex = a.index;
        const bIndex = b.index;
        const ap = Tool.getPositionByIndex(aIndex);
        const bp = Tool.getPositionByIndex(bIndex);
        a.x = ap[0];
        a.y = ap[1];
        b.x = bp[0];
        b.y = bp[1];
    }

    onTap_boomBtn() {
        this.propBtnCon.boomCount ?
            this.useProp(PropType.BOOMS) :
            showToast('道具不足');
    }

    onTap_hammerBtn() {
        this.propBtnCon.hammerCount ?
            this.useProp(PropType.HAMMERS) :
            showToast('道具不足');
    }

    onTap_stepBtn() {
        this.propBtnCon.stepCount ?
            this.useProp(PropType.STEPS) :
            showToast('道具不足');
    }

    //使用道具逻辑
    useProp(prop: PropType) {
        //去掉移动提示侦听
        this.removeEventListener(FYGE.Event.ENTER_FRAME, this.onEnterFrame, this);
        //去掉如果正在提示着的话
        if (this.isShowWarning) {
            this.isShowWarning = false;
            this.resetWarning();
        }
        //添加引导蒙层
        this.addChild(this.propGuide);
        this.propGuide.init(prop);
        //如果是炸弹或者锤子
        if (prop == PropType.BOOMS || prop == PropType.HAMMERS) {
            //去掉元素容器的侦听
            this.elementContainer.removeEventListener(FYGE.MouseEvent.MOUSE_DOWN, this.mouseDownE, this);
            this.elementContainer.removeEventListener(FYGE.MouseEvent.MOUSE_MOVE, this.mouseMoveE, this);
            //去掉如果有选中的元素
            if (this.SELECTED && this.choosed.parent) {
                this.choosed.parent.removeChild(this.choosed)
            }
            let fun: Function;
            this.elementContainer.addEventListener(FYGE.MouseEvent.MOUSE_DOWN, fun = function(e) {
                const ele = e.currentTarget.parent.parent;
                if (ele instanceof Element &&
                    // ele.type != ElementType.ROCK &&
                    ele.type != ElementType.LOLLIPOP) {
                    this.elementContainer.removeEventListener(FYGE.MouseEvent.MOUSE_DOWN, fun, this);
                    const index = ele.index;
                    this.sendPropUse(prop, index);
                }
            }, this)
            //如果点击引导取消使用，要把elementContainer事件移除
            this.propGuide.once(FYGE.MouseEvent.CLICK, () => {
                //点击移除自己
                this.removeChild(this.propGuide)

                this.elementContainer.removeEventListener(FYGE.MouseEvent.MOUSE_DOWN, fun, this);
                //还原事件
                this.addEventListener(FYGE.Event.ENTER_FRAME, this.onEnterFrame, this);
                this.elementContainer.addEventListener(FYGE.MouseEvent.MOUSE_DOWN, this.mouseDownE, this);
                this.elementContainer.addEventListener(FYGE.MouseEvent.MOUSE_MOVE, this.mouseMoveE, this);
            }, this);
        } else {
            if (!this.stepCircle) {
                this.stepCircle = new FYGE.Graphics()
                    .beginFill(0xff0000)
                    .drawCircle(stepPosition[0], stepPosition[1], 55)
                    .endFill()
                    .addEventListener(FYGE.MouseEvent.CLICK, () => {
                        this.sendPropUse(PropType.STEPS)
                    }, this)
                this.stepCircle.alpha = 0;
            }
            this.addChild(this.stepCircle)
            //如果点击引导取消使用
            this.propGuide.once(FYGE.MouseEvent.CLICK, () => {
                //点击移除自己
                this.removeChild(this.propGuide)
                //还原事件
                this.addEventListener(FYGE.Event.ENTER_FRAME, this.onEnterFrame, this);
                if (this.stepCircle.parent) this.removeChild(this.stepCircle)
            }, this);
        }
    }

    async sendPropUse(prop: PropType, index?: number) {
        //移除事件
        this.propGuide.removeAllEventListener();
        //移除自己
        this.removeChild(this.propGuide);
        //接口
        showWaiting();
        const s = await Tools.consumerTools(prop);
        hideWaiting();
        //还原事件
        this.addEventListener(FYGE.Event.ENTER_FRAME, this.onEnterFrame, this);
        //区分
        if (prop == PropType.BOOMS || prop == PropType.HAMMERS) {
            this.enableMouseEvt(false)
            this.elementContainer.addEventListener(FYGE.MouseEvent.MOUSE_DOWN, this.mouseDownE, this);
            this.elementContainer.addEventListener(FYGE.MouseEvent.MOUSE_MOVE, this.mouseMoveE, this);
        } else {
            this.removeChild(this.stepCircle)
        }
        if (s) {
            //成功的
            switch (prop) {
                case PropType.BOOMS:
                    //playSound(SoundType.line)
                    const rc = Tool.indexToRc(index);
                    const p = Tool.getPositionByIndex(index);
                    this.playAni(RecoverName.CROSS_ANI, p);
                    for (let i = 0; i < this.lattices.length; i++) {
                        const lat = this.lattices[i];
                        if (Tool.judgeEliminate(lat) && (lat.row == rc[0] || lat.column == rc[1])) {
                            if (this.eliminatedElements.indexOf(i) == -1) {
                                this.eliminatedElements.push(i);
                                if (Tool.judgeHasScore(lat.element)) {
                                    //算直线+直线LL
                                    this.pushScoreAni(baseScore * effectBaseTimes.LL, Tool.getPositionByIndex(i))
                                }
                            }
                        }
                    }
                    setTimeout(() => {
                        this.eliminate();
                    }, 200)
                    this.isMouseAction = true;
                    break;
                case PropType.HAMMERS:
                    this.eliminatedElements.push(index);
                    if (Tool.judgeHasScore(this.lattices[index].element)) {
                        this.pushScoreAni(
                            baseScore * (effectBaseTimes[this.lattices[index].element.effectType] || 1),
                            Tool.getPositionByIndex(index)
                        )
                    }
                    this.eliminate();
                    this.isMouseAction = true;
                    break;
                case PropType.STEPS:
                    this.playAni(RecoverName.STEP_ANI, [stepPosition[0] - 40, stepPosition[1] + 20]);
                    setTimeout(() => {
                        this.steps += 5;
                    }, 333)
                    // showToast("步数增加成功");
                    break;
            }
            //更新道具数量
            this.updateScene();
        } else {
            //失败的话，要继续能点击
            this.enableMouseEvt(true)
        }
    }

    mouseDownE(e: FYGE.MouseEvent) {
        //现在有两级，element的showImage是FrameAni
        const ele = e.currentTarget.parent.parent;
        // console.log(e.currentTarget.parent.parent)
        const self = this;
        if (ele && ele instanceof Element) {
            //去掉提示动画
            this.isShowWarning = false;
            this.noActionCount = 0;
            this.resetWarning();
            if (!Tool.judgeChosen(ele)) return
            if (self.SELECTED && Tool.checkNeb(ele, self.SELECTED)) {
                self.exchangeElementAni(self.SELECTED, ele, self.callbackEx);
                self.SELECTED.removeChild(self.choosed);
                self.SELECTED = null;
            } else {
                self.SELECTED = ele;
                if (self.SELECTED.effectType == EffectType.MAGICLION) {
                    self.elementContainer.addChildAt(self.SELECTED, 0);
                } else {
                    self.elementContainer.addChild(self.SELECTED);
                }
                self.SELECTED.addChild(self.choosed);
            }
        }
    }

    mouseMoveE(e) {
        if (this.SELECTED && e.currentTarget.parent.parent instanceof Element) {
            const ele = e.currentTarget.parent.parent;
            if (!Tool.judgeChosen(ele)) return
            if (Tool.checkNeb(ele, this.SELECTED)) {
                this.exchangeElementAni(this.SELECTED, ele, this.callbackEx);
                this.SELECTED.removeChild(this.choosed);
                this.SELECTED = null;
            }
        }
    }

    //交换元素回调函数，处理数据及重新交换
    private callbackEx(s, o) {
        this.exchangeData(s, o);
        if (this.simpleMatch(s, o)) {
            //手动触发的消除
            this.isMouseAction = true;
            //有游戏引导时去掉
            if (this.gameGuide) this.gameGuide.hideSelf();
            //次数减1；
            this.steps--;
            this.oneStepScore = 0;
            //开始执行消除
            this.eliminate();
            //同时考虑判断棒棒糖交换后是否可以掉落，是否可以落下，待加
            const aarr = [s, o];
            for (let i = 0; i < aarr.length; i++) {
                const ele = aarr[i];
                //元素是棒棒糖，并且在回收口索引内
                if (ele.type == ElementType.LOLLIPOP && this.recycleIndexs.indexOf(ele.index) > -1) {
                    this.recoverEle(ele.index);
                    this.lollipopCount--;
                    //通关目标还有棒棒糖，并且，图中不足，下次需要生成
                    if (this.passElements &&
                        this.passElements[ElementType.LOLLIPOP] &&
                        this.lollipopCount < this.passElements[ElementType.LOLLIPOP]) {
                        this.lollipopMark = true
                    }
                }
            }
        } else {
            this.exchangeData(s, o);
            this.exchangeElementAni(s, o);
        }
    }

    //交换元素动画
    private exchangeElementAni(s: Element, o: Element, callback?) {
        this.enableMouseEvt(false);
        FYGE.Tween.get(s)
            .to({x: o.x, y: o.y}, 166)
            .call(() => {
                if (callback) {
                    callback.bind(this)(s, o);
                } else {
                    this.enableMouseEvt(true);
                }
            })
        FYGE.Tween.get(o)
            .to({x: s.x, y: s.y}, 166)
    }

    //数据交换
    private exchangeData(a: Element, b: Element) {
        const latA = this.lattices[a.index];
        const latB = this.lattices[b.index];
        latA.element = b;
        latB.element = a;
    }

    fallVerMark: boolean

    /**
     * 掉落方法还有问题，遇到那种少量垂直元素，只能等垂直元素到底后，才考虑斜落，以后再考虑一边掉落逻辑，
     * 比如在一次性fallVerticalEx每列加上斜落元素，必须自身下方是空，无连通的情况
     * @param callback 所有fall完成后的回调,检查三消，
     */
    fall(callback: Function) {
        //去重一次
        Tool.removeReapty(this.emptys);

        //逻辑需要重新梳理
        if (/*!this.fallVerMark ||*/ Tool.judgeOutOne(this.emptys, this.lattices)) {
            // this.fallVerMark = true;
            let anis = this.fallVerticalEx();
            if (!anis.length) {
                anis = this.fallSkewEx()
                if (!anis.length) {
                    callback();
                } else {
                    this.fallAniSuper(anis, () => {
                        this.fall(callback)
                    })
                }
            } else {
                this.fallAniSuper(anis, () => {
                    this.fall(callback)
                }, true)
            }
        } else {
            let anis = this.fallVertical();
            anis = anis.concat(this.fallSkewEx());
            if (!anis.length) {
                // this.fallVerMark = false;
                //如果没有能掉落的就直接回调
                callback();
            } else {
                //有就执行掉落动画
                this.fallAniSuper(anis, () => {
                    this.fall(callback)
                })
            }
        }

    }

    /**
     * 竖直掉落,一格判断,,没有按竖直生成口从上到下，所以有点问题
     */
    private fallVertical() {
        var anis: FallAniData[] = []
        //先把竖直掉落一格
        for (let m = 0; m < Tool.colNum; m++) {
            //同一列
            for (let n = Tool.rowNum - 1; n >= 0; n--) {
                const index = Tool.rcToIndex(n, m);
                let lat = this.lattices[index];
                if (Tool.judgeFall(lat)) {
                    //如果能掉落，，找down，下面，左下，右下是否有空格
                    var latDown, indexDown, connects: { index: number, type: FallType }[] = [];
                    if (lat.down != null) {
                        latDown = this.lattices[lat.down];
                        indexDown = lat.down;
                        connects.push({index: indexDown, type: FallType.THROUGH})
                    } else {
                        latDown = this.lattices[index + Tool.colNum]
                        indexDown = index + Tool.colNum;
                        connects.push({index: indexDown, type: FallType.STRIGHT})
                    }
                    //下方有格子，并且再this.empty中
                    const emptyIndex = this.emptys.indexOf(indexDown);
                    if (emptyIndex > -1) {
                        anis.push({ele: lat.element, indexs: connects});
                        //去掉一个
                        this.emptys.splice(emptyIndex, 1);
                        //加上一个
                        this.emptys.push(index);
                        lat.element = null
                    }
                } else {
                    //如果有格子。无元素，是生成口，且上方没有能掉落的格子时（若有，遍历上面格子时掉落）
                    if (lat &&
                        !lat.element &&
                        lat.isGenerate &&
                        !Tool.judgeFall(this.lattices[index - Tool.colNum])
                    ) {
                        anis.push({ele: null, indexs: [{index: index, type: FallType.STRIGHT}]});
                        Tool.removeEle(index, this.emptys)
                    }
                }
            }
        }
        return anis;
    }

    /**
     * 一次性都竖直掉落
     * 有问题
     * 如果  存在小列连通到大列，从右往左遍历
     * 如果  存在大列连通到小烈，从左往右遍历
     * 都存在，遍历两边
     */
    fallVerticalEx() {
        //1表示存在小列连通到大列，从右往左遍历
        //2表示存在大列连通到小烈，从左往右遍历
        //3表示都有，遍历两边，down比up大，从右先
        //4表示都有，遍历两边，down比up小，从左先
        const anis: FallAniData[] = [];
        let operation = (m) => {
            //同一列,先原有的填满，再查是否有生成口
            for (let n = Tool.rowNum - 1; n >= 0; n--) {
                let index = Tool.rcToIndex(n, m);
                let lat = this.lattices[index];
                if (Tool.judgeFall(lat)) {
                    //待修改
                    const indexs = Tool.findBottom(lat, this.emptys, this.lattices);
                    if (indexs != null) {
                        //最后一个
                        const downIndex = indexs[indexs.length - 1].index;
                        anis.push({ele: lat.element, indexs: indexs});
                        //去掉最后一个
                        // this.emptys.splice(emptyIndex, 1);
                        Tool.removeEle(downIndex, this.emptys)
                        //加上一个
                        this.emptys.push(index);
                        lat.element = null
                    }
                } else {

                }
            }
            //找m列的生成口，可能有多个
            if (this.generateIndexs[m]) {
                for (let aaa = 0; aaa < this.generateIndexs[m].length; aaa++) {
                    let index = this.generateIndexs[m][aaa].index;
                    let lat = this.lattices[index];
                    //如果有格子。无元素，是生成口,要补齐下面能掉落的
                    if (lat && !lat.element && lat.isGenerate) {
                        //找出从生成口一直到能掉落的空格,两个
                        let downIndex = index;
                        let type: FallType = FallType.STRIGHT;
                        let connects: { index: number, type: FallType }[] = []
                        let anisOther: FallAniData[] = []
                        while (this.emptys.indexOf(downIndex) > -1) {
                            connects.push({
                                index: downIndex,
                                type: type,
                            })
                            anis.push({
                                ele: null,
                                indexs: connects.slice()
                            });
                            Tool.removeEle(downIndex, this.emptys)
                            if (this.lattices[downIndex].down != null) {
                                downIndex = this.lattices[downIndex].down;
                                type = FallType.THROUGH
                            } else {
                                downIndex += Tool.colNum;
                                type = FallType.STRIGHT
                            }
                        }
                        let len = anis.length;
                        for (let i = len - 1; i >= len - connects.length; i--) {
                            anis[i].wait = (len - 1 - i) * Tool.latDeltaTime * 2 / 3;
                        }
                    }
                }
            }
        }
        if (this.connectState == 1) {
            for (let m = Tool.colNum; m >= 0; m--) operation(m)
        } else if (this.connectState == 2) {
            for (let m = 0; m < Tool.colNum; m++) operation(m)
        } else if (this.connectState == 3) {
            for (let m = Tool.colNum; m >= 0; m--) operation(m);
            for (let m = 0; m < Tool.colNum; m++) operation(m);
        } else if (this.connectState == 4) {
            for (let m = 0; m < Tool.colNum; m++) operation(m);
            for (let m = Tool.colNum; m >= 0; m--) operation(m);
        }
        return anis;
    }

    /**
     * 存在斜落的
     */
    private fallSkewEx() {
        //记录一个对象和位置信息[obj,index]的数组，如果obj是null则生成，index位置上方
        const anis: FallAniData[] = [];
        const emptys = this.emptys;
        //排序从大到小
        emptys.sort(function(a, b) {
            return a < b ? 1 : -1
        })
        for (let i = 0; i < emptys.length; i++) {
            let indexMy = emptys[i];
            const lat = this.lattices[indexMy];

            let latFall;
            let indexFall;
            let type: FallType = FallType.STRIGHT
            //找联通的up，一期没有复杂的联通，所以动画不用特殊处理
            if (lat.up) {
                const latUnUp = this.lattices[lat.up];
                if (Tool.judgeFall(latUnUp)) {
                    latFall = latUnUp;
                    indexFall = lat.up;
                    type = FallType.THROUGH
                }
            }
            //找正上方
            if (!latFall && Tool.indexToRc(indexMy)[0] != 0) {
                const latUp = this.lattices[indexMy - Tool.colNum];
                if (Tool.judgeFall(latUp)) {
                    latFall = latUp;
                    indexFall = indexMy - Tool.colNum;
                }
            }
            //找左上
            let findLeft = () => {
                if (!latFall && Tool.indexToRc(indexMy)[1] != 0) {
                    const latLeftUp = this.lattices[indexMy - Tool.colNum - 1];
                    if (Tool.judgeFall(latLeftUp)) {
                        latFall = latLeftUp;
                        indexFall = indexMy - Tool.colNum - 1;
                    }
                }
            }
            //找右上
            let findRight = () => {
                if (!latFall && Tool.indexToRc(indexMy)[1] != Tool.colNum - 1) {
                    const latRightUp = this.lattices[indexMy - Tool.colNum + 1];
                    if (Tool.judgeFall(latRightUp)) {
                        latFall = latRightUp;
                        indexFall = indexMy - Tool.colNum + 1;
                    }
                }
            }
            //随机一下，先找左或先找右
            if (Math.random() > 0.5) {
                findLeft();
                findRight();
            } else {
                findRight();
                findLeft();
            }
            //如果latFall存在
            if (latFall) {
                //能掉落，记录元素动画
                anis.push({ele: latFall.element, indexs: [{index: indexMy, type: type}]});
                //去掉
                // console.log(emptys)
                // console.log(indexFall)
                emptys.splice(i, 1);
                //在剩下emptys的加上一个，正上方的那个
                // Tool.insertEX(indexFall, emptys)
                // console.log(emptys)
                // emptys.push(indexFall)
                emptys.splice(i + 1, 0, indexFall);
                // Tool.removeEle(indexMy,emptys)
                //掉落的格子上的元素致null
                latFall.element = null;
                //长度加了，多加+
                i--;
            } else {
                //判断是否是生成口
                if (lat.isGenerate) {
                    //是生成口，就不用管落下乐
                    anis.push({ele: null, indexs: [{index: indexMy, type: FallType.STRIGHT}]})
                    //去掉
                    emptys.splice(i, 1);
                    //优先判断生成的，不用管掉落了，跳下一个
                    i--
                }
            }
        }
        return anis
    }

    fallAniSuper(anis: FallAniData[], callback: Function, isVertical: boolean = false) {
        let deltaTime = Tool.latDeltaTime;
        //是竖直的。播放声音
        if (isVertical) {
            //playSound(SoundType.fall);
            deltaTime = Tool.latDeltaTime * 2 / 3;//加系数有问题，包括wait
        }
        let count = 0;
        let countAll = anis.length;
        //先用后序的吧
        for (let i = anis.length - 1; i >= 0; i--) {
            let ani: FallAniData = anis[i];
            let ele: Element = ani.ele;
            let wait: number = ani.wait || 0;
            let indexs: { index: number, type: FallType }[] = ani.indexs;
            let oriRow: number;
            let oriCol;
            //最后的索引
            let endIndex = indexs[indexs.length - 1].index;
            let tween: FYGE.Tween;
            if (!ele) {
                //取出第一个索引
                let firstIndex: { index: number, type: FallType } = indexs.shift()
                ele = this.createELement(firstIndex.index);
                this.elementContainer.addChild(ele);
                oriRow = Tool.indexToRc(firstIndex.index)[0];
                let fp = Tool.getPositionByIndex(firstIndex.index);
                ele.x = fp[0];
                ele.y = fp[1] - Tool.height;
                tween = FYGE.Tween.get(ele);
                //有等待的话，只有多格子掉落的时候才有
                if (wait) tween.wait(wait);
                //加遮罩，需要遮罩写法，如果该口上方有格子
                if (this.lattices[firstIndex.index - Tool.colNum]) {
                    let mask = Tool.getRectMask();
                    mask.x = fp[0];
                    mask.y = fp[1];
                    this.elementContainer.addChild(mask);
                    ele.mask = mask;
                    tween.to({x: fp[0], y: fp[1]}, deltaTime)
                        .call(() => {
                            //移除遮罩
                            ele.mask = null;
                            //回收
                            mask.recover()
                        })
                } else {
                    tween.to({x: fp[0], y: fp[1]}, deltaTime)
                }
                wait += deltaTime
            } else {
                oriRow = ele.row;
                tween = FYGE.Tween.get(ele);
            }

            let lastX: number, lastY: number;
            //对所有的indexs进行遍历动画
            for (let j = 0; j < indexs.length; j++) {
                let index = indexs[j].index;
                let type = indexs[j].type;
                let p = Tool.getPositionByIndex(index);

                if (type == FallType.THROUGH) {
                    let indexUp = this.lattices[index].up;
                    let downPos = Tool.getPositionByIndex(index);
                    let upPos = Tool.getPositionByIndex(indexUp);
                    //up处的动画
                    //如果上格子下面有格子, 用遮罩的写法，
                    if (this.lattices[indexUp + Tool.colNum]) {
                        EleMaskAni(ele, upPos, wait, this.elementContainer, true, deltaTime);
                        tween.set({alpha: 0})
                            .wait(deltaTime)
                            .set({alpha: 1})
                    } else {
                        tween.to({x: upPos[0], y: upPos[1] + Tool.height}, deltaTime);
                    }
                    //down处的动画
                    //如果下格子上方有格子，用遮罩的写法
                    if (this.lattices[index - Tool.colNum]) {
                        EleMaskAni(ele, downPos, wait, this.elementContainer, false, deltaTime);
                    } else {
                        EleDownAni(ele, downPos, wait, this.elementContainer, false, deltaTime)
                    }

                    //修改oriRow
                    oriRow = Tool.indexToRc(index)[0];
                    lastX = downPos[0];
                    lastY = downPos[1];
                    wait += deltaTime;
                } else {
                    //算间隔
                    const delta = Tool.indexToRc(index)[0] - oriRow;
                    if (delta > 0) {
                        //修改oriRow
                        oriRow = Tool.indexToRc(index)[0];
                        //动画
                        if (lastX != null) {
                            tween.set({x: lastX, y: lastY})
                        }
                        tween.to({x: p[0], y: p[1]}, delta * deltaTime)
                        //累计等待时间
                        wait += delta * deltaTime;
                        lastX = null
                    }
                }
            }
            //判断是否要掉落回弹动效
            let showFallAni: boolean = (() => {
                // console.log(indexs.length)
                // if(!indexs.length)return false
                // let p0 = indexs.length == 1 ? [ele.x, ele.y] : Tool.getPositionByIndex(indexs[0].index);
                // let p1 = Tool.getPositionByIndex(indexs[indexs.length - 1].index);
                // return (Math.abs(p0[0] - p1[0]) < 1)
                return false
            })()
            //元素赋值，考虑到底放哪好
            const lat = this.lattices[endIndex];
            lat.element = ele;
            //最终回调
            tween.call(() => {
                let endP = Tool.getPositionByIndex(endIndex);
                ele.x = endP[0];
                ele.y = endP[1];
                if (ele.type == ElementType.LOLLIPOP && this.recycleIndexs.indexOf(endIndex) > -1) {
                    this.recoverEle(endIndex);
                    this.lollipopCount--;
                    //通关目标还有棒棒糖，并且，图中不足，下次需要生成
                    if (this.passElements &&
                        this.passElements[ElementType.LOLLIPOP] &&
                        this.lollipopCount < this.passElements[ElementType.LOLLIPOP]) {
                        this.lollipopMark = true
                    }
                } else if (showFallAni) {
                    ele.fallAni()
                }

                if (++count == countAll) callback();
            })
        }

    }

    /**
     * 创建一个掉落元素，
     * 逻辑包括棒棒糖逻辑
     * 定制掉落元素逻辑
     * @param index 生成口索引。定制掉落口用，不存在则随机
     */
    createELement(index: number): Element {
        let type: ElementType;
        let effectType: EffectType = null;
        //优先棒棒糖
        if (this.lollipopMark) {
            this.lollipopMark = false;
            type = ElementType.LOLLIPOP;
            this.lollipopCount++;
        }
        //生成口定制掉落
        else {
            //基础元素数组
            const baseElementTypes = this.chapterData.baseElementTypes.slice();
            //列数
            const col = Tool.indexToRc(index)[1];
            //找出索引是index的数据
            const data: GenerateLatData = this.generateIndexs[col].filter((d) => {
                return d.index == index
            })[0];
            if (data) {
                //先去掉cus里的
                let one = data.cus.shift()
                if (one) {
                    const oneData = Tool.praseEleNumber(one);
                    type = oneData[0] === null ? Tool.randomT(baseElementTypes) : oneData[0];
                    effectType = oneData[1];
                }
                //否则根据type来定
                else {
                    //type没有或type长度没有，随机
                    if (!data.type || !data.type.length) {
                        type = Tool.randomT(baseElementTypes)
                    }
                    //type里随机。随机出来如果是0，还是随机
                    else {
                        const code = Tool.randomT(data.type);
                        if (code) {
                            const codeData = Tool.praseEleNumber(code);
                            type = codeData[0] === null ? Tool.randomT(baseElementTypes) : codeData[0];
                            effectType = codeData[1];
                        } else {
                            type = Tool.randomT(baseElementTypes)
                        }
                    }
                }
            }
            //随机
            else {
                type = Tool.randomT(baseElementTypes)
            }
        }
        let ele = Tool.getElement(type);
        //如果未通关，有特效的话可以加
        if (!this.hasPassed) ele.effectType = effectType;
        //不是棒棒糖，且没有特效的时候，考虑加气泡
        if (type != ElementType.LOLLIPOP &&
            effectType == null &&
            this.chapterData.bubbleProbability &&
            Math.random() < this.chapterData.bubbleProbability) {
            ele.setState(StateType.BUBBLE, true, type)
        }
        return ele;
    }

    /**
     * 只关心交换后元素的可消除
     * 或者冰淇淋的掉落
     * 逻辑以后待加，
     * 该方法只针对手动触发的消除，包括特效组合，手动三消的，可以另写方法，并重新修改鸡蛋，石头，果冻，毛球逻辑
     */
    simpleMatch(s: Element, o: Element) {
        //先判断两个都是特效
        if (s.effectType != null && o.effectType != null) {
            this.isManual = true;
            this.eliminatedElements.push(s.index, o.index);
            //特效连消次数
            this.effectContinuityTimes++
            return true;
        }
        //其中一个是魔力鸟
        if (s.effectType == EffectType.MAGICLION || o.effectType == EffectType.MAGICLION) {
            if (s.type == ElementType.LOLLIPOP || o.type == ElementType.LOLLIPOP) {
                return false
            } else {
                this.isManual = true;
                this.eliminatedElements.push(s.index, o.index);
                //特效连消次数
                this.effectContinuityTimes++
                return true;
            }
        }
        //剩下的情况，用三消判断
        return this.threeMatch(s, o);
    }

    /**
     * 三消判断
     */
    threeMatch(s?: Element, o?: Element) {
        //横排检测
        for (let i = 0; i < Tool.rowNum; i++) {
            //集合横排元素
            const arr = []
            for (let g = 0; g < Tool.colNum; g++) {
                arr.push(i * Tool.colNum + g)
            }
            let re = Tool.fn(arr, this.lattices);
            for (let j = 0; j < re.length; j++) {
                let len = re[j].length;
                if (len >= 3) {
                    for (let k = 0; k < len; k++) {
                        let ele = this.lattices[re[j][k]].element;
                        this.eliminatedElements.push(re[j][k]);
                        //是五个的
                        if (len >= 5) {
                            if (k == 2) {
                                ele.temEffectType = EffectType.MAGICLION;
                                this.elementContainer.addChildAt(ele, 0)
                            }
                        }
                        //4个，这里是预留的，竖排还要再判断，是否生成爆炸的
                        else if (len == 4) {
                            if ([s, o].indexOf(ele) > -1) {
                                ele.temEffectType = EffectType.VERTICAL
                            }
                            //如果都没有so,让第三个成为特效
                            else if (!s && k == 2) {
                                ele.temEffectType = EffectType.VERTICAL
                            }
                        }
                    }
                }
            }
        }
        //竖排检测
        for (let m = 0; m < Tool.colNum; m++) {
            //集合竖排元素
            const arr = []
            for (let g = 0; g < Tool.rowNum; g++) {
                arr.push(Tool.colNum * g + m)
            }
            let re = Tool.fn(arr, this.lattices);
            for (let j = 0; j < re.length; j++) {
                let len = re[j].length;
                if (len >= 3) {
                    for (let k = 0; k < len; k++) {
                        let ele = this.lattices[re[j][k]].element;
                        //是五个的,特殊处理
                        if (len >= 5) {
                            if (k == 2) {
                                //只要不是就覆盖
                                if (ele.temEffectType != EffectType.MAGICLION) {
                                    ele.temEffectType = EffectType.MAGICLION;
                                    this.elementContainer.addChildAt(ele, 0)
                                    if (this.eliminatedElements.indexOf(re[j][k]) == -1) {
                                        this.eliminatedElements.push(re[j][k]);
                                    }
                                }
                            } else {
                                if (this.eliminatedElements.indexOf(re[j][k]) == -1) {
                                    this.eliminatedElements.push(re[j][k]);
                                } else {
                                    //原先在消除队列，是交叉点，变成爆炸特效
                                    if (ele.temEffectType != EffectType.MAGICLION) ele.temEffectType = EffectType.EXPLOSIVE;
                                }
                            }
                        }
                        //4个
                        else if (len == 4) {
                            if ([s, o].indexOf(ele) > -1) {
                                ele.temEffectType = EffectType.HORIZONTAL;
                            } else if (!s && k == 2) {
                                ele.temEffectType = EffectType.HORIZONTAL;
                            }
                            //最终判断
                            if (this.eliminatedElements.indexOf(re[j][k]) == -1) {
                                this.eliminatedElements.push(re[j][k]);
                            } else {
                                if (ele.temEffectType != EffectType.MAGICLION) ele.temEffectType = EffectType.EXPLOSIVE;
                            }
                        } else {
                            if (this.eliminatedElements.indexOf(re[j][k]) == -1) {
                                this.eliminatedElements.push(re[j][k]);
                            } else {
                                if (ele.temEffectType != EffectType.MAGICLION) ele.temEffectType = EffectType.EXPLOSIVE;
                            }
                        }
                    }
                }
            }
        }
        //算普通消除分数
        if (this.eliminatedElements.length && !this.hasPassed) this.commonContinuityTimes++;
        for (let aa = 0; aa < this.eliminatedElements.length; aa++) {
            const index = this.eliminatedElements[aa];
            //没有锁，也没有毛球
            if (Tool.judgeHasScore(this.lattices[index].element)) {
                this.pushScoreAni(baseScore * this.commonContinuityTimes, Tool.getPositionByIndex(index))
            }
        }
        return this.eliminatedElements.length > 0;
    }


    /**
     * 执行消除，应该是一个迭代，只要eliminatedElements有东西就一直执行
     * 掉落后的逻辑很复杂
     */
    eliminate() {
        //如果是是带组合特效的，组合时的特效本身的算分在effectCombination内，暂不计
        if (this.isManual) {
            this.isManual = false;
            this.effectCombination()
        } else {
            //先消除有旋转的消除完就置空
            this.rotateEliminate()
            //消除同时找出带特效的
            const effectIndexs = this.commonEliminate();
            let scoreAll = 0;
            //得分动效执行，在上面消除动效后出现，分数动效层级要高
            for (let i = 0; i < this.scoreAnis.length; i++) {
                const scoreAni = this.scoreAnis[i];
                scoreAll += scoreAni.score;
                // this.addChild(scoreAni)
                // scoreAni.play();
            }
            this.scoreAnis.length = 0;
            this.score += scoreAll;
            this.oneStepScore += scoreAll;
            // console.log(this.score)

            //this.eliminatedElements已为0;对有特效的特殊处理，又会添加进eliminatedElements，
            //必须在上面得分动效后，因为下面这波重新出现的元素要在下一次出现
            this.effectEliminate(effectIndexs);
        }
        //如果还有消除元素，继续执行
        if (this.eliminatedElements.length) {
            setTimeout(() => {
                this.eliminate();
            }, 200)
        } else {
            //没有可消除元素，执行掉落,
            setTimeout(() => {
                this.fall(() => {
                    //掉落停止回调
                    this.fallCallback();
                });
            }, 200)
        }
    }

    /**
     * 掉落完后的操作
     */
    fallCallback() {
        //掉落后有消除，执行消除
        if (this.threeMatch()) {
            this.eliminate()
            return
        }
        //有时间改成await
        AiControl.ins.doorMotion(this, () => {
            //鸡蛋的ai操作，存在判断三消，毛球的ai，也一样，所以放前面，存在三消时都直接执行eliminate了。不执行后续的回调
            AiControl.ins.eggMotion(this, () => {
                //后执行毛球跳动
                AiControl.ins.hairballMotion(this, () => {
                    //节日元素
                    AiControl.ins.fesEleMotion(this, () => {
                        //如果早已通关，正在结算
                        if (this.hasPassed) {
                            //如果原先特效还有，
                            if (!this.isCountingTime) {
                                this.terminateSteps();
                            }
                            //如果只有步数转化的，不能再进结算，
                            else {
                                //通关了
                                //提交分数等
                                // var json = this.getSubmitJson()
                                this.submit()
                            }
                            return
                        }
                        //判断是否已达目标
                        this.hasPassed = this.judgePass();
                        // console.log(this.hasPassed)
                        //如果已达目标
                        if (this.hasPassed) {
                            //连消基数为1
                            this.commonContinuityTimes = 1;
                            this.effectContinuityTimes = 1;
                            this.addChild(movieClips.bonusTime);
                            movieClips.bonusTime.play(1, () => {
                                this.removeChild(movieClips.bonusTime)
                                //来个波纹
                                RectsWaveAni(this.lattices, this.map, () => {
                                    this.terminateSteps();
                                })
                            })
                            return
                        }
                        //出累计一步分数的toast；
                        this.oneStepScoreToast();
                        //连消置0
                        this.commonContinuityTimes = 0;
                        this.effectContinuityTimes = 0;
                        //判断步数是否为0；
                        if (this.steps == 0) {
                            //还未提示过加步数，并且有道具
                            if (!this.addedStepWarning && !!Tools.propInfo.steps) {
                                this.addedStepWarning = true;
                                //没步数弹框
                                showPanel(NoStepPanel, {
                                    reviveFun: async () => {
                                        showWaiting();//这种情况要考虑失败嘛？
                                        var s = await Tools.consumerTools(PropType.STEPS);
                                        hideWaiting();
                                        if (!s) {//使用失败，直接提交吧
                                            this.submit(false)
                                        } else {//走完剩下的流程
                                            let count = 0;
                                            let countAll = 2;
                                            AiControl.ins.jellyMotion(this, () => {
                                                if (++count === countAll) this.aiMotionCheckDieMap()
                                            })
                                            AiControl.ins.bubbleMotion(this, () => {
                                                if (++count === countAll) this.aiMotionCheckDieMap()
                                            })
                                            //加步数
                                            this.playAni(RecoverName.STEP_ANI, [stepPosition[0] - 40, stepPosition[1] + 20]);
                                            setTimeout(() => {
                                                this.steps += 5;
                                            }, 333)
                                            //更新场景道具数据
                                            this.updateScene()
                                        }
                                    }
                                })
                            } else {
                                //失败提交
                                this.submit(false)
                            }
                            return
                        }
                        //果冻的时间980，气泡的时间1100，必须考虑直接执行的情况，所以必须同时判断
                        //额外加一步ai果冻的操作，考虑和气泡一起改变，然后再执行检查死图
                        let count = 0;
                        let countAll = 2;//和操作步数一致
                        //果冻的先执行，因为有元素类型的改变(数据基本都在动画之前改变)，会影响变色气泡的动画
                        AiControl.ins.jellyMotion(this, () => {
                            if (++count === countAll) this.aiMotionCheckDieMap()
                        })
                        //然后执行气泡
                        AiControl.ins.bubbleMotion(this, () => {
                            if (++count === countAll) this.aiMotionCheckDieMap()
                        })
                    })
                })
            })
        })
    }

    /**
     * 果冻蔓延和气泡变色需要重新检查死图
     */
    aiMotionCheckDieMap() {
        //检查死图
        this.warningCop = Tool.dieMapCheck(this.lattices);
        if (!this.warningCop) {
            //检查一边是否已死
            if (Tool.alreadyDie(this.lattices)) {
                showToast("元素已无法消除")
                setTimeout(() => {
                    this.submit(false)
                }, 1000)
            } else {
                //死图。替换顺序，出toast
                showToast("没有可以消除的元素")
                setTimeout(() => {
                    this.upsetElement()
                }, 1000)
            }
        } else {
            //如果还有游戏引导
            if (this.gameGuide) {
                this.addChild(this.gameGuide)
                this.gameGuide.show()
            }
            //允许移动
            this.enableMouseEvt(true);
            //检查是否有红包炸弹弹框；有就弹出；然后置空
            this.openRedBombPanel();
        }
    }

    /**
     * 特效元素的组合效果
     */
    effectCombination() {
        const one = this.eliminatedElements[0];
        const two = this.eliminatedElements[1];
        let lat1 = this.lattices[one];
        let lat2 = this.lattices[two];
        let ele1: Element = this.lattices[one].element;
        let ele2: Element = this.lattices[two].element;
        //置空,没用了已经
        this.eliminatedElements = []
        //两个都是魔力鸟,
        if (ele1.effectType == EffectType.MAGICLION &&
            ele2.effectType == EffectType.MAGICLION) {
            //playSound(SoundType.magic)
            //动效
            this.playAni(RecoverName.MAGICLION_ANI, Tool.getPositionByIndex(one));
            this.playAni(RecoverName.MAGICLION_ANI, Tool.getPositionByIndex(two));
            //移除
            this.recoverEle(one);
            this.recoverEle(two);
            //先不管特效组合时的自身的分数
            //把所有元素加入
            for (let i = 0; i < this.lattices.length; i++) {
                const lat = this.lattices[i];
                if (Tool.judgeEliminate(lat)) {
                    //本来没必要判断特效组合时是否有重复的消除元素，但是recoverEle里会有判断石头或果冻的加入，造成重复
                    if (this.eliminatedElements.indexOf(i) == -1) this.eliminatedElements.push(i);
                    //两个魔力鸟分数MM
                    if (Tool.judgeHasScore(lat.element)) {
                        this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes.MM, Tool.getPositionByIndex(i))
                    }
                }
            }
        }
        //一个是魔力鸟，一个是其他特效或无，将所有相同元素变成同类型特效，除掉是魔力鸟的，
        else if ((ele1.effectType == EffectType.MAGICLION || ele2.effectType == EffectType.MAGICLION)) {
            //playSound(SoundType.magic)
            let magic: Element, other: Element;
            let magicIndex: number;
            if (ele1.effectType == EffectType.MAGICLION) {
                magic = ele1;
                other = ele2;
                magicIndex = one;
            } else {
                magic = ele2;
                other = ele1;
                magicIndex = two;
            }
            //动效
            this.playAni(RecoverName.MAGICLION_ANI, Tool.getPositionByIndex(magicIndex));
            //移除
            this.recoverEle(magicIndex);
            const rotateData: MagicRotateData = {
                p: Tool.getPositionByIndex(magicIndex),
                elementsIndex: [],
                type: other.type,
            };
            //手动触发只有一个
            this.magicRotateData = [rotateData];
            for (let i = 0; i < this.lattices.length; i++) {
                const lat = this.lattices[i];
                if (Tool.judgeMagicEliminate(lat) &&
                    lat.element.type == other.type  //类型相等
                ) {
                    //一个魔力鸟时其实不用加，因为石头或果冻的不会进入判断
                    if (this.eliminatedElements.indexOf(i) == -1) this.eliminatedElements.push(i);
                    //如果other带特效，符合的元素都加上特效，没动效，直接加吧先，
                    if (other.effectType != null) {
                        if (other.effectType == EffectType.EXPLOSIVE) {
                            lat.element.effectType = EffectType.EXPLOSIVE
                        } else {
                            lat.element.effectType = Math.random() > 0.5 ? EffectType.HORIZONTAL : EffectType.VERTICAL;
                        }
                    } else {
                        //普通元素的旋转消除，无特效，无锁，无毛球
                        if (Tool.judgeMagicRotate(lat.element)) {
                            rotateData.elementsIndex.push(i);
                        }
                    }
                    if (Tool.judgeHasScore(lat.element)) {
                        //魔力鸟和
                        this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes["3"], Tool.getPositionByIndex(i))
                    }
                }
            }
        }
        //两个都是方向的
        else if (ele1.effectType < 2 && ele2.effectType < 2) {
            //playSound(SoundType.line)
            //以第二个元素为中心
            const p = Tool.getPositionByIndex(one);
            //播放特效
            this.playAni(RecoverName.CROSS_ANI, p);
            //回收
            this.recoverEle(one)
            this.recoverEle(two)
            //添加消除元素two,同行或同列
            for (let i = 0; i < this.lattices.length; i++) {
                const lat = this.lattices[i];
                if (Tool.judgeEliminate(lat) && (lat.row == lat1.row || lat.column == lat1.column)) {
                    if (this.eliminatedElements.indexOf(i) == -1) this.eliminatedElements.push(i);
                    if (Tool.judgeHasScore(lat.element)) {
                        //线与线
                        this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes.LL, Tool.getPositionByIndex(i))
                    }
                }
            }
        }
        //一个方向，一个爆炸
        else if ((ele1.effectType < 2 || ele2.effectType < 2) &&
            (ele1.effectType == EffectType.EXPLOSIVE || ele2.effectType == EffectType.EXPLOSIVE)) {
            //playSound(SoundType.line)
            //以第二个元素为中心
            const p = Tool.getPositionByIndex(one);
            //播放特效
            this.playAni(RecoverName.THREECROSS_ANI, p);
            //回收
            this.recoverEle(one)
            this.recoverEle(two)
            //添加消除元素two,同行或同列,需要优化
            for (let i = 0; i < this.lattices.length; i++) {
                const lat = this.lattices[i];
                if (Tool.judgeEliminate(lat) &&
                    (Math.abs(lat.row - lat1.row) < 2 || Math.abs(lat.column - lat1.column) < 2)) {
                    if (this.eliminatedElements.indexOf(i) == -1) this.eliminatedElements.push(i);
                    if (Tool.judgeHasScore(lat.element)) {
                        //线和炸弹
                        this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes.EL, Tool.getPositionByIndex(i))
                    }
                }
            }
        }
        //如果两个爆炸
        else if (ele1.effectType == EffectType.EXPLOSIVE && ele2.effectType == EffectType.EXPLOSIVE) {
            //playSound(SoundType.boom)
            //以第二个元素为中心
            const p = Tool.getPositionByIndex(one);
            //播放特效
            const boomAni = this.playAni(RecoverName.BOOM_ANI, p);
            boomAni.scaleX = boomAni.scaleY = 2;
            //回收
            this.recoverEle(one)
            this.recoverEle(two)

            //添加消除元素two,5*5，需要优化
            for (let i = 0; i < this.lattices.length; i++) {
                const lat = this.lattices[i];
                if (Tool.judgeEliminate(lat) &&
                    (Math.abs(lat.row - lat1.row) < 3 && Math.abs(lat.column - lat1.column) < 3)) {
                    if (this.eliminatedElements.indexOf(i) == -1) this.eliminatedElements.push(i);
                    if (Tool.judgeHasScore(lat.element)) {
                        //两个炸弹
                        this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes.EE, Tool.getPositionByIndex(i))
                    }
                }
            }
        }
    }

    /**
     * 螺旋线消除
     */
    rotateEliminate() {
        for (let m = 0; m < this.magicRotateData.length; m++) {
            const indexs = this.magicRotateData[m].elementsIndex;
            const position = this.magicRotateData[m].p;
            for (let n = 0; n < indexs.length; n++) {
                let roIndex = indexs[n];
                let roEle = this.lattices[roIndex].element
                //如果即将产生特效的，跳下一个
                if (this.lattices[roIndex].element.temEffectType != null) continue
                //旋转动效
                MagicRotateAni(roEle, position, this)
                this.recoverEle(roIndex)
                //从消除列表去掉
                Tool.removeEle(roIndex, this.eliminatedElements)
            }
        }
        this.magicRotateData.length = 0;
    }

    /**
     * 普通元素消除,先消除无特效的，有特效的存下，然后触发，找下一步消除的元素
     * 加入石头，果冻，鸡蛋，各种状态
     */
    commonEliminate(): number[] {
        //playSound(SoundType.eliminate)
        //记录带特效的
        const effectIndexs: number[] = [];
        for (let i = this.eliminatedElements.length - 1; i >= 0; i--) {
            const index = this.eliminatedElements[i];
            const lat = this.lattices[index];
            //不用判断是否存在元素了吧
            const ele = lat.element;
            const p = Tool.getPositionByIndex(index);
            if (!ele) {
                //以防万一还是判断一次
            }
                //下面带状态的要优先判断
            //有枷锁的
            else if (ele.hasState(StateType.LOCK)) {
                //被禁锢的解开
                ele.setState(StateType.LOCK, false);
                //暂时笼子不算个数，算的话，改图片名称，列入ElementType的状态下
            }
            //有褐色毛球的，记录分裂激活
            else if (ele.hasState(StateType.HAIRBALLBROWN)) {
                const hairballBrownState: HairballBrownState = ele.getState(StateType.HAIRBALLBROWN);
                hairballBrownState.isActive = true;
                //算个数，不能在这里算，得在分裂时算，分裂时算也有问题，目标完成就后置了，会出bug，最好就是不设为目标。
                // this.goElementTarget(ElementType.HAIRBALLBROWN, [ele.x, ele.y]);
            }
            //有灰色毛球的，消失
            else if (ele.hasState(StateType.HAIRBALLGREY)) {
                //播放毛球消失动画
                this.playAni(RecoverName.HAIRBALLGREYDIS_ANI, p)
                //设为无毛球
                ele.setState(StateType.HAIRBALLGREY, false)
                //算个数
                this.goElementTarget(ElementType.HAIRBALLGREY, [ele.x, ele.y]);
            }
            //有黑色毛球的，一次眩晕，两次消失
            else if (ele.hasState(StateType.HAIRBALLGREY)) {
                const blackballBrownState: HairballBlackState = ele.getState(StateType.HAIRBALLBLACK);
                //醒着的就晕
                if (blackballBrownState.isAwake) {
                    blackballBrownState.isAwake = false;
                }
                //已经是晕的就消除，这个再考虑
                else {
                    //播放毛球消失动画
                    this.playAni(RecoverName.HAIRBALLBLACKDIS_ANI, p);
                    //设为无毛球
                    ele.setState(StateType.HAIRBALLBLACK, false)
                    //算个数
                    this.goElementTarget(ElementType.HAIRBALLBLACK, [ele.x, ele.y]);
                }
            }
            //如果有特效，存下
            else if (ele.effectType != null) {
                effectIndexs.push(index);
            } else {
                //即将生成特效的，变成特效，不消除，
                if (ele.temEffectType != null) {
                    ele.effectType = ele.temEffectType;
                    ele.temEffectType = null;
                    //有气泡的需要去掉状态，
                    if (ele.hasState(StateType.BUBBLE)) {
                        ele.setState(StateType.BUBBLE, false)
                    }
                    //但是算个数，
                    this.goElementTarget(ele.type, [ele.x, ele.y]);
                    //附近依旧有影响
                    this.checkNebAll(this.lattices[index])
                } else {
                    //单纯元素消失特效
                    if (FiveBaseElementTypes.indexOf(ele.type) >= 0) {
                        // this.playAni(RecoverName.ELEDIS_ANI, p)
                        this.addChild(EleDisAni(ele.type)).position.set(ele.x, ele.y)
                        //这里面会去算个数
                        this.recoverEle(index)
                    }
                    //石头
                    else if (ele.type == ElementType.ROCK) {
                        this.removeRock(index);
                        //算个数
                        this.goElementTarget(ele.type, [ele.x, ele.y]);
                    }
                    //果冻
                    else if (ele.type == ElementType.JELLY) {
                        this.removeJelly(index);
                        //算个数
                        this.goElementTarget(ele.type, [ele.x, ele.y]);
                    }
                    //鸡蛋
                    else if (ele.type == ElementType.CHICKEN_EGG) {
                        //额外逻辑,
                        ele.chickenEgg.statusNum--;
                        //鸡蛋的摆动
                        ele.chickenEgg.shakeAni();
                        //鸡蛋动效
                        this.playAni(RecoverName.EGGBROKEN_ANI, p)
                    }
                    //大红包
                    else if (ele.type == ElementType.FESTIVALELE_BIG) {
                        //动画也在放这里
                        ele.festivalEle.statusNum--;
                    }
                    //小红包
                    else if (ele.type == ElementType.FESTIVALELE_SMALL) {
                        //统一逻辑，个数等，都进里面
                        this.removeFestivalEleSmall(index);
                    }
                }
            }
            //从消除列表移除
            this.eliminatedElements.splice(i, 1)
        }
        return effectIndexs
    }

    /**
     * 特效元素消除时，波及，添加新的消除元素
     * @param effectIndexs
     */
    effectEliminate(effectIndexs: number[]) {
        if (effectIndexs.length && !this.hasPassed) this.effectContinuityTimes++;
        //判断是否在原先消除队列里或effectIndexs里
        let judgeExclude = (i: number) => {
            return this.eliminatedElements.indexOf(i) < 0 && effectIndexs.indexOf(i) < 0
        }
        // var sounds: SoundType[] = [];
        for (let j = 0; j < effectIndexs.length; j++) {
            const index = effectIndexs[j];
            const lat: Lattice = this.lattices[index];
            let ele = lat.element;
            const p = Tool.getPositionByIndex(index);
            switch (ele.effectType) {
                case EffectType.MAGICLION:
                    // if (sounds.indexOf(SoundType.magic) == -1) sounds.push(SoundType.magic)
                    this.playAni(RecoverName.MAGICLION_ANI, p);
                    this.recoverEle(index);
                    //为了不重复
                    const types = this.chapterData.baseElementTypes.slice();
                    for (let iii = 0; iii < this.magicRotateData.length; iii++) {
                        Tool.removeEle(this.magicRotateData[iii].type, types)
                    }
                    //随一种基础元素
                    const type = types.length ? Tool.randomT(types) : Tool.randomT(this.chapterData.baseElementTypes);
                    //建一个旋转数据
                    let rotateData: MagicRotateData = {
                        p: Tool.getPositionByIndex(index),
                        elementsIndex: [],
                        type: type,
                    }
                    this.magicRotateData.push(rotateData);
                    //对于魔力鸟的效果有不一样的，旋转，该组元素要旋转消除
                    for (let i = 0; i < this.lattices.length; i++) {
                        if (Tool.judgeMagicEliminate(this.lattices[i]) &&
                            this.lattices[i].element.type == type &&  //类型相等
                            judgeExclude(i)
                        ) {
                            this.eliminatedElements.push(i);
                            //无特效未枷锁,无毛球的参与旋转
                            if (Tool.judgeMagicRotate(this.lattices[i].element)) {
                                rotateData.elementsIndex.push(i);
                            }
                            //加分
                            if (Tool.judgeHasScore(this.lattices[i].element)) {
                                this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes["3"], Tool.getPositionByIndex(i))
                            }
                        }
                    }
                    break;
                case EffectType.EXPLOSIVE:
                    // if (sounds.indexOf(SoundType.boom) == -1) sounds.push(SoundType.boom)
                    //动画移除自己
                    const boomAni = this.playAni(RecoverName.BOOM_ANI, p);
                    boomAni.scaleX = boomAni.scaleY = 1;
                    this.recoverEle(index);
                    //3*3，需要优化
                    // for (var i = 0; i < this.lattices.length; i++) {
                    //     if (Tool.judgeEliminate(this.lattices[i]) &&
                    //         (Math.abs(this.lattices[i].row - lat.row) < 2 && Math.abs(this.lattices[i].column - lat.column) < 2)) {
                    //         if (this.eliminatedElements.indexOf(i) < 0 && effectIndexs.indexOf(i) < 0) {
                    //             this.eliminatedElements.push(i);
                    //             //加分
                    //             if (Tool.judgeHasScore(this.lattices[i].element)) {
                    //                 this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes["2"], Tool.getPositionByIndex(i))
                    //             }
                    //         }
                    //     }
                    // }
                    Tool.getExplosiveIndexes(lat, this.lattices).forEach((i) => {
                        if (judgeExclude(i)) {
                            this.eliminatedElements.push(i);
                            //加分
                            if (Tool.judgeHasScore(this.lattices[i].element)) {
                                this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes["2"], Tool.getPositionByIndex(i))
                            }
                        }
                    })
                    break;
                case EffectType.HORIZONTAL:
                    // if (sounds.indexOf(SoundType.line) == -1) sounds.push(SoundType.line)
                    const row = lat.row;
                    //动画移除自己
                    this.playAni(RecoverName.HORIZONTAL_ANI, p);
                    this.recoverEle(index);
                    for (let i = 0; i < Tool.colNum; i++) {
                        let ein = Tool.rcToIndex(row, i);
                        if (Tool.judgeEliminate(this.lattices[ein]) &&
                            judgeExclude(ein)
                        ) {
                            this.eliminatedElements.push(ein);
                            //加分
                            if (Tool.judgeHasScore(this.lattices[ein].element)) {
                                this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes["0"], Tool.getPositionByIndex(ein))
                            }
                        }
                    }
                    break;
                case EffectType.VERTICAL:
                    // if (sounds.indexOf(SoundType.line) == -1) sounds.push(SoundType.line)
                    const col = lat.column;
                    //动画移除自己
                    this.playAni(RecoverName.VERTICAL_ANI, p);
                    this.recoverEle(index);
                    for (let i = 0; i < Tool.rowNum; i++) {
                        let ein = Tool.rcToIndex(i, col);
                        if (Tool.judgeEliminate(this.lattices[ein]) &&
                            judgeExclude(ein)
                        ) {
                            this.eliminatedElements.push(ein);
                            //加分
                            if (Tool.judgeHasScore(this.lattices[ein].element)) {
                                this.pushScoreAni(baseScore * this.effectContinuityTimes * effectBaseTimes["1"], Tool.getPositionByIndex(ein))
                            }
                        }
                    }
                    break;
            }
        }
        //播放音乐
        // for (var a = 0; a < sounds.length; a++) {
        //playSound(sounds[a]);
        // }
    }

    /**
     * 判断是否通关
     */
    judgePass() {
        if (this.passElements) {
            for (var i = 0; i < this.passElements.length; i++) {
                //只要有还没为0就失败
                if (this.passElements[i]) {
                    return false
                }
            }
            return true
        } else {
            //判断分数
            return this.score > this.chapterData.passTarget.score;
        }
    }

    /**
     * 消耗完所有步数，
     */
    terminateSteps() {
        //遍历有特效的索引,和，基础元素
        const effectElements = [], elements = [];
        for (let i = Tool.colNum * Tool.rowNum - 1; i >= 0; i--) {
            const lat = this.lattices[i];
            if (Tool.judgeBaseEle(lat)) {
                if (lat.element.effectType != null) {
                    effectElements.push(i);
                } else {
                    elements.push(lat.element)
                }
            }
        }
        //如果特效有，消除特效
        if (effectElements.length) {
            Array.prototype.push.apply(this.eliminatedElements, effectElements);
            this.eliminate();
        } else {
            //如果步数原先就为0或没有元素了；直接提交了
            if (this.steps == 0 || elements.length == 0) {
                this.score += 1000 * this.steps;
                this.steps = 0;
                // var json = this.getSubmitJson()
                this.submit()
                return
            }
            this.isCountingTime = true;
            let countAll = Math.min(this.steps, elements.length);
            let count = 0;
            let deltaCount = 0;
            //将步数变成横竖特效
            while (deltaCount < countAll) {
                const rand = Math.floor(Math.random() * elements.length);
                let ele: Element = elements.splice(rand, 1)[0];
                if (!ele) break;
                deltaCount++;
                let bonusShootAni: BonusShootAni = Pool.takeOut(RecoverName.BONUSSHOOT_ANI)
                if (!bonusShootAni) {
                    bonusShootAni = new BonusShootAni()
                }
                setTimeout(() => {
                    this.steps--;
                    //分数
                    this.score += 1000;
                    this.addChild(bonusShootAni);
                    bonusShootAni.play([ele.x, ele.y], () => {
                        ele.effectType = Math.random() > 0.5 ? EffectType.HORIZONTAL : EffectType.VERTICAL;
                        //去掉所有状态
                        ele.removeAllState();
                        this.eliminatedElements.push(ele.index);
                        this.pushScoreAni(baseScore * 1.5, [ele.x, ele.y])
                        if (++count == countAll) {
                            this.eliminate();
                            //如果还有步数就置0
                            if (this.steps) {
                                this.score += 1000 * this.steps;
                                this.steps = 0;
                            }
                        }
                    })
                }, 150 * deltaCount)
            }
        }
    }

    /**
     * 创建分数得动画
     * @param score
     * @param p
     */
    pushScoreAni(score: number, p: number[]) {//暂时不用
        let scoreAni = Pool.takeOut(RecoverName.SCORE_ANI);
        if (!scoreAni) {
            scoreAni = new ScoreAni();
        }
        scoreAni.score = score;
        scoreAni.x = p[0];
        scoreAni.y = p[1];
        this.scoreAnis.push(scoreAni)
    }

    /**
     * 一次分数的toast
     */
    oneStepScoreToast() {
        let sv: string;
        if (this.oneStepScore > 5000) {
            //playSound(SoundType.toast);
            sv = "wonderful";
        } else if (this.oneStepScore > 3000) {
            //playSound(SoundType.toast);
            sv = "amazing";
        } else if (this.oneStepScore > 1000) {
            //playSound(SoundType.toast);
            sv = "great";
        }
        if (sv && movieClips[sv]) {
            this.addChild(movieClips[sv])
            movieClips[sv].play(1, () => {
                this.removeChild(movieClips[sv])
            })
        }
        this.oneStepScore = 0;
    }

    /**
     * 打乱元素
     * 不移动冰淇淋，石头，和锁住的元素
     */
    upsetElement() {
        //所有允许交换的格子
        const lats: Lattice[] = [];
        //记录元素和目标位置的索引，然后执行所有动画
        const anis = [];//两个，0下标是元素，1下标是索引
        for (let i = 0; i < this.lattices.length; i++) {
            let lat = this.lattices[i];
            if (Tool.judgeUpsetMove(lat)) lats.push(lat);
        }
        //不改变的
        const latsCopy = lats.slice();
        for (let i = 0; i < lats.length; i++) {
            let ele = lats[i].element;
            const rand = Math.floor(Math.random() * latsCopy.length);
            let lat = latsCopy.splice(rand, 1)[0];
            anis.push([ele, lat.index])
        }
        let count = 0;
        let countAll = anis.length;
        //执行所有的交换动画
        for (let i = 0; i < countAll; i++) {
            let ele: Element = anis[i][0];
            let index: number = anis[i][1];
            const p = Tool.getPositionByIndex(index);
            //现在得位置的赋值
            const lat = this.lattices[index];
            lat.element = ele;
            FYGE.Tween.get(ele)
                .to({x: p[0], y: p[1]}, 500)
                .call(() => {
                    if (++count == countAll) {
                        //交换结束后,判断是否有三消
                        if (this.threeMatch()) {
                            this.eliminate()
                        } else {
                            //检查死图
                            this.warningCop = Tool.dieMapCheck(this.lattices);
                            if (!this.warningCop) {
                                //死图。替换顺序
                                this.upsetElement()
                            } else {
                                //允许移动
                                this.enableMouseEvt(true);
                                //检查是否有红包炸弹弹框；有就弹出；然后置空
                                this.openRedBombPanel();
                            }
                        }
                    }
                })
        }
    }

    /**
     * 该方法基本用于消除的时候，有时异步的时候不用，只用于基本元素和特效和棒棒糖
     * 添加进emptys
     * 置空格子的element,
     * 从容器移除
     * 回收元素
     * 如果格子有冰等等
     * @param index
     */
    recoverEle(index: number) {
        let ele = this.removeOperation(index);
        //棒棒糖消除不产生任何其他效果
        if (ele.type != ElementType.LOLLIPOP) this.checkNebAll(this.lattices[index]);
        //计算元素消除个数
        this.goElementTarget(ele.type, [ele.x, ele.y]);
    }

    /**
     * 获得提交数据的jsob
     */
    getSubmitJson(): string {
        const json = {};
        //分数
        // json[1] = this.score;
        // if (this.passElements) {
        //     //i就是元素类型的索引
        //     for (var i = 0; i < this.hasEliminatedElements.length; i++) {
        //         if (this.hasEliminatedElements[i] != undefined) {
        //             json[submitTran[i]] = this.hasEliminatedElements[i]
        //         }
        //     }
        // }
        return JSON.stringify(json)
    }

    /**
     * 提交方法
     * @param suc 默认true表示成功提交
     */
    submit(suc: boolean = true) {

        if (this.openRedBombPanel(() => {
            this.submit(suc);
        })) return;

        const params = {
            level: this.data.chapter,
            score: this.score,
            stars: this.scoreProgress.starCount,
            // stars: 0,
            isVisitSuccess: suc,
        };
        showWaiting();
        sendTbNet(TbNetName.gameOver, params, (s, res) => {
            hideWaiting();
            if (s) {
                if (suc) {
                    let prizeLevel = 50;
                    if (this.data.chapter > 50 && this.data.chapter <= 100) {
                        prizeLevel = 100;
                    } else if (this.data.chapter > 100) {
                        prizeLevel = 120;
                    }
                    sendTbNet(TbNetName.drawLotteryPrize, {
                            prizeDataType: prizeLevel,
                            level: this.data.chapter,
                            stars: this.scoreProgress.starCount,
                            // stars: 0,
                        },
                        (ds, dr) => {
                            if (!ds || !dr.data || dr.data.type == 5) {
                                //@ts-ignore
                                Tools.gameOver(Object.assign(res.data || {}, params));
                                return;
                            }
                            const data: any = {};
                            if (dr.data.type == 6) {
                                data.tool = {
                                    type: dr.data.toolType
                                };
                            } else {
                                data.prize = {
                                    image: dr.data.image
                                };
                            }
                            //@ts-ignore
                            Tools.gameOver(Object.assign(data, params));
                        }
                    );
                } else {
                    //@ts-ignore
                    Tools.gameOver(Object.assign(res.data || {}, params));
                }
                //@ts-ignore
                // Tools.gameOver(Object.assign(res.data || {}, params))
            } else {
                //没有数据，网络超时，再次提交，到时小程序测试网络断开时返回数据
                if (res.data) {
                    showToast("网络开小差了\n再次连接中")
                    wait(() => {
                        this.submit(suc);
                    }, 1300)
                } else {
                    showToast(res.message || "活动太火爆了\n请稍后再来");
                    wait(() => {
                        changeScene(MapScene)
                    }, 1300)
                }
            }
        }, true)
    }

    /**
     * 打开红包炸弹弹框
     */
    openRedBombPanel(callback: Function = null) {
        if (this.redBombPanelMark) {

            sendTbNet(TbNetName.drawLotteryPrize, {
                    level: this.chapter,
                    prizeDataType: 1
                },
                (success, res) => {
                    if (success && res.data) {
                        showPanel(FesRedPrizePanel, {callback: callback});
                    } else {
                        callback();
                    }
                }
            );

            // showPanel(FesRedPrizePanel, {callback: callback});
            this.redBombPanelMark = false;
            return true;
        }
        return false;
    }

    /**
     * 为了能加入红包炸弹的弹框  如果需要添加直接弹出就是了，或者修改下弹出顺序，红包的后弹，到时残酷tuia的逻辑
     * @param panelName
     * @param data
     */
    returnPanels(panelName, data) {
        const panels = [], params = [];
        //如果需要弹出红包炸弹，要先出
        if (this.redBombPanelMark) {
            panels.push(redBombPanelName);
            params.push(null);
            this.redBombPanelMark = false;
        }
        panels.push(panelName);
        params.push(data);
        return [panels, params]
    }

    /**
     * 播放动画
     * @param name
     * @param p
     */
    playAni(name: RecoverName, p: number[]) {
        let ani = Pool.takeOut(name);
        if (!ani) {
            ani = new aniClass[name]();
        }
        ani.x = p[0];
        ani.y = p[1];
        ani.play();
        // if (name == RecoverName.MAGICLION_ANI) {
        //     具体索引还要改
        //     console.log()
        // this.addChildAt(ani, this.getChildIndex(this.map) + 1);
        // } else {
        this.addChild(ani);
        // }
        return ani
    }

    /**
     * 格子上的冰块破碎
     * @param lat
     */
    iceBroken(lat: Lattice) {
        if (lat && lat.ice) {
            const ice = lat.ice;
            ice.countNum--;
            if (ice.countNum == 0) {
                this.map.removeChild(ice);
                //回收
                Pool.recover(RecoverName.ICE, ice);
                lat.ice = null;
                this.goElementTarget(ElementType.ICE, [ice.x, ice.y]);
            } else {
                ice.alpha = 0.6;
            }
            const p = Tool.getPositionByIndex(lat.index);
            //动画
            this.playAni(RecoverName.ICE_ANI, p);
        }

    }

    goElementTarget(type: ElementType, fromP: number[]) {
        if (!this.passElements) return
        if (this.passElements[type] != undefined) {
            if (this.passElements[type] != 0) {
                this.passElements[type]--;
                // console.log(this.elementTargets.targets[type])
                //@ts-ignore
                const p = this.elementTargets.targets[type].localToGlobal({x: 40 * 0.8, y: 40 * 0.8});
                this.addChild(FlyTargetAni(type, fromP, [p.x, p.y - layers.stageOffsetY], () => {
                    this.elementTargets.targets[type].count--;
                }))
            }
            //数量增加
            this.hasEliminatedElements[type]++;
        }
    }

    //检查附近是否有相应元素
    checkNebEle(lat: Lattice, checkFun: (la: Lattice) => boolean) {
        const indexMy = lat.index;
        const indexs = [
            indexMy - Tool.colNum,
            indexMy + Tool.colNum,
        ];
        if (lat.column > 0) {
            indexs.push(indexMy - 1)
        }
        if (lat.column < Tool.colNum - 1) {
            indexs.push(indexMy + 1)
        }
        for (let i = 0; i < indexs.length; i++) {
            let index = indexs[i];
            let lat = this.lattices[index];
            if (lat && lat.element && !lat.doorClosed && checkFun(lat)) {
                //添加进消除队列，注意重复
                if (this.eliminatedElements.indexOf(index) == -1) this.eliminatedElements.push(index)
            }
        }
    }

    //检查附近所有元素
    checkNebAll(latttice: Lattice) {
        //如果格子有冰
        this.iceBroken(latttice);
        //如果附近有石头
        this.checkNebEle(latttice, (lat) => {
            return lat.element.type == ElementType.ROCK
        });
        //如果附近有果冻
        this.checkNebEle(latttice, (lat) => {
            return lat.element.type == ElementType.JELLY
        });
        //如果附近有鸡蛋
        this.checkNebEle(latttice, (lat) => {
            return lat.element.type == ElementType.CHICKEN_EGG
        });
        //如果附近有灰色毛球
        this.checkNebEle(latttice, (lat) => {
            return lat.element.hasState(StateType.HAIRBALLGREY)
        });
        //如果附近有黑色毛球
        this.checkNebEle(latttice, (lat) => {
            return lat.element.hasState(StateType.HAIRBALLBLACK)
        });
        //如果附近有节日大红包
        this.checkNebEle(latttice, (lat) => {
            return lat.element.type == ElementType.FESTIVALELE_BIG;
        });
        //如果附近有节日小红包
        this.checkNebEle(latttice, (lat) => {
            return lat.element.type == ElementType.FESTIVALELE_SMALL;
        });
    }

    /**
     * 石头的移除，包括动效
     * @param index
     */
    removeRock(index: number) {
        this.removeOperation(index);
        //播放动效
        this.playAni(RecoverName.ROCK_ANI, Tool.getPositionByIndex(index))
    }

    /**
     * 果冻的移除，包括动效
     * @param index
     */
    removeJelly(index: number) {
        this.jellyBrokenMark = true;
        this.removeOperation(index);
        //播放动效，果冻的特效，待写
        this.playAni(RecoverName.JELLYDIS_ANI, Tool.getPositionByIndex(index))
    }

    //移除小红包，包括动效，还有个数累计等等。发接口，等等
    removeFestivalEleSmall(index: number) {
        let ele = this.removeOperation(index);
        //查看次数,原先不为0，
        if (this.festivalTargetNum) {
            this.festivalTargetNum--;
            // this.festivalTarget.count--;

            //@ts-ignore
            // var p = this.festivalTarget.localToGlobal({x: 40 * 0.8, y: 40 * 0.8})
            // this.addChild(FlyTargetAni(ElementType.FESTIVALELE_SMALL, [ele.x, ele.y], [p.x, p.y], () => {
            //     this.festivalTarget.count--;
            // }))

            const texture: FYGE.Texture = RES.getRes("ele" + ElementType.FESTIVALELE_SMALL + ".png");
            //弄一个替代的
            let eleC: BitmapRecycle = Pool.takeOut(RecoverName.BITMAPRECYCLE);
            if (!eleC) {
                eleC = new BitmapRecycle(texture);
            } else {
                eleC.reset(texture);
            }
            eleC.position.set(ele.x, ele.y);

            this.addChild(eleC);
            /// TODO 小红包消失动画
            Tween.get(eleC)
                .to({scaleX: 0, scaleY: 0}, 270, Ease.backIn)
                .call(() => {
                    this.removeChild(eleC);
                    this.festivalTarget.count--;

                });

            //完成了，发接口，到时需要修改，对于接口没成功的不设置
            if (this.festivalTargetNum <= 0) {
                this.redBombPanelMark = true;

                // sendTbNet(TbNetName.drawLotteryPrize, {
                //         level: this.chapter,
                //         prizeDataType: 1
                //     },
                //     (success, res) => {
                //         if (success && res.data) {
                //             this.redBombPanelMark = true;
                //             this.openRedBombPanel();
                //         }
                //     }
                // );
            }

        }
    }

    /**
     * 移除元素的操作:
     * 进入emptys
     * 从场景移除
     * 回收元素
     * 格子元素置空
     * @param index
     */
    removeOperation(index: number): Element {
        //没有格子，或者没有元素，
        if (!this.lattices[index] || !this.lattices[index].element) return null
        //先赋值指向
        let ele = this.lattices[index].element;
        //添加进emptys
        this.emptys.push(index)
        //从容器移除
        this.elementContainer.removeChild(ele);
        //回收元素
        Pool.recover(RecoverName.ELEMENT, ele);
        //置空格子的element,
        this.lattices[index].element = null;
        //返回下，有地方要用
        return ele
    }

    //红包炸弹动效
    redBombLightAni(index: number, callback: Function) {

        const node = new Container();
        const down = new FYGE.Sprite(RES.getRes("fesRedDown.png"));
        down.anchorTexture.set(0.5, 0.5);
        down.scale.set(0.31, 0.31);
        node.addChild(down);

        const ani2Texture: Texture[] = [];
        for (let i = 0; i < 18; i++) {
            ani2Texture.push(RES.getRes(`fesRedBombSta4_${i}.png`));
        }

        const ani2 = node.addChild(new FrameAni(ani2Texture));
        const p = Tool.getPositionByIndex(index);

        this.addChild(node);

        node.x = p[0];
        node.y = p[1];

        ani2.play(1, () => {
            setTimeout(() => {
                callback(node);
            }, 80)
        });
    }
}

/**
 * 后续Element变复杂了，就用这个
 * 现在暂时只有两级
 */
// function findElement(currentTarget: FYGE.DisplayObject): Element {
//     while (!(currentTarget instanceof Element)) {
//         currentTarget = currentTarget.parent;
//         if (!currentTarget) return null;
//     }
//     return currentTarget
// }
let isOn: boolean = true;

class MusicBtn extends FYGE.Sprite {
    constructor() {
        const t = RES.getRes("musicOn.png");
        super(t)
        this.addEventListener(FYGE.MouseEvent.CLICK, () => {
            isOn = !isOn;
            this.texture = RES.getRes(isOn ? "musicOn.png" : "musicOff.png");
            //播放或暂停音乐接口
            sendTbNet(TbNetName.openMusic, {isOn: isOn})
        }, this);
        //默认开启
        // this.isOn = true;
        if (isOn) sendTbNet(TbNetName.openMusic, {isOn: true})
        //添加onSHow事件
        GDispatcher.addEventListener(G_EVENT.ON_SHOW, this.onShow, this)
        GDispatcher.addEventListener(G_EVENT.ON_HIDE, this.onHide, this)
    }

    onShow() {
        if (isOn) sendTbNet(TbNetName.openMusic, {isOn: true})
    }

    onHide() {
        if (isOn) sendTbNet(TbNetName.openMusic, {isOn: false})
    }

    destroy() {
        GDispatcher.removeEventListener(G_EVENT.ON_SHOW, this.onShow, this)
        GDispatcher.removeEventListener(G_EVENT.ON_HIDE, this.onHide, this)
        sendTbNet(TbNetName.openMusic, {isOn: false})
        super.destroy()

    }
}
