/**
 * 代码描述
 */

import { Audio } from "../../common/src/Audio";
import { GameDataCenter } from "../../common/src/GameDataCenter";
import { LevelConfig } from "../../common/src/LevelConfig";
import { UIManager } from "../../common/src/UIManager";
import { Utils } from "../../common/src/Utils";

const { ccclass, property } = cc._decorator;

@ccclass
export class BobbleGamePanel extends cc.Component {

    @property({ type: cc.Node, tooltip: '反弹界面节点' })
    walls: cc.Node = null;

    @property({ type: cc.Graphics, tooltip: '射线节点' })
    ray: cc.Graphics = null;

    @property({ type: cc.Node, tooltip: '地图节点' })
    map: cc.Node = null;

    @property({ type: cc.Node, tooltip: '角色节点' })
    player: cc.Node = null;

    @property({ type: cc.Node, tooltip: 'ui节点' })
    ui: cc.Node = null;

    @property({ type: cc.Prefab, tooltip: '泡泡预制体' })
    bubble: cc.Prefab = null;

    @property({ type: cc.Prefab, tooltip: '射线预制体' })
    rayPrefab: cc.Prefab = null;

    @property({ type: cc.Prefab, tooltip: '激励预制体' })
    appraisePrefab: cc.Prefab = null;

    @property({ type: cc.Prefab, tooltip: '发射小球预制体' })
    shootBubblePre: cc.Prefab = null;

    @property({ type: cc.Prefab, tooltip: '激光预制体' })
    laser_gj: cc.Prefab = null;

    @property({ type: cc.Prefab, tooltip: '闪电预制体' })
    thunder_gj: cc.Prefab = null;

    @property({ type: cc.Prefab, tooltip: '总分预制体' })
    scorePrefab: cc.Prefab = null;

    @property({ type: cc.Prefab, tooltip: '箭头预制体' })
    arrows: cc.Prefab = null;

    @property({ type: [cc.SpriteFrame], tooltip: '普通泡泡纹理集合\n0.紫色\n1.绿色\n2.蓝色\n3.黄色\n4.红色' })
    bubbleSpriteFrame: cc.SpriteFrame[] = [];

    @property({ type: [cc.SpriteFrame], tooltip: '特殊泡泡纹理集合\n0.炸弹\n1.激光\n2.七色\n3.闪电' })
    specialBubbleSpriteFrame: cc.SpriteFrame[] = [];

    @property({ type: [cc.SpriteFrame], tooltip: '图标集合\n0.广告\n1.无限\n2.金币\n3.暗星\n4.亮星' })
    iconSpriteFrame: cc.SpriteFrame[] = [];

    @property({ type: [cc.Prefab], tooltip: '特殊球待机动画预制体\n0.炸弹\n1.激光\n2.七色\n3.闪电' })
    specialBubbleEffect: cc.Prefab[] = [];


    //线段颜色
    private rayColor: cc.Color[] = [cc.color('#a600e6' as any), cc.color('#0ca600' as any), cc.color('#10c8e3' as any), cc.color('#f77916' as any), cc.color('#cd1904' as any)];
    /**特殊球颜色 */
    private specialColor: cc.Color[][] = [[cc.color('#fdce25' as any), cc.color('#f48118' as any), cc.color('#f48118' as any), cc.color('#fdce25' as any)], [cc.color('#f0e288' as any), cc.color('#d999d1' as any), cc.color('#92e7fc' as any)], [cc.color('#48adf7' as any), cc.color('#fdde52' as any), cc.color('#f77923' as any), cc.color('#d7008e' as any)], [cc.color('#073dd3' as any), cc.color('#ffffff' as any), cc.color('#073dd3' as any)]];
    /**总分评价 */
    private appraise: string[] = ['bksy', 'tbl', 'bc'];
    //发射坐标
    private startPos: cc.Vec2 = cc.v2();
    //射线节点坐标
    private rayPos: cc.Vec2[] = [];
    private pathPos: cc.Vec2[] = [];
    //射线节点半径
    private r: number = 0;
    //放大缩小方向
    private op: number = 1;
    //是否隐藏
    private isHide: boolean = true;
    //泡泡半径
    private bubbleR: number = 30;
    //地图高度
    private screenH: number = 0;
    //地图宽度
    private screenW: number = 0;
    /** 当前发射小球 */
    public shootBubble: cc.Node;
    /** 预发射小球 */
    public preBubble: cc.Node;
    /** 特殊小球 */
    public specialBubble: cc.Node;
    /**校正之后泡泡的位置 */
    private bubbleIndex: cc.Vec2 = null;
    /**小球数量 */
    private bubbleCount: number = 0;
    /**连击数 */
    private combo: number = 0;
    /**是否在准备泡泡 */
    private isReady: boolean = false;
    /**场上小球的颜色数量 */
    private colorCount: number[] = [0, 0, 0, 0, 0, 0];
    /**特殊球类型 */
    private specialBubbleType: number = 0;
    /**特殊球消除位置 */
    private specialBubbleIndex: cc.Vec2[] = [];
    /**激光终点向量 */
    private laserVec: cc.Vec2 = null;
    /**闪电爆炸位置 */
    private thunderIndex: cc.Vec2 = null;
    /**游戏是否结束 */
    private isWin: boolean = false;
    /**得分 */
    private score: number = 0;
    /**三星分数 */
    private perfectScore: number = 0;
    /**星星数 */
    private star: number = 0;
    /**首次出现冰球的关卡 */
    private iceTips: number = 31;
    /**在线时间 */
    private onlineTime: number = 0;
    /*要显示的排数*/
    private showCount: number = 10;
    private callBack: Function = null;
    private isPress: boolean = false;


    onLoad() {
        Audio.playMusicByPath('game:res/snd/bgm');
        cc.director.getPhysicsManager().enabled = true;
    }

