local Player, Enemy, Build = table.unpack(require "adv.AdvPlayer") local Buff = require "adv.AdvBuff" local Passive = require "adv.AdvPassive" local Battle = class("Battle") function Battle:ctor(adv) self.adv = adv self.player = nil --玩家 self.isNewPlayer = false self.enemys = {} --怪 self.builds = {} -- 建筑 self.auras = {} -- 光环 self.cachePassiveEvent = {} self:initPlayer() self:initEnemys() self:initAfter() if self.isNewPlayer then self.player:triggerPassive(Passive.BORN_ONCE) end end function Battle:initAfter(mapIdx) if not mapIdx then self.player:initAfter(self.adv.owner:getProperty("advTeam").player) end for idx, mapEnemys in pairs(self.enemys) do if not mapIdx or idx == mapIdx then for _, enemy in ipairs(mapEnemys) do enemy:initAfter(self.adv:getBlock(enemy.roomId, enemy.blockId, idx).event.enemy) end end end for idx, mapBuilds in pairs(self.builds) do if not mapIdx or idx == mapIdx then for _, build in ipairs(mapBuilds) do build:initAfter(self.adv:getBlock(build.roomId, build.blockId, idx).event.build) end end end end --[[ 队伍总属性 = 基础属性 + 等级 × 成长率 基础属性: 固定属性,与章节、队伍够成、养成无关,由策划给出 等级: 根据进入时的关卡,获得一个【初始等级】;消灭怪物获得经验后,累计N点会提升等级 羁绊加成: 加成条件与“战斗”相同,加成目标改为【冒险队】 成长率: 由【队伍总生存力】决定 成长率 =(总生存力 / 80)^0.52 + INT(总角色等级 / 50)* 1 --]] local function getAdvLvAttrUp(upAttrs, attrName, baseAttr) -- 1=冒险队属性;1=点数/百分比=属性枚举=参数;属性枚举(1=生命上限/2=魔法上限/3=攻击/4=防御);点数/百分比(0=点数/1=百分比) local Enem = { hp = 1, sp = 2, atk = 3, def = 4, } if not Enem[attrName] then return baseAttr end return baseAttr + (upAttrs[Enem[attrName]] or 0) end function Battle:initPlayer() local advTeam = self.adv.owner:getProperty("advTeam") if not advTeam.heros or not next(advTeam.heros) then return end local leaderPassive = {} local player = advTeam.player if not player then local advAddAttrs = self.adv.owner:getAdvLvAddAttrs() player = {} -- player.level = 1 -- if self.adv.level ~= 1 then -- local relayData = self.adv:isHaveRelay() -- if relayData then -- player.level = relayData.level -- end -- end -- player.exp = 0 player.sp = getAdvLvAttrUp(advAddAttrs, "sp", 100) player.spMax = player.sp -- player.growth = {} player.passives = {} for slot, heroId in pairs(advTeam.heros) do if heroId == advTeam.leader or heroId == advTeam.leader2 then local hero = self.adv.owner.heros[heroId] if hero then local unit = csvdb["unitCsv"][self.adv.owner.heros[heroId]:getProperty("type")] if unit.advCaptain ~= 0 then table.insert(player.passives, {id = unit.advCaptain, level = 1}) end end end end local attrs = self.adv.owner:getTeamBattleInfo(advTeam).heros for attrName, _ in pairs(AdvAttsEnum) do for _, hero in pairs(attrs) do player[attrName] = (player[attrName] or 0) + hero[attrName] end player[attrName] = getAdvLvAttrUp(advAddAttrs, attrName, player[attrName]) * (globalCsv.adv_battle_attr_ratio[attrName] or 1) -- player.growth[attrName] = player[attrName] * (globalCsv.adv_battle_attr_growth_ratio[attrName] or 1) -- player[attrName] = player[attrName] + player.growth[attrName] * (player.level - 1) end player.hpMax = player.hp or 0 self.isNewPlayer = true advTeam.player = player end self.player = Player.new(self, player) end function Battle:initEnemys() for idx, map in pairs(self.adv.maps) do self:initMapEnemys(idx) end end -- after 是否是 后创建的夹层 function Battle:initMapEnemys(mapIdx, after) self.enemys[mapIdx] = {} self.builds[mapIdx] = {} local map = self.adv.maps[mapIdx] if map then for _, room in pairs(map.rooms) do for _, block in pairs(room.blocks) do self:addEnemy(room, block, mapIdx, true) end end end if after then self:initAfter(mapIdx) end if self.cachePassiveEvent[mapIdx] then for _, passiveC in ipairs(self.cachePassiveEvent or {}) do for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:triggerPassive(passiveC[1], passiveC[2]) end for _, build in ipairs(self.builds[mapIdx]) do build:triggerPassive(passiveC[1], passiveC[2]) end end end self.cachePassiveEvent[mapIdx] = nil end function Battle:addEnemy(room, block, mapIdx, init) mapIdx = mapIdx or self.adv:getCurMapIdx() local isNew, player, data if block:isMonster() then if not block.event.enemy then local enemyCsv = csvdb["event_monsterCsv"][block.event.id] local enemy = {} local curFloorData = self.adv:getCurFloorData() or {} for attrName, _ in pairs(AdvAttsEnum) do enemy[attrName] = enemyCsv[attrName] * curFloorData[attrName] end enemy.passives = {} for _, id in ipairs(enemyCsv.mapPassive:toArray(true, "=")) do table.insert(enemy.passives, {id = id}) end block.event.enemy = enemy isNew = true end if not block.event.mId then block.event.mId = self.adv.lastEnemyId self.adv.lastEnemyId = self.adv.lastEnemyId + 1 end data = block.event.enemy player = Enemy.new(self, block.event.mId, block.event.id, room.roomId, block.blockId, not block.isOpen, block.event.enemy, mapIdx) table.insert(self.enemys[mapIdx], player) elseif block:isBuild() then if not block.event.build then local buildCsv = csvdb["event_buildingCsv"][block.event.id] local build = {} build.passives = {} for _, id in ipairs(buildCsv.passive:toArray(true, "=")) do table.insert(build.passives, {id = id}) end block.event.build = build isNew = true end data = block.event.build player = Build.new(self, block.event.id, room.roomId, block.blockId, not block.isOpen, block.event.build, mapIdx) table.insert(self.builds[mapIdx], player) end if not init then player:initAfter(data) -- 游戏中新创建的 加上地图 floor 效果 if isNew then local passives, buffs = self:getMapEffect(2, true, mapIdx) for _, passive in ipairs(passives) do player:addPassive({id = passive}) end for _, buff in ipairs(buffs) do player:addBuff(buff) end -- 新生成的怪 加上 已有的光环buff player:checkAuraBuff(self:checkDiffAuraBuff({}, self:getAurasByMap())) end end return player end function Battle:getEnemy(roomId, blockId, mapIdx) mapIdx = mapIdx or self.adv:getCurMapIdx() for _, enemy in ipairs(self.enemys[mapIdx] or {}) do if enemy.roomId == roomId and enemy.blockId == blockId then return enemy end end end function Battle:getBuild(roomId, blockId, mapIdx) mapIdx = mapIdx or self.adv:getCurMapIdx() for _, build in ipairs(self.builds[mapIdx] or {}) do if build.roomId == roomId and build.blockId == blockId then return build end end end function Battle:getEnemyById(id) for idx, mapEnemys in pairs(self.enemys) do for _, enemy in ipairs(mapEnemys) do if enemy.id == id then return enemy end end end end function Battle:getRBByEnemyId(enemyId) local enemy = self:getEnemyById(enemyId) return enemy.roomId, enemy.blockId, enemy.mapIdx end --触发全员被动技能 function Battle:triggerPassive(condType, params, mapIdx) mapIdx = mapIdx or self.adv:getCurMapIdx() self.player:triggerPassive(condType, params) if not self.enemys[mapIdx] then -- 缓存一下 self.cachePassiveEvent[mapIdx] = self.cachePassiveEvent[mapIdx] or {} table.insert(self.cachePassiveEvent[mapIdx], {condType, params}) else for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:triggerPassive(condType, params) end for _, build in ipairs(self.builds[mapIdx]) do build:triggerPassive(condType, params) end end end -- 只是从战斗中移除 从地图中移除 在外面操作 function Battle:removeEnemyById(id) local mapIdx = self.adv:getCurMapIdx() for i = #self.enemys[mapIdx], 1, -1 do if self.enemys[mapIdx][i].id == id then local enemy = table.remove(self.enemys[mapIdx], i) enemy:clear() break end end end -- 只是从战斗中移除 从地图中移除 在外面操作 function Battle:removeBuildByPos(roomId, blockId) local mapIdx = self.adv:getCurMapIdx() for i = #self.builds[mapIdx], 1, -1 do if self.builds[mapIdx][i].roomId == roomId and self.builds[mapIdx][i].blockId == blockId then local build = table.remove(self.builds[mapIdx], i) build:clear() break end end end --回合 function Battle:afterRound() local mapIdx = self.adv:getCurMapIdx() table.sort(self.enemys[mapIdx], function(e1, e2) return e1.id < e2.id end) self.player:afterRound("buffBefore") for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:afterRound("buffBefore") end for _, build in ipairs(self.builds[mapIdx]) do build:afterRound("buffBefore") end self.player:afterRound("passive") for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:afterRound("passive") end for _, build in ipairs(self.builds[mapIdx]) do build:afterRound("passive") end self.player:afterRound("buffAfter") for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:afterRound("buffAfter") end for _, build in ipairs(self.builds[mapIdx]) do build:afterRound("buffAfter") end self.player:triggerPassive(Passive.AFTER_ROUND) self:checkAura() self:clearRound() if self.player.isDead then self.adv:over(false, nil, -2) end end function Battle:clearRound() local mapIdx = self.adv:getCurMapIdx() self.player:clearRound() for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:clearRound() end for _, build in ipairs(self.builds[mapIdx]) do build:clearRound() end for i = #self.enemys[mapIdx], 1, -1 do if self.enemys[mapIdx][i].isDead then local enemy = table.remove(self.enemys[mapIdx], i) self.adv:enemyDead(enemy, enemy.isDead == 1) enemy:clear() end end for i = #self.builds[mapIdx], 1, -1 do if self.builds[mapIdx][i].isDead then local build = table.remove(self.builds[mapIdx], i) local room = self.adv:getCurMap().rooms[build.roomId] if room then local block = room.blocks[build.blockId] if block then block:clear() self.adv:backBlockChange(build.roomId, build.blockId) end end build:clear() end end end function Battle:battleBegin(roomId, blockId, params) local enemy = self:getEnemy(roomId, blockId) if not enemy then return end local player = params.player if not player then return end local penemy = params.enemy or {hp = 0} if player.hp ~= 0 then if penemy.hp <= 0 then enemy:kill() self.adv.owner:checkTaskEnter("AdvBattleWin", {id = self.adv.chapterId}) else -- 处理一下怪物 if penemy.hp > enemy.hp then enemy:recover(penemy.hp - enemy.hp) else enemy:hurt(math.max(0, math.ceil(enemy.hp - penemy.hp)), self.player, {hurtType = 5}) --战斗血量只会变少 end if penemy.escape and penemy.escape == 0 then enemy.isDead = 1 end end end -- 玩家没死就是怪死了 if player.hp > 0 then self.player:effectBattleBuff() if params.bySkill then self.player:triggerPassive(Passive.SKILL_KILL) end end if player.hp > self.player.hp then self.player:recover(player.hp - self.player.hp) else self.player:hurt(math.max(0, math.ceil(self.player.hp - player.hp)), enemy, {hurtType = 5}) --战斗血量只会变少 end self.player:changeSp(math.floor(player.sp - self.player.sp) , 0) --战斗魔力只会变少 end function Battle:getMapEffect(ctype, ilayer, mapIdx) mapIdx = mapIdx or self.adv:getCurMapIdx() local mapData = self.adv.maps[mapIdx]:getMapInfoCsv() or {} local floorData = self.adv:getCurFloorData() or {} local passives = {} local buffs = {} for _type, temp in ipairs({mapData, floorData}) do for _, one in ipairs((temp.passive or ""):toTableArray(true)) do passives[one[1]] = passives[one[1]] or {} if ilayer and one[1] == 1 and _type == 2 then else table.insert(passives[one[1]], one[2]) end end for _, one in ipairs((temp.buff or ""):toTableArray(true)) do buffs[one[1]] = buffs[one[1]] or {} if ilayer and one[1] == 1 and _type == 2 then else table.insert(buffs[one[1]], one[2]) end end end if ctype then passives = passives[ctype] or {} buffs = buffs[ctype] or {} end return passives, buffs end -- 夹层玩家 不重复加 floor function Battle:initMapEffect(ilayer) local passives, buffs = self:getMapEffect(nil, ilayer) local mapIdx = self.adv:getCurMapIdx() for _, passive in ipairs(passives[1] or {}) do self.player:addPassive({id = passive}) end for _, passive in ipairs(passives[2] or {}) do for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:addPassive({id = passive}) end end for _, buff in ipairs(buffs[1] or {}) do self.player:addBuff(buff) end for _, buff in ipairs(buffs[2] or {}) do for _, enemy in ipairs(self.enemys[mapIdx]) do enemy:addBuff(buff) end end end -- 夹层 进入退出 接口 清理玩家身上的老光环 添加新的光环 function Battle:iLayerChange(oldMapIdx) local auras = self:getActiveAuras() local playerBuffs = self:checkDiffAuraBuff(self:getAurasByMap(oldMapIdx), auras) local enemyBuffs = self:checkDiffAuraBuff(self:getAurasByMap(), auras) self.player:checkAuraBuff(playerBuffs) for _, enemy in ipairs(self.enemys[self.adv:getCurMapIdx()]) do enemy:checkAuraBuff(enemyBuffs) end self:setMapAuras(auras) self:clearRound() end -- 新的 关卡 关闭旧的战斗模块 清理 玩家身上的光环效果 function Battle:overBattle() local buffs = self:checkDiffAuraBuff(self:getAurasByMap(), {}) self.player:checkAuraBuff(buffs) self:clearRound() self.adv.owner:getProperty("advTeam").player = self.player:getDB() -- 临时缓存住 battle 的player end -- 初始化 新的 关卡 function Battle:newBattle() local auras = self:getActiveAuras() local buffs = self:checkDiffAuraBuff({}, auras) self.player:checkAuraBuff(buffs) for _, enemy in ipairs(self.enemys[self.adv:getCurMapIdx()]) do enemy:checkAuraBuff(buffs) end self:setMapAuras(auras) end function Battle:loadBattle(info) self.auras = info.auras or {} end -- 过了回合 检查光环 function Battle:checkAura() local auras = self:getActiveAuras() local buffs = self:checkDiffAuraBuff(self:getAurasByMap(), auras) self.player:checkAuraBuff(buffs) for _, enemy in pairs(self.player:getTeam(2)) do enemy:checkAuraBuff(buffs) end self:setMapAuras(auras) end -- 查找差异buff function Battle:checkDiffAuraBuff(oldAuras, newAuras) local auras = {} for aurasId , count in pairs(oldAuras) do auras[aurasId] = -count end for aurasId , count in pairs(newAuras) do auras[aurasId] = (auras[aurasId] or 0) + count end local buffs = {} for aurasId , count in pairs(auras) do local auraData = csvdb["adv_map_haloCsv"][aurasId] if auraData then for _, effect in ipairs(auraData.effect:toTableArray(true)) do temp = buffs for i = 1, #effect do temp[effect[i]] = temp[effect[i]] or {} temp = temp[effect[i]] end temp.count = (temp.count or 0) + count if newAuras[aurasId] then -- 加上 未消失标记 temp.exist = true end end end end return buffs end -- 获取所有生效的 光环 function Battle:getActiveAuras() local auras = {} for _, one in pairs(self.player:getAuras()) do auras[one] = (auras[one] or 0) + 1 end for _, enemy in pairs(self.player:getTeam(2)) do for _, one in pairs(enemy:getAuras()) do auras[one] = (auras[one] or 0) + 1 end end for _, build in pairs(self:getBuilds()) do for _, one in pairs(build:getAuras()) do auras[one] = (auras[one] or 0) + 1 end end return auras end function Battle:setMapAuras(auras) self.auras[self.adv:getCurMapIdx()] = auras end function Battle:getAurasByMap(mapIdx) mapIdx = mapIdx or self.adv:getCurMapIdx() local auras = self.auras[mapIdx] or {} return auras end function Battle:getBuilds() local team = {} for _, build in pairs(self.builds[self.adv:getCurMapIdx()]) do if not build.isDead and not build.lock then -- 已经翻开的 table.insert(team, build) end end return team end --写入数据 function Battle:saveDB(advInfo) for idx, mapEnemys in pairs(self.enemys) do for _, enemy in ipairs(mapEnemys) do local block = self.adv:getBlock(enemy.roomId, enemy.blockId, idx) if block and block:isMonster() then block.event.enemy = enemy:getDB() end end end for idx, mapBuilds in pairs(self.builds) do for _, build in ipairs(mapBuilds) do local block = self.adv:getBlock(build.roomId, build.blockId, idx) if block and block:isBuild() then block.event.build = build:getDB() end end end advInfo.auras = self.auras end return Battle