local Lang = require("common.Lang")
local BattleExcel = require("excel.battle")
local MonsterExcel = require("excel.monster")
local DropExcel = require("excel.drop")
local Msg = require("core.Msg")
local ObjHuman = require("core.ObjHuman")
local Grid = require("bag.Grid")
local BagLogic = require("bag.BagLogic")
local ItemDefine = require("bag.ItemDefine")
local Broadcast = require("broadcast.Broadcast")
local CombatDefine = require("combat.CombatDefine")
local CombatPosLogic = require("combat.CombatPosLogic")
local CombatLogic = require("combat.CombatLogic")
local DailyTaskLogic = require("dailyTask.DailyTaskLogic")
local RoleDefine = require("role.RoleDefine")
local ChengjiuLogic = require("chengjiu.ChengjiuLogic")
local ChengjiuDefine = require("chengjiu.ChengjiuDefine")
local HeroExcel = require("excel.hero")
local SkillExcel = require("excel.skill")
local MonthactExcel = require("excel.monthAct")
local Util = require("common.Util")
local KingWorldLogic = require("present.KingWorldLogic")
local ChatPaoMaLogic = require("chat.ChatPaoMaLogic")
local BRoleLogic = require("billboard.BRoleLogic")
local BillboardDefine = require("billboard.BillboardDefine")
local SkinLogic = require("skin.SkinLogic")
local RoleSystemLogic = require("roleSystem.RoleSystemLogic")
local RoleSystemDefine = require("roleSystem.RoleSystemDefine")
local GuideLogic = require("guide.GuideLogic")
local PanelDefine = require("broadcast.PanelDefine")
local HeroGrid = require("hero.HeroGrid")
local TuiSongLiBao = require("present.TuiSongLiBao")
local CombatVideo = require("combat.CombatVideo")
local RoleLogic = require("role.RoleLogic")
local BattleDBLogic = require("battle.BattleDBLogic")
local ProjectLogic = require("platform.ProjectLogic")
local LuaMongo = _G.lua_mongo
local DB = require("common.DB")
local VipLogic = require("vip.VipLogic")
local MoshouLogic = require("moshou.MoshouLogic")
local RoleExcel = require("excel.role")
local TequanShopLogic = require("present.TequanShopLogic")
local HeroDefine = require("hero.HeroDefine")
local BarTaskLogic = require("bar.BarTaskLogic")
local ItemExcel = require("excel.item").item
local EquipExcel = require("excel.equip").equip
local HeroLogic = require("hero.HeroLogic")
local SuipianLogic = require("bag.SuipianLogic")
local JibanLogic = require("combat.JibanLogic")
local YunYingLogic = require("yunying.YunYingLogic")
local PanelDefine = require("broadcast.PanelDefine")
local YyHandler = require("yunying.Handler")
local EquipLogic = require("equip.EquipLogic")
local SysParameter = require("common.SysParameter")
local XingYaoGongMing = require("xingYaoMen.XingYaoGongMing")
local AbsActLogic = require("absAct.AbsActLogic")
local HeroLogLogic = require("absAct.HeroLogLogic")
local DropExchangeLogic = require("absAct.DropExchangeLogic")
local MengxinLogic = require("present.MengxinLogic")
local Log = require("common.Log")

BATTLE_HANG_EXP_OUT_PERIOD = 60 -- 经验产出时间间隔
BATTLE_HANG_ITEM_OUT_PERIOD = 300 -- 战利品道具产出时间间隔
BATTLE_HANG_ITEM_OUT_PERIOD_2 = 7200 -- 战利品道具产出时间间隔2小时掉落一次

BATTLE_LOGIN_LEV_TIPS = 50 -- 登录检测挂机时间过久提示 最高等级
BATTLE_LOGIN_TIME_TIPS = 10 * 60 -- 登录检测挂机超过多久提示

BATTLE_MOPUP_CNT = 4
BATTLE_MOPUP_CNT_VIP = 11

BATTLE_MOPUP_FREE_CNT = 1
BATTLE_MOPUP_FREE_CNT_VIP = 3

BATTLE_LEVEL_PLAYER_LIST = { }

local STATUS_NONE = 1 -- 不可领
local STATUS_CANGET = 2 -- 可领

-- 挂机收益最多累计x秒
function getHangMaxTime(human)
    local hour = VipLogic.getPowerArgs(human, VipLogic.VIP_POWER10)
    return(hour or 0) * 3600
end

local function wrapHangFightNet(net, heroConfig, skillConfig)
    net.body = heroConfig.body
    net.height = heroConfig.height
    net.weight = heroConfig.width
    net.fireType = skillConfig and skillConfig.fireType or 0
    net.attackEffect = skillConfig and skillConfig.attackEffect or ""
    net.flyEffect = skillConfig and skillConfig.flyEffect or ""
    net.flyCoords = skillConfig and skillConfig.flyCoords or ""
    net.hitEffect = skillConfig and skillConfig.hitEffect or ""
    net.hitSound = skillConfig and skillConfig.hitSound or ""
end

local function fontBattleSkillNet(skillNet, skillID)
    local skillConfig = SkillExcel.skill[skillID]
    if not skillConfig then
        return
    end
    skillNet.skillID = skillID
    skillNet.fireType = skillConfig.fireType
    skillNet.readyAction = skillConfig.readyAction
    skillNet.attackPart = skillConfig.attackPart
    skillNet.attackAction = skillConfig.attackAction
    skillNet.readyEffect = skillConfig.readyEffect
    skillNet.attackEffect = skillConfig.attackEffect
    skillNet.hitEffect = skillConfig.hitEffect
    skillNet.flyEffect = skillConfig.flyEffect
    skillNet.flyCoords = skillConfig.flyCoords
    skillNet.hitBack = skillConfig.hitBack
    skillNet.effectTime = skillConfig.effectTime
    skillNet.contentType = skillConfig.contentType
    skillNet.content = skillConfig.content
    skillNet.movieEffect = skillConfig.movieEffect
    skillNet.quake = skillConfig.quake
    skillNet.flySound = skillConfig.flySound
    skillNet.fireSound = skillConfig.fireSound
    skillNet.hitSound = skillConfig.hitSound
    skillNet.cvSound = skillConfig.cvSound
    skillNet.screenMask = skillConfig.screenMask
    skillNet.screenCam = skillConfig.screenCam
    skillNet.largeEffect = skillConfig.largeEffect
    skillNet.lie = skillConfig.lie
    skillNet.cuoZhen = skillConfig.cuoZhen
    skillNet.skillDelay = skillConfig.skillDelay
    skillNet.isFraming = skillConfig.isFraming
    return true
end

function getBattleRoleList(worldMapId)
    BATTLE_LEVEL_PLAYER_LIST[worldMapId] = BATTLE_LEVEL_PLAYER_LIST[worldMapId] or { }
    local config = BattleExcel.map[worldMapId]
    QueryRoleByNodeID = { guajiID = { ["$gte"] = config.minLevel, ["$lte"] = config.maxLevel } }
    local fields = { }
    RoleLogic.makeRoleBaseFields(fields)
    LuaMongo.find(DB.db_char, QueryRoleByNodeID, fields, 50)
    local len = 0
    while true do
        local charDb = { }
        if not LuaMongo.next(charDb) then
            break
        end
        len = len + 1
        BATTLE_LEVEL_PLAYER_LIST[worldMapId][charDb._id] = { }
        RoleLogic.makeRoleBase(charDb, BATTLE_LEVEL_PLAYER_LIST[worldMapId][charDb._id])
        if len >= 50 then
            return
        end
    end
end

function initAfterStart()
    local len = #BattleExcel.map
    for i = 1, len do
        getBattleRoleList(i)
    end
end

function onLogin(human)
    if human.db.lv >= BATTLE_LOGIN_LEV_TIPS then
        return
    end
    calcBattleOut(human)
    local now = os.time()
    local expTime = now - human.db.battleOut.expTs1
    if expTime >= BATTLE_LOGIN_TIME_TIPS then
        local msgRet = Msg.gc.GC_BATTLE_HANG_TIME_SPILL
        msgRet.time = expTime
        Msg.send(msgRet, human.fd)
    end
end

-- 是否领取
local WeiLen = 30
function isGetReward(human, id)
    if not human.db.battleRewards then
        return
    end

    local flags = human.db.battleRewards
    local intIndex = math.ceil(id / WeiLen)
    if not flags[intIndex] then
        return
    end
    local byteIndex = id % WeiLen
    if Util.getBit(flags[intIndex], byteIndex) > 0 then
        return true
    end
end

function getTongguanIndex(human)
    if not human.db.battleRewards then
        return 1
    end
    local flags = human.db.battleRewards
    for id, v in ipairs(BattleExcel.node) do
        if #v.tongguan ~= 0 then
            local intIndex = math.ceil(id / WeiLen)
            if not flags[intIndex] then
                return id
            end
            local byteIndex = id % WeiLen
            if Util.getBit(flags[intIndex], byteIndex) == 0 then
                return id
            end
        end
    end
    return
end

