local Passive = require "adv.AdvPassive" local AdvCommon = require "adv.AdvCommon" local AdvMap = require "adv.AdvMap" local Adv = class("Adv") function Adv:ctor(owner) assert(owner, "Adv instance must have owner(role)") self.owner = owner self.maps = {} self.battle = nil self.backEvents = {} --发给客户端的事件组 self:initByInfo(self.owner:getProperty("advInfo")) end --初始化adv 信息 function Adv:initByInfo(advInfo) if not next(advInfo) then return end --还没有 开始新地图 self.chapterId = advInfo.chapterId self.level = advInfo.level or 1 self.score = advInfo.score or {} self.lastEnemyId = advInfo.lastEId or 1 self.mapStack = advInfo.mstack or {} self.maps = {} for id, map in ipairs(advInfo.maps or {}) do self.maps[id] = AdvMap.new(self, id, map) end self:initBattle() end -- 随机新的地图 function Adv:initByChapter(chapterId, level, isToNext, notNotify) if self.chapterId and chapterId ~= self.chapterId then return end --新的关卡 或者 去到下一层 self.chapterId = chapterId self.level = level or 1 self.score = self.score or {} self.lastEnemyId = 1 self.mapStack = {1} -- 最后一个为当前的地图 -- 随机出地图 local mapId = self:randomMapId(chapterId, level) self.maps = {} self.maps[1] = AdvMap.new(self, 1, mapId) if isToNext then self:scoreChange(AdvScoreType.Level) --增加层级加分 end self:initBattle() if not notNotify then self:saveDB(notNotify) end end function Adv:clear() self.chapterId = nil self.level = nil self.score = {} self.lastEnemyId = 1 self.mapStack = {} self.maps = {} self.battle = nil end function Adv:saveDB(notNotify) local advInfo, advTeam = {}, self.owner:getProperty("advTeam") if self.chapterId then advInfo.chapterId = self.chapterId advInfo.level = self.level advInfo.score = self.score advInfo.lastEId = self.lastEnemyId advInfo.mstack = self.mapStack advInfo.maps = {} self.battle:saveDB() for id , map in ipairs(self.maps) do advInfo.maps[id] = map:getDB() end advTeam.player = self.battle.player:getDB() else advTeam.player = nil end self.owner:updateProperties({advInfo = advInfo, advTeam = advTeam}, notNotify) end function Adv:initBattle() self.battle = require("adv.AdvBattle").new(self) for _, passiveC in ipairs(self.cachePassiveEvent or {}) do self.battle:triggerPassive(passiveC[1], passiveC[2]) end self.cachePassiveEvent = {} end function Adv:triggerPassive(condType, params) self.cachePassiveEvent = self.cachePassiveEvent or {} if not self.battle then table.insert(self.cachePassiveEvent, {condType, params}) else self.battle:triggerPassive(condType, params) end end function Adv:getCurMap() return self.maps[self.mapStack[#self.mapStack]] end function Adv:getCurMapIdx() return self.mapStack[#self.mapStack] end function Adv:getRoom(roomId, mapIdx) mapIdx = mapIdx or self:getCurMapIdx() local map = self.maps[mapIdx] if map then return map.rooms[roomId] end end function Adv:getBlock(roomId, blockId, mapIdx) local room = self:getRoom(roomId, mapIdx) if room then return room.blocks[blockId] end end --关卡通关,非层 score < 0 失败 function Adv:over(success) local score = self:getScore() local scoreInfo = self.score local reward if success then local advPass = self.owner:getProperty("advPass") if self.level > (advPass[self.chapterId] or 0) then self.owner:changeUpdates({{type = "advPass", field = self.chapterId, value = self.level}}) end reward = self.owner:award(self.owner:getProperty("advItems"):toNumMap()) self.owner:checkTaskEnter(self.owner.TaskType.AdvPass, {id = self.chapterId}) end self:clear() self.owner:updateProperty({field = "advItems", value = ""}) self:backEnd(success, score, scoreInfo, reward) end function Adv:exit() self:over(false) self:saveDB() end function Adv:randomMapId(chapterId, level) local chapterData = csvdb["adv_chapterCsv"][chapterId] if not chapterData then error("chapterId " .. chapterId .. " dont exist!") return end if level > chapterData.limitlevel then error("level overflow!") return end --随出地图Id local raw_pool = chapterData.mapid:toArray(true, "=") local lastMapIds = {} for id, map in ipairs(self.maps or {}) do lastMapIds[map.mapId] = 1 end local pool = {} for _, mapId in ipairs(raw_pool) do local temp = csvdb["mapCsv"][mapId] if temp and not lastMapIds[mapId] then if AdvCommon.checkIsIn(level, temp.leveltype, temp.levellimit) then table.insert(pool, mapId) end end end if not next(pool) then error("mapIds is empty!") return end return pool[math.randomInt(1, #pool)] end -- 在冒险中获得的物品都发放在冒险背包内 function Adv:award(gift, params) params = params or {} local tgift = {} if type(gift) == "string" then for _, one in pairs(gift:toTableArray(true)) do tgift[one[1]] = (tgift[one[1]] or 0) + one[2] end else tgift = gift end local items = self.owner:getProperty("advItems") for itemId, count in pairs(tgift) do if count > 0 then self:scoreChange(AdvScoreType.Item, {itemId, count}) end local origin = items:getv(itemId, 0) local nums = origin + count if nums <= 0 then items = items:delk(itemId) nums = 0 else items = items:incrv(itemId, count) end end self.owner:updateProperty({field = "advItems", value = items, notNotify = params.notNotify}) return tgift end -- 消耗物品 优先冒险背包 --check 只是检查够不够 function Adv:cost(item, params, check) local items = self.owner:getProperty("advItems") local less = {} local advCost = {} for itemId, count in pairs(item) do advCost[itemId] = - math.min(items:getv(itemId, 0), count) if advCost[itemId] == 0 then advCost[itemId] = nil end local last = items:getv(itemId, 0) - count if last < 0 then less[itemId] = -last end end if next(less) and not self.owner:checkItemEnough(less) then return end --不够 if check then return true end self:award(advCost, params) self.owner:costItems(less, params) return true end --事件点击处理 local function clickOut(self, room, block, params) if self:getCurMap():checkOver() then --检查是否可以出去了 if #self.mapStack > 1 then -- 处于夹层中 table.remove(self.mapStack) --退出夹层 self:backLayer() else --处于底层 if self.level >= csvdb["adv_chapterCsv"][self.chapterId].limitlevel then --关卡结束 self:over(true) else self:initByChapter(self.chapterId, self.level + 1, true, true) self:backNext() --下一关 end end return true end end --战斗 普通攻击 local function clickMonster(self, room, block, params) self.battle:battleBegin(room.roomId, block.blockId, params) return true end local function clickChoose(self, room, block, params) local choose = params.choose local chooseData = csvdb["event_chooseCsv"][block.event.id] if not chooseData or not chooseData["button".. choose .."cond"] then return end local cond = chooseData["button".. choose .."cond"]:toArray(true, "=") local checkCond = { -- 拥有道具 [1] = function() if self:cost({[cond[2]] = cond[3]}, {}, true) then return true end end, -- xx角色(todo 队长) [2] = function() local hero = self.owner.heros[self.owner:getProperty("advTeam").leader] if hero and hero:getProperty("type") == cond[2] then return true end end, --消灭所有怪 [3] = function() for _, room in pairs(self:getCurMap().rooms) do for _, block in pairs(room.blocks) do if block.event and (block.event.etype == AdvEventType.BOSS or block.event.etype == AdvEventType.Monster) then return end end end return true end, --制定属性 > [4] = function() if (self.battle.player[AttsEnumEx[cond[2]]] or 0) > cond[3] then return true end end, } assert(not cond[1] or checkCond[cond[1]], "error cond, event_chooseCsv id :" .. block.event.id) if cond[1] and not checkCond[cond[1]]() then return end local clearBlock = true local effects = chooseData["button".. choose .."effect"]:toTableArray(true) for _, effect in ipairs(effects) do if effect[1] == 1 then local reward = csvdb["event_dropCsv"][effect[2]]["range"]:randWeight(true) effect[2] = reward[1] effect[3] = reward[2] end local doEffect = { [1] = function() -- 获得某道具N个 self:backReward(self:award({[effect[2]] = effect[3]}, {})) end, [2] = function() --获得冒险buff self.battle.player:addBuff(effect[2]) end, [3] = function() --发现怪物 self:getCurMap():addNewMonsterRand(effect[2], {room, block}) clearBlock = false end, [4] = function() --无事发生 end } assert(doEffect[effect[1]], "error effect, event_chooseCsv id :" .. block.event.id) doEffect[effect[1]]() end if clearBlock then block:clear() end return true end local function clickDrop(self, room, block, params) local reward = {} if not block.event.item then return end local reward = self:award({[block.event.item[1]] = block.event.item[2]}) block:clear() self:backReward(reward) return true end local function clickTrader(self, room, block, params) local buyId = params.id local traderData = csvdb["event_traderCsv"][block.event.id] if not traderData then return end -- 偷偷改表了 if not block.event.shop or not block.event.shop[buyId] then return end if (block.event.status or ""):getv(buyId, 0) == 1 then return end -- 买过了 if not self:cost({[traderData.type] = block.event.shop[buyId][3]}, {}) then return end --不够 local reward = self:award({[block.event.shop[buyId][1]] = block.event.shop[buyId][2]}) block.event.status = block.event.status:setv(buyId, 1) self:backReward(reward) return true end local function clickBuild(self, room, block, params) local buildData = csvdb["event_buildingCsv"][block.event.id] if not buildData then return end-- 偷偷改表了 if not block.event.effect then return end -- 没有效果 气人不 local clearBlock = true local effect = block.event.effect --todo 效果生效 local doEffect = { [1] = function() -- 获得某道具N个 self:backReward(self:award({[effect[2]] = effect[3]}, {})) end, [2] = function() --获得冒险buff self.battle.player:addBuff(effect[2]) end, [3] = function() --发现怪物 self:getCurMap():addNewMonsterRand(effect[2], {room, block}) clearBlock = false end, [4] = function() --无事发生 end } assert(doEffect[effect[1]], "error effect, event_buildingCsv id :" .. block.event.id) if not self:cost(buildData.required:toNumMap(), {}) then return end doEffect[effect[1]]() if clearBlock then block:clear() end return true end local function clickClick(self, room, block, params) local clickData = csvdb["event_clickCsv"][block.event.id] if not clickData then return end local clearBlock = true local doEffect = { [1] = function() -- 技能 for _, skillId in ipairs(clickData.effect:toArray(true, "=")) do self.battle.player:releaseSkill(skillId) end end, [2] = function() -- dropId local reward = {} for _, dropId in ipairs(clickData.effect:toArray(true, "=")) do local item = csvdb["event_dropCsv"][dropId]["range"]:randWeight(true) reward[item[1]] = (reward[item[1]] or 0) + reward[item[2]] end self:backReward(self:award(reward, {})) end, } if clearBlock then block:clear() end return true end local function clickLayer(self, room, block, params) if block.event.mapIdx then table.insert(self.mapStack, block.event.mapIdx) --进入夹层 else --生成夹层 local mapId = csvdb["event_layerCsv"][block.event.id].effect local mapIdx = #self.maps + 1 block.event.mapIdx = mapIdx table.insert(self.mapStack, mapIdx) self.maps[mapIdx] = AdvMap.new(self, mapIdx, mapId) self.battle:initMapEnemys(mapIdx) end self:backLayer() return true end local eventCallFunc = { [AdvEventType.Out] = clickOut, [AdvEventType.BOSS] = clickMonster, [AdvEventType.Monster] = clickMonster, [AdvEventType.Choose] = clickChoose, [AdvEventType.Drop] = clickDrop, [AdvEventType.Trader] = clickTrader, [AdvEventType.Build] = clickBuild, [AdvEventType.Click] = clickClick, [AdvEventType.Layer] = clickLayer, } --点击处理 roomId, blockId --params 某些事件需要的客户端传递的参数 function Adv:clickBlock(roomId, blockId, params) local map = self:getCurMap() local room = self:getRoom(roomId) local block = self:getBlock(roomId, blockId) if not block then return end local status = false local clickEvent = false if not block.isOpen then local canOpen = false --如果未开放是否可以开放 local hadMonster = false -- 周围是否有解锁的怪未击败 for _, one in ipairs(map:getAroundBlocks(room, block)) do local _room, _block = one[1], one[2] if _block.isOpen then canOpen = true end if _block.isOpen and _block:isMonster() then hadMonster = true end end if canOpen and not hadMonster then --开放 room:openBlock(block) status = true end else clickEvent = true --点了空地 if not block.event then return end --可点击的事件 if not room.isBossRoom or block:isBoss() then if eventCallFunc[block.event.etype] then status = eventCallFunc[block:getEventType()](self, room, block, params) end end end local needChange = true if clickEvent and block.event then if block:getEventType() == AdvEventType.Out then needChange = false end end if status and needChange then --出去了就不计算回合了 self:backBlockChange(roomId, blockId) self:afterRound() end self:saveDB() return status end --使用道具产生效果 function Adv:useItem(itemId, count, target) count = count or 1 local itemData = csvdb["adv_itemCsv"][itemId] if not itemData then return end --重置数量 if itemData["function"] == 0 or itemData["function"] == 2 then count = 1 end if not self:cost({[itemId] = count}, {}, true) then return true end --消耗 if itemData["function"] == 0 or itemData["function"] == 1 then self:cost({[itemId] = count}, {}) end --生效 if itemData.type == 1 or itemData.type == 0 then --技能 self.battle.player:releaseSkill(itemData.effect, target) elseif itemData.type == 2 then --掉落 local item = csvdb["event_dropCsv"][itemData.effect]["range"]:randWeight(true) self:backReward(self:award({[item[1]] = item[2]}, {})) else return end self:afterRound() self:saveDB() return true end --使用技能 function Adv:usePotion(potionId, potionLevel, target) -- cost local potionData = csvdb["adv_potionCsv"][potionId][potionLevel] local enemy = self.battle:getEnemy(target.roomId, target.blockId) if not enemy then return end --生效 if potionData.type == 1 or potionData.type == 0 then --技能 self.battle.player:releaseSkill(potionData.effect, enemy) elseif potionData.type == 2 then --掉落 local item = csvdb["event_dropCsv"][potionData.effect]["range"]:randWeight(true) self:backReward(self:award({[item[1]] = item[2]}, {})) else return end self:afterRound() self:saveDB() return true end --敌人死亡 function Adv:enemyDead(roomId, blockId, escape) local map = self:getCurMap() local room = self:getRoom(roomId) local block = self:getBlock(roomId, blockId) if not block then return end --死了以后掉东西 if block:isMonster() then --处理死亡 if block:isBoss() then room.isBossRoom = false end if escape then block:clear() else local monsterData = csvdb["event_monsterCsv"][block.event.id] self:scoreChange(AdvScoreType.Kill, monsterData.type) local item = block.event.item if not item then local dropData = csvdb["event_dropCsv"][monsterData.dropid] item = dropData["range"]:randWeight(true) end if item[1] == 0 then block:clear() else block:updateEvent({ etype = AdvEventType.Drop, item = item }) end end end self:backBlockChange(roomId, blockId) end function Adv:pushBackEvent(btype, params) table.insert(self.backEvents, {btype = btype, params = params}) end function Adv:backReward(items) self:pushBackEvent(AdvBackEventType.Reward, {items = items}) end -- if is player enemyId is nil --isMax 是否是改变血量上限 function Adv:backHpChange(enemyId, change, isMax) self:pushBackEvent(AdvBackEventType.HpChange, {enemyId = enemyId, change = change, isMax = isMax}) end function Adv:backMiss(enemyId) self:pushBackEvent(AdvBackEventType.Miss, {enemyId = enemyId}) end -- if is player enemyId is nil function Adv:backAtkChange(enemyId, change) self:pushBackEvent(AdvBackEventType.AtkChange, {enemyId = enemyId, change = change}) end -- if is player enemyId is nil function Adv:backDefChange(enemyId, change) self:pushBackEvent(AdvBackEventType.DefChange, {enemyId = enemyId, change = change}) end -- if is player enemyId is nil function Adv:backBuff(enemyId, buffId, isDel) self:pushBackEvent(AdvBackEventType.Buff, {enemyId = enemyId, buffId = buffId, isDel = isDel}) end -- if is player enemyId is nil function Adv:backSkill(enemyId, skillId, receiver) self:pushBackEvent(AdvBackEventType.Skill, {enemyId = enemyId, skillId = skillId, receiver = receiver}) end -- if is player enemyId is nil function Adv:backPassive(enemyId, passiveId) self:pushBackEvent(AdvBackEventType.Passive, {enemyId = enemyId, passiveId = passiveId}) end function Adv:backNext() self:pushBackEvent(AdvBackEventType.Next, {}) end function Adv:backEnd(success, score, scoreInfo, reward) self:pushBackEvent(AdvBackEventType.End, {success = success, score = score, scoreInfo = scoreInfo, reward = reward}) end function Adv:backBlockChange(roomId, blockId) self:pushBackEvent(AdvBackEventType.BlockChange, {roomId = roomId, blockId = blockId}) end function Adv:backAtk(enemyId, receiver) self:pushBackEvent(AdvBackEventType.Atk, {enemyId = enemyId, receiver = receiver}) end function Adv:backDead(enemyId) self:pushBackEvent(AdvBackEventType.Dead, {enemyId = enemyId}) end function Adv:backTrap() self:pushBackEvent(AdvBackEventType.Trap, {}) end function Adv:backLayer() self:pushBackEvent(AdvBackEventType.Layer, {}) end function Adv:scoreChange(scoreType, pms) local cutTypes = {} local score = 0 cutTypes[AdvScoreType.Level] = function() score = globalCsv.adv_score_floor end cutTypes[AdvScoreType.Kill] = function() local chapterData = csvdb["adv_chapterCsv"][self.chapterId] score = globalCsv.adv_score_monster[pms] * chapterData["monRatio"] end cutTypes[AdvScoreType.Item] = function() score = csvdb["itemCsv"][pms[1]].adv_score_item * pms[2] end cutTypes[AdvScoreType.Hurt] = function() score = globalCsv.adv_score_hurt * pms end cutTypes[AdvScoreType.Block] = function() score = globalCsv.adv_score_block end if cutTypes[scoreType] then cutTypes[scoreType]() else return end self.score[scoreType] = self.score[scoreType] or 0 self.score[scoreType] = self.score[scoreType] + score end function Adv:getScore() self.score[AdvScoreType.Level] = math.floor(self.score[AdvScoreType.Level] or 0) self.score[AdvScoreType.Block] = math.floor(self.score[AdvScoreType.Block] or 0) self.score[AdvScoreType.Hurt] = math.max(math.floor(self.score[AdvScoreType.Hurt] or 0), - (self.score[AdvScoreType.Level] + self.score[AdvScoreType.Block])) self.score[AdvScoreType.Kill] = math.floor(self.score[AdvScoreType.Kill] or 0) self.score[AdvScoreType.Item] = math.floor(self.score[AdvScoreType.Item] or 0) return self.score[AdvScoreType.Level] + self.score[AdvScoreType.Block] + self.score[AdvScoreType.Hurt] + self.score[AdvScoreType.Kill] + self.score[AdvScoreType.Item] end function Adv:popBackEvents() local events = self.backEvents self.backEvents = {} return events end --回合事件处理 function Adv:afterRound() if self.battle then self.battle:afterRound() end end return Adv