    onEnable() {
        this.scheduleOnce(() => {
            this.init();
        }, 0.5);

        let specialBubble = this.ui.getChildByName('specialBubble');
        for (let i = 0; i < specialBubble.childrenCount; i++) {
            specialBubble.children[i].on('click', this.onSpecialBubble, this);
        }
        this.player.getChildByName('icon').on('click', this.onSwitchBubble, this)
        this.ui.getChildByName('cancel').on('click', this.onCancel, this);
        this.ui.getChildByName('topPanel').getChildByName('pauseButton').on('click', this.onPause, this);
        this.ui.getChildByName('topPanel').getChildByName('coin').getChildByName('addButton').on('click', this.onCoinStore, this);
        this.ui.getChildByName('online').on('click', this.onOnline, this);
        this.ui.getChildByName('ten').on('click', () => {
            cc.systemEvent.emit('updateBubble', 10);
        }, this);
        cc.systemEvent.on('restart', this.onRestart, this);
        cc.systemEvent.on('updateBubble', this.onUpdateBubble, this);
        cc.systemEvent.on('updateUI', this.onUpdateUI, this);
        cc.systemEvent.on('startTimer', this.onStartTimer, this);
        cc.systemEvent.on('showTenButton', (isShow: boolean) => { this.ui.getChildByName('ten').active = isShow }, this);

        this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMoved, this);
        this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnded, this);
        this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchCancelled, this);
    }

    onDisable() {
        this.node.off(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.node.off(cc.Node.EventType.TOUCH_MOVE, this.onTouchMoved, this);
        this.node.off(cc.Node.EventType.TOUCH_END, this.onTouchEnded, this);
        this.node.off(cc.Node.EventType.TOUCH_CANCEL, this.onTouchCancelled, this);
    }

    /**点击特殊球 */
    private onSpecialBubble(button): void {
        if (GameDataCenter.gameLevel == 12) {
            this.ui.getChildByName('colorTip').active = false;
            GameDataCenter.tipType = 2;
            UIManager.open('tip:TipPanel');
        }
        if (!this.isReady || this.isWin) return;
        this.isPress = true;
        Audio.playSoundByPath('game:res/snd/click');
        if (button.node.getChildByName('null').active == true && GameDataCenter.coin < 100) {
            switch (button.node.name) {
                case 'bombBubble': this.specialBubbleCallback(1); break;
                case 'laserBubble': this.specialBubbleCallback(2); break;
                case 'omnipotentBubble': this.specialBubbleCallback(3); break;
                case 'lightningBubble': this.specialBubbleCallback(4); break;
            }
            return;
        }
        switch (button.node.name) {
            case 'bombBubble': this.specialBubbleType = 1; break;
            case 'laserBubble': this.specialBubbleType = 2; break;
            case 'omnipotentBubble': this.specialBubbleType = 3; break;
            case 'lightningBubble': this.specialBubbleType = 4; break;
        }
        this.player.getChildByName('icon').active = false;
        let specialBubble = this.ui.getChildByName('specialBubble');
        for (let i = 0; i < specialBubble.childrenCount; i++) {
            specialBubble.children[i].getComponent(cc.Button).interactable = false;
        }
        this.switchSpecialBubble(1);
    }

    /**点击取消 */
    private onCancel(): void {
        Audio.playSoundByPath('game:res/snd/click');
        this.specialBubbleType = 0;
        this.switchSpecialBubble(-1);
        this.isPress = false;
    }

    /**暂停界面 */
    private onPause(): void {
        Audio.playSoundByPath('game:res/snd/click');
        UIManager.open('pause:PausePanel');
    }

    /**金币商店 */
    private onCoinStore(): void {
        Audio.playSoundByPath('game:res/snd/click');
        UIManager.open('coinStore:CoinStorePanel');
    }

    /**点击切换小球 */
    private onSwitchBubble(button): void {
        if (!this.isReady || this.isWin) return;
        Audio.playSoundByPath('game:res/snd/click');
        this.isReady = false;
        button.node.getComponent(cc.Button).interactable = false;
        if (button.node.children[0].name == '0') {
            this.callBack = () => {
                button.node.children[0].getComponent(cc.Sprite).spriteFrame = this.iconSpriteFrame[1];
                button.node.children[0].name = '1';
                let pos = this.player.convertToNodeSpaceAR(this.startPos);
                cc.tween(this.shootBubble).to(0.2, { position: cc.v3(pos.x + 60, pos.y - 60), scale: 0.75 }).start();
                cc.tween(this.preBubble).to(0.2, { position: cc.v3(pos.x, pos.y), scale: 1 }).call(() => {
                    let node: cc.Node = null;
                    node = this.shootBubble;
                    this.shootBubble = this.preBubble;
                    this.preBubble = node;
                    this.scheduleOnce(() => {
                        this.isReady = true;
                    }, 0);
                    this.ray.fillColor = this.rayColor[this.shootBubble.getComponent('Bubble').color - 1];
                    button.node.getComponent(cc.Button).interactable = true;
                }).start();
            }
            this.scheduleOnce(() => {
                button.node.getComponent(cc.Button).interactable = true;
                this.isReady = true;
                this.callBack();
            }, 1);
            return;
        }
        let pos = this.player.convertToNodeSpaceAR(this.startPos);
        cc.tween(this.shootBubble).to(0.2, { position: cc.v3(pos.x + 60, pos.y - 60), scale: 0.75 }).start();
        cc.tween(this.preBubble).to(0.2, { position: cc.v3(pos.x, pos.y), scale: 1 }).call(() => {
            let node: cc.Node = null;
            node = this.shootBubble;
            this.shootBubble = this.preBubble;
            this.preBubble = node;
            this.scheduleOnce(() => {
                this.isReady = true;
            }, 0);
            this.ray.fillColor = this.rayColor[this.shootBubble.getComponent('Bubble').color - 1];
            button.node.getComponent(cc.Button).interactable = true;
        }).start();
    }

    /**在线奖励面板 */
    private onOnline(): void {
        Audio.playSoundByPath('game:res/snd/click');
        if (this.onlineTime > 0) {
            this.creatLabel(`再多玩一会吧，在线时间还不够哦~`);
            return;
        }
        else {
            GameDataCenter.rewarded = [`${Math.floor(Math.random() * 4)}:1`, '4:80'];
            UIManager.open('rewarded:RewardedPanel');
            cc.systemEvent.emit('startTimer');
        }
    }

    /**再来一次 */
    private onRestart(): void {
        this.isReady = false;
        this.initMap(true);
    }

    /**更新泡泡数量 */
    private onUpdateBubble(num: number): void {
        this.isReady = false;
        let count: number = this.bubbleCount;
        this.bubbleCount += num;
        for (let i = 0; i < num; i++) {
            this.scheduleOnce(() => {
                count++;
                this.player.getChildByName('bubbleCount').getComponent(cc.Label).string = `${count}`
            }, i * 0.1);
        }
        this.scheduleOnce(() => {
            this.isReady = true;
        }, 0.5);
    }

    /**更新UI数量 */
    private onUpdateUI(type: string, num: number) {
        this.changeUi(type, num);
    }

    /**开始在线奖励倒计时 */
    private onStartTimer(): void {
        let node = this.ui.getChildByName('online').getChildByName('gift');
        cc.Tween.stopAllByTarget(node);
        node.scale = 1;
        node.angle = 0;
        this.onlineTime = 120;
        this.ui.getChildByName('online').getChildByName('time').getComponent(cc.Label).string = '02;00';
        this.ui.getChildByName('online').getChildByName('time').active = true;
        this.ui.getChildByName('online').getChildByName('name').getChildByName('label').getComponent(cc.Label).string = `在线奖励`;
        this.ui.getChildByName('online').getChildByName('name').getChildByName('label').x -= 14;
        this.ui.getChildByName('online').getChildByName('name').getChildByName('videoIcon').active = false;
        // this.ui.getChildByName('online').getComponent(cc.Button).interactable = false;
        this.schedule(this.online, 1);
    }

    private specialBubbleCallback(type) {
        this.specialBubbleType = type;
        this.player.getChildByName('icon').active = false;
        let specialBubble = this.ui.getChildByName('specialBubble');
        for (let i = 0; i < specialBubble.childrenCount; i++) {
            specialBubble.children[i].getComponent(cc.Button).interactable = false;
        }
        this.switchSpecialBubble(1);
        this.changeUi(`special${this.specialBubbleType}`, 1);
    }

    /**
     * 场景动画更新前回调
     * @param dt 游戏帧时长
     */
    update(dt: number) {
        if (!this.isHide) {
            this.updateRay(dt);
        }
    }


    /** touch事件回调 */
    onTouchStart(event: cc.Event.EventTouch) {
        this.isPress = true;
        if (GameDataCenter.gameLevel == 1) {
            if (this.ui.getChildByName('tip').getChildByName('tip1').activeInHierarchy == true) {
                cc.Tween.stopAllByTarget(this.ui.getChildByName('tip').getChildByName('tip1').getChildByName('finger'));
                this.ui.getChildByName('tip').getChildByName('tip1').active = false;
            }
        }
        if (GameDataCenter.gameLevel == 2) {
            if (this.ui.getChildByName('tip').getChildByName('tip3').activeInHierarchy == true) {
                this.ui.getChildByName('tip').getChildByName('tip3').active = false;
                this.node.getChildByName('effect').removeAllChildren();
            }
        }
        // if (GameDataCenter.gameLevel == 11) {
        //     if (this.ui.getChildByName('tip').getChildByName('tip4').activeInHierarchy == true) {
        //         this.ui.getChildByName('tip').getChildByName('tip4').active = false;
        //     }
        // }
        if (!this.isReadyRay(event)) { return; }
        this.topTween(0);
        this.rayPos = [];
        this.pathPos = [this.ray.node.convertToNodeSpaceAR(this.startPos)];
        const pos = event.getLocation();
        this.drawRayCast(this.startPos, pos.subSelf(this.startPos).normalizeSelf(), this.ray);
    }
    onTouchMoved(event: cc.Event.EventTouch) {
        if (!this.isReadyRay(event)) { return; }
        this.topTween(0);
        this.rayPos = [];
        this.pathPos = [this.ray.node.convertToNodeSpaceAR(this.startPos)];
        const pos = event.getLocation();
        this.drawRayCast(this.startPos, pos.subSelf(this.startPos).normalizeSelf(), this.ray);
    }
    onTouchEnded(event: cc.Event.EventTouch) {
        this.isPress = false;
        if (this.isHide) { return; }
        this.topTween(1);
        this.ray.clear();
        for (let i = 0; i < this.ray.node.childrenCount; i++) {
            this.ray.node.children[i].getComponent(cc.Graphics).clear();
        }
        this.isReady = false;
        this.laserVec = event.getLocation();
        this.shootBubble.getChildByName('trailing_normal').active = true;
        switch (this.specialBubbleType) {
            case 0:
                Audio.playSoundByPath('game:res/snd/shoot');
                break;
            case 1:
                Audio.playSoundByPath('game:res/snd/bomb');
                break;
            case 2:
                Audio.playSoundByPath('game:res/snd/laser');
                break;
            case 3:
                Audio.playSoundByPath('game:res/snd/omnipotent');
                break;
            case 4:
                Audio.playSoundByPath('game:res/snd/lightning');
                break;
        }
        this.ui.getChildByName('cancel').active = false;
        this.bubbleMove(1);
        this.isHide = true;
    }
    onTouchCancelled(event: cc.Event.EventTouch) {
        this.isPress = false;
        this.ray.clear();
        for (let i = 0; i < this.ray.node.childrenCount; i++) {
            this.ray.node.children[i].getComponent(cc.Graphics).clear();
        }
        this.isHide = true;
    }

    /**初始化 */
    private init(): void {
        if (!GameDataCenter.isInitSpecial) {
            GameDataCenter.isInitSpecial = true;
            cc.systemEvent.emit('updateUI', 'special1', 1);
            cc.systemEvent.emit('updateUI', 'special2', 1);
            cc.systemEvent.emit('updateUI', 'special3', 1);
            cc.systemEvent.emit('updateUI', 'special4', 1);
        }

        if (GameDataCenter.gameLevel >= 8) {
            //签到
            if (!GameDataCenter.isSignIn) {
                this.scheduleOnce(() => {
                    UIManager.open('attendance:AttendancePanel');
                }, 0.1)
            }
        }

        //初始化ui
        this.onStartTimer();
        this.changeUi('coin');
        this.changeUi('special0');
        this.changeUi('ingot');
        //初始化初始发射点
        this.startPos = this.node.convertToWorldSpaceAR(cc.v2(this.player.position.x, this.player.position.y));
        //初始化泡泡半径
        this.bubbleR = Math.floor(this.node.getContentSize().width / 11 / 2);
        //初始化地图宽度
        this.screenW = this.node.getContentSize().width;
        //设置地图大小
        // this.map.setContentSize(this.screenW, this.node.getContentSize().height * 3 / 7);
        //初始化地图高度
        this.screenH = this.map.getContentSize().height;
        //初始化地图
        this.scheduleOnce(() => {
            this.createShootBubble(true);
            this.createShootBubble();
            this.initMap(true);
        }, 0.5);
    }

    /**初始化地图 */
    private initMap(isInit: boolean = false): void {
        this.ui.getChildByName('ten').active = false;
        GameDataCenter.bubbles = [];
        this.map.removeAllChildren();
        //初始化广告图片
        let icon = this.player.getChildByName('icon');
        icon.zIndex = 100;
        icon.children[0].getComponent(cc.Sprite).spriteFrame = this.iconSpriteFrame[0];
        icon.children[0].name = '0';
        this.colorCount = [0, 0, 0, 0, 0, 0];
        this.specialBubbleType = 0;
        this.combo = 0;
        this.star = 0;
        this.isWin = false;
        this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('levelLabel').getComponent(cc.Label).string = `第${GameDataCenter.gameLevel}关`
        this.map.position = cc.v3(0, -this.node.getContentSize().height);
        this.walls.getChildByName('top').position = this.walls.convertToNodeSpaceAR(this.map.convertToWorldSpaceAR(cc.v3(this.map.position.x, this.map.getContentSize().height + this.walls.getChildByName('top').getContentSize().height / 2)));
        let length = Object.keys(LevelConfig.level).length;
        let gameLevel = LevelConfig.level[GameDataCenter.gameLevel > length ? Math.floor(Math.random() * length) + 1 : GameDataCenter.gameLevel]
        let bubbleCount = this.bubbleCount;
        let count = 0;
        this.bubbleCount = isInit ? gameLevel.bubbleCount : gameLevel.bubbleCount + 1;
        for (let i = 0; i < Math.max(gameLevel.bubbleCount - bubbleCount, 0); i++) {
            this.scheduleOnce(() => {
                bubbleCount++;
                this.player.getChildByName('bubbleCount').getComponent(cc.Label).string = `${bubbleCount}`
            }, i * 0.05);
        }
        let data = gameLevel.map;
        let time: number = 0;
        for (let i = 0; i < data.length; i++) {
            GameDataCenter.bubbles[i] = [];
            for (let j = 0; j < data[i].length; j++) {
                let type = data[i][j];
                if (type == 0) continue;
                this.scheduleOnce(() => {
                    this.creatBubble(i, j, type);
                    count++;
                }, time * 0.01);
                time = (time++) / 5;
            }
        }
        this.scheduleOnce(() => {
            this.changeUi('score', 0);
            this.perfectScore = count * 40;
            GameDataCenter.bubbles[data.length] = [];
            let t = ((1.5 * this.node.getContentSize().height - 210) / (this.bubbleR * Math.sqrt(3))) + Math.max(GameDataCenter.bubbles.length - 10, 0)
            this.setMapPosition(t);
            this.scheduleOnce(() => {
                if (GameDataCenter.gameLevel == 2) {
                    this.ui.getChildByName('tip').getChildByName('tip3').active = true;
                    Utils.openPanel(this.ui.getChildByName('tip').getChildByName('tip3'));
                    this.rayPos = [];
                    this.drawRayCast(this.startPos, cc.v2(this.startPos.x - 100, this.startPos.y + 140).subSelf(this.startPos).normalizeSelf(), this.ray);
                    let rayPos = this.rayPos;
                    for (let i = 0; i < rayPos.length - 2; i++) {
                        let node = cc.instantiate(this.arrows);
                        let angle = this.vectorsToDegree(rayPos[i], rayPos[i + 1]);
                        if (angle != this.vectorsToDegree(rayPos[i + 1], rayPos[i + 2])) {
                            cc.tween(node)
                                .to(0.5, { opacity: 0 })
                                .call(() => {
                                    node.opacity = 255;
                                })
                                .union()
                                .repeatForever()
                                .start();
                        }
                        if (i == rayPos.length - 3) {
                            cc.tween(node)
                                .to(0.5, { opacity: 0 })
                                .call(() => {
                                    node.opacity = 255;
                                })
                                .union()
                                .repeatForever()
                                .start();
                        }
                        node.parent = this.node.getChildByName('effect');
                        node.position = cc.v3(rayPos[i].x, rayPos[i].y);
                        node.angle += angle;
                        cc.tween(node)
                            .to(0.5, { position: cc.v3(rayPos[i + 1].x, rayPos[i + 1].y) })
                            .call(() => {
                                node.position = cc.v3(rayPos[i].x, rayPos[i].y);
                            })
                            .union()
                            .repeatForever()
                            .start();
                    }
                }
                this.switchShootBubble();
            }, 0.03 * t + 0.1);
            if (GameDataCenter.gameLevel == 1) {
                this.ui.getChildByName('tip').getChildByName('tip1').active = true;
                cc.tween(this.ui.getChildByName('tip').getChildByName('tip1').getChildByName('finger'))
                    .by(0.3, { x: -100 })
                    .by(0.6, { x: 200 })
                    .by(0.3, { x: -100 })
                    .union()
                    .repeatForever()
                    .start();
            }
            if (GameDataCenter.gameLevel >= 3) {
                this.ui.getChildByName('specialBubble').active = true;
            }
            if (GameDataCenter.gameLevel >= 8) {
                //签到
                if (!GameDataCenter.isSignIn && GameDataCenter.gameLevel < 16) {
                    this.scheduleOnce(() => {
                        UIManager.open('attendance:AttendancePanel');
                    }, 0.1);
                }
            }
            // if (GameDataCenter.gameLevel == 11) {
            //     this.ui.getChildByName('tip').getChildByName('tip4').active = true;
            // }
            if (GameDataCenter.gameLevel == 12) {
                this.ui.getChildByName('colorTip').active = true;
                cc.systemEvent.emit('updateUI', `special3`, 1);
            }
            if (GameDataCenter.gameLevel == 13) {
                if (this.onlineTime == 0) {
                    this.onOnline();
                }
            }
            if (GameDataCenter.gameLevel == this.iceTips || GameDataCenter.gameLevel == 11) {
                GameDataCenter.tipType = 2;
                UIManager.open('tip:TipPanel');
            }
        }, time * 0.01 + 1);
    }

    /**判断射线出现 */
    private isReadyRay(event): boolean {
        let pos = this.player.parent.convertToNodeSpaceAR(event.getLocation());
        if (pos.y < Math.tan(cc.misc.degreesToRadians(20)) * (Math.abs(pos.x)) + this.player.position.y || !this.isReady) {
            this.ray.clear();
            this.topTween(1);
            for (let i = 0; i < this.ray.node.childrenCount; i++) {
                this.ray.node.children[i].getComponent(cc.Graphics).clear();
            }
            this.node.getChildByName('tipRay').getComponent(cc.Graphics).clear();
            this.isHide = true;
            return false;
        }
        this.isHide = false;
        return true;
    }

    /**
     * @description 计算射线
     * @param startLocation 起始位置 世界坐标系
     * @param vector_dir 单位方向向量
     */
    private drawRayCast(startLocation: cc.Vec2, vector_dir: cc.Vec2, graphic: cc.Graphics): void {
        if (this.specialBubbleType == 2) {
            this.specialBubbleIndex = [];
            const endLocation = startLocation.add(vector_dir.mul(1500));
            const results = cc.director.getPhysicsManager().rayCast(startLocation, endLocation, cc.RayCastType.AllClosest);
            this.drawAimLine(startLocation, results[results.length - 1].point, graphic);
            //计算激光经过的泡泡
            let result1 = cc.director.getPhysicsManager().rayCast(startLocation.sub(cc.v2(this.bubbleR - 1)), endLocation.sub(cc.v2(this.bubbleR - 1)), cc.RayCastType.AllClosest);
            for (let i in result1) {
                if (result1[i].collider.node.name == 'bubble') {
                    let hitBubble = result1[i].collider.node.parent.getComponent('Bubble');
                    this.specialBubbleIndex.push(cc.v2(hitBubble.i, hitBubble.j));
                }
            }
            let result2 = cc.director.getPhysicsManager().rayCast(startLocation.sub(cc.v2(-this.bubbleR + 1)), endLocation.sub(cc.v2(-this.bubbleR + 1)), cc.RayCastType.AllClosest);
            for (let i in result2) {
                if (result2[i].collider.node.name == 'bubble') {
                    let hitBubble = result2[i].collider.node.parent.getComponent('Bubble');
                    this.specialBubbleIndex.push(cc.v2(hitBubble.i, hitBubble.j));
                }
            }
            if (result1[result1.length - 1].collider.node.name != 'bubble' && result2[result2.length - 1].collider.node.name != 'bubble') {
                this.node.getChildByName('tipRay').getComponent(cc.Graphics).clear();
            }
            //加入点位
            if (this.specialBubbleIndex.length > 0) {
                let tipRay = this.node.getChildByName('tipRay').getComponent(cc.Graphics);
                tipRay.clear();
                this.unique();
                for (let i = 0; i < this.specialBubbleIndex.length; i++) {
                    let pos = this.convertRowColToPos(this.specialBubbleIndex[i].x, this.specialBubbleIndex[i].y);
                    pos = this.node.convertToNodeSpaceAR(this.map.convertToWorldSpaceAR(pos))
                    tipRay.circle(pos.x, pos.y, this.bubbleR);
                }
                tipRay.stroke();
            }
        }
        else {
            const endLocation = startLocation.add(vector_dir.mul(10000));
            const results = cc.director.getPhysicsManager().rayCast(startLocation, endLocation, cc.RayCastType.Closest);
            const result = results[0];
            // 指定射线与穿过的碰撞体在哪一点相交。
            const point = result.point;
            this.drawAimLine(startLocation, point, graphic);
            //碰撞体
            const collider = result.collider.node.name;
            // 画入射线段
            if (collider == 'left' || collider == 'right') {
                // 指定碰撞体在相交点的表面的法线单位向量。
                const vector_n = result.normal;
                const vector_r = vector_dir.sub(vector_n.mul(2 * vector_dir.dot(vector_n)));
                this.drawRayCast(point, vector_r, graphic);
            }
            else {
                let hitBubble = result.collider.node.parent;
                if (collider == 'bubble') {
                    this.bubbleIndex = this.setBubble(hitBubble.getComponent('Bubble').i, hitBubble.getComponent('Bubble').j, this.map.convertToNodeSpaceAR(point));
                }
                if (collider == 'top') {
                    this.bubbleIndex = this.setBubble(-1, -1, this.map.convertToNodeSpaceAR(point));
                }
                this.specialBubbleIndex = [];
                switch (this.specialBubbleType) {
                    case 1:
                        this.bombBubble();
                        break;
                    case 3:
                        this.omnipotentBubble();
                        break;
                    case 4:
                        this.lightningBubble(hitBubble, collider == 'top' ? true : false);
                        break;
                }
                let tipRay = this.node.getChildByName('tipRay').getComponent(cc.Graphics);
                tipRay.clear();
                // this.specialBubbleIndex.push(cc.v2(this.bubbleIndex.x, this.bubbleIndex.y));
                this.unique();
                for (let i = 0; i < this.specialBubbleIndex.length; i++) {
                    let pos = this.convertRowColToPos(this.specialBubbleIndex[i].x, this.specialBubbleIndex[i].y);
                    pos = this.node.convertToNodeSpaceAR(this.map.convertToWorldSpaceAR(pos))
                    tipRay.circle(pos.x, pos.y, this.bubbleR);
                }
                tipRay.stroke();
            }
        }
    }

    /**
     * @description 画瞄准线
     * @param startLocation 起始位置 世界坐标系
     * @param endLocation 结束位置 世界坐标系
     */
    private drawAimLine(startLocation: cc.Vec2, endLocation: cc.Vec2, graphic: cc.Graphics): void {
        const graphic_startLocation = graphic.node.convertToNodeSpaceAR(startLocation);
        const graphic_endLocation = graphic.node.convertToNodeSpaceAR(endLocation);
        graphic.moveTo(graphic_startLocation.x, graphic_startLocation.y);
        /*画间隔圆线*/
        //圆与圆之间的间隔
        let delta = 60;
        //直线方向
        const vector_dir = endLocation.sub(startLocation);
        //圆的数量
        const total_count = Math.floor(vector_dir.mag() / delta);
        //每次间隔向量
        vector_dir.normalizeSelf().mulSelf(delta);

        for (let index = 0; index < total_count; index++) {
            graphic_startLocation.addSelf(vector_dir);
            this.rayPos.push(cc.v2(graphic_startLocation.x, graphic_startLocation.y));
        }
        this.rayPos.push(cc.v2(graphic_endLocation.x, graphic_endLocation.y));
        this.pathPos.push(cc.v2(graphic_endLocation.x, graphic_endLocation.y));
    }

    /**更新射线 */
    private updateRay(dt): void {
        this.ray.clear();
        for (let i = 0; i < this.ray.node.childrenCount; i++) {
            this.ray.node.children[i].getComponent(cc.Graphics).clear();
        }
        // (0.4:min+5:max)/2
        let offset: number = 10;
        for (let i = 0; i < this.rayPos.length; i++) {
            if (i % 2 == 0) {
                this.ray.circle(this.rayPos[i].x, this.rayPos[i].y, offset + this.r);
                for (let j = 0; j < this.ray.node.childrenCount; j++) {
                    this.ray.node.children[j].getComponent(cc.Graphics).circle(this.rayPos[i].x, this.rayPos[i].y, offset + this.r - (j + 1) * 3);
                }
            }
            else {
                this.ray.circle(this.rayPos[i].x, this.rayPos[i].y, offset - this.r);
                for (let j = 0; j < this.ray.node.childrenCount; j++) {
                    this.ray.node.children[j].getComponent(cc.Graphics).circle(this.rayPos[i].x, this.rayPos[i].y, offset - this.r - (j + 1) * 3);
                }
            }
        }
        this.r += this.op * 10 * dt;
        if (Math.abs(this.r) > 3) {
            this.op = -this.op;
        }
        this.ray.fill();
        for (let i = 0; i < this.ray.node.childrenCount; i++) {
            this.ray.node.children[i].getComponent(cc.Graphics).fill();
        }
    }

    /**创建小球 */
    private creatBubble(i: number, j: number, color: number): void {
        let node = cc.instantiate(this.bubble);
        node.position = this.convertRowColToPos(i, j);
        if (this.specialBubbleType == 0) {
            node.getComponent(cc.Sprite).spriteFrame = this.bubbleSpriteFrame[color - 1];
        }
        else {
            node.getComponent(cc.Sprite).spriteFrame = this.specialBubbleSpriteFrame[color - 1];
        }
        node.parent = this.map;
        node.getComponent('Bubble').init(i, j, color);
        node.setContentSize(this.bubbleR * 2, this.bubbleR * 2);
        node.children[0].getComponent(cc.PhysicsCircleCollider).radius = this.bubbleR;
        node.children[0].getComponent(cc.PhysicsCircleCollider).apply();
        GameDataCenter.bubbles[i][j] = node;
        if (color >= 1 && color <= 6) { this.colorCount[color - 1]++; }
    }

    /**创建预发射小球 */
    private createShootBubble(isInit: boolean = false): void {
        if (!isInit) {
            this.shootBubble = this.preBubble;
            this.ray.fillColor = this.rayColor[this.shootBubble.getComponent('Bubble').color - 1];
            this.bubbleCount--;
            if (this.bubbleCount == 0) {
                GameDataCenter.tipType = 0;
                UIManager.open('tip:TipPanel');
            }
            if (this.bubbleCount == 3) {
                GameDataCenter.tipType = 3;
                UIManager.open('tip:TipPanel');
            }
            if (this.bubbleCount == 10) {
                GameDataCenter.tipType = 1;
                UIManager.open('tip:TipPanel');
            }
        }
        let node = cc.instantiate(this.shootBubblePre);
        let pos = this.player.convertToNodeSpaceAR(this.startPos);
        let color = this.randNum();
        node.position = cc.v3(pos.x + 60, pos.y - 60);
        node.parent = this.player;
        node.getChildByName('sprite').getComponent(cc.Sprite).spriteFrame = this.bubbleSpriteFrame[color - 1];
        node.setContentSize(this.bubbleR * 2, this.bubbleR * 2);
        node.getComponent('Bubble').color = color;
        node.scale = 0;
        this.preBubble = node;
    }

    /**发射小球切换动画 */
    private switchShootBubble(): void {
        this.scheduleOnce(() => {
            this.specialBubbleType = 0;
            let pos = this.player.convertToNodeSpaceAR(this.startPos);
            if (this.colorCount[this.shootBubble.getComponent('Bubble').color - 1] == 0) {
                let color = this.randNum();
                this.shootBubble.getComponent('Bubble').color = color;
                this.shootBubble.getChildByName('sprite').getComponent(cc.Sprite).spriteFrame = this.bubbleSpriteFrame[color - 1];
            }
            cc.tween(this.shootBubble).to(0.5, { position: cc.v3(pos.x, pos.y), scale: 1 }).start();
            cc.tween(this.preBubble).to(0.5, { scale: 0.75 }).start();
            this.ray.fillColor = this.rayColor[this.shootBubble.getComponent('Bubble').color - 1];
            this.player.getChildByName('bubbleCount').getComponent(cc.Label).string = `${this.bubbleCount}`
            this.scheduleOnce(() => {
                this.isReady = true;
            }, 0.5);
            this.player.getChildByName('icon').active = true;
        }, 0.1);
    }

    /**特殊球切换 */
    private switchSpecialBubble(op: number): void {
        let specialBubble = this.ui.getChildByName('specialBubble');
        for (let i = 0; i < specialBubble.childrenCount; i++) {
            cc.tween(specialBubble.children[i])
                .by(0.5, { y: op * -200, opacity: op * -255 })
                .call(() => {
                    if (op == 1) {
                        this.ui.getChildByName('cancel').active = true;
                        specialBubble.children[i].getComponent(cc.Button).interactable = true;
                    }
                })
                .start();
        }
        if (op == 1) {
            this.setspecialColor();
            let node = cc.instantiate(this.shootBubblePre);
            let pos = this.player.convertToNodeSpaceAR(this.startPos);
            node.position = cc.v3(pos.x, pos.y);
            node.parent = this.player;
            node.getChildByName('sprite').getComponent(cc.Sprite).spriteFrame = this.specialBubbleSpriteFrame[this.specialBubbleType - 1];
            node.setContentSize(this.bubbleR * 2, this.bubbleR * 2);
            node.scale = 0;
            let effect = cc.instantiate(this.specialBubbleEffect[this.specialBubbleType - 1]);
            effect.parent = node;
            this.specialBubble = node;
            cc.tween(this.specialBubble)
                .to(0.3, { scale: 1 })
                .start();
            cc.tween(this.shootBubble)
                .to(0.3, { scale: 0, opacity: 0 })
                .call(() => {
                    this.shootBubble.active = false;
                    this.shootBubble.opacity = 255;
                    this.shootBubble.scale = 0;
                    this.shootBubble.position = cc.v3(pos.x + 60, pos.y - 60);
                })
                .start();
            cc.tween(this.preBubble)
                .to(0.3, { scale: 0 })
                .start();
        }
        else {
            this.ray.node.removeAllChildren();
            this.specialBubble.destroy();
            this.shootBubble.active = true;
            cc.tween(this.shootBubble).to(0.3, { scale: 0.75 }).start();
            if (!this.isWin) { this.switchShootBubble(); }
            this.ui.getChildByName('cancel').active = false;
        }
    }

    /**小球运动 */
    private bubbleMove(i): void {
        // let pos: cc.Vec2 = this.player.convertToNodeSpaceAR(this.ray.node.convertToWorldSpaceAR(this.rayPos[i]));
        // let end: cc.Vec2 = this.player.convertToNodeSpaceAR(this.ray.node.convertToWorldSpaceAR(this.rayPos[i - 1]));
        let pos: cc.Vec2 = this.player.convertToNodeSpaceAR(this.ray.node.convertToWorldSpaceAR(this.pathPos[i]));
        let end: cc.Vec2 = this.player.convertToNodeSpaceAR(this.ray.node.convertToWorldSpaceAR(this.pathPos[i - 1]));
        if (this.specialBubbleType == 2) {
            this.combo++;
            this.node.getChildByName('tipRay').getComponent(cc.Graphics).clear();
            let node = cc.instantiate(this.laser_gj);
            node.parent = this.specialBubble;
            node.angle = this.vectorsToDegree(this.startPos, this.laserVec);
            for (let i in this.specialBubbleIndex) {
                let color = GameDataCenter.bubbles[this.specialBubbleIndex[i].x][this.specialBubbleIndex[i].y].getComponent('Bubble').color;
                if (color >= 1 && color <= 6) { this.colorCount[color - 1]--; }
                GameDataCenter.bubbles[this.specialBubbleIndex[i].x][this.specialBubbleIndex[i].y].getComponent('Bubble').playDeathAnimation(this.combo, this.specialBubbleType);
                this.changeUi('score', this.combo * 10);
            }
            this.isSuspension();
            return;
        }
        cc.tween(this.specialBubbleType == 0 ? this.shootBubble : this.specialBubble)
            .to(Math.floor(pos.sub(end).mag()) / (1000 * 2), { position: cc.v3(pos.x, pos.y, 0) })
            .call(() => {
                if (i < this.pathPos.length - 1) {
                    this.bubbleMove(i + 1);
                }
                else {
                    if (this.specialBubbleType == 0) {
                        this.creatBubble(this.bubbleIndex.x, this.bubbleIndex.y, this.shootBubble.getComponent('Bubble').color);
                        this.crashTween(this.bubbleIndex.x, this.bubbleIndex.y);
                        this.shootBubble.destroy();
                        this.isElimination();

                    }
                    else {
                        this.combo++;
                        this.node.getChildByName('tipRay').getComponent(cc.Graphics).clear();
                        if (this.specialBubbleType == 4) {
                            if (!GameDataCenter.bubbles[this.thunderIndex.x][this.thunderIndex.y]) {
                                this.creatBubble(this.thunderIndex.x, this.thunderIndex.y, this.specialBubbleType);
                                this.specialBubbleIndex.push(cc.v2(this.thunderIndex.x, this.thunderIndex.y));
                            }
                            let node = cc.instantiate(this.thunder_gj);
                            node.parent = GameDataCenter.bubbles[this.thunderIndex.x][this.thunderIndex.y];
                            node.position = node.parent.convertToNodeSpaceAR(this.map.convertToWorldSpaceAR(cc.v3(0, this.convertRowColToPos(this.thunderIndex.x, this.thunderIndex.y).y)));
                            let children = node.getChildByName('qiu')
                            children.position = node.convertToNodeSpaceAR(this.map.convertToWorldSpaceAR(this.convertRowColToPos(this.thunderIndex.x, this.thunderIndex.y)));
                        }
                        else {
                            this.creatBubble(this.bubbleIndex.x, this.bubbleIndex.y, this.specialBubbleType);
                            this.specialBubbleIndex.push(cc.v2(this.bubbleIndex.x, this.bubbleIndex.y));
                        }
                        for (let i in this.specialBubbleIndex) {
                            let color = GameDataCenter.bubbles[this.specialBubbleIndex[i].x][this.specialBubbleIndex[i].y].getComponent('Bubble').color;
                            if (color >= 1 && color <= 6) { this.colorCount[color - 1]--; }
                            GameDataCenter.bubbles[this.specialBubbleIndex[i].x][this.specialBubbleIndex[i].y].getComponent('Bubble').playDeathAnimation(this.combo, this.specialBubbleType);
                            this.changeUi('score', this.combo * 10);
                        }
                        this.isSuspension();
                        this.specialBubble.destroy();
                    }
                }
            })
            .start();
    }

    /**更新地图大小及位置 */
    private setMapPosition(op: number = 1): void {
        let offset: number = this.bubbleR * Math.sqrt(3);
        cc.tween(this.map)
            .by(Math.max(0.03 * op, 0.3), { y: op * offset })
            .call(() => {
                for (let i = 0; i < this.map.childrenCount; i++) {
                    this.map.children[i].children[0].position = cc.v3(0, 0, 0);
                    this.map.children[i].children[0].getComponent(cc.RigidBody).syncPosition(false);
                }
            })
            .start();
        cc.tween(this.walls.getChildByName('top'))
            .by(Math.max(0.03 * op, 0.3), { y: op * offset })
            .start();
        // this.schedule(() => {
        //     this.showBubble();
        // }, 0.1, Math.ceil(Math.max(0.02 * op, 0.3) / 0.1) + 1);
    }

    /**泡泡位置校正 */
    private setBubble(i: number, j: number, position: cc.Vec2): cc.Vec2 {
        let mag: number = 99999;
        let index: cc.Vec2 = cc.v2();
        if (i == -1 && j == -1) {
            index.x = 0;
            for (let k = 0; k < 11; k++) {
                if (GameDataCenter.bubbles[0][k]) { continue; }
                let t: cc.Vec3 = this.convertRowColToPos(0, k);
                let p: cc.Vec2 = cc.v2(t.x, t.y);
                if (p.sub(position).mag() < mag) {
                    mag = p.sub(position).mag();
                    index.y = k;
                }
            }
        }
        else {
            this.traversal(i, j, (row: number, col: number) => {
                if (GameDataCenter.bubbles[row][col]) return true;
                let t: cc.Vec3 = this.convertRowColToPos(row, col);
                let p: cc.Vec2 = cc.v2(t.x, t.y);
                if (p.sub(position).mag() < mag) {
                    mag = p.sub(position).mag();
                    index.x = row;
                    index.y = col;
                }
            })
        }
        return index;
    }

    /**检测泡泡是否消除 */
    private isElimination(): void {
        // 记录消除行列值
        let record: cc.Vec2[] = [];
        let color = this.shootBubble.getComponent('Bubble').color;
        GameDataCenter.bubbles[this.bubbleIndex.x][this.bubbleIndex.y].getComponent('Bubble').isVisited = true;
        record.push(cc.v2(this.bubbleIndex.x, this.bubbleIndex.y));
        this.traversalAll(this.bubbleIndex.x, this.bubbleIndex.y, (row: number, col: number) => {
            // if (row < GameDataCenter.bubbles.length - this.showCount - 1) return true;
            let nodeData = GameDataCenter.bubbles[row][col].getComponent('Bubble');
            // 如果被访问过
            if (nodeData.isVisited) return true;
            // 如果颜色不同
            if (nodeData.color !== color) return true;
            // 符合条件
            nodeData.isVisited = true;
            record.push(cc.v2(row, col));
        })
        let count: number = 0;
        for (let row = 0; row < GameDataCenter.bubbles.length; row++) {
            for (let col = 0; col < GameDataCenter.bubbles[row].length; col++) {
                if (!GameDataCenter.bubbles[row][col]) continue;
                if (GameDataCenter.bubbles[row][col].getComponent('Bubble').isVisited) {
                    GameDataCenter.bubbles[row][col].getComponent('Bubble').isVisited = false;
                    count++;
                }
            }
        }
        if (count >= 3) {
            this.combo++;
            // 执行消除
            for (let i in record) {
                this.scheduleOnce(() => {
                    this.isIceBubble(record[i].x, record[i].y, GameDataCenter.bubbles[record[i].x][record[i].y].getComponent('Bubble').color);
                    this.colorCount[GameDataCenter.bubbles[record[i].x][record[i].y].getComponent('Bubble').color - 1]--;
                    GameDataCenter.bubbles[record[i].x][record[i].y].getComponent('Bubble').playDeathAnimation(this.combo);
                }, +i * (0.04 - record.length * 0.001));
            }
            this.scheduleOnce(() => {
                this.changeUi('score', 10 * this.combo * count);
                // this.scheduleOnce(() => {
                //     Audio.playSoundByPath('game:res/snd/score');
                // }, 0.5);
                //分数激励
                let node = cc.instantiate(this.scorePrefab);
                node.parent = this.node;
                let pos = this.convertRowColToPos(this.bubbleIndex.x, this.bubbleIndex.y);
                pos = this.node.convertToNodeSpaceAR(this.map.convertToWorldSpaceAR(pos));
                node.position = cc.v3(pos.x, pos.y - 100);
                node.children[0].getComponent(cc.Label).string = `${count * this.combo * 10}`;
                node.getChildByName(this.appraise[Math.floor(Math.random() * 3)]).active = true;
                cc.tween(node)
                    .by(0.5, { y: 100 })
                    .removeSelf()
                    .start();
                if (count > 10) {
                    node = cc.instantiate(this.appraisePrefab);
                    node.parent = this.node;
                    node.scale = 0;
                    node.children[Math.floor(Math.random() * 3)].active = true;
                }
                cc.tween(node)
                    .to(0.2, { scale: 1 })
                    .delay(1)
                    .to(0.2, { scale: 0 })
                    .removeSelf()
                    .start();
                this.isSuspension();
            }, record.length * (0.04 - record.length * 0.001));
        } else {
            this.combo = 0;
            //数组变化及地图移动 
            if (this.bubbleIndex.x >= GameDataCenter.bubbles.length - 1) {
                GameDataCenter.bubbles[GameDataCenter.bubbles.length] = []
                if (GameDataCenter.bubbles.length > 10) {
                    this.setMapPosition();
                }
            }
            //创建下一个小球 
            this.scheduleOnce(() => {
                this.createShootBubble();
                this.switchShootBubble();
            }, 0);
        }
    }

    /**悬空检测 */
    private isSuspension(): void {
        this.scheduleOnce(() => {
            for (let i = 0; i < GameDataCenter.bubbles[0].length; i++) {
                // 执行最上的一排泡泡
                if (!GameDataCenter.bubbles[0][i]) continue;
                this.traversalAll(0, i, (row: number, col: number) => {
                    let nodeData = GameDataCenter.bubbles[row][col].getComponent('Bubble');
                    if (nodeData.isVisited) return true;
                    // 符合条件
                    nodeData.isVisited = true;
                    nodeData.isLinked = true;
                }, true);
            }
            let count: number = 0;
            for (let row = 0; row < GameDataCenter.bubbles.length; row++) {
                for (let col = 0; col < GameDataCenter.bubbles[row].length; col++) {
                    if (!GameDataCenter.bubbles[row][col]) continue;
                    if (!GameDataCenter.bubbles[row][col].getComponent('Bubble').isLinked) {
                        this.scheduleOnce(() => {
                            let color = GameDataCenter.bubbles[row][col].getComponent('Bubble').color;
                            if (color >= 1 && color <= 6) { this.colorCount[color - 1]--; }
                            GameDataCenter.bubbles[row][col].getComponent('Bubble').playDownAnimation(this.combo * 2, this.map.convertToNodeSpaceAR(this.startPos));
                        }, count * 0.02);
                        count++;
                    } else {
                        GameDataCenter.bubbles[row][col].getComponent('Bubble').isVisited = false;
                        GameDataCenter.bubbles[row][col].getComponent('Bubble').isLinked = false;
                    }
                }
            }
            this.scheduleOnce(() => {
                if (count > 0) { this.changeUi('score', 10 * this.combo * count * 2); }
                this.removeEmpty();
            }, count * 0.02 + count > 0 ? 1.5 : 0);
        }, 0);
    }

    /**冰球检测*/
    private isIceBubble(i: number, j: number, color: number): void {
        this.traversal(i, j, (row: number, col: number) => {
            if (!GameDataCenter.bubbles[row] || !GameDataCenter.bubbles[row][col]) return true;
            if (GameDataCenter.bubbles[row][col].getComponent('Bubble').color == 6) {
                GameDataCenter.bubbles[row][col].destroy();
                Audio.playSoundByPath('game:res/snd/ice');
                this.colorCount[5]--;
                this.creatBubble(row, col, color);
            }
        })
    }

    /**碰撞动画 */
    private crashTween(i: number, j: number): void {
        this.traversal(i, j, (row: number, col: number) => {
            if (!GameDataCenter.bubbles[row] || !GameDataCenter.bubbles[row][col]) return true;
            let start = this.convertRowColToPos(i, j);
            let end = this.convertRowColToPos(row, col);
            let dir = end.sub(start);
            cc.tween(GameDataCenter.bubbles[row][col])
                .by(0.1, { position: dir.normalize().mul(10) })
                .by(0.1, { position: dir.normalize().mul(-10) })
                .start();
        })
    }

    /**炸弹 */
    private bombBubble(): void {
        this.traversal(this.bubbleIndex.x, this.bubbleIndex.y, (row: number, col: number) => {
            this.traversal(row, col, (i, j) => {
                if (!GameDataCenter.bubbles[i] || !GameDataCenter.bubbles[i][j]) return true;
                this.specialBubbleIndex.push(cc.v2(i, j));
            })
        })
    }

    /**七色 */
    private omnipotentBubble(): void {
        this.traversal(this.bubbleIndex.x, this.bubbleIndex.y, (row: number, col: number) => {
            if (!GameDataCenter.bubbles[row] || !GameDataCenter.bubbles[row][col]) return true;
            let color = GameDataCenter.bubbles[row][col].getComponent('Bubble').color;
            this.traversalAll(row, col, (i: number, j: number) => {
                let nodeData = GameDataCenter.bubbles[i][j].getComponent('Bubble');
                // 如果被访问过
                if (nodeData.isVisited) return true;
                // 如果颜色不同
                if (nodeData.color !== color) return true;
                // 符合条件
                nodeData.isVisited = true;
            })
        });
        for (let row = 0; row < GameDataCenter.bubbles.length; row++) {
            for (let col = 0; col < GameDataCenter.bubbles[row].length; col++) {
                if (!GameDataCenter.bubbles[row][col]) continue;
                if (GameDataCenter.bubbles[row][col].getComponent('Bubble').isVisited) {
                    GameDataCenter.bubbles[row][col].getComponent('Bubble').isVisited = false;
                    this.specialBubbleIndex.push(cc.v2(row, col));
                }
            }
        }
    }

    /**闪电 */
    private lightningBubble(hitBubble: cc.Node, isTop: boolean): void {
        if (isTop) {
            this.thunderIndex = cc.v2(this.bubbleIndex.x, this.bubbleIndex.y);
        }
        else {
            this.thunderIndex = cc.v2(hitBubble.getComponent('Bubble').i, hitBubble.getComponent('Bubble').j);
        }
        for (let i = 0; i < GameDataCenter.bubbles[this.thunderIndex.x].length; i++) {
            let node = GameDataCenter.bubbles[this.thunderIndex.x][i]
            if (!node) { continue; }
            this.specialBubbleIndex.push(cc.v2(node.getComponent('Bubble').i, node.getComponent('Bubble').j));
        }
    }

    /**清空空数组 */
    private removeEmpty(): void {
        let removeCount: number = 0;
        for (let i = GameDataCenter.bubbles.length - 2; i >= 0; i--) {
            let count = 0;
            for (let j = 0; j < GameDataCenter.bubbles[i].length; j++) {
                if (GameDataCenter.bubbles[i][j]) {
                    count++;
                }
            }
            if (count == 0) {
                GameDataCenter.bubbles.length--;
                if (GameDataCenter.bubbles.length >= 10) { removeCount++; }

            }
        }
        this.setMapPosition(-removeCount);
        let sum: number = 0;
        for (let i = 0; i < this.colorCount.length; i++) {
            sum += this.colorCount[i];
        }
        let count = 0;
        if (sum == 0) {
            this.isWin = true;
            Audio.playSoundByPath('game:res/snd/victory');
            GameDataCenter.gameLevel++;
            for (let i = 0; i < GameDataCenter.bubbles.length; i++) {
                for (let j = 0; j < GameDataCenter.bubbles[i].length; j++) {
                    if (!GameDataCenter.bubbles[i][j]) { continue; }
                    this.scheduleOnce(() => {
                        GameDataCenter.bubbles[i][j].getComponent('Bubble').playDownAnimation(this.combo * 2, this.map.convertToNodeSpaceAR(this.startPos));
                    }, count * 0.01);
                    count++;
                }
            }
            if (count > 0) {
                this.scheduleOnce(() => {
                    this.changeUi('score', this.combo * 2 * count * 10);
                }, count * 0.01);
            }
            this.scheduleOnce(() => {
                this.initMap();
            }, 2 + count * 0.01);
        }
        this.scheduleOnce(() => {
            if (this.specialBubbleType == 0) {
                this.scheduleOnce(() => {
                    this.createShootBubble();
                    this.switchShootBubble();
                }, this.isWin ? 3 : 0);
            }
            else {
                if (GameDataCenter.specialBubbles[this.specialBubbleType - 1] > 0) {
                    this.changeUi(`special${this.specialBubbleType}`, -1);
                }
                else if (GameDataCenter.coin >= 100) {
                    this.changeUi('coin', -100);
                }
                this.scheduleOnce(() => {
                    this.switchSpecialBubble(-1);
                }, this.specialBubbleType == 2 ? 1 : 0);
            }
        }, count * 0.01);
    }

    /**传入二维数组行列, 返回对应位置坐标 */
    private convertRowColToPos(row: number, col: number): cc.Vec3 {
        let posX: number = this.bubbleR * ((row % 2) + 1) + col * this.bubbleR * 2 - this.screenW / 2;
        let posY: number = this.screenH - (this.bubbleR + this.bubbleR * row * Math.sqrt(3));
        return cc.v3(posX, posY);
    }

    /**计算向量与y轴之间的夹角 */
    private vectorsToDegree(comVec, dirVec) {
        // 求方向向量与对比向量间的弧度
        let dir = comVec.sub(dirVec);
        let radian = dir.signAngle(cc.v2(1, 0));
        // 将弧度转换为角度
        let degree = cc.misc.radiansToDegrees(radian);
        return Math.round(-(degree - 90));
    }

    /**随机颜色函数 */
    private randNum(): number {
        if (GameDataCenter.gameLevel == 1) {
            if (LevelConfig.level[1].index < 4) { return LevelConfig.level[1].bubble[LevelConfig.level[1].index++]; }
        }
        let color: number[] = [];
        for (let i = 0; i < this.colorCount.length - 1; i++) {
            if (this.colorCount[i] != 0) {
                color.push(i + 1);
            }
        }

        let min: number = 0;
        let max: number = color.length - 1;
        // random 0 - 1 不包括 1 
        let random = min + Math.floor((max - min + 1) * Math.random())
        if (color.length == 0) {
            min = 1;
            max = this.colorCount.length - 1
            random = min + Math.floor((max - min + 1) * Math.random())
            return random;
        }
        return color[random];
    }

    /**特殊消除球向量数组去重 */
    private unique(): void {
        let array: cc.Vec2[] = []
        for (let i in this.specialBubbleIndex) {
            let isExit = false;
            for (let j in array) {
                if (this.specialBubbleIndex[i].x == array[j].x && this.specialBubbleIndex[i].y == array[j].y) {
                    isExit = true;
                    break;
                }
            }
            if (!isExit) {
                array.push(cc.v2(this.specialBubbleIndex[i].x, this.specialBubbleIndex[i].y))
            }
        }
        this.specialBubbleIndex = array;
    }

    /**设置特殊球瞄准线线段颜色 */
    private setspecialColor(): void {
        let specialColor = this.specialColor[this.specialBubbleType - 1];
        this.ray.fillColor = specialColor[0];
        for (let i = 1; i < specialColor.length; i++) {
            let node = cc.instantiate(this.rayPrefab);
            node.parent = this.ray.node;
            let graphic = node.getComponent(cc.Graphics);
            graphic.fillColor = specialColor[i];
        }
    }

    /**ui变化 */
    private changeUi(type: string, num: number = 0): void {
        if (type == 'coin') {
            this.ui.getChildByName('topPanel').getChildByName('coin').getChildByName('num').children[0].getComponent(cc.Label).string = `${GameDataCenter.coin}`;
            if (num == 0) { return; }
            let t: number = GameDataCenter.coin;
            GameDataCenter.coin += num;
            for (let i = 0; i < Math.abs(GameDataCenter.coin - t); i++) {
                this.scheduleOnce(() => {
                    num > 0 ? t++ : t--;
                    this.ui.getChildByName('topPanel').getChildByName('coin').getChildByName('num').children[0].getComponent(cc.Label).string = `${t}`;
                }, 0.002 * i);
            }
            let specialBubble = this.ui.getChildByName('specialBubble');
            for (let i = 0; i < specialBubble.childrenCount; i++) {
                specialBubble.children[i].getChildByName('null').getChildByName('icon').getComponent(cc.Sprite).spriteFrame = GameDataCenter.coin >= 100 ? this.iconSpriteFrame[2] : this.iconSpriteFrame[0];
                specialBubble.children[i].getChildByName('null').getChildByName('label').getComponent(cc.Label).string = GameDataCenter.coin >= 100 ? '100' : ':1';
            }
        }
        else if (type == 'score') {
            if (num == 0) {
                this.score = 0;
                this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('scoreLabel').getComponent(cc.Label).string = `得分:${this.score}`;
                this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('progressBar').getComponent(cc.ProgressBar).progress = 0;
                for (let i = 1; i <= 3; i++) {
                    this.ui.getChildByName('topPanel').getChildByName('score').getChildByName(`star${i}`).getComponent(cc.Sprite).spriteFrame = this.iconSpriteFrame[3];
                }
                return;
            }
            let t = this.score;
            this.score += num;
            for (let i = 0; i < Math.abs(this.score - t); i++) {
                this.scheduleOnce(() => {
                    num > 0 ? t++ : t--;
                    this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('scoreLabel').getComponent(cc.Label).string = `得分:${t}`;
                    this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('progressBar').getComponent(cc.ProgressBar).progress = t / this.perfectScore;
                }, 0.5 * i / num);
            }
            this.scheduleOnce(() => {
                if (this.score >= this.perfectScore && this.star == 2) {
                    this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('star3').getComponent(cc.Sprite).spriteFrame = this.iconSpriteFrame[4];
                    this.star++;
                }
                if (this.score >= this.perfectScore * 0.7 && this.star == 1) {
                    this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('star2').getComponent(cc.Sprite).spriteFrame = this.iconSpriteFrame[4];
                    this.star++;
                }
                if (this.score >= this.perfectScore * 0.2 && this.star == 0) {
                    this.ui.getChildByName('topPanel').getChildByName('score').getChildByName('star1').getComponent(cc.Sprite).spriteFrame = this.iconSpriteFrame[4];
                    this.star++;
                }
            }, 0.5);
        }
        else if (type.substring(0, type.length - 1) == 'special') {
            let specialBubble = this.ui.getChildByName('specialBubble');
            let enableSwitch: Function = (i) => {
                specialBubble.children[i].getChildByName('num').active = GameDataCenter.specialBubbles[i] <= 0 ? false : true;
                specialBubble.children[i].getChildByName('null').active = GameDataCenter.specialBubbles[i] <= 0 ? true : false;
                specialBubble.children[i].getChildByName('null').getChildByName('icon').getComponent(cc.Sprite).spriteFrame = GameDataCenter.coin >= 100 ? this.iconSpriteFrame[2] : this.iconSpriteFrame[0];
                specialBubble.children[i].getChildByName('null').getChildByName('label').getComponent(cc.Label).string = GameDataCenter.coin >= 100 ? '100' : ':1';
                specialBubble.children[i].getChildByName('null').getChildByName('icon').setContentSize(GameDataCenter.coin >= 100 ? cc.size(40, 40) : cc.size(40, 30));
            }
            for (let i = 0; i < specialBubble.childrenCount; i++) {
                specialBubble.children[i].getChildByName('num').getChildByName('label').getComponent(cc.Label).string = `${GameDataCenter.specialBubbles[i]}`;
                enableSwitch(i);
            }
            let specialBubbleType = (+type[type.length - 1]) - 1;
            if (specialBubbleType < 0 || num == 0) { return; }
            let t: number = GameDataCenter.specialBubbles[specialBubbleType];
            let array = GameDataCenter.specialBubbles;
            array[specialBubbleType] += num;
            GameDataCenter.specialBubbles = array;
            for (let i = 0; i < Math.abs(GameDataCenter.specialBubbles[specialBubbleType] - t); i++) {
                this.scheduleOnce(() => {
                    num > 0 ? t++ : t--;
                    specialBubble.children[specialBubbleType].getChildByName('num').getChildByName('label').getComponent(cc.Label).string = `${t}`;
                }, 0.1 * i);
            }
            enableSwitch(specialBubbleType);
        }
        else if (type == 'ingot') {

        }
    }

    /**遍历小球一周 */
    private traversal(i: number, j: number, callback: Function): void {
        let judge: Function = (row: number, col: number) => {
            let leftTop = col;
            // 根据不同的行做偏移
            if (row % 2 === 0) {
                leftTop = col - 1;
            }
            // 每个泡泡周围有6个,依次检测
            // 左上
            test(row - 1, leftTop);
            //右上
            test(row - 1, leftTop + 1);
            //左
            test(row, col - 1);
            //右
            test(row, col + 1);
            //左下
            test(row + 1, leftTop);
            //右下
            test(row + 1, leftTop + 1);
        }
        let test: Function = (row: number, col: number) => {
            if (row < 0) return;
            if (col < 0 || col >= 11) return;
            if (row % 2 == 1 && (col < 0 || col >= 10)) { return; }
            if (callback(row, col)) return;
        }
        judge(i, j);
    }

    /**遍历所有小球 */
    private traversalAll(i: number, j: number, callback: Function, isDFS: boolean = false): void {
        if (isDFS) {
            //深搜
            let test: Function = (row: number, col: number) => {
                if (!GameDataCenter.bubbles[row] || !GameDataCenter.bubbles[row][col]) return;
                if (row < 0) return;
                if (col < 0 || col >= 11) return;
                if (row % 2 == 1 && (col < 0 || col >= 10)) { return; }
                if (callback(row, col)) return;
                let leftTop = col;
                if (row % 2 === 0) {
                    leftTop = col - 1;
                }
                // 每个泡泡周围有6个,依次检测
                // 左上
                test(row - 1, leftTop);
                //右上
                test(row - 1, leftTop + 1);
                //左
                test(row, col - 1);
                //右
                test(row, col + 1);
                //左下
                test(row + 1, leftTop);
                //右下
                test(row + 1, leftTop + 1);
            }
            test(i, j);
        }
        else {
            //广搜
            let indexs: cc.Vec2[] = [];
            indexs.push(cc.v2(i, j));
            while (indexs.length != 0) {
                let index = indexs[0];
                indexs.shift();
                this.traversal(index.x, index.y, (row: number, col: number) => {
                    if (!GameDataCenter.bubbles[row] || !GameDataCenter.bubbles[row][col]) return;
                    if (row < 0) return;
                    if (col < 0 || col >= 11) return;
                    if (row % 2 == 1 && (col < 0 || col >= 10)) { return; }
                    if (callback(row, col)) return;
                    indexs.push(cc.v2(row, col));
                })
            }
        }
    }

    /**倒计时回调 */
    private online(): void {
        this.onlineTime--;
        let mm = Math.floor(this.onlineTime / 60);
        let ss = this.onlineTime % 60;
        this.ui.getChildByName('online').getChildByName('time').getComponent(cc.Label).string = `${Utils.formatLen(mm, 2)};${Utils.formatLen(ss, 2)}`;
        let fun: Function = () => {
            this.scheduleOnce(() => {
                if (this.isPress) {
                    fun();
                }
                else {
                    GameDataCenter.tipType = 4;
                    UIManager.open('tip:TipPanel');
                    // this.onOnline();
                }
            }, 1)
        }
        if (this.onlineTime <= 0) {
            this.ui.getChildByName('online').getChildByName('time').active = false;
            this.ui.getChildByName('online').getChildByName('name').getChildByName('label').getComponent(cc.Label).string = `领取`;
            this.ui.getChildByName('online').getChildByName('name').getChildByName('label').x += 14;
            this.ui.getChildByName('online').getChildByName('name').getChildByName('videoIcon').active = true;
            // this.ui.getChildByName('online').getComponent(cc.Button).interactable = true;
            this.unschedule(this.online);
            Utils.shake(this.ui.getChildByName('online').getChildByName('gift'));
            if (GameDataCenter.gameLevel >= 13) {
                fun();
            }
        }
    }

    /**顶板透明 */
    private topTween(op: number) {
        if (op) {
            this.ui.getChildByName('topPanel').opacity = 255;
        }
        else {
            this.ui.getChildByName('topPanel').opacity = 100;
        }
    }

    //创建数字
    private creatLabel(s: string) {
        let node = new cc.Node();
        node.parent = this.node;
        node.color = cc.color('#f5bf61' as any);
        let label = node.addComponent(cc.Label);
        label.string = s;
        label.fontSize = 34;
        label.lineHeight = 34;
        label.enableBold = true;
        this.scheduleOnce(() => { node.destroy() }, 1);
    }
}