-- 设置领取
function setGetReward(human, id)
    if not human.db.battleRewards then
        human.db.battleRewards = { }
    end
    local flags = human.db.battleRewards
    local intIndex = math.ceil(id / WeiLen)
    local byteIndex = id % WeiLen
    flags[intIndex] = flags[intIndex] or 0
    flags[intIndex] = Util.setBit(flags[intIndex], byteIndex)
end

-- 是否有可领取的
function hasCanGetReward(human)
    for id, cf in ipairs(BattleExcel.node) do
        if id > human.db.guajiID then
            break
        end
        if #cf.tongguan > 0 and not isGetReward(human, id) then
            return true
        end
    end
end

local BATTLEID_2_NAME = nil
function getBattleName(battleID)
    if not BATTLEID_2_NAME then
        BATTLEID_2_NAME = { }
        for id, cf in ipairs(BattleExcel.node) do
            BATTLEID_2_NAME[id] = cf.name
        end
    end
    local nodeConfig = BattleExcel.node[battleID]
    local mapConfig = nodeConfig and BattleExcel.map[nodeConfig.mapID]
    local mapName = mapConfig and mapConfig.name or ""
    return BATTLEID_2_NAME[battleID] or "", mapName
end

function hangFightQuery(human)
    local battleID = human.db.battleID > #BattleExcel.node and #BattleExcel.node or human.db.battleID
    local nowNodeConfig = BattleExcel.node[battleID]
    local monsterOutID = nowNodeConfig.monsterOutID
    local monsterOutConfig = MonsterExcel.monsterOut[monsterOutID]
    local msgRet = Msg.gc.GC_BATTLE_HANG_FIGHT
    local len = 0
    for _, monster in ipairs(monsterOutConfig.member) do
        local monsterID = monster[1]
        len = len + 1
        local monsterConfig = MonsterExcel.monster[monsterID]
        local skillConfig = SkillExcel.skill[monsterConfig.normalAtkID]
        wrapHangFightNet(msgRet.monsterList[len], monsterConfig, skillConfig)
    end
    msgRet.monsterList[0] = len
    len = 0
    if human.db.combatHero[CombatDefine.COMBAT_TYPE1] then
        local tb = { }
        for i = 1, #human.db.combatHero[CombatDefine.COMBAT_TYPE1] do
            tb[#tb + 1] = human.db.combatHero[CombatDefine.COMBAT_TYPE1][i]
        end
        local tb2 = { }
        for i = 1, 3 do
            local r = math.ceil(math.random(0, #tb))
            r = r == 0 and 1 or r
            tb2[#tb2 + 1] = tb[r]
            tb[r] = tb[#tb]
            tb[#tb] = nil
            if not tb[1] then
                break
            end
        end
        for i = 1, #tb2 do
            len = len + 1
            local heroGrid = HeroLogic.getHeroGridByUuid(human, tb2[i])
            if heroGrid then
                local heroConfig = HeroExcel.hero[heroGrid.id]
                local _, skinSkillConf = SkinLogic.getHeroSkin(human, tb2[i])
                local skillConfig = SkillExcel.skill[heroConfig.normalAtkID]
                if skinSkillConf then
                    skillConfig = SkillExcel.skill[skinSkillConf.normalAtkID]
                end
                wrapHangFightNet(msgRet.attackerList[i], heroConfig, skillConfig)
                local body, head = SkinLogic.getBody(human, tb2[i])
                if body then
                    msgRet.attackerList[i].body = body
                end
            end
        end
    end
    msgRet.attackerList[0] = len
    -- Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

local function getExtraRewardID(human)
    local config = BattleExcel.extraReward
    local battleExtraReward = human.db.battleExtraReward
    for id, data in ipairs(config) do
        if not battleExtraReward or not battleExtraReward[id] then
            return id, data
        end
    end
end

local function getExtraRewardState(human, extraRewardID)
    local config = BattleExcel.extraReward[extraRewardID]
    if not config then
        return 0
    end

    if human.db.battleExtraReward and human.db.battleExtraReward[extraRewardID] then
        return 0
    end

    local guajiID = human.db.guajiID
    if guajiID >= config.needBattleID then
        return 1
    end
    return 0
end

local function makeExtraRewardShow(human, net)
    local config = BattleExcel.extraReward
    local extraID, data = getExtraRewardID(human)
    if not extraID then
        return
    end

    net[0] = 1
    net[1].needBattleID = data.needBattleID
    net[1].descID = data.descID
    net[1].desc = data.desc or ""
    net[1].extraHero[0] = 0
    net[1].extraItem[0] = 0
    if data.extraHeroID > 0 then
        net[1].extraHero[0] = 1
        HeroGrid.makeHeroSimpleByID(net[1].extraHero[1], data.extraHeroID, nil, nil, human)
        HeroGrid.makeHeroSimpleByGeneral(net[1].extraHero[1], data.extraHeroID)
    else
        net[1].extraItem[0] = 1
        Grid.makeItem(net[1].extraItem[1], data.extraReward[1], data.extraReward[2])
    end

    net[1].state = getExtraRewardState(human, extraID)
end

-- 随机道具
local function randItemOut(dropRule, weightSum)
    if weightSum < 1 then
        return
    end
    local r = math.random(1, weightSum)
    for i = 1, #dropRule do
        local itemID = dropRule[i][1]
        local itemCnt1 = dropRule[i][2]
        local itemCnt2 = dropRule[i][3]
        local weight = dropRule[i][4]
        if r <= weight then
            local itemCnt = math.random(itemCnt1, itemCnt2)
            return itemID, itemCnt
        end
        r = r - weight
    end
end

function fontExtraDataSSZH(net, mapID, nowBattleID)
    local nodeCnt = 0
    if ProjectLogic.isSszh() then
        for i = 1, #BattleExcel.node do
            local nodeTempConfig = BattleExcel.node[i]
            if nodeTempConfig.mapID == mapID then
                nodeCnt = nodeCnt + 1
                local posNet = net.posList[nodeCnt]
                local pos = nodeTempConfig.pos
                posNet.battleID = i
                posNet.posX = pos and pos[1] or 0
                posNet.posY = pos and pos[2] or 0
                posNet.nodeName = getBattleName(i)
                posNet.roleBase[0] = 0
                if i == nowBattleID then
                    local videoTb = BattleDBLogic.queryBattleDbByNodeID(i)
                    local sharkData = videoTb and videoTb.shark[2]
                    if sharkData then
                        posNet.roleBase[0] = 1
                        RoleLogic.makeRoleBase(videoTb.roleBase, posNet.roleBase[1])
                    end
                end
                posNet.isBig = nodeTempConfig.isBig or 0
            end
        end
    end
    net.posList[0] = nodeCnt
end

function fontVideoInfo(human, net, monsterConfig)
    local combatHero = CombatPosLogic.getCombatHeros(human, CombatDefine.COMBAT_TYPE1)
    local len = #combatHero
    -- 自己没队伍时，不显示
    if len == 0 then
        net.ownBody[0] = 0
        net.enemyBody[0] = 0
    else



        -- 设置真实数据
        local cnt = 0
        for k, v in pairs(combatHero) do
            -- body数据
            if v ~= "0" then
                local heroGird, bagIndex = HeroLogic.getHeroGridByUuid(human, v)
                if heroGird ~= nil then
                    cnt = cnt + 1
                    local config = HeroExcel.hero[heroGird.id]
                    if config then
                        local attrConfig = HeroDefine.getAttrConfig(heroGird.id, config.star)
                        local normalAtkID = attrConfig.normalAtkID
                        local skill = attrConfig.battleSkill

                        local body, head = SkinLogic.getBody(human, bagIndex)

                        net.ownBody[cnt].body = body or config.body
                        fontBattleSkillNet(net.ownBody[cnt].atkId, normalAtkID)
                        fontBattleSkillNet(net.ownBody[cnt].skill, skill)
                        net.ownBody[cnt].pos = k
                        net.ownBody[cnt].atkType = SkillExcel.skill[normalAtkID].areaPara

                        local attrs = ObjHuman.getHeroAttrs(human, heroGird.bagIndex)
                        net.ownBody[cnt].hp = attrs[RoleDefine.HP]
                    end
                end
            end
        end
        net.ownBody[0] = cnt

        cnt = 0
        local memberConfig = monsterConfig.member
        for k, v in pairs(memberConfig) do
            -- body数据
            cnt = cnt + 1
            local config = MonsterExcel.monster[v[1]]
            if config then
                local normalAtkID = config.normalAtkID

                net.enemyBody[cnt].body = config.body
                fontBattleSkillNet(net.enemyBody[cnt].atkId, normalAtkID)
                fontBattleSkillNet(net.enemyBody[cnt].skill, normalAtkID)
                net.enemyBody[cnt].pos = k
                net.enemyBody[cnt].atkType = SkillExcel.skill[normalAtkID].areaPara

                local attrID = monsterConfig.attrID[k]
                local attrConfig = MonsterExcel.monsterAttr[attrID].attrs
                for _, value in pairs(attrConfig) do
                    if value[1] == RoleDefine.HP then
                        net.enemyBody[cnt].hp = value[2]
                        break
                    end
                end
            end
        end
        net.enemyBody[0] = cnt
    end
end

-- 战役主界面查询
function query(human)
    local msgRet = Msg.gc.GC_BATTLE_QUERY

    msgRet.nowBattleID = human.db.battleID
    local configNode = #BattleExcel.node
    local nowBattleID = human.db.battleID > configNode and configNode or human.db.battleID

    local nowNodeConfig = BattleExcel.node[nowBattleID]
    msgRet.maxBattleID = configNode
    msgRet.mapID = nowNodeConfig.mapID
    msgRet.nodeID = nowNodeConfig.curLevel
    msgRet.sceneID = nowNodeConfig.sceneID
    local nowMapConfig = BattleExcel.map[nowNodeConfig.mapID]

    msgRet.mapName = nowMapConfig.name
    msgRet.nodeName = nowNodeConfig.name
    msgRet.needLv = nowNodeConfig.needLv
    msgRet.bg = nowMapConfig.bg
    msgRet.canBattle = 0
    msgRet.doubleCnt = 0
    if nowNodeConfig.needLv > human.db.lv then
        msgRet.canBattle = nowNodeConfig.needLv
    end

    local double = RoleSystemLogic.isDouble(human, RoleSystemDefine.ROLE_SYS_ID_1104)
    if
        double and human.db.bar and human.db.bar.doubleCnt and
        human.db.bar.doubleCnt <= BarTaskLogic.DEFAULT_MAX_DOUBLE_CNT
    then
        msgRet.doubleCnt = BarTaskLogic.DEFAULT_MAX_DOUBLE_CNT - human.db.bar.doubleCnt
    elseif double and not human.db.bar then
        msgRet.doubleCnt = BarTaskLogic.DEFAULT_MAX_DOUBLE_CNT
    end

    msgRet.tongguan[0] = 0
    local id = getTongguanIndex(human)
    if id == nil then
        msgRet.tongguan[0] = 0
    else
        msgRet.tongguan[0] = 1
        local state = isGetReward(human, id)
        local cf = BattleExcel.node[id].tongguan
        local net = msgRet.tongguan[1]
        if state == true then
            net.status = STATUS_NONE
        else
            net.status =(id > human.db.guajiID) and STATUS_CANGET or STATUS_NONE
        end
        net.levelName = getBattleName(id)
        net.index = id
        net.nowBattle = getBattleName(human.db.guajiID)
        net.reward[0] = 0
        net.heroReward[0] = 0
        -- 是英雄
        if cf[1][3] then
            net.heroReward[0] = 1
            local other = { }
            other.star = cf[1][3]
            HeroGrid.makeHeroSimpleByID(net.heroReward[1], cf[1][1], nil, other, human)
            HeroGrid.makeHeroSimpleByGeneral(net.heroReward[1], cf[1][1])
        else
            net.reward[0] = 1
            for i = 1, net.reward[0] do
                local itemID = cf[i][1]
                local itemCnt = cf[i][2]
                Grid.makeItem(net.reward[i], itemID, itemCnt)
            end
        end
    end

    local outSec = 0
    if human.db.battleOut ~= nil then
        local now = os.time()
        outSec = now - human.db.battleOut.expTs1
    end

    local maxHangTime = getHangMaxTime(human)
    msgRet.maxTime = maxHangTime
    if maxHangTime >= outSec then
        msgRet.time = outSec
    else
        msgRet.time = maxHangTime
    end

    -- 悬赏情报数量
    msgRet.xushang = human.db.bar and human.db.bar.qingbao or 0
    msgRet.xushangMax = RoleExcel.exp[human.db.lv].qingBaoMax
    -- 悬赏红点
    msgRet.xsDot = 0
    if BarTaskLogic.isDot(human) then
        msgRet.xsDot = 1
    end

    msgRet.canMopup = 0
    if TequanShopLogic.isActiveMopup(human) then
        msgRet.canMopup = human.db.mopupDoCnt < BATTLE_MOPUP_CNT_VIP and 1 or 0
    else
        msgRet.canMopup = human.db.mopupDoCnt < BATTLE_MOPUP_CNT and 1 or 0
    end

    local monsterOutId = nowNodeConfig.monsterOutID
    local monsterConfig = MonsterExcel.monsterOut[monsterOutId]
    fontVideoInfo(human, msgRet.videoInfo, monsterConfig)
    fontExtraDataSSZH(msgRet.extraDataSSZH, nowNodeConfig.mapID, nowBattleID)
    -- Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)

    hangFightQuery(human)
end

-- 获取当前地图ID
function getMapID(human, args)
    local battleID = human.db.battleID
    local config = BattleExcel.node[battleID]
    if not config then
        return
    end

    local mapConfig = BattleExcel.map[config.mapID]
    if not config then
        return
    end

    local mapID = config.sceneID

    return mapID
end

-- 战役挂机界面查询
function onHookQuery(human)
    local msgRet = Msg.gc.GC_BATTLE_HANG_QUERY
    calcBattleOut(human)
    local expAdd = human.db.battleOut.exp
    local jinbiAdd = human.db.battleOut.jinbi
    local greenExpAdd = human.db.battleOut.greenExp
    local qingbaoAdd = human.db.battleOut.qingbao

    local skip = GuideLogic.getGuideSkip(human, GuideLogic.SKIPTYPE_JUMP_BATTLE_EXP)
    if not skip then
        expAdd = 1510
    end

    local items = { }
    local len = #items
    local tempTable = { }
    if human.db.battleOut.items then
        tempTable = Util.copyTable(human.db.battleOut.items)
    end
    tempTable[ItemDefine.ITEM_EXP_ID] = tempTable[ItemDefine.ITEM_EXP_ID] or 0
    tempTable[ItemDefine.ITEM_EXP_ID] = expAdd
    tempTable[ItemDefine.ITEM_JINBI_ID] = tempTable[ItemDefine.ITEM_JINBI_ID] or 0
    tempTable[ItemDefine.ITEM_JINBI_ID] = jinbiAdd
    tempTable[ItemDefine.ITEM_GREEN_EXP_ID] = tempTable[ItemDefine.ITEM_GREEN_EXP_ID] or 0
    tempTable[ItemDefine.ITEM_GREEN_EXP_ID] = greenExpAdd
    tempTable[ItemDefine.ITEM_QINGBAO_ID] = tempTable[ItemDefine.ITEM_QINGBAO_ID] or 0
    tempTable[ItemDefine.ITEM_QINGBAO_ID] = qingbaoAdd

    local list = DropExchangeLogic.getAbsCanDrop(human)
    if list then
        for k, v in pairs(list) do
            if tempTable[k] and tempTable[k] > 0 then
                len = len + 1
                items[len] = { }
                items[len].id = k
                items[len].cnt = tempTable[k]
            end
        end
    end

    for k, v in pairs(tempTable) do
        if not list or not list[k] then
            len = len + 1
            items[len] = { }
            items[len].id = k
            items[len].cnt = v
        end
    end

    -- 发消息
    local guajiID = human.db.guajiID > #BattleExcel.node and #BattleExcel.node or human.db.guajiID
    if guajiID == 0 then
        return Broadcast.sendErr(human, Lang.BATTLE_GUAJI_ERR)
    end

    local nodeConfig = BattleExcel.node[guajiID]
    local now = os.time()
    local outSec = now - human.db.battleOut.expTs1
    local maxHangTime = getHangMaxTime(human)
    msgRet.maxTime = maxHangTime
    if maxHangTime >= outSec then
        msgRet.time = outSec
    else
        msgRet.time = maxHangTime
    end
    for i = 1, #items do
        Grid.makeItem(msgRet.items[i], items[i].id, items[i].cnt)
    end

    local len = #items
    if human.db.battleOut.equip then
        for k, equipGrid in ipairs(human.db.battleOut.equip) do
            len = len + 1
            Grid.makeItem(msgRet.items[len], equipGrid.id, 1, nil, equipGrid)
        end
    end

    msgRet.items[0] = len
    msgRet.exp = nodeConfig.hangExp
    msgRet.jinbi = nodeConfig.hangJinbi
    msgRet.greenExp = nodeConfig.hangGreenExp
    msgRet.qingbao = nodeConfig.hangQingbao or 0
    Msg.send(msgRet, human.fd)
end

local function isChapterDot(human, mapID)
    local chapterReward = human.db.chapterReward or { }
    if chapterReward[mapID] then
        return false
    end

    local battleID = human.db.battleID
    local battleNodeConfig = BattleExcel.node[battleID]
    if not battleNodeConfig then
        if battleID == #BattleExcel.node + 1 then
            return true
        else
            return false
        end
    else
        local roleMapId = battleNodeConfig.mapID
        if roleMapId <= mapID then
            return false
        end
    end

    return true
end

function battleWorldMapQuery(human)
    local msgRet = Msg.gc.GC_BATTLE_WORLD_MAP_QUERY
    local worldMapConfig = BattleExcel.map
    local battleID = human.db.battleID > #BattleExcel.node and #BattleExcel.node or human.db.battleID
    local battleNodeConfig = BattleExcel.node[battleID]
    local len = #worldMapConfig
    for i = 1, len do
        local v = worldMapConfig[i]
        msgRet.worldMap[i].mapID = i
        msgRet.worldMap[i].mapName = v.name or ""
        msgRet.worldMap[i].mapBuild = v.build or 0
        msgRet.worldMap[i].mapBg = v.bg or 0
        msgRet.worldMap[i].mapPos[0] = 2
        msgRet.worldMap[i].mapPos[1] = v.pos[1]
        msgRet.worldMap[i].mapPos[2] = v.pos[2]
        msgRet.worldMap[i].status =(battleNodeConfig.mapID >= i) and 1 or 0
        msgRet.worldMap[i].story = v.des or ""
        if battleNodeConfig.mapID == i then
            msgRet.worldMap[i].maxLevel = battleNodeConfig.maxLevel
            msgRet.worldMap[i].curLevel = battleNodeConfig.curLevel
        else
            msgRet.worldMap[i].maxLevel = 0
            msgRet.worldMap[i].curLevel = 0
        end

        local reward = v.reward
        local l = 0
        msgRet.worldMap[i].chapterReward[0] = #reward
        for _, rewardData in pairs(reward) do
            l = l + 1
            Grid.makeItem(msgRet.worldMap[i].chapterReward[l], rewardData[1], rewardData[2])
        end

        msgRet.worldMap[i].chapterDot = 0
        if isChapterDot(human, i) then
            msgRet.worldMap[i].chapterDot = 1
        end

        local cnt = 0
        BATTLE_LEVEL_PLAYER_LIST[i] = BATTLE_LEVEL_PLAYER_LIST[i] or { }
        for s, t in pairs(BATTLE_LEVEL_PLAYER_LIST[i]) do
            cnt = cnt + 1
            RoleLogic.makeRoleBase(t, msgRet.worldMap[i].roleList[cnt])
            if cnt >= 3 then
                break
            end
        end
        msgRet.worldMap[i].roleList[0] = cnt
    end

    msgRet.worldMap[0] = len
    Msg.send(msgRet, human.fd)
end

function battleNodeQuery(human, mapID)
    local msgRet = Msg.gc.GC_BATTLE_NODE_QUERY
    local nodeConfig = BattleExcel.node
    local lenConfig = #nodeConfig
    local len = 0
    msgRet.battleID = human.db.battleID or 0
    for i = 1, lenConfig do
        local v = nodeConfig[i]
        if v.mapID == mapID then
            len = len + 1
            msgRet.nodeInfo[len].nodeName = v.name
            msgRet.nodeInfo[len].battleID = i
            msgRet.nodeInfo[len].needLv = v.needLv
            msgRet.nodeInfo[len].needZDL = v.needZDL
            msgRet.nodeInfo[len].hasSuipian = v.hasSuipian
        end
    end
    msgRet.nodeInfo[0] = len
    Msg.send(msgRet, human.fd)
end

function initBattleOut(human, now)
    if human.db.battleOut then
        return
    end

    human.db.battleOut = { }
    human.db.battleOut.expTs1 = now
    -- 经验开始产出时间戳
    human.db.battleOut.expTs2 = now
    -- 经验产出结算时间戳
    human.db.battleOut.itemTs1 = now
    -- 道具产出时间戳
    human.db.battleOut.itemTs2 = now
    -- 道具产出结算时间戳
    human.db.battleOut.exp = 0
    human.db.battleOut.jinbi = 0
    human.db.battleOut.greenExp = 0
    human.db.battleOut.qingbao = 0
    human.db.battleOut.items = nil
    -- 产出的待收获的战利品
    human.db.battleOut.equip = nil
    -- 产出的待收获的装备
end

function calcBattleOut(human)
    local now = os.time()

    initBattleOut(human, now)

    calcBattleExpOut(human, now)

    calcBattleItemOut(human, now)
end

-- 获取正确的剩余可收益的结算时间
function getSurMaxHangTime(maxHangTime, ts1, now)
    if ts1 + maxHangTime > now then
        return now
    else
        return ts1 + maxHangTime
    end
end

function calcBattleExpOut(human, now)
    local maxHangTime = getHangMaxTime(human)
    if human.db.battleOut.expTs2 - human.db.battleOut.expTs1 >= maxHangTime then
        return
    end
    local outTime = getSurMaxHangTime(maxHangTime, human.db.battleOut.expTs1, now)
    local outSec = outTime - human.db.battleOut.expTs2
    local outCnt = math.floor(outSec / BATTLE_HANG_EXP_OUT_PERIOD)
    if outCnt < 1 then
        return
    end

    local guajiID = human.db.guajiID > #BattleExcel.node and #BattleExcel.node or human.db.guajiID
    if guajiID == 0 then
        return
    end

    local nowNodeConfig = BattleExcel.node[guajiID]

    human.db.battleOut.expTs2 = human.db.battleOut.expTs2 + outCnt * BATTLE_HANG_EXP_OUT_PERIOD
    if now - human.db.battleOut.expTs1 >= maxHangTime then
        human.db.battleOut.expTs2 = now
    end

    local vipExpAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER7) or 0) / 100
    local vipJinAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER8) or 0) / 100
    local vipGreenExpAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER9) or 0) / 100
    local vipQingbaoAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER19) or 0) / 100
    local exp = nowNodeConfig.hangExp
    local jinbi = nowNodeConfig.hangJinbi
    local greenExp = nowNodeConfig.hangGreenExp
    local qingbao = nowNodeConfig.hangQingbao
    human.db.battleOut.exp = human.db.battleOut.exp + math.floor(outCnt * exp *(1 + vipExpAdd))
    human.db.battleOut.jinbi = human.db.battleOut.jinbi + math.floor(outCnt * jinbi *(1 + vipJinAdd))
    human.db.battleOut.greenExp = human.db.battleOut.greenExp + math.floor(outCnt * greenExp *(1 + vipGreenExpAdd))
    human.db.battleOut.qingbao = human.db.battleOut.qingbao + math.floor(outCnt * qingbao *(1 + vipQingbaoAdd))
end

-- 根据时间计算道具产出
local ITEM_OUT_LIST = { }
function getItemOutsByTime(config, sec)
    Util.cleanTable(ITEM_OUT_LIST)
    local outCnt = math.floor(sec / BATTLE_HANG_ITEM_OUT_PERIOD)
    if outCnt < 1 then
        return ITEM_OUT_LIST
    end

    -- 普通掉落
    local dropID = config.dropID
    local dropConfig = DropExcel.dropBattle[dropID]
    local weightSum = 0
    for i = 1, #dropConfig.dropRule do
        local tempConfig = dropConfig.dropRule[i]
        weightSum = weightSum + tempConfig[4]
    end
    for i = 1, outCnt do
        local itemID, itemCnt = randItemOut(dropConfig.dropRule, weightSum)
        if itemCnt and itemCnt > 0 then
            ITEM_OUT_LIST[itemID] =(ITEM_OUT_LIST[itemID] or 0) + itemCnt
        end
    end
    for i = 1, #dropConfig.dropRule2 do
        local tempConfig = dropConfig.dropRule2[i]
        local itemID = tempConfig[1]
        local itemMin = tempConfig[2]
        local itemMax = tempConfig[3]
        local itemCnt = math.random(itemMin, itemMax) * outCnt
        if itemCnt > 0 then
            ITEM_OUT_LIST[itemID] =(ITEM_OUT_LIST[itemID] or 0) + itemCnt
        end
    end

    -- 三小时掉落
    outCnt = math.floor(sec / BATTLE_HANG_ITEM_OUT_PERIOD_2)
    if outCnt < 1 then
        return ITEM_OUT_LIST
    end
    weightSum = 0
    for i = 1, #dropConfig.dropRule3 do
        local tempConfig = dropConfig.dropRule3[i]
        weightSum = weightSum + tempConfig[4]
    end
    for i = 1, outCnt do
        local itemID, itemCnt = randItemOut(dropConfig.dropRule3, weightSum)
        if itemCnt and itemCnt > 0 then
            ITEM_OUT_LIST[itemID] =(ITEM_OUT_LIST[itemID] or 0) + itemCnt
        end
    end

    return ITEM_OUT_LIST
