-- 秘境副本 由于秘境副本比较特殊,无法复用副本大多数通用方法,所以没有使用通用方法,单独开发 SecretRealm = {} local this = {} function SecretRealm.isSecretRealmMap(mapCfgId) local type = ConfigDataManager.getTableValue("cfg_BOSS_challenge", "monstertype", "repId", mapCfgId) return tonumber(type) == 3 end function SecretRealm.UpdateHurtTopRank(uniqueMapId, mapCfgId) if not SecretRealm.isSecretRealmMap(mapCfgId) then return end local monActors = getmapmon(uniqueMapId) local playActors = getmapplayer(uniqueMapId) for _, playerActor in ipairs(playActors) do for _, monsterActor in pairs(monActors) do SecretRealm.getTop1HurtInfo0(playerActor, monsterActor:toString()) end end end --- 请求进入秘境副本 ---@param actor 玩家对象 ---@param repId cfg_repfairyland表id function SecretRealm.reqEnterSecretRealm(actor, msgData) local repId = msgData["repId"] local tableValue = ConfigDataManager.getTable("cfg_repfairyland", "id", repId) if not tableValue then gameDebug.print("reqEnterSecretRealm cfg_repfairyland is nil") return end -- 检查进入秘境副本的条件 if not this.checkEnterSecretRealmCondition(actor, tableValue[1]) then return end setplaydef(actor, PlayerDefKey.secretRealm.LAST_MAP_ID, getbaseinfo(actor, "mapid")) -- 满足条件执行传送进副本操作 local mapId = tableValue[1]["mapid"] local mapMove = tableValue[1]["mapmove"] local x = ConfigDataManager.getTableValue("cfg_mapMove", "mapX", "id", mapMove) local y = ConfigDataManager.getTableValue("cfg_mapMove", "mapY", "id", mapMove) maptransfer(actor, x, y, mapId, 1) end --- 获取剩余挑战次数 ---@param actor 玩家对象 function SecretRealm.sendRemainingChallenges(actor) local secretRealmCount = getplaydef(actor, PlayerDefKey.secretRealm.COUNT) sendluamsg(actor, LuaMessageIdToClient.SECRET_REALM_COUNT, { count = secretRealmCount and secretRealmCount or 0 }) end --- 进入地图后处理 ---@param actor 玩家对象 ---@param lastMapCfgId 上一个地图id ---@param mapCfgId 当前地图id function SecretRealm.afterEnterSecretRealm(actor, lastMapCfgId, mapCfgId) lastMapCfgId = tonumber(lastMapCfgId) mapCfgId = tonumber(mapCfgId) if lastMapCfgId == mapCfgId then gameDebug.print("afterEnterSecretRealm lastMapCfgId == mapCfgId") return end if this.isSecretRealmMap(mapCfgId) then -- 清除自身所有buff,并将血量和蓝量补满,刷新回血、技能的冷却时间 -- 清除自身所有buff clearallbuff(actor) -- 血量和蓝量补满 sethp(actor, getattrinfo(actor, "maxHP")) setmp(actor, getattrinfo(actor, "maxMP")) -- 刷新技能的冷却时间 clearallskillcd(actor) -- 获取当前地图的boss信息 local monsterId = ConfigDataManager.getTableValue("cfg_BOSS_challenge", "monsterid", "repid", mapCfgId) local mapId = gamemap.getMapKey(mapCfgId, 1) local monsterInfo = mapbossinfo(actor, mapId, 3, monsterId) sendluamsg(actor, LuaMessageIdToClient.AFTER_ENTER_SECRET_REALM, { mapId = mapCfgId, monsterInfo = monsterInfo, time = 0, }) end end --- 退出秘境副本 ---@param actor 玩家对象 function SecretRealm.reqExitSecretRealm(actor) -- 请求退出副本 local mapId = getplaydef(actor, PlayerDefKey.secretRealm.LAST_MAP_ID) local x local y if mapId then x, y = CustomTransmit.GetPointFromMapMove(mapId) else local career = tonumber(getbaseinfo(actor, "getbasecareer")) local set = ConfigDataManager.getTableValue("cfg_character_create", "set", "id", career) local set_split = string.split(set, "#") mapId = set_split[1] x = set_split[2] y = set_split[3] end maptransfer(actor, x, y, mapId) -- 删除该玩家的对怪物的伤害信息 SecretRealm.removePlayerHurt(actor, 0) sendluamsg(actor, LuaMessageIdToClient.RES_QUIT_SECRET_REALM, {}) end --- 首次登录初始化秘境挑战次数 ---@param actor 玩家对象 function SecretRealm.login(actor) local count = getplaydef(actor, PlayerDefKey.secretRealm.COUNT) if not count then local secretRealmCountInit = ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.SECRET_REALM_INIT) setplaydef(actor, PlayerDefKey.secretRealm.COUNT, tonumber(secretRealmCountInit)) end -- 判断重新登录后玩家是否在秘境地图中,在秘境地图中并且没有超时发送消息给客户端 local currentMapId = getbaseinfo(actor, "mapid") local countZeroTime = getplaydef(actor, PlayerDefKey.secretRealm.COUNT_ZERO_TIME) if this.isSecretRealmMap(currentMapId) and not string.isNullOrEmpty(countZeroTime) then local timeDiff = getbaseinfo(actor, "nowsec") - countZeroTime if countZeroTime and timeDiff > 30 then -- 退出秘境地图 SecretRealm.reqExitSecretRealm(actor) else -- 获取当前地图的boss信息 local monsterId = ConfigDataManager.getTableValue("cfg_BOSS_challenge", "monsterid", "repid", currentMapId) local mapCfgId = gamemap.getMapKey(currentMapId, 1) local monsterInfo = mapbossinfo(actor, mapCfgId, 3, monsterId) sendluamsg(actor, LuaMessageIdToClient.AFTER_ENTER_SECRET_REALM, { mapId = currentMapId, monsterInfo = monsterInfo, time = 30 - timeDiff, }) end end end --- 减少秘境副本挑战次数 ---@param actor 玩家对象 ---@param monsterId 怪物配置id ---@param mapId 地图唯一id function SecretRealm.reduceCount(actor, monsterId, mapId) local mapCfgId, _ = gamemap.parseMapKey(mapId) if this.isSecretRealmBoss(monsterId) and this.isSecretRealmMap(mapCfgId) then local count = tonumber(getplaydef(actor, PlayerDefKey.secretRealm.COUNT)) if count == 1 then setplaydef(actor, PlayerDefKey.secretRealm.COUNT, 0) sendluamsg(actor, LuaMessageIdToClient.SECRET_REALM_COUNT_ZEROED, { secretRealmCount = 0 }) -- 记录玩家挑战次数为0的时间 setplaydef(actor, PlayerDefKey.secretRealm.COUNT_ZERO_TIME, getbaseinfo(actor, "nowsec")) if isofflineplay(actor) then local exitMapId = getplaydef(actor, PlayerDefKey.secretRealm.LAST_MAP_ID) local x local y if exitMapId then x, y = CustomTransmit.GetPointFromMapMove(exitMapId) else local career = tonumber(getbaseinfo(actor, "getbasecareer")) local set = ConfigDataManager.getTableValue("cfg_character_create", "set", "id", career) local set_split = string.split(set, "#") exitMapId = set_split[1] x = set_split[2] y = set_split[3] end maptransfer(actor, x, y, exitMapId) end -- 删除该玩家的对怪物的伤害信息 SecretRealm.removePlayerHurt(actor, 0) end if count > 1 then setplaydef(actor, PlayerDefKey.secretRealm.COUNT, count - 1) end -- 宝箱直接发放到背包中 local repId = ConfigDataManager.getTableValue("cfg_repfairyland", "id", "mapid", mapCfgId) local boxDrop = ConfigDataManager.getTableValue("cfg_BOSS_challenge", "boxdrop", "repid", repId, "monsterid", monsterId) if not boxDrop then gameDebug.printTraceback("cfg_BOSS_challenge boxdrop is nil repId:[" .. repId .. "] monsterId:[" .. monsterId) return end local boxDropTable = string.split(boxDrop, "#") -- 专属奖励只有一个,所以此处没有循环遍历 additemtobag(actor, boxDropTable[1], boxDropTable[2], 0, 9999, '秘境boss') -- 刷新任务进度 TaskHandler.TriggerTaskGoal(actor, TaskTargetType.FINISH_SECRET_REALM_DUPLICATE, repId) end end --- 增加秘境副本挑战次数 ---@param secretRealmCountStep number 每日增加次数 ---@param secretRealmMaxCount number 最大次数 function SecretRealm.incrementedSecretRealmCount(secretRealmCountStep, secretRealmMaxCount) local allRoleInfos = getallrolesummaryinfos() if table.isNullOrEmpty(allRoleInfos) then return end for _, roleInfo in pairs(allRoleInfos) do local actor = roleInfo["actor"] local count = getplaydef(actor, PlayerDefKey.secretRealm.COUNT) -- 重置玩家挑战次数为0的时间 setplaydef(actor, PlayerDefKey.secretRealm.COUNT_ZERO_TIME, "") if count then info(actor, actor:toString() .. "玩家秘境副本次数刷新 count:" .. count) if tonumber(count) + secretRealmCountStep > secretRealmMaxCount then setplaydef(actor, PlayerDefKey.secretRealm.COUNT, secretRealmMaxCount) else setplaydef(actor, PlayerDefKey.secretRealm.COUNT, tonumber(count) + secretRealmCountStep) end else info(actor, actor:toString() .. "玩家秘境副本次数刷新") local secretRealmCountInit = ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.SECRET_REALM_INIT) setplaydef(actor, PlayerDefKey.secretRealm.COUNT, tonumber(secretRealmCountInit)) end end end --- 获取秘境副本怪物数量 ---@param actor 玩家对象 ---@param msgData 请求参数 function SecretRealm.getMonsterCount(actor, msgData) local repId = msgData["repId"] if not repId then gameDebug.print("SecretRealm.getMonsterCount repId is nil") return end local res = {} local monsterInfo = ConfigDataManager.getTableValue("cfg_repfairyland_monster", "monster1", "id", repId) if not monsterInfo then return end local monsterList = string.split(monsterInfo, "|") for _, value in pairs(monsterList) do local splitString = string.split(value, "#") local monsterId = splitString[1] local x = splitString[3] local y = splitString[4] local range = splitString[5] local aiId = ConfigDataManager.getTableValue("cfg_monster", "ai", "id", monsterId) local maxMove = ConfigDataManager.getTableValue("cfg_monster_ai", "leavebasedistance", "id", aiId) local mapId = gamemap.getMapKey(repId, 1) local maxRange = range + tonumber(maxMove) + 1 local monsterCount = getalivemonsterinmap(actor, mapId, x, y, maxRange) if monsterCount and table.count(monsterCount) > 0 then table.insert(res, { repId = tonumber(repId), x = tonumber(x), y = tonumber(y), range = tonumber(range), monsterId = tonumber(monsterId), count = table.count(monsterCount), time = 0 }) else local monInfo = mapbossinfo(actor, mapId, 3) if not table.isNullOrEmpty(monInfo) then for _, v in pairs(monInfo) do local minX = x - maxRange local maxX = x + maxRange local minY = y - maxRange local maxY = y + maxRange if v.isdead and v.x >= minX and v.x <= maxX and v.y >= minY and v.y <= maxY then local monId = ConfigDataManager.getTableValue("cfg_BOSS_challenge", "monsterid", "repid", repId) local ai = ConfigDataManager.getTableValue("cfg_monster", "ai", "id", monId) local reliveDelay, reliveServiceTime = this.getMonsterReliveTime(ai) local dieTime = getsysvar(actor, string.format(PlayerDefKey.secretRealm.BOSS_TIME_BY_ID, v.id)) if not string.isNullOrEmpty(dieTime) then dieTime = tonumber(dieTime) + tonumber(reliveDelay) + tonumber(reliveServiceTime) end table.insert(res, { repId = tonumber(repId), x = tonumber(x), y = tonumber(y), range = tonumber(range), monsterId = tonumber(monId), count = 0, time = tostring(dieTime and dieTime or 0), id = v.id }) end end end end end sendluamsg(actor, LuaMessageIdToClient.SEND_SECRET_REALM_MONSTER_COUNT, res) end --- 保存玩家实时伤害信息 ---@param actor 玩家对象 ---@param target 攻击怪物id ---@param hurt 造成的伤害 function SecretRealm.savePlayerHurtInfo(actor, target, hurt) local hurtTable = getsysvar(actor, PlayerDefKey.secretRealm.HURT) if not hurtTable then hurtTable = {} end local roleName = getbaseinfo(actor, "rolename") local exist = false if hurtTable then for k, _ in pairs(hurtTable) do if tostring(k) == target:toString() then exist = true end end end if not exist then local temp = {} temp[roleName] = hurt hurtTable[target:toString()] = temp setsysvar(actor, PlayerDefKey.secretRealm.HURT, hurtTable) else for k, v in pairs(hurtTable) do if tostring(k) == target:toString() then if v[roleName] then v[roleName] = v[roleName] + hurt else v[roleName] = hurt end end end setsysvar(actor, PlayerDefKey.secretRealm.HURT, hurtTable) end this.sendSecretRealmHurt(actor, target) end --- 删除对应玩家存储的实时伤害 ---@param actor 玩家对象 function SecretRealm.removePlayerHurt(actor, monsterId) local hurtTable = getsysvar(actor, PlayerDefKey.secretRealm.HURT) local roleName = getbaseinfo(actor, "rolename") if table.isNullOrEmpty(hurtTable) then return end if monsterId == 0 then -- 删除该玩家所有伤害信息 for _, value in pairs(hurtTable) do for k, v in pairs(value) do if k == roleName then value[roleName] = nil end end end setsysvar(actor, PlayerDefKey.secretRealm.HURT, hurtTable) else -- 清空对应怪物的伤害记录 hurtTable[tostring(monsterId)] = nil setsysvar(actor, PlayerDefKey.secretRealm.HURT, hurtTable) end end --- boss死亡删除伤害信息与发送怪物列表信息 ---@param actor 玩家对象 ---@param mapId 地图唯一id ---@param monCfgId 怪物配置id ---@param monsterId 怪物id function SecretRealm.recordMonsterDie(actor, mapId, monCfgId, monsterId) local mapCfgId, _ = gamemap.parseMapKey(mapId) if this.isSecretRealmBoss(monCfgId) and this.isSecretRealmMap(mapCfgId) then setsysvar(actor, string.format(PlayerDefKey.secretRealm.BOSS_TIME_BY_ID, monsterId), getbaseinfo("now")) this.sendMonsterListInfo(actor, mapId) SecretRealm.removePlayerHurt(actor, monsterId) end end --- 发送怪物列表信息 ---@param actor 玩家对象 ---@param msgData 客户端数据——地图id function SecretRealm.ReqBossList(actor, msgData) local mapId = msgData["mapId"] if not mapId then gameDebug.print("SecretRealm.ReqBossList mapId is nil") return end mapId = gamemap.getMapKey(mapId, 1) this.sendMonsterListInfo(actor, mapId) end --- 请求获取对制定怪物造成伤害的第一名信息 function SecretRealm.getTop1HurtInfo(actor, msgData) local monsterId = msgData["monsterId"] SecretRealm.getTop1HurtInfo0(actor, monsterId) end function SecretRealm.getTop1HurtInfo0(actor, monsterId) local hurtInfo = getsysvar(actor, PlayerDefKey.secretRealm.HURT) local info = {} if hurtInfo then for _, v in pairs(hurtInfo) do for key, value in pairs(v) do if tostring(key) == tostring(monsterId) then info = value end end end end if table.isNullOrEmpty(info) then return end local message = {} local top1 = this.getTopN(info, 1) if table.isNullOrEmpty(top1) then message["monsterId"] = tonumber(monsterId) else message["monsterId"] = tonumber(monsterId) message["name"] = top1[1].name message["hurt"] = top1[1].hurt end sendluamsg(actor, LuaMessageIdToClient.SECRET_REALM_HURT_TOP1, message) end --- 判断是否满足进入秘境副本的要求 ---@param actor 玩家对象 ---@param tableValue cfg_repfairyland表单行记录 ---@return boolean 是否可以进入秘境副本,true-可以进入,false-不能进入 function this.checkEnterSecretRealmCondition(actor, tableValue) -- 判断当前地图是否是在秘境地图中,如果在秘境地图先退出再进入 local mapId = getbaseinfo(actor, "mapid") if this.isSecretRealmMap(mapId) then noticeTip.noticeinfo(actor, StringIdConst.DUPLICATE_CAN_NOT_TRANSMIT) return false end -- 等级判断 local level = tableValue["level"] if string.isNullOrEmpty(level) then gameDebug.print("checkEnterSecretRealmCondition cfg_repfairyland level is nil") else local actorLevel = getbaseinfo(actor, "level") if tonumber(level) > tonumber(actorLevel) then tipinfo(actor, string.format("等级不满足,%s等级后开启", level)) return false end end -- 进入次数判断 local secretRealmCount = getplaydef(actor, PlayerDefKey.secretRealm.COUNT) if string.isNullOrEmpty(secretRealmCount) or tonumber(secretRealmCount) <= 0 then tipinfo(actor, "今日次数不足") return false end -- 全身穿戴装备强化等级判断 local strengthenLevel = tableValue["strengthenlevel"] if string.isNullOrEmpty(strengthenLevel) then gameDebug.print("checkEnterSecretRealmCondition cfg_repfairyland strengthenLevel is nil") else if not string.isNullOrEmpty(strengthenLevel) then -- 获取全身强化等级 local allStrengthLevel = EquipAndAppear.allequipstrengthlv(actor) if tonumber(allStrengthLevel) < tonumber(strengthenLevel) then tipinfo(actor, string.format("强化等级不满足,需要全身穿戴装备强化等级到达%s级", strengthenLevel)) return false end end end -- 开服天数限制 local startDay = tableValue["startday"] if string.isNullOrEmpty(startDay) then gameDebug.print("checkEnterSecretRealmCondition cfg_repfairyland startDay is nil") else if not string.isNullOrEmpty(startDay) then local serverOpenDays = tonumber(getserveropendays(actor)) return tonumber(startDay) <= serverOpenDays end end return true end --- 判断怪物类型是否是秘境副本中的怪物类型 ---@param monCfgId 怪物id ---@return boolean true——是,false——否 function this.isSecretRealmBoss(monCfgId) local bossChallenge = ConfigDataManager.getTable("cfg_BOSS_challenge", "monsterType", BossType.SECRET_REALM_BOSS) for i = 1, #bossChallenge do if tonumber(monCfgId) == tonumber(bossChallenge[i].monsterid) then return true end end return false end --- 判断指定地图是否为秘境副本地图 ---@param mapId 地图id ---@return boolean 是否为秘境副本地图 function this.isSecretRealmMap(mapId) local tableValue = ConfigDataManager.getList("cfg_repfairyland") for _, value in pairs(tableValue) do if tostring(value.id) == tostring(mapId) then return true end end return false end --- 发送秘境副本玩家实时伤害消息 ---@param actor 玩家对象 ---@param monsterId 怪物id function this.sendSecretRealmHurt(actor, monsterId) local roleName = getbaseinfo(actor, "rolename") local hurtInfo = getsysvar(actor, PlayerDefKey.secretRealm.HURT) local info = {} if hurtInfo then for k, v in pairs(hurtInfo) do if tostring(k) == monsterId:toString() then info = v end end end if table.isNullOrEmpty(info) then gameDebug.print("sendSecretRealmHurt hurtInfo is nil") return end local message = {} message["monsterId"] = monsterId:toString() local top3 = this.getTopN(info, 3) message["top3"] = top3 local rank = 0 for key, value in pairs(info) do rank = rank + 1 if key == roleName then message["my"] = { name = roleName, hurt = value, rank = this.getRank(info, roleName) } end end sendluamsg(actor, LuaMessageIdToClient.SECRET_REALM_DAMAGE, message) end --- 发送秘境boss列表信息 ---@param actor 玩家对象 ---@param mapId 地图id function this.sendMonsterListInfo(actor, mapId) local mapCfgId = gamemap.parseMapKey(mapId) local monsterId = ConfigDataManager.getTableValue("cfg_BOSS_challenge", "monsterid", "repid", mapCfgId) local ai = ConfigDataManager.getTableValue("cfg_monster", "ai", "id", monsterId) local reliveDelayTime, reliveServiceTime = this.getMonsterReliveTime(ai) local monsterInfo = mapbossinfo(actor, mapId, 3) local message = {} if not table.isNullOrEmpty(monsterInfo) then for _, value in pairs(monsterInfo) do local dieTime = getsysvar(actor, string.format(PlayerDefKey.secretRealm.BOSS_TIME_BY_ID, value.id)) if not string.isNullOrEmpty(dieTime) then dieTime = tonumber(dieTime) + tonumber(reliveDelayTime) + tonumber(reliveServiceTime) end table.insert(message, { monsterId = value.id, monsterName = value.name, x = value.x, y = value.y, reviveTime = tostring(dieTime and dieTime or 0) }) end end sendluamsg(actor, LuaMessageIdToClient.SECRET_REALM_BOSS_LIST, message) end --- 获取boss复活时间 ---@param id 怪物id function this.getMonsterReliveTime(id) local aiConfig = ConfigDataManager.getById("cfg_monster_ai", id) if aiConfig == nil then gameDebug.print("cfg_monster_ai为nil id:" .. id) return 0, 0 end local reliveType = tonumber(aiConfig['relivetype']) local reliveDelay = aiConfig['relivedelay'] local reliveServiceTime = 0 if reliveType == 4 then local str = aiConfig['reliveservicetime'] if string.isNullOrEmpty(str) then return 0, 0 end local strShuXian = string.split(str, "|") local serverOpenDays = tonumber(getbaseinfo("serveropendays")) local cfg = {} for _, v in pairs(strShuXian) do local strJinHao = string.split(v, "#") local day = tonumber(strJinHao[1]) if serverOpenDays >= day then cfg = strJinHao end end if table.isNullOrEmpty(cfg) then gameDebug.print("updateMonsterReliveTime 未找到匹配的数据 开服天数:" .. tostring(serverOpenDays) .. ",reliveDelay字段内容:" .. str) return end reliveServiceTime = tonumber(cfg[2]) end if string.isNullOrEmpty(reliveDelay) then reliveDelay = 0 end return reliveDelay, reliveServiceTime end --- 获取指定数据中的前N个数据 ---@param info table 数据 ---@param N number 前N个 ---@return table 前N个数据 function this.getTopN(info, N) local temp = {} for k, _ in pairs(info) do table.insert(temp, k) end table.sort(temp, function(a, b) return info[a] > info[b] end) local topNKey = table.getTopN(N, temp) local topN = {} for _, key in ipairs(topNKey) do table.insert(topN, { name = key, hurt = info[key] }) end return topN end function this.getRank(info, key) local temp = {} for k, _ in pairs(info) do table.insert(temp, k) end table.sort(temp, function(a, b) return info[a] > info[b] end) for index, k in ipairs(temp) do if k == key then return index end end end --- 玩家死亡清空该玩家伤害 --- @param actor table 玩家对象 function SecretRealm.playerDie(actor) local mapId = getbaseinfo(actor, "mapid") if this.isSecretRealmMap(mapId) then SecretRealm.removePlayerHurt(actor, 0) end end --- 秘境副本次数刷新 function SecretRealm.refreshCount() local serverType = getbaseinfo("servertype") if serverType == 2 then return end local seconds = getbaseinfo("nowsec") local now = TimeUtil.timeToDate(seconds) if now.min == 0 then local activityRule = ConfigDataManager.getTable("cfg_activity_rule", "id", DuplicateType.SECRET_REALM_BOSS) local numberTime = activityRule[1]["numbertime"] local numberAdd = activityRule[1]["numberadd"] if not string.isNullOrEmpty(numberTime) and not string.isNullOrEmpty(numberAdd) then local split = string.split(numberTime, "#") local type = tonumber(split[1]) local numberTime1 = tonumber(split[2]) local numSplit = string.split(numberAdd, "#") local secretRealmCountStep = tonumber(numSplit[1]) local secretRealmMaxCount = tonumber(numSplit[2]) if type == 1 then if numberTime1 == now.hour then SecretRealm.incrementedSecretRealmCount(secretRealmCountStep, secretRealmMaxCount) end else local numberTime2 = tonumber(split[3]) if numberTime1 == now.wday and numberTime2 == now.hour then SecretRealm.incrementedSecretRealmCount(secretRealmCountStep, secretRealmMaxCount) end end end end end