"use strict";
cc._RF.push(module, '4c5c4SRsBdHkbvo2x85R+vo', 'NpcSuper');
// scripts/EVA_npc/NpcSuper.ts

"use strict";
/*
  Npc 们的公共继承类
  决定了普通NPC和特殊NPC的原型
*/
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var AStarManager_1 = require("../AStar/AStarManager");
var ResLoaderManager_1 = require("../Manager/ResLoaderManager");
var NpcAniManager_1 = require("./NpcAniManager");
var _a = cc._decorator, ccclass = _a.ccclass, property = _a.property;
// 定义位置 常量
var Posi;
(function (Posi) {
    Posi[Posi["TOP"] = 0] = "TOP";
    Posi[Posi["RIGHT"] = 1] = "RIGHT";
    Posi[Posi["BOTTOM"] = 2] = "BOTTOM";
    Posi[Posi["LEFT"] = 3] = "LEFT";
})(Posi || (Posi = {}));
var NpcState;
(function (NpcState) {
    NpcState[NpcState["VISIT"] = 0] = "VISIT";
    NpcState[NpcState["WALK"] = 1] = "WALK";
    NpcState[NpcState["FREE"] = 2] = "FREE";
    NpcState[NpcState["LEAVE"] = 3] = "LEAVE";
    NpcState[NpcState["INTERACTIVE"] = 4] = "INTERACTIVE";
})(NpcState = exports.NpcState || (exports.NpcState = {}));
// NPC 的心情常量
var Mood;
(function (Mood) {
    Mood[Mood["AMAZEMENT"] = 0] = "AMAZEMENT";
    Mood[Mood["ANGER"] = 1] = "ANGER";
    Mood[Mood["CHERISH"] = 2] = "CHERISH";
    Mood[Mood["CRY"] = 3] = "CRY";
    Mood[Mood["CRYING"] = 4] = "CRYING";
    Mood[Mood["FIGHTING"] = 5] = "FIGHTING";
    Mood[Mood["LIKE"] = 6] = "LIKE";
    Mood[Mood["QUERY"] = 7] = "QUERY";
    Mood[Mood["SMILE"] = 8] = "SMILE";
    Mood[Mood["SUN_SMILE"] = 9] = "SUN_SMILE";
    Mood[Mood["SWEAT"] = 10] = "SWEAT";
})(Mood = exports.Mood || (exports.Mood = {}));
var NpcSupper = /** @class */ (function (_super) {
    __extends(NpcSupper, _super);
    function NpcSupper() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        /*********特殊属性 决定npc的核心*********/
        // 当前npc在配置表中的ID 方便以后的操作
        _this.npcId = null;
        // 性别
        _this.sex = null;
        // 年龄
        _this.age = null;
        // 口头禅
        _this.phraseList = null;
        // 图集资源地址 每个NPC都有一个独一无二的标识地址, 程序会根据这个标识地址去添加固定字符串然后寻找特定图片
        _this.source = null;
        // npc名称
        _this.npcName = null;
        /*********特殊属性 决定npc的核心*********/
        // 墙的图层 用于做 坐标处理
        _this.wallLayer = null;
        /** 透明坐标图层*/
        _this.moveLayer = null;
        // npc 节点
        _this.npcNode = null;
        // npc 节点上的动画组件
        _this.npcAnim = null;
        // npc节点上的文字节点
        _this.npcStateWrap = null;
        // 描述NPC 状态信息的表情节点
        _this.npcMoodNode = null;
        // 描述NPC 状态信息的表情Sprite组件
        _this.npcMood = null;
        // 资源管理的实例
        _this.sm = null;
        // 移动的下标
        _this.moveIndex = null;
        // 移动的数组
        _this.moveList = null;
        // 当前npc所在的 x，y坐标
        _this.coordinate = null;
        // 外界要求前往的目标点
        _this.targetCoordinate = null;
        // 是否在参观展品还是在行走, 需要一个状态 
        _this.state = null;
        // npc 当前移动的方向
        _this.movePosi = null;
        // 已经参观过多少个展品
        _this.visited = null;
        // 期待参观数 当已参观数量大于等于 期待参观数时，就通知外界， 这个npc想要离开
        _this.visit = null;
        // 获取A* 管理器的实例
        _this.aStarInstance = null;
        // npc 的移动速度
        _this.ispeed = null;
        // npc 的移动动画执行速度
        _this.walkIspeed = null;
        // 所有NPC的动画配置对象
        _this.npcAnimConfig = null;
        // npc的标志
        _this.sign = null;
        return _this;
        // start() { }
        // update() { }
    }
    // 初始化数据
    NpcSupper.prototype.init = function () {
        // 获取表情节点
        this.npcMoodNode = this.node.getChildByName('look');
        // 获取表情节点的 Sprite 组件
        this.npcMood = this.npcMoodNode.getChildByName('icon').getComponent(cc.Sprite);
        // 获取文字容器
        this.npcStateWrap = this.node.getChildByName('stateWrap');
        // 初始化npc节点
        this.npcNode = cc.find('img_npc', this.node);
        // 获取动画组件
        this.npcAnim = this.npcNode.getComponent(cc.Animation);
        // 初始化npc的速度 最快0.5 - 1 保留2位有限小数
        var offset = +(Math.random() * 0.3 + 0.2).toFixed(2); // 一点小偏移
        this.ispeed = 1 - offset; // 这个是移动一段距离的时间
        // npc 的移动动画执行速度
        this.walkIspeed = 1 + offset;
        // 获取墙的图层
        var map = cc.find('Canvas/Baseview/obj_map/map100').getComponent(cc.TiledMap);
        this.wallLayer = map.getLayer('wall');
        this.moveLayer = map.getLayer('move');
        // 初始化已参观数
        this.visited = 0;
        // 初始化期待参观数
        this.visit = Number.MAX_VALUE;
        // 初始化A*实例
        this.aStarInstance = AStarManager_1.default.getInstance();
        // 初始化资源管理的实例
        var sm = this.sm = ResLoaderManager_1.default.getInstance();
        // 读取配置表
        this.npcAnimConfig = sm.getConfig('animation');
    };
    // 更改当前移动方向
    NpcSupper.prototype.changeMovePosi = function (state) {
        this.movePosi = state;
    };
    // 更改当前NPC状态
    NpcSupper.prototype.changeState = function (state) {
        this.state = state;
    };
    // 转向 将原先4个函数 合并成一个函数
    NpcSupper.prototype.turnACorner = function (direction, scaleX, animName) {
        // 避免重复触发
        if (this.movePosi === direction)
            return;
        // 改变行进方向
        this.changeMovePosi(direction);
        // 镜像翻转节点
        this.npcNode.scaleX = scaleX;
        // 播放动画
        this.npcAnim.play(animName);
    };
    // 从当前点, 移动到某一点
    NpcSupper.prototype.moveTo = function (target) {
        var _this = this;
        // 当前位置信息
        var coordinate = this.coordinate;
        // 坐标点比对
        if (coordinate.x - target.x > 0) {
            // 向左
            this.turnACorner(Posi.LEFT, 1, 'npc1_rear');
        }
        else if (coordinate.x - target.x < 0) {
            // 向右
            this.turnACorner(Posi.RIGHT, -1, 'npc1_front');
        }
        else if (coordinate.y - target.y > 0) { // 因为当前程序不走斜角， 所以可以连着esle
            // 向前
            this.turnACorner(Posi.TOP, -1, 'npc1_rear');
        }
        else if (coordinate.y - target.y < 0) {
            // 向后
            this.turnACorner(Posi.BOTTOM, 1, 'npc1_front');
        }
        // 切换坐标
        this.coordinate = target;
        // 判断是否透明点
        if (this.node.opacity !== 0) {
            this.moveLayer.getTileGIDAt(target.x, target.y) != 0 ? this.node.opacity = 127.5 : this.node.opacity = 255;
        }
        // 计算坐标位置
        var v2 = this.correctCoordinate(target);
        // 前往
        cc.tween(this.node)
            .to(this.ispeed, { position: v2 })
            .call(function () {
            // 因为需要多次访问 提前存下
            var len = _this.moveList.length;
            // 移动未结束
            if (_this.moveIndex < len - 1) {
                // 可以进行下一次移动
                _this.moveTo(_this.moveList[++_this.moveIndex]);
            }
            else {
                // 移动结束 进入分支 开始执行不同的程序
                _this.shunt();
            }
            // 在这里处理 当moveIndex 移动到倒数第二个格子的时候, 同时是离开的时候淡出
            if (_this.moveIndex === len - 2 && _this.visit === _this.visited) {
                // 淡出
                _this.fadeOut();
            }
        })
            .start();
    };
    // 由管理器决定终点位置
    NpcSupper.prototype.go = function (posi) {
        // 每次管理器调用这个函数时, 不管当前终点有无抵达, 一样切换行进坐标点 同时更改数组下标
        var moveList = this.aStarInstance.findPath(this.coordinate, posi);
        // 容错判断 如果没有查找到可行路径
        if (moveList === null) {
            // 先退出当前寻路
            // 丢弃当前点，直接进入下一次
            return false;
        }
        this.moveList = moveList;
        // 下标归零
        this.moveIndex = 0;
        // 决定目标点
        this.targetCoordinate = posi;
        // 如果npc 当前还在行走中, 你就切换终点, 那么就直接return 程序会自己接上去的
        if (this.state != NpcState.WALK) {
            // 迭代器启动
            this.moveTo(this.moveList[++this.moveIndex]);
        }
        // npc 状态进入 行走
        this.changeState(NpcState.WALK);
        // 路径可行
        return true;
    };
    // 分流 当moveTo抵达终点后, 有三种情况需要处理
    NpcSupper.prototype.shunt = function () {
        // 1 抵达是展台 那么进入参观阶段
        // 2 抵达的是闲逛点 那么进入等待阶段
        // 判断终点是否是展台并且没有参观完成 -1 是因为如果函数进入这里
        if (this.targetCoordinate.direction && this.visited < this.visit) {
            // npc需要去参观当前展台
            this.lookAround();
            // 是否未抵达参观数量要求
            if (this.visited < this.visit) {
                // 这个函数中有延迟器 逗留一段时间后 通知外界 npc需要前往下一个参观点
                this.delayGoNext(this.produce); // 传入的参数是一个函数， 这个函数是用于制作产出
            }
            else if (this.visited === this.visit) {
                // 该离开这里令人怀念的地方了
                this.leval();
            }
        }
        else if (!this.targetCoordinate.direction && this.visited < this.visit) {
            // 当前npc没有参观完成展台, 并且终点不是站台位置 说明处于闲逛中
            // 等待
            this.wait();
            // 我要前往下一个参观点
            this.delayGoNext();
        }
        else if (this.visited === this.visit) { // 因为每当npc移动到一个点后都会触发这个函数
            // 销毁阶段 
            this.node.removeFromParent();
        }
    };
    // NPC 处于等待状态 这只是个动画的切换, 它会根据你当前的方位执行等待动画
    NpcSupper.prototype.wait = function () {
        // 切换NPC状态
        this.changeState(NpcState.FREE);
        // 移动结束 移动方向置为空
        this.movePosi = null;
        Math.random() > 0.5 ? this.npcAnim.play('stand_b') : this.npcAnim.play('action_b');
        // 镜像翻转
        this.npcNode.scaleX = 1;
    };
    // 参观
    NpcSupper.prototype.lookAround = function () {
        // 更改npc状态进入参观
        this.changeState(NpcState.VISIT);
        // 根据当前展台朝向 定义人物位置 依据的是展台朝向
        switch (this.targetCoordinate.direction) {
            case 'top':
                Math.random() > 0.5 ? this.npcAnim.play('stand_a') : this.npcAnim.play('action_a');
                // 镜像翻转
                this.npcNode.scaleX = -1;
                break;
            case 'right':
                Math.random() > 0.5 ? this.npcAnim.play('stand_b') : this.npcAnim.play('action_b');
                // 镜像翻转
                this.npcNode.scaleX = -1;
                break;
            case 'bottom':
                Math.random() > 0.5 ? this.npcAnim.play('stand_b') : this.npcAnim.play('action_b');
                // 镜像翻转
                this.npcNode.scaleX = 1;
                break;
            case 'left':
                Math.random() > 0.5 ? this.npcAnim.play('stand_a') : this.npcAnim.play('action_a');
                // 镜像翻转
                this.npcNode.scaleX = 1;
                break;
        }
        // 移动结束 移动方向置为空
        this.movePosi = null;
        // 参观完成 参观数 ++
        this.visited++;
    };
    // 淡入
    NpcSupper.prototype.fadeIn = function (cb) {
        if (cb === void 0) { cb = function () { }; }
        // 新版API
        cc.tween(this.node)
            .to(.5, { opacity: 255 })
            .call(cb.bind(this))
            .start();
    };
    // 淡出
    NpcSupper.prototype.fadeOut = function (cb) {
        if (cb === void 0) { cb = function () { }; }
        // 
        cc.tween(this.node)
            .to(.5, { opacity: 0 })
            .call(cb.bind(this))
            .start();
    };
    // 离场
    NpcSupper.prototype.leval = function () {
        var _this = this;
        // 通知外界 我要离开
        this.scheduleOnce(function () {
            // 当参观展品数达到数量 那么就直接告诉外界, 我要离开
            var event = new cc.Event.EventCustom('leave', false);
            // 设置参数
            event.setUserData(_this);
            // 发送事件
            cc.systemEvent.dispatchEvent(event);
            // 最后一次金币生成
            _this.produce();
            // 更改状态
            _this.changeState(NpcState.LEAVE);
        }, 3);
    };
    // 对goNext进行一个延迟
    NpcSupper.prototype.delayGoNext = function (func) {
        var _this = this;
        // 延迟一段时间
        this.scheduleOnce(function () {
            // goNext 执行
            _this.goNext(func);
        }, Math.random() * 5 + 5);
        // 停留时间是 最低5秒 最长9.9秒 Math.random() * 5 + 5
    };
    // 前往下一个逗留点
    NpcSupper.prototype.goNext = function (func) {
        // 这个一般是等npc离开后产出金币用的
        func && func.call(this);
        // 参观一段时间通知管理， 当前npc 希望前往另一个参观点
        var event = new cc.Event.EventCustom('visitfinish', false);
        // 设置参数
        event.setUserData(this);
        // 发射
        cc.systemEvent.dispatchEvent(event);
    };
    // 计算并修正坐标
    NpcSupper.prototype.correctCoordinate = function (_a) {
        var x = _a.x, y = _a.y;
        var _b;
        (_b = this.wallLayer.getPositionAt(x, y), x = _b.x, y = _b.y);
        // 因为 坐标转换有些偏差， 需要矫正这些偏差.0
        return cc.v2(x + 32, y + 16);
    };
    // 根据管理器要求 设置出生点
    NpcSupper.prototype.setBirthPoint = function (posi) {
        // 赋值当前点坐标
        this.coordinate = posi;
        // 计算坐标 并赋值
        this.node.setPosition(this.correctCoordinate(posi));
    };
    // 外界调用 注入模板 暂时不要进行内部自己随机
    NpcSupper.prototype.soulInit = function (config) {
        var _this = this;
        // 需要初始化的信息包括 npc的外观，npc的口头禅， npc的年龄， npc的性别等类人信息
        this.phraseList = config.visitor_phrase; // 口头禅 是数组， 从中随机
        this.source = config.visitor_resources; // npc图集地址
        // 避免重复访问
        var npcAnim = this.npcAnim;
        var animId = config.animation_id;
        // 查询一次 如果一个没有，说明其他也没有
        var walkA1 = NpcAniManager_1.default.getAnimClip(animId, 'npc1_front');
        if (walkA1) { // 存在的情况
            // 更改动画速度
            var walkB1 = NpcAniManager_1.default.getAnimClip(animId, 'npc1_rear');
            walkA1.speed = this.walkIspeed;
            walkB1.speed = this.walkIspeed;
            // 添加动画帧
            npcAnim.addClip(walkA1);
            npcAnim.addClip(walkB1);
            npcAnim.addClip(NpcAniManager_1.default.getAnimClip(animId, 'stand_a'));
            npcAnim.addClip(NpcAniManager_1.default.getAnimClip(animId, 'stand_b'));
            npcAnim.addClip(NpcAniManager_1.default.getAnimClip(animId, 'action_a'));
            npcAnim.addClip(NpcAniManager_1.default.getAnimClip(animId, 'action_b'));
        }
        else { // 没有的情况
            // 拿到对应的动画配置表
            var animConfig = this.npcAnimConfig[animId];
            // 动作集
            var walkA = animConfig.walk_a, walkB = animConfig.walk_b, standA = animConfig.stand_b_1, standB = animConfig.stand_a_1, actionA = animConfig.stand_b_2, actionB = animConfig.stand_a_2;
            // 获取图集资源 TEST
            var atlas = this.sm.getSpriteAtlasByName(this.source.split('/')[1]);
            // 创建动画帧并添加
            npcAnim.addClip(NpcAniManager_1.default.createClip(animId, NpcAniManager_1.default.createFrames(walkA, atlas), 'npc1_front', this.walkIspeed));
            npcAnim.addClip(NpcAniManager_1.default.createClip(animId, NpcAniManager_1.default.createFrames(walkB, atlas), 'npc1_rear', this.walkIspeed));
            npcAnim.addClip(NpcAniManager_1.default.createClip(animId, NpcAniManager_1.default.createFrames(standA, atlas), 'stand_a', animConfig.stand_a_1_gap));
            npcAnim.addClip(NpcAniManager_1.default.createClip(animId, NpcAniManager_1.default.createFrames(standB, atlas), 'stand_b', animConfig.stand_a_1_gap));
            npcAnim.addClip(NpcAniManager_1.default.createClip(animId, NpcAniManager_1.default.createFrames(actionA, atlas), 'action_a', animConfig.stand_a_2_gap));
            npcAnim.addClip(NpcAniManager_1.default.createClip(animId, NpcAniManager_1.default.createFrames(actionB, atlas), 'action_b', animConfig.stand_a_2_gap));
        }
        // UI 渲染 过1秒钟左右后再获取最新的高度
        this.scheduleOnce(function () {
            _this.initEnd();
        }, 1);
    };
    // 当所有同步和异步操作都执行完成 执行这里的代码， 一般都是UI处理
    NpcSupper.prototype.initEnd = function () {
        // 处理节点高度问题 每个NPC高度都不一致 
        this.npcMoodNode.y = this.npcNode.height + 5;
        this.npcStateWrap.y = this.npcNode.height + 10;
    };
    // 产出人气
    NpcSupper.prototype.produce = function () {
    };
    NpcSupper.prototype.onLoad = function () {
        // 设置当前节点透明
        this.node.opacity = 0;
        // 初始化中
        this.init();
        // init 完成后 执行淡入
        this.fadeIn();
    };
    __decorate([
        property(cc.TiledLayer)
    ], NpcSupper.prototype, "wallLayer", void 0);
    __decorate([
        property(cc.TiledLayer)
    ], NpcSupper.prototype, "moveLayer", void 0);
    __decorate([
        property(cc.Node)
    ], NpcSupper.prototype, "npcNode", void 0);
    __decorate([
        property(cc.Animation)
    ], NpcSupper.prototype, "npcAnim", void 0);
    __decorate([
        property(cc.Node)
    ], NpcSupper.prototype, "npcStateWrap", void 0);
    __decorate([
        property(cc.Node)
    ], NpcSupper.prototype, "npcMoodNode", void 0);
    __decorate([
        property(cc.Sprite)
    ], NpcSupper.prototype, "npcMood", void 0);
    NpcSupper = __decorate([
        ccclass
    ], NpcSupper);
    return NpcSupper;
}(cc.Component));
exports.default = NpcSupper;
/*
  A* 优化
  寻找离终点最近的中转点
  判断是否离npc过远， 如果过远，再以当前中转点为距离
  寻找离当前点最近的中转点

  13.2 14.25 10.9 15.9 14.7
  68.95 / 5
  13.790000000000001

  18.0 20.1 20.6 16.5 17.8
  93 / 5
  18.6

  18.5 16.4 20.4 19.6 20.0
  94.9 / 5
  18.98

  对话处理
    相同特点 -> 框会根据字体动态拉伸大小， 而且是动画形式

    如何确定框的大小

    大声说话 -> 字体变大
    长句陈述 ->
    内心想法

*/ 

cc._RF.pop();