end

function calcBattleItemOut(human, now)
    local maxHangTime = getHangMaxTime(human)
    if human.db.battleOut.itemTs2 - human.db.battleOut.itemTs1 >= maxHangTime then
        return
    end

    local outTime = getSurMaxHangTime(maxHangTime, human.db.battleOut.itemTs1, now)
    local outSec = outTime - human.db.battleOut.itemTs2
    local outCnt = math.floor(outSec / BATTLE_HANG_ITEM_OUT_PERIOD)
    if outCnt < 1 then
        return
    end

    local guajiID = human.db.guajiID > #BattleExcel.node and #BattleExcel.node or human.db.guajiID

    if guajiID == 0 then
        return
    end

    local sec = outCnt * BATTLE_HANG_ITEM_OUT_PERIOD
    human.db.battleOut.itemTs2 = human.db.battleOut.itemTs2 + sec
    if now - human.db.battleOut.itemTs1 >= maxHangTime then
        human.db.battleOut.itemTs2 = now
    end

    local nodeConfig = BattleExcel.node[guajiID]
    local items = getItemOutsByTime(nodeConfig, sec)
    for itemID, itemCnt in pairs(items) do
        -- 判定是否是装备
        if ItemDefine.isEquip(itemID) then
            -- 生成装备
            local equipGrid = EquipLogic.makeEquip(itemID)
            if equipGrid then
                human.db.battleOut.equip = human.db.battleOut.equip or { }
                human.db.battleOut.equip[#human.db.battleOut.equip + 1] = equipGrid
            end
        else
            human.db.battleOut.items = human.db.battleOut.items or { }
            human.db.battleOut.items[itemID] = human.db.battleOut.items[itemID] or 0
            human.db.battleOut.items[itemID] = human.db.battleOut.items[itemID] + itemCnt
        end
    end

    DropExchangeLogic.getDropItem(human, outSec, BATTLE_HANG_ITEM_OUT_PERIOD, human.db.battleOut)
end

-- 获取挂机收益
function hangGet(human)
    local ret, items = hangExpGet(human)
    if ret == 1 then
        return Broadcast.sendErr(human, Lang.BATTLE_HANG_GET_EXP_ERR_NONE)
    elseif ret == 2 then
        return Broadcast.sendErr(human, Lang.COMMON_ADD_JINBI_LIMIT)
    elseif ret == 0 then
        query(human)

        RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1101)
    end
