Created
January 27, 2021 15:01
-
-
Save Elmuti/48e9912fcee32b0b6d9df029cb651327 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local Orakel = require(game.ReplicatedStorage.Orakel.Main) | |
local mathLib = Orakel.LoadModule("MathLib") | |
local Die = require(game.ReplicatedStorage.Die) | |
local Combat = {} | |
local UnitCombatDieSides = require(game.ReplicatedStorage.Enums.UnitCombatDieSides) | |
local DamageSchool = require(game.ReplicatedStorage.Enums.DamageSchool) | |
local EquipmentSlot = require(game.ReplicatedStorage.Enums.EquipmentSlot) | |
local UNARMED_DAMAGE = NumberRange.new(1, 3) | |
function Combat.CalculateArmorReduction(attacker, victimArmor) | |
local tmpvalue = 0.1 * victimArmor / (8.5 * attacker:GetLevel() + 40) --what the fuck? | |
return math.clamp(tmpvalue / (1 + tmpvalue), 0, 0.75) | |
end | |
function Combat.CalculateMeleeAP(unit) | |
local class = unit:GetClass() | |
local ap = 0 -- + ap from items and talents | |
local str = unit:GetStat("Strength") | |
local dex = unit:GetStat("Dexterity") | |
if class == "Hunter" or unit:GetForm() == "Cat" or class == "Rogue" then | |
ap = ap + dex | |
end | |
if class == "Druid" or class == "Paladin" or class == "Shaman" or class == "Warrior" then | |
ap = ap + (str * 2) | |
else | |
ap = ap + str | |
end | |
return ap | |
end | |
function Combat.CalculateRangedAP(unit) | |
local class = unit:GetClass() | |
local ap = 0 -- + ap from items and talents | |
local dex = unit:GetStat("Dexterity") | |
if class == "Hunter" then | |
ap = ap + (dex * 2) | |
end | |
return ap | |
end | |
function Combat.CalculateCritChance(unit) | |
local class = unit:GetClass() | |
local dex = unit:GetStat("Dexterity") | |
local crit = 5 -- + crit from items, talents and auras | |
if class == "Hunter" then | |
crit = crit + (dex / 45) | |
elseif class == "Rogue" then | |
crit = crit + (dex / 31) | |
elseif class == "Druid" or class == "Paladin" or class == "Shaman" or class == "Warrior" then | |
crit = crit + (dex / 23) | |
end | |
return crit | |
end | |
function IsCritical(critChance) | |
if math.random() <= (critChance / 100) then | |
return true | |
end | |
return false | |
end | |
--unit:GetAPMultiplier(attType, normalised) | |
function Combat.CalculateMinMaxDamage(plr, attType, normalised) | |
local attSpeed = plr:GetAPMultiplier(attType, normalised) | |
local addedFlat = 10 --= ADED FLAT DAMAGE (sundays: idfk i made this 10 because it was nil help) | |
local baseValue = addedFlat | |
print(string.format("attacking with %s basevalue from %s attack power", baseValue, plr:GetStat("AttackPower"))) | |
-- If unarmed then this will be nil | |
local mainHandItem = plr:GetItem(EquipmentSlot.MainHand) | |
local min = mainHandItem and mainHandItem.Damage.Min or UNARMED_DAMAGE.Min | |
local max = mainHandItem and mainHandItem.Damage.Max or UNARMED_DAMAGE.Max | |
local wepname = mainHandItem and mainHandItem.DisplayName or "Unarmed" | |
baseValue += mathLib.RandomFloat(min, max) | |
baseValue += (plr:GetStat("AttackPower") / 14 * attSpeed) | |
print(string.format("min:%s, max:%s", min, max)) | |
if plr:GetForm() == "Cat" or plr:GetForm() == "Bear" then | |
local lvl = plr:GetLevel() | |
if lvl > 60 then | |
lvl = 60 | |
end | |
min = lvl * 0.85 * attSpeed | |
max = lvl * 1.25 * attSpeed | |
else | |
if attType == "RangedAttack" then | |
--TODO: increase "min" by ammunition DPS | |
--TODO: increase "max" by ammunition DPS | |
end | |
min = baseValue * (plr:GetStat("DamageMultiplier") or 1) | |
max = baseValue * (plr:GetStat("DamageMultiplier") or 1) | |
end | |
local dmgRoll = mathLib.RandomFloat(min, max) | |
print(string.format("attacking with %s for %s damage from (%s min, %s max)", wepname, dmgRoll, min, max)) | |
return dmgRoll | |
end | |
--Block% = 5% base chance + contribution from Block Rating + contribution from talents + ((Defense skill - attacker's weapon skill) * 0.04) | |
--shieldblockvalue = (shieldblockvalue + strength/2) * addedBlock | |
--Unit::CalculateSpellDamage | |
--MeleeDamageBonusDone, MeleeDamageBonusTaken, RollMeleeOutcomeAgainst, GetShieldBlockValue, CalculateGlanceAmount | |
--Crushing is 150% | |
--[[ | |
CalculateEffectiveGlanceChance | |
CalculateEffectiveBlockChance | |
CalculateEffectiveParryChance | |
CalculateEffectiveDodgeChance | |
CalculateEffectiveCritChance | |
CalculateEffectiveCrushChance | |
CalculateEffectiveMissChance | |
]] | |
function Combat.CalculateEffectiveMissChance(attacker, target) | |
local chance = 0 | |
local hit = target:GetStat("Hit") | |
local attSkill = 300 | |
local defense = 300 | |
--if not isAbility and isOffhand then | |
--chance += 19 | |
--end | |
local difference = defense - attSkill | |
local factor = 0.04 | |
if not attacker:IsPlayerOrPet() and difference > 0 then | |
if difference > 10 then | |
chance += 1 | |
difference -= 10 | |
factor = 0.4 | |
chance += difference * 0.2 | |
else | |
factor = 0.1 | |
end | |
end | |
chance += difference * factor | |
chance -= target:GetStat("Hit") | |
-- chance -= (ability ? GetHitChance(ability, SPELL_SCHOOL_MASK_NORMAL) : GetHitChance(attType)); | |
local minimum = difference > 10 and 1 or 0 | |
return math.clamp(chance, 0, 100) | |
end | |
function Combat.CalculateEffectiveDodgeChance(attacker, target) | |
local chance = target:GetStat("Dodge") | |
local attSkill = 300 | |
local defense = 300 | |
local skilldiff = defense - attSkill | |
local factor = 0.04 | |
if not target:IsPlayerOrPet() then | |
factor = 0.1 | |
end | |
chance += skilldiff * factor | |
return chance | |
end | |
function Combat.CalculateEffectiveParryChance(attacker, target) | |
local chance = target:GetStat("Parry") | |
local attSkill = 300 | |
local defense = 300 | |
local skilldiff = defense - attSkill | |
local factor = 0.04 | |
if not target:IsPlayerOrPet() then | |
if skilldiff > 10 then | |
factor = 0.6 | |
else | |
factor = 0.1 | |
end | |
end | |
chance += skilldiff * factor | |
chance = (skilldiff >= 15) and 0.14 or chance | |
return chance | |
end | |
function Combat.CalculateEffectiveBlockChance(attacker, target) | |
local chance = target:GetStat("Block") | |
local attSkill = 300 | |
local defense = 300 | |
local skilldiff = defense - attSkill | |
local factor = 0.04 | |
if not target:IsPlayerOrPet() then | |
factor = 0 | |
end | |
chance += skilldiff * factor | |
return chance | |
end | |
function Combat.CalculateEffectiveCrushChance(attacker, target) | |
local chance = 0 | |
local attSkill = 300 | |
local defense = 300 | |
local deficit = attSkill - defense | |
if deficit >= 15 then | |
chance +=((2 * deficit) - 15) | |
end | |
return chance | |
end | |
function Combat.CalculateEffectiveGlanceChance(attacker, target) | |
local chance = 0 | |
local attSkill = 300 | |
local defense = 300 | |
local level = target:GetLevel() | |
if attacker:GetClass() == "Mage" or attacker:GetClass() == "Warlock" or attacker:GetClass() == "Priest" then | |
chance += (level < 30) and (level + ((defense - attSkill) * 2)) or (30 + ((defense - attSkill) * 2)) | |
else | |
chance += (10 + ((defense - attSkill) * 2)) | |
end | |
return chance | |
end | |
--returns damage multiplier | |
function Combat.CalculateGlanceAmount(attacker, target) | |
local attSkill = 300 | |
local defense = 300 | |
local skilldiff = defense - attSkill | |
local caster = attacker:IsCaster() | |
local baseHighEnd = (caster and 0.9 or 1.2) | |
local baseLowEnd = (caster and 0.6 or 1.3) | |
local maxLowEnd = (caster and 0.6 or 0.91) | |
local highEnd = baseHighEnd - (0.03 * skilldiff) | |
local lowEnd = baseLowEnd - (0.05 * skilldiff) | |
highEnd = math.min(math.max(highEnd, 0.2), 0.99) | |
lowEnd = math.min(math.max(lowEnd, 0.01), math.min(maxLowEnd, highEnd)) | |
return mathLib.RandomFloat(lowEnd, highEnd) | |
end | |
function Combat.CalculateEffectiveCritChance(attacker, target) | |
local attSkill = 300 | |
local defense = 300 | |
local chance = Combat.CalculateCritChance(attacker) | |
local skilldiff = defense - attSkill | |
local factor = 0.04 | |
if not target:IsPlayerOrPet() then | |
factor = 0.2 | |
end | |
chance += skilldiff * factor | |
return math.clamp(chance, 0, 100) | |
end | |
--(Dodge, Parry, Miss, Glancing, Crushing, Crit, Normal, Evade) | |
function Combat.CalculateMeleeOutcome(attacker, target, attType) | |
local die = Die:new(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_HIT"), #UnitCombatDieSides) | |
--if target:Sitting() then | |
--die:Set(Orakel.TGetIndex(UnitCombatDieSides, "UNIT_COMBAT_DIE_CRIT"), 1000) | |
--end | |
if target:CanAct() then | |
if target:CanDodge() and not attType == "RangedAttack" then | |
die:Set(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_DODGE"), Combat.CalculateEffectiveDodgeChance(attacker, target)) | |
end | |
if target:CanParry() and not attType == "RangedAttack" then | |
die:Set(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_PARRY"), Combat.CalculateEffectiveParryChance(attacker, target)) | |
end | |
if target:CanBlock() and not attType == "RangedAttack" then | |
die:Set(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_BLOCK"), Combat.CalculateEffectiveBlockChance(attacker, target)) | |
end | |
end | |
if attacker:CanGlance(target) and not attType == "RangedAttack" then | |
die:Set(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_GLANCE"), Combat.CalculateEffectiveGlanceChance(attacker, target)) | |
end | |
if attacker:CanCrush(target) and not target:IsPlayerOrPet() and not attType == "RangedAttack" then | |
die:Set(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_CRUSH"), Combat.CalculateEffectiveCrushChance(attacker, target)) | |
end | |
die:Set(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_MISS"), Combat.CalculateEffectiveMissChance(attacker, target)) | |
die:Set(Orakel.TGetIndexFromValue(UnitCombatDieSides, "UNIT_COMBAT_DIE_CRIT"), Combat.CalculateEffectiveCritChance(attacker, target)) | |
local random = math.random(1, 10000) | |
local side = UnitCombatDieSides[die:Roll(random)] | |
print("COMBAT ROLL RESULT: "..side) | |
return side | |
end | |
--(Glancing, Crushing, Crit, Normal) > Reduction > Armor > Block > Absorb | |
function Combat.CalculateMeleeDamage(attacker, target, attType) | |
local outcome = Combat.CalculateMeleeOutcome(attacker, target) | |
local dmg = Combat.CalculateMinMaxDamage(attacker, attType, true) | |
if outcome == "UNIT_COMBAT_DIE_CRUSH" then | |
dmg *= 1.5 | |
elseif outcome == "UNIT_COMBAT_DIE_GLANCE" then | |
dmg *= Combat.CalculateGlanceAmount(attacker, target) | |
elseif outcome == "UNIT_COMBAT_DIE_CRIT" then | |
dmg *= (attacker:GetStat("CritMultiplier") + 1) or 2 | |
--if target:Sitting() then | |
--target:StandUp() | |
--end | |
end | |
--Damage reduction | |
dmg *= (target:GetStat("DamageReduction") + target:GetStat("DamageReductionPhysical")) | |
print(string.format("%s dmg before armor", dmg)) | |
--Armor reduction | |
local armorMult = 1 - Combat.CalculateArmorReduction(attacker, target:GetStat("Armor")) | |
dmg *= armorMult | |
print(string.format("%s dmg after armor", dmg)) | |
--Block reduction | |
if outcome == "UNIT_COMBAT_DIE_BLOCK" then | |
local blockValue = (target:GetStat("BlockValue") + target:GetStat("Strength") / 2) * 1 | |
blockValue = 1 - blockValue | |
dmg *= blockValue | |
end | |
--Damage absorb | |
-- NOTE: Handled by the Absorb aura effect, no longer needed here! | |
--dmg -= target:GetStat("PhysicalAbsorb") | |
--target:SetStat("PhysicalAbsorb", math.clamp(target:GetStat("PhysicalAbsorb") - dmg, 0, math.huge)) | |
return math.clamp(math.ceil(dmg), 0, math.huge), outcome | |
end | |
--Returns damage multiplier | |
function Combat.CalculateSpellResistanceReduction(caster, target, spellId) | |
--TODO: Implement me | |
return 1 | |
end | |
--DamageSchool | |
function Combat.CalculateSpellDamage(caster, target, spellId) | |
local spellDB = require(game.ReplicatedStorage.SpellDatabase) | |
local spell = spellDB.GetById(spellId) | |
assert(spell, string.format("spellId:%s doesnt exist u mongoloid", spellId)) | |
--Get total spell power and spell power for specific schools | |
local sp = caster:GetStat("SpellPower") + caster:GetStat(spell.DamageSchool.."SpellPower") | |
--Increase by spell power and modify by coefficient | |
local dmg = mathLib.RandomFloat(spell.Damage.Min, spell.Damage.Max) + sp * spell.Coefficient | |
--Reduce by resistances | |
dmg = dmg * Combat.CalculateSpellResistanceReduction(caster, target, spellId) | |
return math.ceil(dmg) | |
end | |
return Combat |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment