| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400 |
- -- 百战成神(普通服 NS)
- --
- -- 玩法: 跨服 5v5 积分 PvP, 五族独立阵容 COMBAT_TYPE39~43, 5 局 3 胜。
- -- 开服>=45 天; 周六 0:10~周日 23:00; 每 21 天新轮。
- --
- -- 数据分工:
- -- 跨服 DB: 积分、排行、匹配池、玩家展示(REGISTER/UPDATE_SHOW)
- -- 本地 human.db.baiZhanChengShen: 免费次数、战报、crossRegistered
- -- human.bzcs_Match_Cache: 匹配列表/积分内存缓存(TTL 见 BZCS_MATCH_CACHE_TTL, 不持久化)
- -- CommonDB.KEY_BZCS_START_TIME: 本服活动轮次标记(与跨服 WL_BZCS_ACT_START 同步)
- --
- -- 跨服同步策略:
- -- 首次完成挑战 -> 先 LW_BZCS_REGISTER 再 LW_BZCS_FIGHT_END (crossRegistered=true, 每轮仅一次)
- -- 阵容/战力变更 -> LW_BZCS_UPDATE_SHOW (须已注册)
- -- 每场战斗结束 -> LW_BZCS_FIGHT_END (不重复 REGISTER)
- --
- -- 战斗:
- -- 挑战一次依次走 CG_COMBAT_BEGIN(COMBAT_TYPE39~43); 仅首场校验次数/道具并请求跨服
- -- 扣次在 C2N_CanFight; onFightEnd 链式 nextCombatType 打满 5 族或 3 胜, 整场结束发 fightReward
- -- 机器人: combatBegin; 真人: MiddleCommonLogic_CombatBegin_LW
- --
- -- 文件结构:
- -- 本地辅助 -> 客户端请求(BZCS_*) -> 跨服通知(C2N_*) -> 战斗相关 -> 对外接口
- local Config = require("Config")
- local Msg = require("core.Msg")
- local Lang = require("common.Lang")
- local Util = require("common.Util")
- local ObjHuman = require("core.ObjHuman")
- local Broadcast = require("broadcast.Broadcast")
- local CombatDefine = require("combat.CombatDefine")
- local CombatLogic = require("combat.CombatLogic")
- local CombatPosLogic = require("combat.CombatPosLogic")
- local RoleDBLogic = require("role.RoleDBLogic")
- local RoleHeadLogic = require("role.RoleHeadLogic")
- local RoleAttr = require("role.RoleAttr")
- local InnerMsg = require("core.InnerMsg")
- local MailManager = require("mail.MailManager")
- local MailExcel = require("excel.mail")
- local Timer = require("core.Timer")
- local Grid = require("bag.Grid")
- local BagLogic = require("bag.BagLogic")
- local CommonDB = require("common.CommonDB")
- local MiddleConnect = require("middle.MiddleConnect")
- local MiddleCommonLogic = require("middle.MiddleCommonLogic")
- local HeroExcel = require("excel.hero")
- local BaiZhanChengShenDefine = require("baiZhanChengShen.BaiZhanChengShenDefine")
- local BzcsLog = require("baiZhanChengShen.BaiZhanChengShenLog")
- local BzcsConfig = require("excel.baiZhanChengShen")
- local SkillExcel = require("excel.skill")
- local Skill = require("combat.Skill")
- local HeroLogic
- local MoshouLogic
- local ElfLogic
- local LOGTAG = "BaiZhanChengShen"
- -- 战斗类型 -> 种族 camp, 用于 checkUpdatePos 限制上阵
- local COMBATTYPE_2_CAMP = {
- [CombatDefine.COMBAT_TYPE39] = 1,
- [CombatDefine.COMBAT_TYPE40] = 2,
- [CombatDefine.COMBAT_TYPE41] = 3,
- [CombatDefine.COMBAT_TYPE42] = 4,
- [CombatDefine.COMBAT_TYPE43] = 5,
- }
- ------------------------------------ 本地辅助 ------------------------------------
- -- 逻辑服与跨服链路是否可用(WL_HELLO 成功后为 true)
- local function isMiddleReady()
- if _G.is_middle then return true end
- return MiddleConnect.IS_MIDDLE_CONNECT == true
- end
- -- 发送 LW 到跨服; 断连时提示玩家并返回 false
- local function sendLwToMiddle(human, msgData)
- if not isMiddleReady() then
- if human and human.fd then
- Broadcast.sendErr(human, Lang.MIDDLE_SVR_ERR_CONNECT)
- end
- return false
- end
- InnerMsg.sendMsg(0, msgData)
- return true
- end
- -- 当前星期(Util.getWeekDay: 1=周日, 7=周六)
- local function getWDay()
- return Util.getWeekDay()
- end
- -- 开服天数是否满足参与条件
- local function isServerEligible()
- return CommonDB.getServerOpenDay() >= BaiZhanChengShenDefine.BZCS_OPEN_SVR_DAY
- end
- -- 本服是否在活动期(跨服已广播 KEY_BZCS_START_TIME 且 IsRunning)
- local function actStartTimeCheck()
- local wDay = getWDay()
- if wDay > BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[2]
- and wDay < BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[1] then
- return false
- end
- local startTime = CommonDB.getValueByKey(CommonDB.KEY_BZCS_START_TIME)
- if not startTime or startTime == 0 then
- return false
- end
- return IsRunning(startTime)
- end
- -- 新活动轮次时清空本地战报
- local function clearRoundLocalData(db)
- db.warReport = {}
- db.crossRegistered = false
- end
- -- 清除匹配内存缓存(积分变化/新轮/强制刷新)
- local function clearMatchCache(human)
- if human then
- human.bzcs_Match_Cache = nil
- end
- end
- -- 活动开始时间变化(新轮开启/结束)时重置本地轮次数据
- local function resetRoundLocalIfNeeded(human, db)
- local actStart = CommonDB.getValueByKey(CommonDB.KEY_BZCS_START_TIME) or 0
- if (db.actStartTime or 0) ~= actStart then
- db.actStartTime = actStart
- clearRoundLocalData(db)
- clearMatchCache(human)
- end
- end
- -- 玩家本地百战成神数据 (不入跨服 Mongo)
- -- freeTimes 今日剩余免费挑战次数(跨天由 updateDaily 重置)
- -- actStartTime 已同步的活动轮开始时间(与 KEY_BZCS_START_TIME 对比清轮次数据)
- -- crossRegistered 本轮是否已向跨服 REGISTER
- -- warReport 本地战报 {warType,...}, 按时间尾插(最老在[1]), 下发时倒序为新在前
- local function getBzcsDb(human)
- human.db.baiZhanChengShen = human.db.baiZhanChengShen or {
- freeTimes = BaiZhanChengShenDefine.BZCS_FREE_TIMES,
- actStartTime = 0,
- crossRegistered = false,
- warReport = {},
- }
- resetRoundLocalIfNeeded(human, human.db.baiZhanChengShen)
- return human.db.baiZhanChengShen
- end
- local function getMatchCache(human)
- return human and human.bzcs_Match_Cache
- end
- local function setMatchCache(human, myScore, opponentList)
- human.bzcs_Match_Cache = {
- myScore = myScore,
- matchScore = myScore,
- opponentList = opponentList or {},
- cacheTime = os.time(),
- }
- end
- -- 展示刷新: 保留 matchScore(匹配基准), 更新对手展示并重置 TTL
- local function refreshMatchCacheDisplay(human, myScore, opponentList)
- local cache = getMatchCache(human)
- human.bzcs_Match_Cache = {
- myScore = myScore,
- matchScore = cache and cache.matchScore or myScore,
- opponentList = opponentList or {},
- cacheTime = os.time(),
- }
- end
- -- 积分变动: 清空对手列表, 重置 matchScore/TTL(从变动时刻重新计时)
- local function resetMatchCacheOnScoreChange(human, newScore)
- if not human then return end
- newScore = newScore or BaiZhanChengShenDefine.BZCS_INIT_SCORE
- human.bzcs_Match_Cache = {
- myScore = newScore,
- matchScore = newScore,
- opponentList = {},
- cacheTime = os.time(),
- }
- end
- local function isMatchCacheFresh(human)
- local cache = getMatchCache(human)
- if not cache or not cache.opponentList or not next(cache.opponentList) then
- return false
- end
- if (cache.myScore or 0) ~= (cache.matchScore or 0) then
- return false
- end
- local ttl = BaiZhanChengShenDefine.BZCS_MATCH_CACHE_TTL or 30
- return (os.time() - (cache.cacheTime or 0)) < ttl
- end
- local function collectRefreshRanks(opponentList)
- local ranks = {}
- for _, opp in ipairs(opponentList or {}) do
- if opp.rank and opp.rank > 0 then
- ranks[#ranks + 1] = opp.rank
- end
- end
- return ranks
- end
- -- 校验 rank 是否属于当前匹配缓存(全服名次, 非列表下标 1~3)
- local function resolveMatchOpponentByRank(human, rank)
- if not rank or rank < 1 then
- return nil
- end
- local cache = getMatchCache(human)
- for _, opp in ipairs(cache and cache.opponentList or {}) do
- if opp.rank == rank and opp.uuid then
- return opp
- end
- end
- return nil
- end
- -- 指定种族阵容是否至少上阵 1 名英雄(空位 "0"/"" 不算)
- local function hasRaceLineupHero(human, race)
- local combatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[race]
- if not combatType then return false end
- local heroList = CombatPosLogic.getCombatHeros(human, combatType)
- if not heroList then return false end
- for _, heroUuid in pairs(heroList) do
- if heroUuid and heroUuid ~= "" and heroUuid ~= "0" then
- return true
- end
- end
- return false
- end
- -- 五族阵容是否均已至少上阵 1 名英雄(供 GC_BZCS_MATCH_LIST.allLineupReady)
- local function isAllRaceLineupReady(human)
- for _, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- if not hasRaceLineupHero(human, race) then
- return false
- end
- end
- return true
- end
- -- 挑战前校验五族阵容均已上阵英雄
- local function checkAllRaceLineupForFight(human)
- for _, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- if not hasRaceLineupHero(human, race) then
- local raceName = BaiZhanChengShenDefine.BZCS_RACE_NAME[race] or ""
- Broadcast.sendErr(human, Util.format(Lang.BZCS_NEED_RACE_LINEUP, raceName))
- return false
- end
- end
- return true
- end
- -- 组装并下发 GC_BZCS_MATCH_LIST
- local function sendMatchListGC(human, myScore, opponentList)
- local db = getBzcsDb(human)
- local msgRet = Msg.gc.GC_BZCS_MATCH_LIST
- msgRet.myScore = myScore or BaiZhanChengShenDefine.BZCS_INIT_SCORE
- msgRet.freeTimes = db.freeTimes or 0
- Grid.makeItem(
- msgRet.ticketCost,
- BaiZhanChengShenDefine.BZCS_TICKET_ITEM_ID,
- BaiZhanChengShenDefine.BZCS_TICKET_COST
- )
- msgRet.allLineupReady = isAllRaceLineupReady(human) and 1 or 0
- msgRet.opponentList[0] = #(opponentList or {})
- for i, opp in ipairs(opponentList or {}) do
- local net = msgRet.opponentList[i]
- net.rank = opp.rank or 0
- net.name = opp.name
- net.body = opp.body or 0
- net.power = opp.power
- net.score = opp.score
- net.serverId = BaiZhanChengShenDefine.ToClientServerId(opp.serverId, opp.isRobot)
- end
- Msg.send(msgRet, human.fd)
- end
- -- 单个英雄展示字段(阵容/协议用)
- local function getHeroInfo(human, heroUuid)
- HeroLogic = HeroLogic or require("hero.HeroLogic")
- local heroGrid = HeroLogic.getHeroGridByUuid(human, heroUuid)
- if not heroGrid then return end
- local heroCfg = HeroExcel.hero[heroGrid.id]
- RoleAttr.calcHeroGrid(heroGrid, nil, human)
- return {
- heroUuid = heroUuid,
- heroStar = heroGrid.star,
- heroLevel = heroGrid.lv,
- heroCamp = heroGrid.camp or heroCfg.camp,
- heroBody = heroCfg.body,
- heroIcon = heroGrid.head or heroCfg.head,
- heroPower = heroGrid.zhandouli,
- heroId = heroGrid.id,
- heroQuality = heroCfg.grade,
- }
- end
- -- 某战斗类型阵容内已上阵英雄展示列表
- local function getRaceHeroListInfo(human, combatType)
- local heroArr = {}
- local len = 0
- local heroList = CombatPosLogic.getCombatHeros(human, combatType)
- if not heroList then
- return heroArr
- end
- for _, heroUuid in pairs(heroList) do
- if heroUuid ~= "" and heroUuid ~= "0" then
- local heroInfo = getHeroInfo(human, heroUuid)
- if heroInfo then
- len = len + 1
- heroArr[len] = heroInfo
- end
- end
- end
- return heroArr
- end
- -- 技能 icon(助阵凤凰 moshouSkill 用)
- local function getBzcsSkillIcon(skillId)
- if not skillId or skillId < 1 then
- return ""
- end
- local skillConfig = SkillExcel.skill[skillId] or Skill.GetSkillConfig(skillId)
- return skillConfig and skillConfig.icon or ""
- end
- -- 填充精灵位展示(同 CombatPosLogic.Elf_Pos_Query)
- local function fillBzcsElfPosArr(msgElfPosArr, elfList, elfSkillIds, human)
- ElfLogic = ElfLogic or require("elf.ElfLogic")
- msgElfPosArr[0] = CombatDefine.COMBAT_ELF_NOW_CNT
- for i = 1, CombatDefine.COMBAT_ELF_NOW_CNT do
- local net = msgElfPosArr[i]
- net.elfId = 0
- net.nowSkillDesc = ""
- net.nowSkillIcon = ""
- local elfId = elfList and elfList[i]
- if elfId and elfId > 0 then
- local skillId
- if human then
- skillId = ElfLogic.GetElfSkill(human, elfId)
- else
- skillId = elfSkillIds and elfSkillIds[i]
- end
- if skillId and skillId > 0 then
- local skillConfig = Skill.GetSkillConfig(skillId)
- if skillConfig then
- net.elfId = elfId
- net.nowSkillDesc = skillConfig.desc
- net.nowSkillIcon = skillConfig.icon
- end
- end
- end
- end
- end
- -- 填充 BZCS_TEAM_INFO(英雄/助阵 icon/精灵展示)
- local function fillBzcsTeamNet(teamNet, show, human, combatType)
- show = BaiZhanChengShenDefine.ExpandBzcsRaceShow(show) or show
- local heroList = show.heroArr or {}
- teamNet.power = BaiZhanChengShenDefine.CalcRaceTeamPower(show)
- teamNet.formation = show.formation or 1
- teamNet.heroArr[0] = #heroList
- for j, h in ipairs(heroList) do
- local net = teamNet.heroArr[j]
- net.heroBody = h.heroBody or 0
- net.heroStar = h.heroStar or 0
- net.heroLv = h.heroLevel or h.heroLv or 0
- net.heroCamp = h.heroCamp or 0
- net.heroIcon = h.heroIcon or 0
- net.heroId = h.heroId or 0
- net.heroQuality = h.heroQuality or 1
- end
- local helpSkillId = show.helpSkillId or 0
- if human and combatType and helpSkillId < 1 then
- MoshouLogic = MoshouLogic or require("moshou.MoshouLogic")
- local combatHeroDB = CombatPosLogic.getCombatHeroDB(human, combatType)
- if combatHeroDB and combatHeroDB.helpList then
- helpSkillId = MoshouLogic.getPutMoshouSkillID(human, combatType, combatHeroDB.helpList) or 0
- end
- end
- teamNet.moshouSkill = getBzcsSkillIcon(helpSkillId)
- fillBzcsElfPosArr(teamNet.elfPosArr, show.elfList, show.elfSkillIds, human)
- end
- -- 单种族阵容展示(跨服仅存 heroArr/阵法/助阵技能id/精灵id+技能id; 战力见各英雄 heroPower)
- local function getRaceLineupShow(human, race)
- local combatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[race]
- local heroArr = getRaceHeroListInfo(human, combatType)
- local combatHeroDB = CombatPosLogic.getCombatHeroDB(human, combatType)
- local helpSkillId = 0
- local elfList, elfSkillIds
- if combatHeroDB then
- MoshouLogic = MoshouLogic or require("moshou.MoshouLogic")
- ElfLogic = ElfLogic or require("elf.ElfLogic")
- if combatHeroDB.helpList then
- helpSkillId = MoshouLogic.getPutMoshouSkillID(human, combatType, combatHeroDB.helpList) or 0
- end
- if combatHeroDB.elfList then
- elfList = {}
- elfSkillIds = {}
- for i = 1, CombatDefine.COMBAT_ELF_NOW_CNT do
- local elfId = combatHeroDB.elfList[i]
- if elfId and elfId > 0 then
- elfList[i] = elfId
- elfSkillIds[i] = ElfLogic.GetElfSkill(human, elfId) or 0
- end
- end
- end
- end
- return {
- heroArr = heroArr,
- formation = combatHeroDB and combatHeroDB.formation or 1,
- helpSkillId = helpSkillId,
- elfList = elfList,
- elfSkillIds = elfSkillIds,
- }
- end
- -- 跨服展示包: 外观 + 五族阵容(存入 DB.playerList[].showInfo, 仅 REGISTER 全量使用)
- local function buildShowInfo(human)
- local heroArr = {}
- for _, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- heroArr[race] = getRaceLineupShow(human, race)
- end
- return {
- name = human.db.name,
- head = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_1),
- headFrame = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_2),
- body = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_3),
- heroArr = heroArr,
- }
- end
- -- 按 updateType 组装增量 showInfo(仅含本次变更字段)
- local function buildShowPatch(human, updateType, race)
- if updateType == BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_NAME then
- return {name = human.db.name}
- elseif updateType == BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_HEAD then
- return {head = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_1)}
- elseif updateType == BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_HEAD_FRAME then
- return {headFrame = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_2)}
- elseif updateType == BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_BODY then
- return {body = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_3)}
- elseif updateType == BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_LINEUP then
- if not race then return end
- return {heroArr = {[race] = getRaceLineupShow(human, race)}}
- end
- end
- -- 按排名取周期奖励道具配置
- local function getRankReward(rank)
- for _, v in ipairs(BzcsConfig.rankReward) do
- if rank >= v.rankArea[1] and rank <= v.rankArea[2] then
- return v.awardList
- end
- end
- end
- -- 道具列表转日志串 itemId:cnt,...
- local function formatAwardList(awardList)
- if not awardList or #awardList < 1 then
- return ""
- end
- local parts = {}
- for i, item in ipairs(awardList) do
- parts[i] = string.format("%s:%s", item[1] or 0, item[2] or 0)
- end
- return table.concat(parts, ",")
- end
- -- 整场挑战结束发放道具奖励(读 excel fightReward: 1=胜 2=负)
- -- combatInfo.rewardItem 供 GC_COMBAT_FINISH 展示, 参考 BattleLogic.onFightEnd
- local function grantFightReward(human, atkWin, combatInfo)
- if not human or not human.db then return end
- local rewardId = atkWin and BaiZhanChengShenDefine.BZCS_FIGHT_REWARD_WIN_ID
- or BaiZhanChengShenDefine.BZCS_FIGHT_REWARD_LOSE_ID
- local fightReward = BzcsConfig.fightReward
- local cfg = fightReward and fightReward[rewardId]
- if not cfg or not cfg.awardList or not next(cfg.awardList) then
- BzcsLog.logAction("fight_reward_miss", string.format(
- "uuid=%s atkWin=%s rewardId=%s", human.db._id, atkWin and 1 or 0, rewardId
- ))
- return
- end
- if combatInfo then
- combatInfo.rewardItem = {}
- end
- local awardList = {}
- for i, itemInfo in ipairs(cfg.awardList) do
- local itemID = itemInfo[1]
- local itemCnt = itemInfo[2]
- awardList[i] = {itemID, itemCnt}
- if combatInfo then
- combatInfo.rewardItem[i] = {itemID, itemCnt}
- end
- BagLogic.addItem(human, itemID, itemCnt, LOGTAG)
- end
- BzcsLog.logAction("fight_reward", string.format(
- "uuid=%s atkWin=%s rewardId=%s itemCnt=%s items=%s",
- human.db._id, atkWin and 1 or 0, rewardId, #awardList, formatAwardList(awardList)
- ))
- end
- -- 发周期结算邮件(排名写入正文)
- local function sendMail(mailId, receiverUuid, itemArray, rank)
- local mailCfg = MailExcel.mail[mailId]
- if not mailCfg then
- BzcsLog.logAction("reward_mail_miss", string.format(
- "uuid=%s rank=%s mailId=%s", receiverUuid or "", rank or 0, mailId or 0
- ))
- return
- end
- if not itemArray or not next(itemArray) then
- BzcsLog.logAction("reward_mail_empty", string.format(
- "uuid=%s rank=%s mailId=%s", receiverUuid or "", rank or 0, mailId
- ))
- return
- end
- local content = mailCfg.content
- if rank then
- if rank > BaiZhanChengShenDefine.BZCS_RANK_MAX then
- rank = "100+"
- end
- content = Util.format(content, rank)
- end
- MailManager.add(MailManager.SYSTEM, receiverUuid, mailCfg.title, content, itemArray, mailCfg.senderName or "GM")
- BzcsLog.logAction("reward_mail_ok", string.format(
- "uuid=%s rank=%s mailId=%s itemCnt=%s items=%s",
- receiverUuid, rank or 0, mailId, #itemArray, formatAwardList(itemArray)
- ))
- end
- -- 批量发奖队列(分批发邮件, 失败重试)
- local function createRewardQueue()
- local q = {playerArray = {}, insertMaxNum = 100, repeatMaxTimes = 3, repeatTb = {}}
- function q:add(info) table.insert(self.playerArray, info) end
- function q:insertDB()
- local maxNum = math.min(self.insertMaxNum, #self.playerArray)
- for _ = 1, maxNum do
- local info = table.remove(self.playerArray)
- local uuid, rank = info[1], info[2]
- local itemArray = getRankReward(rank)
- if not itemArray then
- BzcsLog.logAction("reward_cfg_miss", string.format("uuid=%s rank=%s", uuid or "", rank or 0))
- else
- local ok, err = pcall(sendMail, BaiZhanChengShenDefine.BZCS_AWARD_MAIL_ID, uuid, itemArray, rank)
- if not ok then
- local retry = (self.repeatTb[uuid] or 0) + 1
- if retry <= self.repeatMaxTimes then
- self.repeatTb[uuid] = retry
- q:add(info)
- BzcsLog.logAction("reward_mail_fail", string.format(
- "uuid=%s rank=%s retry=%s err=%s", uuid, rank, retry, err
- ))
- else
- BzcsLog.logAction("reward_mail_giveup", string.format(
- "uuid=%s rank=%s retry=%s err=%s", uuid, rank, retry, err
- ))
- end
- end
- end
- end
- if #self.playerArray > 0 then
- Timer.addLater(3, self.insertDB, self)
- end
- end
- return q
- end
- -- bzcs_Battle_Cache: defRank/defUuid/defServerId/defName/defScore/isRobot, raceIdx, atkW/defW, rounds
- -- 战报尾插; 超上限删最老; 下发 GC 时倒序
- local function addWarReport(human, report)
- local db = getBzcsDb(human)
- db.warReport = db.warReport or {}
- db.warReport[#db.warReport + 1] = report
- if #db.warReport > BaiZhanChengShenDefine.BZCS_WARREPORT_MAX then
- table.remove(db.warReport, 1)
- end
- end
- -- 是否仍有挑战次数(免费或道具115); 仅校验不扣
- local function canChallenge(human)
- local db = getBzcsDb(human)
- if db.freeTimes > 0 then
- return true
- end
- return BagLogic.getItemCnt(human, BaiZhanChengShenDefine.BZCS_TICKET_ITEM_ID) >= BaiZhanChengShenDefine.BZCS_TICKET_COST
- end
- -- 优先扣免费次数, 否则扣门票; 整场仅在 C2N_CanFight 扣 1 次
- local function deductChallengeTimes(human)
- local db = getBzcsDb(human)
- if db.freeTimes > 0 then
- db.freeTimes = db.freeTimes - 1
- return true
- end
- if BagLogic.getItemCnt(human, BaiZhanChengShenDefine.BZCS_TICKET_ITEM_ID) < BaiZhanChengShenDefine.BZCS_TICKET_COST then
- return false
- end
- BagLogic.delItem(human, BaiZhanChengShenDefine.BZCS_TICKET_ITEM_ID, BaiZhanChengShenDefine.BZCS_TICKET_COST, LOGTAG)
- return true
- end
- -- 本轮首次完成挑战时向跨服注册, 每轮仅一次
- local function tryRegisterToCross(human)
- local db = getBzcsDb(human)
- if db.crossRegistered then
- return
- end
- local reg = InnerMsg.lw.LW_BZCS_REGISTER
- reg.playerInfo = {
- uuid = human.db._id,
- serverId = Config.SVR_INDEX,
- firstJoinTime = os.time(),
- showInfo = buildShowInfo(human),
- }
- InnerMsg.sendMsg(0, reg)
- BzcsLog.logAction("register_send", string.format("uuid=%s serverId=%s", human.db._id, Config.SVR_INDEX))
- end
- -- 按 raceIdx 开打当前族; 机器人本地战, 真人跨服
- local function startRaceCombat(human, cache)
- local raceIdx = cache.raceIdx
- local combatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[raceIdx]
- if cache.isRobot == 1 then
- CombatLogic.combatBegin(human, nil, {cache.defUuid, raceIdx}, combatType)
- else
- MiddleCommonLogic.MiddleCommonLogic_CombatBegin_LW(human, {
- combatType = combatType,
- nServerIndex = cache.defServerId,
- param = cache.defUuid,
- })
- end
- end
- -- 5 局系列结束: 注册跨服(首次) -> LW_BZCS_FIGHT_END, 攻方战报, 清 cache
- local function challengeFinish(human, cache)
- if not isMiddleReady() then
- BzcsLog.logAction("fight_end_middle_down", string.format("uuid=%s def=%s", human.db._id, cache.defUuid or ""))
- if human.fd then
- Broadcast.sendErr(human, Lang.MIDDLE_SVR_ERR_CONNECT)
- end
- human.bzcs_Battle_Cache = nil
- return
- end
- -- 须先于 FIGHT_END: 跨服 UpdateScore 依赖 playerList 中已有攻方
- tryRegisterToCross(human)
- local atkWin = cache.atkW >= BaiZhanChengShenDefine.BZCS_WIN_TARGET
- local msgData = InnerMsg.lw.LW_BZCS_FIGHT_END
- msgData.atkUuid = human.db._id
- msgData.atkServerId = Config.SVR_INDEX
- msgData.atkName = human.db.name
- msgData.defUuid = cache.defUuid
- msgData.defName = cache.defName
- msgData.defServerId = cache.defServerId
- msgData.atkWin = atkWin and 1 or 0
- msgData.raceResults = cache.rounds
- InnerMsg.sendMsg(0, msgData)
- BzcsLog.logAction("fight_end_send", string.format(
- "atk=%s def=%s atkWin=%s isRobot=%s atkW=%s defW=%s",
- human.db._id, cache.defUuid or "", msgData.atkWin, cache.isRobot or 0, cache.atkW, cache.defW
- ))
- local scoreChange = atkWin and BaiZhanChengShenDefine.BZCS_ATK_WIN_SCORE or BaiZhanChengShenDefine.BZCS_ATK_LOSE_SCORE
- addWarReport(human, {
- warType = atkWin and BaiZhanChengShenDefine.BZCS_WAR_TYPE_ATK_WIN or BaiZhanChengShenDefine.BZCS_WAR_TYPE_ATK_LOSE,
- oppServerId = cache.defServerId or 0,
- oppName = cache.defName or "",
- scoreChange = scoreChange,
- isRobot = cache.isRobot or 0,
- })
- human.bzcs_Battle_Cache = nil
- end
- ------------------------------------ 客户端请求 (Handler.lua -> BZCS_*) ------------------------------------
- -- 由 Handler.lua 转发; 需跨服的走 sendLwToMiddle, 仅本地数据的直接 GC
- -- 请求跨服匹配列表; forceRefresh=true 时全量重匹配
- local function requestMatchList(human, forceRefresh)
- if human.fd then
- ObjHuman.updateDaily(human)
- end
- getBzcsDb(human)
- if not isServerEligible() then
- return Broadcast.sendErr(human, Lang.COMMOM_NOT_ENABLED)
- end
- if not actStartTimeCheck() then
- return Broadcast.sendErr(human, Lang.COMMOM_NOT_ENABLED)
- end
- if forceRefresh then
- clearMatchCache(human)
- end
- if isMatchCacheFresh(human) then
- local cache = getMatchCache(human)
- return sendMatchListGC(human, cache.myScore, cache.opponentList)
- end
- if not isMiddleReady() then
- return Broadcast.sendErr(human, Lang.MIDDLE_SVR_ERR_CONNECT)
- end
- local cache = getMatchCache(human)
- local msgData = InnerMsg.lw.LW_BZCS_MATCH
- msgData.sourceServerId = Config.SVR_INDEX
- msgData.playerUuid = human.db._id
- msgData.refreshRanks = {}
- if cache and cache.opponentList and next(cache.opponentList)
- and (cache.myScore or 0) == (cache.matchScore or 0) then
- msgData.refreshRanks = collectRefreshRanks(cache.opponentList)
- end
- if not sendLwToMiddle(human, msgData) then
- return
- end
- end
- -- 匹配主界面: 下发 myScore + 3 名对手(score/name/power)
- -- 内存缓存未过期直返; 过期则跨服按 rank 刷新展示; 无缓存则全量匹配
- function BZCS_MatchList(human)
- requestMatchList(human, false)
- end
- -- 客户端刷新匹配对手(清缓存后跨服按积分±500步进重匹配3人, 回包 GC_BZCS_MATCH_LIST)
- function BZCS_MatchRefresh(human)
- if not actStartTimeCheck() then
- return Broadcast.sendErr(human, Lang.COMMOM_NOT_ENABLED)
- end
- if human.bzcs_Battle_Cache then
- return Broadcast.sendErr(human, Lang.DATA_ERR)
- end
- requestMatchList(human, true)
- end
- -- 排行榜前 100
- function BZCS_RankList(human)
- local msgData = InnerMsg.lw.LW_BZCS_RANK_LIST
- msgData.sourceServerId = Config.SVR_INDEX
- msgData.playerUuid = human.db._id
- sendLwToMiddle(human, msgData)
- end
- -- 对手头像/战力/积分(按全服名次 rank, 跨服实时解析该名次上的玩家)
- function BZCS_OpponentInfo(human, rank)
- local opp = resolveMatchOpponentByRank(human, rank)
- if not opp then
- return Broadcast.sendErr(human, Lang.BZCS_MATCH_OPPONENT_INVALID)
- end
- local msgData = InnerMsg.lw.LW_BZCS_OPPONENT_INFO
- msgData.sourceServerId = Config.SVR_INDEX
- msgData.playerUuid = human.db._id
- msgData.targetRank = rank
- sendLwToMiddle(human, msgData)
- end
- -- 对手五族阵容(按全服名次 rank)
- function BZCS_OpponentLineup(human, rank)
- local opp = resolveMatchOpponentByRank(human, rank)
- if not opp then
- return Broadcast.sendErr(human, Lang.BZCS_MATCH_OPPONENT_INVALID)
- end
- local msgData = InnerMsg.lw.LW_BZCS_OPPONENT_LINEUP
- msgData.sourceServerId = Config.SVR_INDEX
- msgData.playerUuid = human.db._id
- msgData.targetRank = rank
- sendLwToMiddle(human, msgData)
- end
- -- 己方五族阵容(仅读本地, 不访问跨服)
- function BZCS_MyLineup(human)
- local msgRet = Msg.gc.GC_BZCS_MY_LINEUP
- msgRet.teamList[0] = BaiZhanChengShenDefine.BZCS_RACE_CNT
- for i, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- local combatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[race]
- local show = getRaceLineupShow(human, race)
- msgRet.teamList[i].race = race
- fillBzcsTeamNet(msgRet.teamList[i], show, human, combatType)
- end
- Msg.send(msgRet, human.fd)
- end
- -- 本地战报列表(存储最老->最新, 协议仍新记录在前)
- function BZCS_WarReport(human)
- local db = getBzcsDb(human)
- local list = db.warReport or {}
- local total = #list
- local cnt = math.min(total, BaiZhanChengShenDefine.BZCS_WARREPORT_MAX, #Msg.gc.GC_BZCS_WAR_REPORT.reportList)
- local msgRet = Msg.gc.GC_BZCS_WAR_REPORT
- msgRet.reportList[0] = cnt
- for j = 1, cnt do
- local r = list[total - j + 1]
- local net = msgRet.reportList[j]
- net.warType = r.warType or 0
- local isRobot = r.isRobot or 0
- if isRobot ~= 1 and (r.oppServerId or 0) == 0 then
- local warType = r.warType or 0
- if warType == BaiZhanChengShenDefine.BZCS_WAR_TYPE_ATK_WIN
- or warType == BaiZhanChengShenDefine.BZCS_WAR_TYPE_ATK_LOSE then
- isRobot = 1
- end
- end
- net.oppServerId = BaiZhanChengShenDefine.ToClientServerId(r.oppServerId, isRobot)
- net.oppName = r.oppName or ""
- net.scoreChange = r.scoreChange or 0
- end
- Msg.send(msgRet, human.fd)
- end
- -- 排名奖励配置预览(读 excel)
- function BZCS_RankReward(human)
- local msgRet = Msg.gc.GC_BZCS_RANK_REWARD
- msgRet.rewardList[0] = #BzcsConfig.rankReward
- for k, v in ipairs(BzcsConfig.rankReward) do
- msgRet.rewardList[k].rankLeft = v.rankArea[1]
- msgRet.rewardList[k].rankRight = v.rankArea[2]
- local items = msgRet.rewardList[k].itemList
- items[0] = #v.awardList
- for idx, itemCfg in ipairs(v.awardList) do
- Grid.makeItem(items[idx], itemCfg[1], itemCfg[2])
- end
- end
- Msg.send(msgRet, human.fd)
- end
- ------------------------------------ 跨服通知本地服 (InnerHandler.lua -> C2N_*) ------------------------------------
- -- 跨服 WL 回调, 组装 GC 或写本地数据
- -- WL_BZCS_ACT_START: 记录本轮开始时间(重连补偿广播也会走到这里)
- function C2N_Act_Start(msg)
- local oldStart = CommonDB.getValueByKey(CommonDB.KEY_BZCS_START_TIME) or 0
- local resetCnt = 0
- if msg.startTime and msg.startTime > 0 then
- CommonDB.updateValue(CommonDB.KEY_BZCS_START_TIME, msg.startTime)
- for uuid, human in pairs(ObjHuman.onlineUuid) do
- if human.db and human.db.baiZhanChengShen then
- local actStart = msg.startTime
- if (human.db.baiZhanChengShen.actStartTime or 0) ~= actStart then
- human.db.baiZhanChengShen.actStartTime = actStart
- clearRoundLocalData(human.db.baiZhanChengShen)
- clearMatchCache(human)
- resetCnt = resetCnt + 1
- end
- end
- end
- end
- BzcsLog.logAction("act_open", string.format(
- "svr=%s start=%s end=%s oldStart=%s online=%s resetCnt=%s",
- Config.SVR_INDEX, msg.startTime or 0, msg.endTime or 0, oldStart,
- Util.getTableCount(ObjHuman.onlineUuid), resetCnt
- ))
- end
- -- WL_BZCS_ACT_END: 清除活动开始时间, 并清空在线玩家本轮本地战报/匹配缓存
- function C2N_Act_End(msg)
- local oldStart = CommonDB.getValueByKey(CommonDB.KEY_BZCS_START_TIME) or 0
- CommonDB.updateValue(CommonDB.KEY_BZCS_START_TIME, nil)
- local resetCnt = 0
- for uuid, human in pairs(ObjHuman.onlineUuid) do
- if human.db and human.db.baiZhanChengShen then
- clearRoundLocalData(human.db.baiZhanChengShen)
- human.db.baiZhanChengShen.actStartTime = 0
- clearMatchCache(human)
- resetCnt = resetCnt + 1
- end
- end
- BzcsLog.logAction("act_close", string.format(
- "svr=%s oldStart=%s online=%s resetCnt=%s",
- Config.SVR_INDEX, oldStart, Util.getTableCount(ObjHuman.onlineUuid), resetCnt
- ))
- end
- -- WL_BZCS_TIPS: 跨服业务错误(活动未开/对手无效等)
- function C2N_ErrTips(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- if not human then return end
- local errCode = msg.errCode
- if errCode == BaiZhanChengShenDefine.BZCS_ERR_NOT_OPEN then
- return Broadcast.sendErr(human, Lang.COMMOM_NOT_ENABLED)
- elseif errCode == BaiZhanChengShenDefine.BZCS_ERR_TARGET_INVALID then
- return Broadcast.sendErr(human, Lang.BZCS_MATCH_OPPONENT_INVALID)
- elseif errCode == BaiZhanChengShenDefine.BZCS_ERR_NO_TIMES then
- return Broadcast.sendErr(human, Lang.ITEM_NOT_ENOUGH)
- end
- Broadcast.sendErr(human, Lang.DATA_ERR)
- end
- -- WL_BZCS_MATCH -> GC_BZCS_MATCH_LIST, 写入内存匹配缓存
- function C2N_Match(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- if not human then return end
- local myScore = msg.myScore or BaiZhanChengShenDefine.BZCS_INIT_SCORE
- local opponentList = msg.opponentList or {}
- local cache = getMatchCache(human)
- local isDisplayRefresh = cache and cache.matchScore and cache.matchScore == myScore
- and cache.opponentList and next(cache.opponentList)
- if isDisplayRefresh then
- refreshMatchCacheDisplay(human, myScore, opponentList)
- else
- setMatchCache(human, myScore, opponentList)
- end
- sendMatchListGC(human, myScore, opponentList)
- end
- local function fillBzcsRankNet(net, info)
- info = info or {}
- net.rank = info.rank or 0
- net.name = info.name or ""
- net.head = info.head or 0
- net.headFrame = info.headFrame or 0
- net.power = info.power or 0
- net.score = info.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE
- net.serverId = BaiZhanChengShenDefine.ToClientServerId(info.serverId, info.isRobot or 0)
- net.uuid = info.uuid or ""
- end
- -- 未上榜/未注册跨服时, 用本地五族阵容实时战力(同 CombatPosLogic.getCombatHeroZDL)
- local function calcLocalBzcsPower(human)
- local total = 0
- for _, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- local combatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[race]
- total = total + CombatPosLogic.getCombatHeroZDL(human, combatType)
- end
- return total
- end
- local function enrichMyRankInfoForClient(human, myInfo)
- myInfo = myInfo or {}
- if (myInfo.rank or 0) > 0 then
- return myInfo
- end
- return {
- rank = 0,
- uuid = human.db._id,
- name = human.db.name or "",
- head = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_1),
- headFrame = RoleHeadLogic.getRoleAppearance(human, RoleHeadLogic.HEAD_TYPE_2),
- serverId = Config.SVR_INDEX,
- power = calcLocalBzcsPower(human),
- score = myInfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE,
- }
- end
- -- WL_BZCS_RANK_LIST -> GC_BZCS_RANK_LIST
- function C2N_RankList(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- if not human then return end
- local msgRet = Msg.gc.GC_BZCS_RANK_LIST
- local myInfo = enrichMyRankInfoForClient(human, msg.myRankInfo)
- fillBzcsRankNet(msgRet.myRankInfo, myInfo)
- msgRet.rankList[0] = #(msg.rankList or {})
- for i, info in ipairs(msg.rankList or {}) do
- fillBzcsRankNet(msgRet.rankList[i], info)
- end
- Msg.send(msgRet, human.fd)
- end
- -- WL_BZCS_OPPONENT_INFO -> GC_BZCS_OPPONENT_INFO
- function C2N_OpponentInfo(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- if not human then return end
- if msg.res ~= 0 then
- return Broadcast.sendErr(human, Lang.DATA_ERR)
- end
- local t = msg.targetInfo or {}
- local msgRet = Msg.gc.GC_BZCS_OPPONENT_INFO
- msgRet.name = t.name or ""
- msgRet.head = t.head or 0
- msgRet.headFrame = t.headFrame or 0
- msgRet.power = t.power or 0
- msgRet.score = t.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE
- Msg.send(msgRet, human.fd)
- end
- -- WL_BZCS_OPPONENT_LINEUP -> GC_BZCS_OPPONENT_LINEUP
- function C2N_OpponentLineup(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- if not human then return end
- local msgRet = Msg.gc.GC_BZCS_OPPONENT_LINEUP
- msgRet.teamList[0] = BaiZhanChengShenDefine.BZCS_RACE_CNT
- local heroArr = msg.showInfo and msg.showInfo.heroArr or {}
- for i, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- local show = heroArr[race] or {}
- msgRet.teamList[i].race = race
- fillBzcsTeamNet(msgRet.teamList[i], show, nil, nil)
- end
- Msg.send(msgRet, human.fd)
- end
- -- WL_BZCS_ISSUE_REWARD: 本服批量发周期结算邮件(rewardList={{uuid,rank},...})
- function C2N_IssueReward(msg)
- local rewardList = msg.rewardList
- if not rewardList or #rewardList == 0 then
- return
- end
- BzcsLog.logAction("reward_issue_ns", string.format("cnt=%s svr=%s", #rewardList, Config.SVR_INDEX))
- local q = createRewardQueue()
- for _, info in ipairs(rewardList) do
- q:add({info[1], info[2]})
- end
- q:insertDB()
- end
- ------------------------------------ 战斗相关 (CombatLogic / InnerHandler) ------------------------------------
- -- CG_COMBAT_BEGIN / CombatLogic.moduleFn / CombatPosLogic / InnerHandler C2N 战斗回调
- -- combatType -> 五族场次下标(1~5)
- local function getBzcsRaceIdxByCombatType(combatType)
- for raceIdx, t in ipairs(BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE) do
- if t == combatType then
- return raceIdx
- end
- end
- end
- -- CG_COMBAT_BEGIN: 挑战一次依次调用 TYPE39~43; param=全服名次rank(须在内存匹配缓存中)
- function fight(human, args, combatType)
- local raceIdx = getBzcsRaceIdxByCombatType(combatType)
- if not raceIdx then
- return Broadcast.sendErr(human, Lang.DATA_ERR)
- end
- if raceIdx == 1 then
- -- 首场: 校验次数/道具、五族阵容, 请求跨服 CAN_FIGHT(扣次在 C2N_CanFight)
- ObjHuman.updateDaily(human)
- local rank = tonumber(args[1])
- if not rank or rank < 1 then
- return Broadcast.sendErr(human, Lang.BZCS_MATCH_OPPONENT_INVALID)
- end
- if human.bzcs_Battle_Cache then
- return Broadcast.sendErr(human, Lang.BZCS_FIGHT_STATE_ERR)
- end
- if not actStartTimeCheck() then
- return Broadcast.sendErr(human, Lang.COMMOM_NOT_ENABLED)
- end
- if not isMiddleReady() then
- return Broadcast.sendErr(human, Lang.MIDDLE_SVR_ERR_CONNECT)
- end
- if not canChallenge(human) then
- return Broadcast.sendErr(human, Lang.ITEM_NOT_ENOUGH)
- end
- if not checkAllRaceLineupForFight(human) then
- return
- end
- if not resolveMatchOpponentByRank(human, rank) then
- return Broadcast.sendErr(human, Lang.BZCS_MATCH_OPPONENT_INVALID)
- end
- local msgData = InnerMsg.lw.LW_BZCS_CAN_FIGHT
- msgData.sourceServerId = Config.SVR_INDEX
- msgData.playerUuid = human.db._id
- msgData.targetRank = rank
- sendLwToMiddle(human, msgData)
- return
- end
- -- 第 2~5 场: 已有 bzcs_Battle_Cache 则直接开打, 不再请求跨服/扣次
- local cache = human.bzcs_Battle_Cache
- if not cache then
- return Broadcast.sendErr(human, Lang.BZCS_FIGHT_STATE_ERR)
- end
- if cache.atkW >= BaiZhanChengShenDefine.BZCS_WIN_TARGET
- or cache.defW >= BaiZhanChengShenDefine.BZCS_WIN_TARGET then
- return Broadcast.sendErr(human, Lang.BZCS_FIGHT_STATE_ERR)
- end
- cache.raceIdx = raceIdx
- startRaceCombat(human, cache)
- end
- function getCombatMonsterOutID(human, side, args)
- if side ~= CombatDefine.DEFEND_SIDE then return end
- local defUuid = args[1]
- local raceIdx = args[2]
- if not defUuid or not raceIdx then return end
- local cfg = BaiZhanChengShenDefine.GetRobotListCfg(defUuid)
- if not cfg or not cfg.monsterOutIDs then return end
- local monsterOutID = cfg.monsterOutIDs[raceIdx]
- if not monsterOutID then return end
- return monsterOutID, BaiZhanChengShenDefine.CalcMonsterOutPower(monsterOutID)
- end
- function getCombatObjList(human, side, args, combatType)
- if side == CombatDefine.ATTACK_SIDE then
- if not human then return end
- return CombatLogic.getHumanObjList(human, combatType)
- end
- -- 防守方: args[2]=raceIdx 为本地机器人, 返回 nil 走 getCombatMonsterOutID
- if args[2] then
- return nil
- end
- local defUuid = args[1]
- if not defUuid then return end
- local db = RoleDBLogic.getDb(defUuid)
- if not db then return end
- return CombatLogic.getHumanObjList({db = db}, combatType)
- end
- function onFightEnd(human, result, combatType, cbParam, combatInfo)
- local cache = human.bzcs_Battle_Cache
- if not cache then return end
- local roundResult = (result == CombatDefine.RESULT_WIN) and 1 or 2
- cache.rounds[cache.raceIdx] = roundResult
- if result == CombatDefine.RESULT_WIN then
- cache.atkW = cache.atkW + 1
- else
- cache.defW = cache.defW + 1
- end
- combatInfo.defender.name = cache.defName
- local seriesOver = cache.atkW >= BaiZhanChengShenDefine.BZCS_WIN_TARGET
- or cache.defW >= BaiZhanChengShenDefine.BZCS_WIN_TARGET
- or cache.raceIdx >= BaiZhanChengShenDefine.BZCS_RACE_CNT
- if not seriesOver then
- cache.raceIdx = cache.raceIdx + 1
- combatInfo.nextCombatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[cache.raceIdx]
- return
- end
- -- 整场结束: 发挑战奖励并写入 combatInfo.rewardItem(GC_COMBAT_FINISH 展示)
- local atkWin = cache.atkW >= BaiZhanChengShenDefine.BZCS_WIN_TARGET
- grantFightReward(human, atkWin, combatInfo)
- challengeFinish(human, cache)
- end
- function checkUpdatePos(human, msg)
- local condiCamp = COMBATTYPE_2_CAMP[msg.type]
- if not condiCamp then return false end
- HeroLogic = HeroLogic or require("hero.HeroLogic")
- local heroList = Util.split(msg.heroList, ",")
- for i = 1, CombatDefine.COMBAT_HERO_CNT do
- local uuid = heroList[i] or ""
- if uuid ~= "0" and uuid ~= "" then
- local heroGrid = HeroLogic.getHeroGridByUuid(human, uuid)
- if not heroGrid then return false end
- local heroCfg = HeroExcel.hero[heroGrid.id]
- if not heroCfg or heroCfg.camp ~= condiCamp then
- return false
- end
- end
- end
- return true
- end
- function onCombatPosUpdate(human, combatType)
- local race = COMBATTYPE_2_CAMP[combatType]
- if not race then return end
- UpdateShowInfo(human, BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_LINEUP, race)
- end
- -- 百战成神阵容精灵位变更(CombatPosLogic.Elf_Pos_Update 调用)
- function onElfPosUpdate(human, combatType)
- onCombatPosUpdate(human, combatType)
- end
- -- 该族阵容精灵位是否包含指定 elfId
- local function raceLineupHasElf(elfList, elfId)
- if not elfList or not elfId or elfId < 1 then
- return false
- end
- for i = 1, CombatDefine.COMBAT_ELF_NOW_CNT do
- if elfList[i] == elfId then
- return true
- end
- end
- return false
- end
- -- 精灵升星后同步所有上阵了该精灵的百战成神队伍(同精灵可出现在多族阵容)
- function onElfStarUp(human, elfId)
- if not elfId or elfId < 1 then return end
- if not actStartTimeCheck() or not isMiddleReady() then return end
- local db = getBzcsDb(human)
- if not db.crossRegistered then return end
- for _, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- local combatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[race]
- local combatHeroDB = CombatPosLogic.getCombatHeroDB(human, combatType)
- if raceLineupHasElf(combatHeroDB and combatHeroDB.elfList, elfId) then
- UpdateShowInfo(human, BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_LINEUP, race)
- end
- end
- end
- -- 该族阵容是否包含指定英雄 uuid
- local function raceLineupHasHero(heroList, heroUuid)
- if not heroUuid or heroUuid == "" or heroUuid == "0" then
- return false
- end
- if not heroList then return false end
- for _, uuid in pairs(heroList) do
- if uuid == heroUuid then
- return true
- end
- end
- return false
- end
- -- 收集百战成神五族中上阵了该英雄的种族列表(删除英雄前调用)
- function collectBzcsRacesWithHero(human, heroUuid)
- local races = {}
- if not heroUuid or heroUuid == "" or heroUuid == "0" then
- return races
- end
- for _, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
- local combatType = BaiZhanChengShenDefine.BZCS_RACE_COMBAT_TYPE[race]
- local heroList = CombatPosLogic.getCombatHeros(human, combatType)
- if raceLineupHasHero(heroList, heroUuid) then
- races[#races + 1] = race
- end
- end
- return races
- end
- -- 英雄数据变更后同步跨服阵容; raceList 可选(重生等删除英雄后传入删除前收集的种族)
- function onHeroDataUpdate(human, heroUuid, raceList)
- if not actStartTimeCheck() or not isMiddleReady() then return end
- local db = getBzcsDb(human)
- if not db.crossRegistered then return end
- local races = raceList
- if not races or #races < 1 then
- if not heroUuid or heroUuid == "" or heroUuid == "0" then return end
- races = collectBzcsRacesWithHero(human, heroUuid)
- end
- if #races < 1 then return end
- for _, race in ipairs(races) do
- UpdateShowInfo(human, BaiZhanChengShenDefine.BZCS_UPDATE_SHOW_LINEUP, race)
- end
- end
- -- WL_BZCS_CAN_FIGHT: 扣次并初始化 bzcs_Battle_Cache, 开打第一族
- function C2N_CanFight(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- if not human then return end
- if msg.res ~= 0 then
- return Broadcast.sendErr(human, Lang.DATA_ERR)
- end
- if not deductChallengeTimes(human) then
- return Broadcast.sendErr(human, Lang.ITEM_NOT_ENOUGH)
- end
- human.bzcs_Battle_Cache = {
- defRank = msg.targetRank,
- defUuid = msg.defUuid,
- defServerId = msg.defServerId,
- defName = msg.defName,
- defScore = msg.defScore,
- isRobot = msg.isRobot,
- raceIdx = 1,
- atkW = 0,
- defW = 0,
- rounds = {},
- }
- BzcsLog.logAction("can_fight", string.format(
- "atk=%s defRank=%s def=%s defScore=%s isRobot=%s defSvr=%s",
- msg.playerUuid or "", msg.targetRank or 0, msg.defUuid or "", msg.defScore or 0, msg.isRobot or 0, msg.defServerId or 0
- ))
- startRaceCombat(human, human.bzcs_Battle_Cache)
- end
- -- WL_BZCS_FIGHT_END: 攻方积分变化, 重置匹配缓存与 TTL
- function C2N_FightEnd(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- if not human then return end
- local db = getBzcsDb(human)
- db.crossRegistered = true
- resetMatchCacheOnScoreChange(human, msg.myScore)
- BzcsLog.logAction("fight_end_ns", string.format("uuid=%s atkWin=%s scoreChange=%s myScore=%s", msg.playerUuid or "", msg.atkWin or 0, msg.scoreChange or 0, msg.myScore or 0))
- end
- -- WL_BZCS_DEF_NOTIFY: 防守方战报(在线/离线)
- function C2N_DefNotify(msg)
- local human = ObjHuman.onlineUuid[msg.playerUuid]
- local offlineSave = false
- if not human then
- local db = RoleDBLogic.getDb(msg.playerUuid, {baiZhanChengShen = 1})
- if not db then return end
- human = {db = db}
- offlineSave = true
- end
- local defWin = msg.atkWin == 1
- addWarReport(human, {
- warType = defWin and BaiZhanChengShenDefine.BZCS_WAR_TYPE_DEF_WIN or BaiZhanChengShenDefine.BZCS_WAR_TYPE_DEF_LOSE,
- oppServerId = msg.atkServerId or 0,
- oppName = msg.atkName or "",
- scoreChange = msg.scoreChange or 0,
- })
- if human.fd then
- resetMatchCacheOnScoreChange(human, msg.myScore)
- elseif offlineSave then
- RoleDBLogic.saveRoleSset(human.db)
- end
- end
- ------------------------------------ 对外接口 ------------------------------------
- -- 供红点/活动状态/阵容展示同步等其它模块调用
- -- 跨天重置免费挑战次数(由 ObjHuman.updateDaily 调用)
- function updateDaily(human)
- if not human.db.baiZhanChengShen then
- return
- end
- human.db.baiZhanChengShen.freeTimes = BaiZhanChengShenDefine.BZCS_FREE_TIMES
- end
- local ACT_STATE_NOOPEN = 0 -- 活动未开启
- local ACT_STATE_START = 2 -- 活动进行中(与 JjcActLogic.STATE_START 一致)
- -- 本轮活动剩余秒数(至周日23:00)
- local function calcActLeftSec()
- local startTime = CommonDB.getValueByKey(CommonDB.KEY_BZCS_START_TIME)
- if not startTime or startTime == 0 or not IsRunning(startTime) then
- return 0
- end
- local endDayOffset = BaiZhanChengShenDefine.GetOpenEndDayOffset()
- local endTime = Util.getDayStartTime(startTime) + endDayOffset * 86400
- + BaiZhanChengShenDefine.BZCS_END_SEC
- local now = os.time()
- if endTime > now then
- return endTime - now
- end
- return 0
- end
- -- 百战成神活动是否处于开启状态(挑战日 + 跨服已开轮且在活动期内)
- function ModuleisOpen(human)
- return actStartTimeCheck()
- end
- -- 玩法状态与剩余时间: state 0=未开启 2=进行中; leftSec=剩余秒数
- function getActState(human)
- if not actStartTimeCheck() then
- return ACT_STATE_NOOPEN, 0
- end
- return ACT_STATE_START, calcActLeftSec()
- end
- -- 活动红点: 可参与且仍有免费挑战次数
- function isActRed(human)
- if not isServerEligible() then return false end
- if not actStartTimeCheck() then return false end
- local db = getBzcsDb(human)
- return db.freeTimes > 0
- end
- -- joinTime 为跨服下发的本轮开始时间; 无参时仅判断星期
- function IsRunning(joinTime)
- if not joinTime then
- return true
- end
- local wDay = getWDay()
- if wDay > BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[2]
- and wDay < BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[1] then
- return false
- end
- local now = os.time()
- if now < joinTime then
- return false
- end
- local endDayOffset = BaiZhanChengShenDefine.GetOpenEndDayOffset()
- local endTime = Util.getDayStartTime(joinTime) + endDayOffset * 86400
- + BaiZhanChengShenDefine.BZCS_END_SEC
- if now > endTime then
- return false
- end
- local dayStart = Util.getDayStartTime(now)
- if wDay == BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[2]
- and now > (dayStart + BaiZhanChengShenDefine.BZCS_END_SEC) then
- return false
- end
- local diffDays = Util.diffDay(joinTime)
- if diffDays > BaiZhanChengShenDefine.BZCS_OPEN_DAYS then
- return false
- end
- return true
- end
- -- 增量同步跨服展示(LW_BZCS_UPDATE_SHOW, 须已注册)
- -- updateType: BZCS_UPDATE_SHOW_NAME/HEAD/HEAD_FRAME/BODY/LINEUP; LINEUP 时 race 必填(1~5)
- function UpdateShowInfo(human, updateType, race)
- if not actStartTimeCheck() or not isMiddleReady() then return end
- if not updateType then return end
- local db = getBzcsDb(human)
- if not db.crossRegistered then
- return
- end
- local patch = buildShowPatch(human, updateType, race)
- if not patch then return end
- local msgData = InnerMsg.lw.LW_BZCS_UPDATE_SHOW
- msgData.playerUuid = human.db._id
- msgData.updateType = updateType
- msgData.race = race or 0
- msgData.showInfo = patch
- InnerMsg.sendMsg(0, msgData)
- BzcsLog.logAction("update_show_send", string.format("uuid=%s type=%s race=%s", human.db._id, updateType, race or 0))
- end
|