end

function hangExpGet(human)
    calcBattleOut(human)
    if human.db.battleOut.exp == 0 then
        return 1
    end
    local jinbiAdd = human.db.battleOut.jinbi
    if not ObjHuman.canAddJinbi(human, jinbiAdd) then
        return 2
    end

    -- 检测装备数量
    if human.db.battleOut.equip then
        local equipCnt = #human.db.battleOut.equip
        if not EquipLogic.checkEmptyCnt(human, equipCnt) then
            return
        end
    end

    local items = { }
    local len = #items
    local maxHangTime = getHangMaxTime(human)
    if human.db.battleOut.expTs2 - human.db.battleOut.expTs1 >= maxHangTime then
        local now = os.time()
        human.db.battleOut.expTs2 = now
    end

    if human.db.battleOut.itemTs2 - human.db.battleOut.itemTs1 >= maxHangTime then
        local now = os.time()
        human.db.battleOut.itemTs2 = now
    end

    human.db.battleOut.itemTs1 = human.db.battleOut.itemTs2

    -- 改db
    local tempTable = human.db.battleOut.items or { }
    human.db.battleOut.items = nil

    for k, v in pairs(tempTable) do
        len = len + 1
        items[len] = { }
        items[len].id = k
        items[len].cnt = v
    end

    human.db.battleOut.expTs1 = human.db.battleOut.expTs2

    local expAdd = human.db.battleOut.exp
    local greenExpAdd = human.db.battleOut.greenExp
    local qingbaoAdd = human.db.battleOut.qingbao
    -- 新手指引 强制经验
    local flag = GuideLogic.getGuideSkip(human, GuideLogic.SKIPTYPE_JUMP_BATTLE_EXP)
    if not flag then
        expAdd = 1510
    end
    GuideLogic.setDoSpecialGuide(human, GuideLogic.SKIPTYPE_JUMP_BATTLE_EXP)
    GuideLogic.setDoSpecialGuide(human, GuideLogic.SKIPTYPE_JUMP_BATTLE_GUAJI)

    human.db.battleOut.exp = 0
    human.db.battleOut.jinbi = 0
    human.db.battleOut.greenExp = 0
    human.db.battleOut.qingbao = 0

    len = len + 1
    items[len] = { }
    items[len].id = ItemDefine.ITEM_EXP_ID
    items[len].cnt = expAdd

    len = len + 1
    items[len] = { }
    items[len].id = ItemDefine.ITEM_JINBI_ID
    items[len].cnt = jinbiAdd

    len = len + 1
    items[len] = { }
    items[len].id = ItemDefine.ITEM_GREEN_EXP_ID
    items[len].cnt = greenExpAdd

    len = len + 1
    items[len] = { }
    items[len].id = ItemDefine.ITEM_QINGBAO_ID
    items[len].cnt = qingbaoAdd

    -- 给道具
    for i = 1, #items do
        local tempID = items[i].id
        local tempCnt = items[i].cnt
        BagLogic.addItem(human, tempID, tempCnt, "battle")
    end

    -- 给装备
    if human.db.battleOut.equip then
        for k, equipGrid in ipairs(human.db.battleOut.equip) do
            EquipLogic.addByEquipGrid(human, equipGrid, "battle", true)
        end
        human.db.battleOut.equip = nil
    end

    return 0, items
end

-- 挂机节点设置
function nodeSet(human, battleID)
    local nodeConfig = BattleExcel.node[battleID]
    if nodeConfig == nil then
        return
    end

    -- 前置条件判断
    if battleID > human.db.battleID then
        return Broadcast.sendErr(human, Lang.BATTLE_ID_OVER)
    end

    -- 等级判断
    if human.db.lv < nodeConfig.needLv then
        return Broadcast.sendErr(human, Util.format(Lang.ROLE_LEV_ERROR, nodeConfig.needLv))
    end

    calcBattleOut(human)

    human.db.guajiID = battleID
    setBattleID(human, battleID)

    Msg.send(Msg.gc.GC_BATTLE_NODE_SET, human.fd)

    query(human)
end

-- 挂机节点详细信息查询
function nodeDetailQuery(human, battleID)
    local nodeConfig = BattleExcel.node[battleID]
    if nodeConfig == nil then
        return
    end

    local msgRet = Msg.gc.GC_BATTLE_NODE_DETAIL_QUERY
    msgRet.battleID = battleID
    msgRet.exp = nodeConfig.hangExp
    msgRet.jinbi = nodeConfig.hangJinbi
    msgRet.greenExp = nodeConfig.hangGreenExp
    msgRet.qingbao = nodeConfig.hangQingbao or 0
    msgRet.nodeName = getBattleName(battleID)
    local dropID = nodeConfig.dropID
    local dropConfig = DropExcel.dropBattle[dropID]
    local dropCnt = 0
    local list = DropExchangeLogic.getAbsCanDrop(human)
    if list then
        for k, v in pairs(list) do
            dropCnt = dropCnt + 1
            Grid.makeItem(msgRet.items[dropCnt], k, v)
        end
    end

    for i = 1, #dropConfig.dropRule do
        local tempConfig = dropConfig.dropRule[i]
        local itemID = tempConfig[1]
        local itemCnt = tempConfig[3]
        local itemRate = tempConfig[4]
        if itemCnt > 0 and itemRate > 0 then
            dropCnt = dropCnt + 1
            Grid.makeItem(msgRet.items[dropCnt], itemID, 1)
        end
    end
    for i = 1, #dropConfig.dropRule3 do
        local tempConfig = dropConfig.dropRule3[i]
        local itemID = tempConfig[1]
        local itemCnt = tempConfig[3]
        local itemRate = tempConfig[4]
        if itemCnt > 0 and itemRate > 0 then
            dropCnt = dropCnt + 1
            Grid.makeItem(msgRet.items[dropCnt], itemID, 1)
        end
    end
    msgRet.items[0] = dropCnt

    Msg.send(msgRet, human.fd)
end

-- 挑战当前挂机节点
function fight(human)
    local battleID = human.db.battleID

    if BattleExcel.node[human.db.battleID] == nil then
        local msgRet = Msg.gc.GC_BATTLE_GOBACK_MAIN
        msgRet.panelIDs[0] = 1
        msgRet.panelIDs[1] = PanelDefine.PANEL_ID_1008
        Msg.send(msgRet, human.fd)
        return Broadcast.sendErr(human, Lang.BATTLE_MAX_LEVEL)
    end
    if human.db.lv < BattleExcel.node[human.db.battleID].needLv then
        local msgRet = Msg.gc.GC_BATTLE_GOBACK_MAIN
        msgRet.panelIDs[0] = 1
        msgRet.panelIDs[1] = PanelDefine.PANEL_ID_1008
        Msg.send(msgRet, human.fd)
        return Broadcast.sendErr(human, Util.format(Lang.ROLE_LEV_ERROR, BattleExcel.node[human.db.battleID].needLv))
    end
    local config = BattleExcel.node[battleID]
    local mapConfig = BattleExcel.map[config.mapID]
    local monsterOutID = config.monsterOutID
    local mapID = mapConfig.bg
    CombatLogic.combatBegin(human, config.sceneID, monsterOutID, CombatDefine.COMBAT_TYPE1, battleID)

    GuideLogic.setDoSpecialGuide(human, GuideLogic.SKIPTYPE_JUMP_BATTLE)
end

function onFightEnd(human, result, fightTypeID, param1, combatInfo)
    -- 设置一些战斗结算信息
    combatInfo.defender.name = Util.format(Lang.COMBAT_BATTLE_DEFEND_NAME, param1)

    if CombatDefine.RESULT_WIN ~= result then
        return
    end

    local guajiID = human.db.guajiID
    local battleID = human.db.battleID

    if battleID ~= param1 then
        return
    end

    local oldGuajiID = guajiID
    local nextGuajiID = guajiID + 1
    local nextBattleID = battleID + 1
    local nextBattleConfig = BattleExcel.node[nextBattleID]
    local config = BattleExcel.node[battleID]

    -- 如果过大关卡
    if nextBattleConfig and nextBattleConfig.mapID ~= config.mapID then
        -- 清除记录
        BATTLE_LEVEL_PLAYER_LIST[config.mapID] = BATTLE_LEVEL_PLAYER_LIST[config.mapID] or { }
        BATTLE_LEVEL_PLAYER_LIST[config.mapID][human.db._id] = nil
        -- 增加新纪录
        BATTLE_LEVEL_PLAYER_LIST[nextBattleConfig.mapID] = BATTLE_LEVEL_PLAYER_LIST[nextBattleConfig.mapID] or { }
        BATTLE_LEVEL_PLAYER_LIST[nextBattleConfig.mapID][human.db._id] = { }
        RoleLogic.getRoleBase(human, BATTLE_LEVEL_PLAYER_LIST[nextBattleConfig.mapID][human.db._id])
    else
        if config.mapID == 1 then
            BATTLE_LEVEL_PLAYER_LIST[config.mapID] = BATTLE_LEVEL_PLAYER_LIST[config.mapID] or { }
            if BATTLE_LEVEL_PLAYER_LIST[config.mapID][human.db._id] == nil then
                -- 增加新纪录
                BATTLE_LEVEL_PLAYER_LIST[config.mapID][human.db._id] = { }
                RoleLogic.getRoleBase(human, BATTLE_LEVEL_PLAYER_LIST[config.mapID][human.db._id])
            end
        else
            if not BATTLE_LEVEL_PLAYER_LIST[config.mapID] or not BATTLE_LEVEL_PLAYER_LIST[config.mapID][human.db._id] then
                BATTLE_LEVEL_PLAYER_LIST[config.mapID] = BATTLE_LEVEL_PLAYER_LIST[config.mapID] or { }
                BATTLE_LEVEL_PLAYER_LIST[config.mapID][human.db._id] = { }
                RoleLogic.getRoleBase(human, BATTLE_LEVEL_PLAYER_LIST[config.mapID][human.db._id])
            end
        end
    end

    human.db.battleID = battleID + 1
    -- 通过第10关，默认开启二倍速
    if human.db.battleID == 10 then
        human.db.combatSpeed = 2
    end
    combatInfo.attacker.oldLv = human.db.lv
    -- 给奖励
    combatInfo.rewardItem = { }
    for i = 1, #config.winReward do
        local itemID = config.winReward[i][1]
        local itemCnt = config.winReward[i][2]
        -- 装备不在这显示
        combatInfo.rewardItem[i] = combatInfo.rewardItem[i] or { }
        combatInfo.rewardItem[i] = { itemID, itemCnt }
        BagLogic.addItem(human, itemID, itemCnt, "battle_win")
    end
    combatInfo.attacker.lv = human.db.lv

    combatInfo.getEquip = human.getEquip
    human.getEquip = nil

    -- TODO:记录主线关卡
    Log.write(Log.LOGID_OSS_BATTLE_MAINGATE, human.db._id, human.db.account, human.db.name, human.db.battleID)

    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1101)
    GuideLogic.onCallback(human)
    JibanLogic.onCallback(human, 1, battleID)
    TuiSongLiBao.tuiSongLiBaoOnTask(
    human,
    TuiSongLiBao.TUISONGLIBAOTASK_ZHENGZHAN,
    human.db.guajiID - 1,
    human.db.guajiID - 2
    )
    ChengjiuLogic.onCallback(human, ChengjiuDefine.CJ_TASK_TYPE_1, nextGuajiID)
    MengxinLogic.onCallBack(human, MengxinLogic.MX_TASK_TYPE_1, nextGuajiID)
    for k, v in pairs(KingWorldLogic.funcID) do
        YunYingLogic.updateIcon(KingWorldLogic.YYInfo[k], human)
        break
    end
    -- 存储战斗记录
    local videoUuid = CombatVideo.saveBattleVideo(human.db._id, combatInfo)
    human.db.battleVideoUuid = videoUuid

    BattleDBLogic.updateBattleDB(combatInfo.attacker, battleID, combatInfo, videoUuid)
    if nextBattleConfig and nextBattleConfig.mapID ~= config.mapID then
        -- 新的地图
        -- 通知客户端
        Msg.send(Msg.gc.GC_BATTLE_NODE_SET, human.fd)
    end

    human.db.guajiID = nextGuajiID
    if nextBattleConfig then
        setBattleID(human, nextGuajiID)
    end

    local showNext = 1
    if BattleExcel.node[human.db.battleID] and human.db.lv < BattleExcel.node[human.db.battleID].needLv then
        showNext = 2
    end

    if human.db.lv < 5 then
        showNext = 0
    end

    nextBattleConfig = BattleExcel.node[human.db.battleID]
    if not nextBattleConfig then
        showNext = 0
    end

    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1105)

    -- 额外奖励
    local needLevel, itemName = getNextRewardName(human)
    if needLevel ~= nil then
        local tb = { needLevel, Lang.BATTLE_FIGHT_GUAN, "|", itemName, "|", showNext }
        local str = table.concat(tb)
        combatInfo.endParam = str
    else
        local tb = { showNext }
        local str = table.concat(tb)
        combatInfo.endParam = str
    end
end

function setBattleID(human, guajiID)
    if guajiID > human.db.guajiID and human.db.guajiID ~= 0 then
        -- 下发挂机收益升级提示
        local msgRet = Msg.gc.GC_BATTLE_REWARD_UP

        Grid.makeItem(msgRet.itemExp, ItemDefine.ITEM_EXP_ID, 1)
        Grid.makeItem(msgRet.itemJinbi, ItemDefine.ITEM_JINBI_ID, 1)
        Grid.makeItem(msgRet.itemGreenExp, ItemDefine.ITEM_GREEN_EXP_ID, 1)
        Grid.makeItem(msgRet.itemQingbao, ItemDefine.ITEM_QINGBAO_ID, 1)

        local oldConfig = BattleExcel.node[human.db.guajiID]
        local newConfig = BattleExcel.node[guajiID]

        msgRet.expOld = oldConfig.hangExp
        msgRet.expNew = newConfig.hangExp
        msgRet.jinbiOld = oldConfig.hangJinbi
        msgRet.jinbiNew = newConfig.hangJinbi
        msgRet.greenExpOld = oldConfig.hangGreenExp
        msgRet.greenExpNew = newConfig.hangGreenExp
        msgRet.qingbaoOld = oldConfig.hangQingbao
        msgRet.qingbaoNew = newConfig.hangQingbao

        Msg.send(msgRet, human.fd)
    end

    -- 刷新排行榜
    BRoleLogic.updateData(BillboardDefine.TYPE_BATTLE, human.db)
end

-- GM
function setBattleByGm(human, val, maxBattleID)
    val = tonumber(val)
    if not val or val <= 1 then
        return
    end
    local nodeConfig = BattleExcel.node[val]
    if not nodeConfig then
        return
    end

    if maxBattleID then
        maxBattleID = tonumber(maxBattleID)
    else
        maxBattleID = val
    end

    setBattleID(human, val)
    human.db.battleID = val
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1105)
end

function mopupQuery(human)
    ObjHuman.updateDaily(human)
    local msgRet = Msg.gc.GC_BATTLE_MOPUP_QUERY
    local guajiID = human.db.guajiID > #BattleExcel.node and #BattleExcel.node or human.db.guajiID
    if guajiID == 0 then
        return Broadcast.sendErr(human, Lang.BATTLE_MOPUP_ERR)
    end
    human.db.mopupDoCnt = human.db.mopupDoCnt or 0
    local nextDoCnt = human.db.mopupDoCnt + 1
    local nodeConfig = BattleExcel.node[guajiID]
    local dropID = nodeConfig.dropID
    local dropConfig = DropExcel.dropBattle[dropID]
    local dropCnt = 0

    -- ABS掉落活动显示到第一个
    dropCnt = DropExchangeLogic.getDropItemSaoQuery(human, msgRet, dropCnt)
    for i = 1, #dropConfig.dropRule do
        if dropCnt >= #msgRet.item then
            break
        end
        dropCnt = dropCnt + 1
        local tempConfig = dropConfig.dropRule[i]
        local itemID = tempConfig[1]
        Grid.makeItem(msgRet.item[dropCnt], itemID, 1)
    end
    for i = 1, #dropConfig.dropRule3 do
        if dropCnt >= #msgRet.item then
            break
        end
        dropCnt = dropCnt + 1
        local tempConfig = dropConfig.dropRule3[i]
        local itemID = tempConfig[1]
        Grid.makeItem(msgRet.item[dropCnt], itemID, 1)
    end
    local mapConfig = BattleExcel.map[nodeConfig.mapID]
    msgRet.name = mapConfig.name
    msgRet.item[0] = dropCnt
    if TequanShopLogic.isActiveMopup(human) then
        msgRet.leftCnt = BATTLE_MOPUP_CNT_VIP - human.db.mopupDoCnt
    else
        msgRet.leftCnt = BATTLE_MOPUP_CNT - human.db.mopupDoCnt
    end
    local mupopExcel = BattleExcel.mupop[nextDoCnt]
    if mupopExcel then
        if not TequanShopLogic.isActiveMopup(human) then
            msgRet.need = mupopExcel.cost
            if human.db.mopupFreeCnt < BATTLE_MOPUP_FREE_CNT then
                msgRet.need = 0
            end
        else
            msgRet.need = mupopExcel.vipCost
            if human.db.mopupFreeCnt < BATTLE_MOPUP_FREE_CNT_VIP then
                msgRet.need = 0
            end
        end
    else
        msgRet.need = 0
    end
    msgRet.vip = TequanShopLogic.isActiveMopup(human) and 1 or 0
    msgRet.nowCnt = human.db.mopupDoCnt
    msgRet.exp = nodeConfig.hangExp
    msgRet.jinbi = nodeConfig.hangJinbi
    msgRet.greenExp = nodeConfig.hangGreenExp
    msgRet.qingbao = nodeConfig.hangQingbao
    Msg.send(msgRet, human.fd)
