miduo_server/luafight/Modules/Battle/Logic/Misc/BattleUtil.lua

465 lines
18 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

BattleUtil = {}
--local BattleUtil = BattleUtil
local floor = math.floor
local max = math.max
local min = math.min
--local Random = Random
--local RoleDataName = RoleDataName
--local BattleEventName = BattleEventName
BattleUtil.Passivity = require("Modules/Battle/Logic/Base/Passivity")
local function clamp(v, minValue, maxValue)
if v < minValue then
return minValue
end
if v > maxValue then
return maxValue
end
return v
end
function BattleUtil.ErrorCorrection(f) --进行精度处理,避免前后端计算不一致
return floor(f * 100000 + 0.5) / 100000
end
function BattleUtil.FP_Mul(...)
local f = 1
for i, v in ipairs{...} do
f = floor(f * v * 100000 + 0.5) / 100000
end
if BattleLogic.IsOpenBattleRecord then
BattleLogic.RecordMul({...})
end
return f
end
-- 选择前排
function BattleUtil.ChooseFRow(arr)
local tempArr = {}
for _, r in ipairs(arr) do
if r.position <= 3 and not r.isDead then
table.insert(tempArr, r)
end
end
return tempArr
end
-- 选择后排
function BattleUtil.ChooseBRow(arr)
local tempArr = {}
for _, r in ipairs(arr) do
if r.position > 3 and not r.isDead then
table.insert(tempArr, r)
end
end
return tempArr
end
-- 选择一列(col == 0 表示第三列哦)
function BattleUtil.ChooseCol(arr, col)
local tempArr = {}
for _, role in ipairs(arr) do
if role.position % 3 == col then
table.insert(tempArr, role)
end
end
return tempArr
end
-- 根据属性排序
function BattleUtil.SortByProp(arr, prop, sort)
BattleUtil.Sort(arr, function(a, b)
local r1 = a:GetRoleData(prop)
local r2 = b:GetRoleData(prop)
if sort == 1 then return r1 > r2 else return r1 < r2 end
end)
return arr
end
function BattleUtil.ChooseTarget(role, chooseId)
local chooseType = floor(chooseId / 100000) % 10
local chooseLimit = floor(chooseId / 10000) % 10
local chooseWeight = floor(chooseId / 100) % 100
local sort = floor(chooseId / 10) % 10
local num = chooseId % 10
local arr
-- 选择类型
if chooseType == 1 then
arr = BattleLogic.Query(function (r) return r.camp == role.camp end)
elseif chooseType == 2 then
if role.lockTarget and not role.lockTarget.isDead and num == 1 then --嘲讽时对单个敌军生效
return {role.lockTarget}
end
arr = BattleLogic.Query(function (r) return r.camp ~= role.camp end)
elseif chooseType == 3 then
if role.ctrl_blind then --致盲时自身变随机友军
arr = BattleLogic.Query(function (r) return r.camp == role.camp end)
BattleUtil.RandomList(arr)
return {arr[1]}
end
return {role}
elseif chooseType == 4 then
if role.lockTarget and not role.lockTarget.isDead then --嘲讽时对仇恨目标生效
return {role.lockTarget}
end
if role.ctrl_blind then --致盲时仇恨目标变随机
arr = BattleLogic.Query(function (r) return r.camp ~= role.camp end)
BattleUtil.RandomList(arr)
return {arr[1]}
end
return {BattleLogic.GetAggro(role)}
else
arr = BattleLogic.Query()
end
--选择范围
if chooseLimit == 0 then --选择全体不做任何操作
elseif chooseLimit == 1 then-- 前排
local tempArr = BattleUtil.ChooseFRow(arr)
if #tempArr == 0 then -- 没有选择后排
tempArr = BattleUtil.ChooseBRow(arr)
end
arr = tempArr
elseif chooseLimit == 2 then-- 后排
local tempArr = BattleUtil.ChooseBRow(arr)
if #tempArr == 0 then -- 没有选择前排
tempArr = BattleUtil.ChooseFRow(arr)
end
arr = tempArr
elseif chooseLimit == 3 then-- 对列
local myCol = role.position % 3
local tempArr = BattleUtil.ChooseCol(arr, myCol)
if #tempArr == 0 then -- 对列没有人,按顺序找到有人得列
for i = 1, 3 do
local col = i % 3 -- 0 表示第三列嗷
tempArr = BattleUtil.ChooseCol(arr, col)
if #tempArr ~= 0 then
break
end
end
end
arr = tempArr
end
-- 选择条件
if chooseWeight == 0 or role.ctrl_blind then --致盲时排序无效
BattleUtil.RandomList(arr)
elseif chooseWeight == 1 then -- 生命值
BattleUtil.SortByProp(arr, RoleDataName.Hp, sort)
elseif chooseWeight == 2 then -- 血量百分比
BattleUtil.Sort(arr, function(a, b)
local r1 = a:GetRoleData(RoleDataName.Hp) / a:GetRoleData(RoleDataName.MaxHp)
local r2 = b:GetRoleData(RoleDataName.Hp) / b:GetRoleData(RoleDataName.MaxHp)
if sort == 1 then return r1 > r2 else return r1 < r2 end
end)
elseif chooseWeight == 3 then -- 攻击力
BattleUtil.SortByProp(arr, RoleDataName.Attack, sort)
elseif chooseWeight == 4 then -- 防御
BattleUtil.SortByProp(arr, RoleDataName.PhysicalDefence, sort)
elseif chooseWeight == 5 then -- 护甲
BattleUtil.SortByProp(arr, RoleDataName.PhysicalDefence, sort)
elseif chooseWeight == 6 then -- 魔抗
BattleUtil.SortByProp(arr, RoleDataName.MagicDefence, sort)
elseif chooseWeight == 7 then -- 对位及其相邻目标
arr = BattleLogic.GetNeighbor(role, chooseType)
-- elseif chooseWeight == 9 then -- 展位距离
-- BattleUtil.Sort(arr, function(a, b)
-- local r1 = math.abs(role.position - a.position) * 10 + a.position
-- local r2 = math.abs(role.position - b.position) * 10 + b.position
-- if sort == 1 then return r1 > r2 else return r1 < r2 end
-- end)
end
return arr
end
function BattleUtil.CreateBuffId(skill, index)
local id = 0
if skill.preAI then --主动技能生成buff
id = skill.owner.uid * 10000 + index
else --被动技能生成buff
id = skill.owner.uid * 1000 + index
end
return id
end
-- 计算命中率
function BattleUtil.CalHit(atkRole, defRole)
--命中率 = clamp(自身命中率-敌方闪避率,0,1)
local hit = atkRole:GetRoleData(RoleDataName.Hit)
local dodge = defRole:GetRoleData(RoleDataName.Dodge)
local bHit = Random.Range01() <= clamp(hit - dodge, 0, 1)
if bHit then
else
atkRole.Event:DispatchEvent(BattleEventName.RoleDodge)
defRole.Event:DispatchEvent(BattleEventName.RoleBeDodge)
end
return bHit
end
-- 计算护盾
function BattleUtil.CalShield(atkRole, defRole, damage)
for i=1, defRole.shield.size do
local buff = defRole.shield.buffer[i]
damage = buff:CountShield(damage, atkRole)
-- buff.atk = atkRole
-- if damage < buff.shieldValue then
-- buff.shieldValue = buff.shieldValue - damage
-- buff.damageSum = buff.damageSum + damage
-- return 0
-- else
-- buff.damageSum = buff.damageSum + buff.shieldValue
-- damage = damage - buff.shieldValue
-- buff.shieldValue = 0
-- buff.disperse = true
-- end
end
return damage
end
-- 计算真实伤害
function BattleUtil.ApplyDamage(skill, atkRole, defRole, damage, bCrit, damageType, isDot)
bCrit = bCrit or false
damageType = damageType or 0
isDot = isDot or false
if atkRole.isTeam then
--max技能基础伤害*1+已方异妖伤害加成-目标异妖伤害减免30%×技能基础伤害)
local arr = BattleLogic.Query(function (r) return r.camp == atkRole.camp end)
local n = 0
for i=1, #arr do
n = n + arr[i]:GetRoleData(RoleDataName.TeamDamageBocusFactor)
end
damage = floor(max(damage * (1 + n / #arr - defRole:GetRoleData(RoleDataName.TeamDamageReduceFactor)), 0.3 * damage))
end
--加入被动效果
local damagingFunc = function(dmgDeduction) damage = damage - dmgDeduction end
atkRole.Event:DispatchEvent(BattleEventName.PassiveDamaging, damagingFunc, defRole, damage)
defRole.Event:DispatchEvent(BattleEventName.PassiveBeDamaging, damagingFunc, atkRole, damage)
-- 计算护盾减伤
damage = BattleUtil.CalShield(atkRole, defRole, damage)
if damage > 0 then
local finalDmg = defRole.data:SubValue(RoleDataName.Hp, damage)
if finalDmg > 0 then
if defRole:GetRoleData(RoleDataName.Hp) <= 0 then
defRole.isDead = true
BattleLogic.BuffMgr:ClearBuff(defRole)
defRole.Event:DispatchEvent(BattleEventName.RoleDead, atkRole)
atkRole.Event:DispatchEvent(BattleEventName.RoleKill, defRole)
BattleLogic.Event:DispatchEvent(BattleEventName.BattleRoleDead, defRole, atkRole)
end
atkRole.Event:DispatchEvent(BattleEventName.RoleDamage, defRole, damage, bCrit, finalDmg, damageType, isDot, skill)
defRole.Event:DispatchEvent(BattleEventName.RoleBeDamaged, atkRole, damage, bCrit, finalDmg, damageType, isDot, skill)
BattleLogic.Event:DispatchEvent(BattleEventName.RoleDamage, atkRole, defRole, damage, bCrit, finalDmg, damageType, isDot, skill)
BattleLogic.Event:DispatchEvent(BattleEventName.RoleBeDamaged, defRole, atkRole, damage, bCrit, finalDmg, damageType, isDot, skill)
if bCrit then
atkRole.Event:DispatchEvent(BattleEventName.RoleCrit, defRole, damage, bCrit, finalDmg, damageType)
defRole.Event:DispatchEvent(BattleEventName.RoleBeCrit, atkRole, damage, bCrit, finalDmg, damageType)
end
if not isDot then
atkRole.Event:DispatchEvent(BattleEventName.RoleHit, defRole, damage, bCrit, finalDmg, damageType)
defRole.Event:DispatchEvent(BattleEventName.RoleBeHit, atkRole, damage, bCrit, finalDmg, damageType)
end
end
return finalDmg
else
return damage
end
end
--执行完整的命中,伤害,暴击计算,返回命中,暴击
--skill造成伤害的技能 atkRole攻击者 defRole受击者 damageType伤害类型 baseFactor伤害系数 ignoreDef无视防御参数 isDot是否是dot
function BattleUtil.CalDamage(skill, atkRole, defRole, damageType, baseFactor, ignoreDef, isDot)
if atkRole.isTeam and not defRole.isDead then --如果是队伍技能计算真实伤害则damageType为伤害值
return BattleUtil.ApplyDamage(skill, atkRole, defRole, damageType), false
end
-- 计算技能额外伤害系数加成
baseFactor = baseFactor or 1
local factorFunc = function(exFactor) baseFactor = baseFactor + exFactor end
atkRole.Event:DispatchEvent(BattleEventName.RoleDamageBefore, defRole, factorFunc, damageType)
defRole.Event:DispatchEvent(BattleEventName.RoleBeDamagedBefore, atkRole, factorFunc, damageType)
baseFactor = BattleUtil.ErrorCorrection(baseFactor)
local bCrit = false
local baseDamage
-- 是否暴击: 暴击率 = 自身暴击率 - 对方抗暴率
bCrit = Random.Range01() <= clamp(atkRole:GetRoleData(RoleDataName.Crit)-defRole:GetRoleData(RoleDataName.Tenacity), 0, 1)
bCrit = bCrit or defRole.isFlagCrit == true -- 必定暴击
-- 防御(据伤害类型决定)
local defence = 0
if damageType == 1 then --1 物理 2 魔法
defence = defRole:GetRoleData(RoleDataName.PhysicalDefence)
else
defence = defRole:GetRoleData(RoleDataName.MagicDefence)
end
-- 攻击力
local attack = atkRole:GetRoleData(RoleDataName.Attack)
-- 无视防御系数
ignoreDef = 1 - (ignoreDef or 0)
--计算暴击
if bCrit then
-- 计算额外暴击伤害
local critDamageFactor = atkRole:GetRoleData(RoleDataName.CritDamageFactor)
--加入被动效果 触发暴击被动
local critFunc = function(critEx) critDamageFactor = critEx end
atkRole.Event:DispatchEvent(BattleEventName.PassiveCriting, critFunc)
--计算基础伤害:伤害 = 攻击力 * 暴击伤害系数 - 防御 *1 - 无视防御系数)
baseDamage = max(BattleUtil.FP_Mul(attack, critDamageFactor) - BattleUtil.FP_Mul(0.5, defence, ignoreDef), 0)
else
baseDamage = max(attack - BattleUtil.FP_Mul(0.5, defence, ignoreDef), 0)
end
-- 自身属性对应对方的属性伤害减免系数
local elementDamageReduceFactor = RoleDataName.FireDamageReduceFactor
if atkRole.element == 1 then
elementDamageReduceFactor = RoleDataName.FireDamageReduceFactor
elseif atkRole.element == 2 then
elementDamageReduceFactor = RoleDataName.WindDamageReduceFactor
elseif atkRole.element == 3 then
elementDamageReduceFactor = RoleDataName.IceDamageReduceFactor
elseif atkRole.element == 4 then
elementDamageReduceFactor = RoleDataName.LandDamageReduceFactor
elseif atkRole.element == 5 then
elementDamageReduceFactor = RoleDataName.LightDamageReduceFactor
elseif atkRole.element == 6 then
elementDamageReduceFactor = RoleDataName.DarkDamageReduceFactor
end
--伤害 =(基础伤害*1+己伤害加成-目标伤害减免)*1+己方属性伤害加成-目标属性伤害减免10%*己攻)
baseDamage = BattleUtil.FP_Mul(baseDamage, baseFactor,
1 + atkRole:GetRoleData(RoleDataName.DamageBocusFactor) - defRole:GetRoleData(RoleDataName.DamageReduceFactor),
(1 + atkRole:GetRoleData(RoleDataName.ElementDamageBocusFactor) - defRole:GetRoleData(elementDamageReduceFactor)))
--属性克制关系:光暗互克,火克风,风克地,地克水,水克火
local atkEle = atkRole.roleData.element
local defEle = defRole.roleData.element
local restrain = atkEle == 1 and defEle == 2 or
atkEle == 4 and defEle == 3 or
atkEle == 2 and defEle == 4 or
atkEle == 3 and defEle == 1 or
atkEle == 5 and defEle == 6 or
atkEle == 6 and defEle == 5
if restrain then
baseDamage = floor(max(BattleUtil.FP_Mul(1.25, baseDamage), BattleUtil.FP_Mul(0.1, attack)))
else
baseDamage = floor(max(baseDamage, BattleUtil.FP_Mul(0.1, attack)))
end
-- 计算额外伤害数值加成
local damageFunc = function(damage) baseDamage = damage end
atkRole.Event:DispatchEvent(BattleEventName.RoleDamageAfter, defRole, damageFunc, baseDamage)
defRole.Event:DispatchEvent(BattleEventName.RoleBeDamagedAfter, atkRole, damageFunc, baseDamage)
local finalDmg = 0 --计算实际造成的扣血
if not defRole.isDead then
finalDmg = BattleUtil.ApplyDamage(skill, atkRole, defRole, baseDamage, bCrit, damageType, isDot)
end
return finalDmg, bCrit
end
function BattleUtil.CalTreat(castRole, targetRole, value, baseFactor)
if targetRole.ctrl_noheal or targetRole.isDead then --禁疗和死亡无法加血
return
end
targetRole.Event:DispatchEvent(BattleEventName.RoleBeHealed, castRole)
BattleUtil.ApplyTreat(castRole, targetRole, value, baseFactor)
end
function BattleUtil.ApplyTreat(castRole, targetRole, value, baseFactor)
baseFactor = baseFactor or 1
local maxHp = targetRole:GetRoleData(RoleDataName.MaxHp)
local hp = targetRole:GetRoleData(RoleDataName.Hp)
local factor = castRole.isTeam and 1 or castRole:GetRoleData(RoleDataName.TreatFacter) --释放者为team则不计算治疗加成属性
local factor2 = targetRole:GetRoleData(RoleDataName.CureFacter)
local baseTreat = floor(BattleUtil.FP_Mul(value, baseFactor, factor, factor2) + 0.5)
--加入被动效果
local treatingFunc = function(exFactor) baseTreat = floor(baseTreat * (exFactor + 1) + 0.5) end
castRole.Event:DispatchEvent(BattleEventName.PassiveTreating, treatingFunc, targetRole)
targetRole.Event:DispatchEvent(BattleEventName.PassiveBeTreated, treatingFunc, castRole)
local treat = min(baseTreat, maxHp - hp)
if treat > 0 then
targetRole.data:AddValue(RoleDataName.Hp, treat)
castRole.Event:DispatchEvent(BattleEventName.RoleTreat, targetRole, treat)
targetRole.Event:DispatchEvent(BattleEventName.RoleBeTreated, castRole, treat)
end
end
function BattleUtil.RandomAction(rand, action)
if Random.Range01() <= rand and action then
action()
end
end
function BattleUtil.RandomList(arr)
if #arr <= 1 then return end
local index
for i=#arr, 1, -1 do
index = Random.RangeInt(1, i)
arr[i], arr[index] = arr[index], arr[i]
end
end
function BattleUtil.Sort(arr, comp)
if #arr <= 1 then return arr end
for i=1, #arr do
for j = #arr, i+1, -1 do
if comp(arr[j-1], arr[j]) then
arr[j-1], arr[j] = arr[j], arr[j-1]
end
end
end
return arr
end
function BattleUtil.GetPropertyName(type)
if type == 1 then
return RoleDataName.Strength
elseif type == 2 then
return RoleDataName.Energy
elseif type == 3 then
return RoleDataName.Vitality
elseif type == 4 then
return RoleDataName.Dexterity
elseif type == 5 then
return RoleDataName.Speed
elseif type == 6 then
return RoleDataName.PhysicalAttack
elseif type == 7 then
return RoleDataName.MagicAttack
elseif type == 8 then
return RoleDataName.PhysicalDefence
elseif type == 9 then
return RoleDataName.MagicDefence
end
end
function BattleUtil.GetHPPencent(role)
return role:GetRoleData(RoleDataName.Hp) / role:GetRoleData(RoleDataName.MaxHp)
end
-- 计算数值
function BattleUtil.CountValue(v1, v2, ct)
local v = v1
if ct == 1 then --加算
v = v + v2
elseif ct == 2 then --乘加算(百分比属性加算)
v = v * (1 + v2)
elseif ct == 3 then --减算
v = v - v2
elseif ct == 4 then --乘减算(百分比属性减算)
v = v * (1 - v2)
end
-- 做一个正确性检测
-- v = BattleUtil.ErrorCorrection(v)
return v
end