end

function mopupFight(human)
    -- 等级判断
    ObjHuman.updateDaily(human)
    local need = 0
    human.db.mopupDoCnt = human.db.mopupDoCnt or 0
    human.db.mopupFreeCnt = human.db.mopupFreeCnt or 0
    local nextDoCnt = human.db.mopupDoCnt + 1
    local mupopExcel = BattleExcel.mupop[nextDoCnt]
    if not mupopExcel then
        return Broadcast.sendErr(human, Lang.BATTLE_MOPUP_CNT_OVER)
    end
    if not TequanShopLogic.isActiveMopup(human) then
        if human.db.mopupDoCnt >= BATTLE_MOPUP_CNT then
            return Broadcast.sendErr(human, Lang.BATTLE_MOPUP_CNT_OVER)
        end
        need = mupopExcel.cost
        -- 如果免费次数小于1次，则按免费算
        if human.db.mopupFreeCnt < BATTLE_MOPUP_FREE_CNT then
            need = 0
        end
    else
        if human.db.mopupDoCnt >= BATTLE_MOPUP_CNT_VIP then
            return Broadcast.sendErr(human, Lang.BATTLE_MOPUP_CNT_OVER)
        end
        need = mupopExcel.vipCost
        -- 如果免费次数小于3次，则按免费算
        if human.db.mopupFreeCnt < BATTLE_MOPUP_FREE_CNT_VIP then
            need = 0
        end
    end

    if not ObjHuman.checkRMB(human, need) then
        return
    end

    if need <= 0 then
        human.db.mopupFreeCnt = human.db.mopupFreeCnt + 1
    end

    ObjHuman.decZuanshi(human, - need, "battleMopup")

    local len = #BattleExcel.node

    human.db.mopupDoCnt = human.db.mopupDoCnt + 1

    local guajiID = human.db.guajiID > #BattleExcel.node and #BattleExcel.node or human.db.guajiID
    if guajiID == 0 then
        return Broadcast.sendErr(human, Lang.BATTLE_MOPUP_ERR)
    end

    local config = BattleExcel.node[guajiID]
    local msgRet = Msg.gc.GC_BATTLE_MOPUP_FIGHT

    local vipExpAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER7) or 0) / 100
    local vipJinAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER8) or 0) / 100
    local vipGreenExpAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER9) or 0) / 100
    local vipQingbaoAdd =(VipLogic.getPowerArgs(human, VipLogic.VIP_POWER19) or 0) / 100
    local exp = config.hangExp * 120
    local jinbi = config.hangJinbi * 120
    local greenExp = config.hangGreenExp * 120
    local qingbao = config.hangQingbao * 120
    local addExp = math.floor(exp *(1 + vipExpAdd))
    local addJinbi = math.floor(jinbi *(1 + vipJinAdd))
    local addGreenExp = math.floor(greenExp *(1 + vipGreenExpAdd))
    local addQingbao = math.floor(qingbao *(1 + vipQingbaoAdd))
    local time = 7200
    local itemTable = getItemOutsByTime(config, time)
    -- 掉落活动
    DropExchangeLogic.getDropItemSao(human, time, itemTable)

    local flag = GuideLogic.getGuideSkip(human, GuideLogic.SKIPTYPE_JUMP_BATTLE_SD)
    if not flag then
        local equipID = SysParameter.getSysParameter(SysParameter.PARAMETER_10)
        itemTable[equipID] = 1
    end

    itemTable[ItemDefine.ITEM_JINBI_ID] = itemTable[ItemDefine.ITEM_JINBI_ID] or 0
    itemTable[ItemDefine.ITEM_EXP_ID] = itemTable[ItemDefine.ITEM_EXP_ID] or 0
    itemTable[ItemDefine.ITEM_GREEN_EXP_ID] = itemTable[ItemDefine.ITEM_GREEN_EXP_ID] or 0
    itemTable[ItemDefine.ITEM_QINGBAO_ID] = itemTable[ItemDefine.ITEM_QINGBAO_ID] or 0

    itemTable[ItemDefine.ITEM_JINBI_ID] = itemTable[ItemDefine.ITEM_JINBI_ID] + addJinbi
    itemTable[ItemDefine.ITEM_EXP_ID] = itemTable[ItemDefine.ITEM_EXP_ID] + addExp
    itemTable[ItemDefine.ITEM_GREEN_EXP_ID] = itemTable[ItemDefine.ITEM_GREEN_EXP_ID] + addGreenExp
    itemTable[ItemDefine.ITEM_QINGBAO_ID] = itemTable[ItemDefine.ITEM_QINGBAO_ID] + addQingbao
    msgRet.item[0] = 0

    -- 双倍日
    local double = RoleSystemLogic.isDouble(human, RoleSystemDefine.ROLE_SYS_ID_1101)
    local rewardCnt = double and 2 or 1
    for itemID, itemCnt in pairs(itemTable) do
        itemCnt = itemCnt * rewardCnt
        if not ItemDefine.isEquip(itemID) then
            msgRet.item[0] = msgRet.item[0] + 1
            Grid.makeItem(msgRet.item[msgRet.item[0]], itemID, itemCnt)
        end

        BagLogic.addItem(human, itemID, itemCnt, "battle")
    end
    msgRet.item[0] = EquipLogic.makeEquipItem(human, msgRet.item, msgRet.item[0])
    msgRet.double = double and 1 or 0

    msgRet.exp = itemTable[ItemDefine.ITEM_EXP_ID] * rewardCnt
    msgRet.jinbi = itemTable[ItemDefine.ITEM_JINBI_ID] * rewardCnt
    msgRet.greenExp = itemTable[ItemDefine.ITEM_GREEN_EXP_ID] * rewardCnt
    msgRet.qingbao = itemTable[ItemDefine.ITEM_QINGBAO_ID] * rewardCnt

    Msg.send(msgRet, human.fd)
    mopupQuery(human)
    query(human)

    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1101)
    GuideLogic.setDoSpecialGuide(human, GuideLogic.SKIPTYPE_JUMP_BATTLE_SD)
    DailyTaskLogic.recordDailyTaskFinishCnt(human, DailyTaskLogic.DAILY_TASK_ID_12, 1)
    ChengjiuLogic.onCallback(human, ChengjiuDefine.CJ_TASK_TYPE_3, 1)

    HeroLogLogic.finishTaskCB(human, HeroLogLogic.HERO_LOG_TYPE_3, 1)
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_2001)
    YunYingLogic.onCallBack(human, "onMopup", 1)
end

function updateDaily(human)
    human.db.mopupFreeCnt = 0
    human.db.mopupDoCnt = 0
end

function getTongGuanReward(human, id)
    local cf = BattleExcel.node[id]
    if not cf then
        return
    end
    if #cf.tongguan < 1 then
        return
    end
    print("human.db.guajiID，id", human.db.guajiID, id)
    if human.db.guajiID < id then
        return
    end

    if isGetReward(human, id) then
        return
    end

    if #cf.tongguan == 1 and cf.tongguan[1][3] then
        -- 英雄
        if HeroLogic.getEmptyCnt(human) < 1 then
            return Broadcast.sendErr(human, Lang.HERO_BAG_FULL)
        end
        local heroID = cf.tongguan[1][1]
        local cnt = cf.tongguan[1][2]
        local star = cf.tongguan[1][3]
        if cnt <= 0 then
            return
        end
        setGetReward(human, id)
        local msg = Msg.gc.GC_SUIPIAN_SUMMON
        for i = 1, cnt do
            local heroGrid = HeroGrid.createHeroGrid(heroID, star)

            HeroLogic.addHeroByGrid(human, heroGrid, "battle_extra_reward")

            SuipianLogic.makeResultItemData(msg.heroList[i], heroID, 1, 1)
        end

        msg.isHero = 1
        msg.list[0] = 0
        msg.heroList[0] = cnt
        msg.fenJieList[0] = 0

        Msg.send(msg, human.fd)
    else
        setGetReward(human, id)
        BagLogic.addItemList(human, cf.tongguan, "tongguan")
    end

    -- 触发国王君临奖励
    ChatPaoMaLogic.broadcast(human, ChatPaoMaLogic.PAOMA_TYPE_BROAD_TYPE5, id)
    query(human)

    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1101)
    if id == 80 then
        KingWorldLogic.setState(human, 1)
    end
end

function isDot(human)
    -- 通过大关卡奖励

    local chapterReward = human.db.chapterReward or { }
    local battleID = human.db.battleID
    local battleNodeConfig = BattleExcel.node[battleID]
    if not battleNodeConfig then
        local nodeCnt = #BattleExcel.node
        local mapCnt = #BattleExcel.map
        if battleID == nodeCnt + 1 and not chapterReward[mapCnt] then
            return true
        else
            return false
        end
    else
        local mapID = battleNodeConfig.mapID
        for i = mapID - 1, 1, -1 do
            if not chapterReward[i] then
                return true
            end
        end
    end

    return false

    -- if human.db.lv < 9 then
    --     return false
    -- end

    -- -- 有免费扫荡次数的时候有红点
    -- human.db.mopupFreeCnt = human.db.mopupFreeCnt or 0
    -- if TequanShopLogic.isActiveMopup(human) then
    --     if human.db.mopupFreeCnt < 3 then
    --         return true
    --     end
    -- else
    --     if human.db.mopupFreeCnt < 1 then
    --         return true
    --     end
    -- end
    -- -- 有通关奖励可领取的时候有红点
    -- if hasCanGetReward(human) then
    --     return true
    -- end

    -- -- 战役挂机时间超过1小时的时候有红点
    -- if human.db.battleOut ~= nil then
    --     local now = os.time()
    --     local outSec = now - human.db.battleOut.expTs1
    --     if outSec >= 3600 then
    --         return true
    --     end
    -- end

    -- return false
end

function clacItemTwoHours(human)
    local items = { }
    local outCnt = math.floor(7200 / BATTLE_HANG_ITEM_OUT_PERIOD)
    local guajiID = human.db.guajiID > #BattleExcel.node and #BattleExcel.node or human.db.guajiID
    if guajiID == 0 then
        return items
    end

    local nodeConfig = BattleExcel.node[guajiID]
    local dropID = nodeConfig.dropID
    local dropConfig = DropExcel.dropBattle[dropID]
    local totalWeight = 0
    for i = 1, #dropConfig.dropRule do
        local tempConfig = dropConfig.dropRule[i]
        totalWeight = totalWeight + tempConfig[4]
    end
    for j = 1, outCnt do
        local randomNum = math.random(1, totalWeight)
        for i = 1, #dropConfig.dropRule do
            local tempConfig = dropConfig.dropRule[i]
            local itemID = tempConfig[1]
            local itemCnt1 = tempConfig[2]
            local itemCnt2 = tempConfig[3]
            local itemRate = tempConfig[4]
            if randomNum <= itemRate then
                local realAdd = math.random(itemCnt1, itemCnt2)
                items[itemID] = realAdd
                break
            end
            randomNum = randomNum - itemRate
        end
    end

    return items
end

local MailManager = require("mail.MailManager")
function clacItemCntGM(human, cnt, guajiID)
    local items = { }
    local outCnt = cnt
    local nodeConfig = BattleExcel.node[guajiID]
    local dropID = nodeConfig.dropID
    local dropConfig = DropExcel.dropBattle[dropID]
    local totalWeight = 0
    for i = 1, #dropConfig.dropRule do
        local tempConfig = dropConfig.dropRule[i]
        totalWeight = totalWeight + tempConfig[4]
    end
    for j = 1, outCnt do
        local randomNum = math.random(1, totalWeight)
        for i = 1, #dropConfig.dropRule do
            local tempConfig = dropConfig.dropRule[i]
            local itemID = tempConfig[1]
            local itemCnt1 = tempConfig[2]
            local itemCnt2 = tempConfig[3]
            local itemRate = tempConfig[4]
            if randomNum <= itemRate then
                local realAdd = math.random(itemCnt1, itemCnt2)
                items[itemID] = items[itemID] or 0
                items[itemID] = items[itemID] + realAdd
                break
            end
            randomNum = randomNum - itemRate
        end
    end
    local item = { }
    local index = 0
    for k, v in pairs(items) do
        index = index + 1
        item[index] = { k, v }
    end
    MailManager.add(MailManager.SYSTEM, human.db._id, "", "", item, "")
end

function getBattleID(human)
    local num = 0

    if human.db.guajiID then
        num = human.db.guajiID or 0
    end
    return num
end

--------------------------------------------- combat ----------------------------------------------
function getCombatMonsterOutID(human, side)
    if side ~= CombatDefine.DEFEND_SIDE then
        return
    end
    local config = BattleExcel.node[human.db.battleID]
    if not config then
        return
    end

    return config.monsterOutID
end

function getCombatName(human)
    local name = getBattleName(human.db.battleID)

    return Util.format(Lang.COMBAT_BATTLE_EXNAME, name)
end
-------------------------------------------combat end--------------------

function battleSharkQuery(human, nodeID)
    local msgRet = Msg.gc.GC_BATTLE_SHARK_QUERY
    local videoTb = BattleDBLogic.queryBattleDbByNodeID(nodeID)
    if videoTb == nil then
        msgRet.battleShark[0] = 0
    else
        local len = #videoTb.shark
        for i = 1, len do
            local v = videoTb.shark[i]
            RoleLogic.makeRoleBase(v.roleBase, msgRet.battleShark[i].roleBase)
            msgRet.battleShark[i].type = i
            msgRet.battleShark[i].videoUuid = v.videoUuid
            msgRet.battleShark[i].param = v.param or 0
        end
        msgRet.battleShark[0] = len
    end
    -- Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

local QueryRoleByNodeID = { }
function worldMapRoleListQuery(human, worldMapId)
    -- if worldMapId == 1 then return end
    local msgRet = Msg.gc.GC_BATTLE_WORLD_MAP_ROLELIST_QUERY
    local len = 0
    for k, v in pairs(BATTLE_LEVEL_PLAYER_LIST[worldMapId]) do
        len = len + 1
        RoleLogic.makeRoleBase(v, msgRet.roleList[len])
        if len >= 50 then
            break
        end
    end
    msgRet.roleList[0] = len
    Msg.send(msgRet, human.fd)
end

-- 章节(地图)掉落列表
function sendMapDroItemsList(human)
    local battleID = human.db.battleID or 1
    local maxBattleConfig = BattleExcel.node[battleID]
    local maxMapID = maxBattleConfig and maxBattleConfig.mapID or(#BattleExcel.node + 1)
    local msgRet = Msg.gc.GC_BATTLE_MAP_DROPITEMS_LIST
    msgRet.list[0] = 0
    for mapID, mapConfig in ipairs(BattleExcel.map) do
        msgRet.list[0] = msgRet.list[0] + 1
        local net = msgRet.list[msgRet.list[0]]
        net.mapID = mapID
        net.mapName = mapConfig.name
        net.isOpen =(mapID <= maxMapID) and 1 or 0
    end
    -- Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 章节(地图)掉落详情
function sendMapDroItemsDetail(human, mapID)
    local mapConfig = BattleExcel.map[mapID]
    if not mapConfig then
        return
    end

    local msgRet = Msg.gc.GC_BATTLE_MAP_DROPITEMS_DETAIL
    msgRet.mapID = mapID
    msgRet.items[0] = math.min(#mapConfig.dropItemsShow, #msgRet.items)
    for i = 1, msgRet.items[0] do
        local itemID = mapConfig.dropItemsShow[i][1]
        local itemCnt = mapConfig.dropItemsShow[i][2]
        Grid.makeItem(msgRet.items[i], itemID, itemCnt)
    end
    -- Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 获取当前战役的 怪物ID
function getBattleMonsterOutID(human)
    local battleID = human.db.battleID
    local config = BattleExcel.node[battleID]
    return config.monsterOutID
end

function onUpdatePos(human)
    BRoleLogic.updateData(BillboardDefine.TYPE_ZHANDOULI, human.db)
end

function getNextRewardName(human)
    for i = human.db.guajiID + 1, #BattleExcel.node do
        if #BattleExcel.node[i].tongguan > 0 then
            local itemID = BattleExcel.node[i].tongguan[1][1]
            local itemConfig = ItemDefine.getConfig(itemID)
            if itemConfig == nil then
                return
            else
                return i - human.db.guajiID, itemConfig.name
            end
        end
    end
end

--[[
    通关章节后领取奖励
]]
function battleChapterReward(human, mapID)
    local mapConf = BattleExcel.map[mapID]
    if not mapConf then
        return
    end

    -- 没有通过当前大关卡
    local battleID = human.db.battleID
    local battleNodeConfig = BattleExcel.node[battleID]
    local roleMapId
    if not battleNodeConfig then
        if battleID ~= #BattleExcel.node + 1 then
            return
        else
            roleMapId = #BattleExcel.map
        end
    else
        roleMapId = battleNodeConfig.mapID
    end

    if mapID > roleMapId then
        return
    end

    if not human.db.chapterReward then
        human.db.chapterReward = { }
    end

    -- 判断是否已经领取了当前大关卡的奖励
    local chapterReward = human.db.chapterReward
    if chapterReward[mapID] then
        return Broadcast.sendErr(human, Lang.FRIEND_HEART_GET_HAD)
    end

    human.db.chapterReward[mapID] = true
    ObjHuman.save(human)

    local reward = mapConf.reward
    BagLogic.addItemList(human, reward, "chapterReward")
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1105)

    local msgRet = Msg.gc.GC_BATTLE_CHAPTER_REWARD
    msgRet.mapID = mapID
    Msg.send(msgRet, human.fd)
end