Spoiler
function( TimelineContext,DamageModifiers,DamageStat_Right,DamageStat_Left,AbilityDamageMultiplier,AbilityDamageMultiplierDefault,CriticalBonus,ShieldedTag,Class,Level,Invulnerable,Attack,BarrierDamageModifier,Barrier,GuardDamageModifier,StatGuard,HealthDamageModifier,Unkillable,HealthMax,StatDamageBonusBarrier,StatDamageBonusGuard,StatDefenseMelee,StatDefenseRanged,StatDefenseMagic,MeleeRange,StatResistanceGuard,StatResistanceBarrier,StatResistanceCritical,IsRune,StatArmor,StatArmorPenetration,StatArmorPenetrationPercentage,FlankingBonusStat,PhysicalResistanceTag,MagicalResistanceTag,AbilityDamageOverride,StatCreatureDamageMultiplier,BasicAttackTag,Stat_Critical_Chance_Override,StatArmorFront,Stat_Defense_All_Front,ArmorPercentageForMagicDefense,RankStat,DifficultyDamageMultiplierProvider,DamageTakenMultiplier ) do damage = 0; CLASS_INVALID = 4; --[[ ATTACKER AND TARGET PARAMETERS ]]-- -- get target targetCharacter = DA3.GetOwnerCharacter(TimelineContext); print("target = " .. DA3.ToString(targetCharacter)); if (targetCharacter == nil) then print("ERROR - no target character"); return damage; end -- target class targetclass = DA3.GetCharacterClass(targetCharacter); print("target class = " .. targetclass); -- target level targetlevel = math.max(1, DA3.GetStatTotalValue(targetCharacter, Level)); print("target creature level = " .. targetlevel); -- get attacker sourceCharacter = DA3.GetCasterCharacter(TimelineContext); print("source = " .. DA3.ToString(sourceCharacter)); if (sourceCharacter == nil) then print("ERROR - no source character"); return damage; end if (DA3.CharacterIsCharacter(sourceCharacter) == false) then print("!!! source character is not a character, using target character as attacker character to activate trap logic path !!!"); sourceCharacter = targetCharacter; end -- attacker class attackerclass = DA3.GetCharacterClass(sourceCharacter); print("attacker class = " .. attackerclass); -- attacker level attackerlevel = math.max(1, DA3.GetStatTotalValue(sourceCharacter, Level)); print("attacker creature level = " .. attackerlevel); -- attack damage type type = DA3.GetDamageType(TimelineContext); print("Initial damage type = " .. DA3.ToString(type)); -- attack hand hand = DA3.GetDamageEquipSlot(TimelineContext); print("initial item hand = " .. DA3.ToString(hand)); isTrap = false; if(sourceCharacter == targetCharacter) then print("Trap/Environment damage!"); isTrap = true; end MeleeRange = 3.0 isMeleeRange = false; distance = DA3.GetDistanceBetween(targetCharacter, sourceCharacter, false); if (distance <= MeleeRange) then isMeleeRange = true; end; --[[ EARLY EXIT SCENARIOS ]]-- -- invulnerable check invulnerable = DA3.CharacterHasTimelineTag(targetCharacter, Invulnerable); print("invulnerable = " .. DA3.ToString(invulnerable)); if (invulnerable) then print("target is invulnerable"); return 0; end --[[ ATTACKER SECTION ]]-- -- get damage type if (type <= 0) then print("Timeline context damage type invalid - trying to get damage type from items."); print("IsRune = " .. DA3.ToString(IsRune)); if (IsRune) then print("Rune - using rune damage type"); type = DA3.GetRuneDamageType(sourceCharacter, hand); else print("no rune......"); type = DA3.GetItemDamageType(sourceCharacter, hand); end print(" item damage type = " .. DA3.ToString(type)); if (type <= 0) then print(" setting damage type to default/physical"); type = 1; end end if (type >= 8 and attackerclass ~= CLASS_INVALID) then type = DA3.GetItemDamageType(sourceCharacter, hand); print("VS-Race damage type - using regular item damage type: " .. DA3.ToString(type)); elseif(type >= 8 and attackerclass == CLASS_INVALID) then print("Creature class with VS-Race damage type - setting to Physical (1)"); type = 1; end print("Final damage type = " .. DA3.ToString(type)); resistantToPhysicalDamage = DA3.CharacterHasTimelineTag(targetCharacter, PhysicalResistanceTag); resistantToMagicalDamage = DA3.CharacterHasTimelineTag(targetCharacter, MagicalResistanceTag); print("target physical resistance: " .. DA3.ToString(resistantToPhysicalDamage) .. ", magical damage: " .. DA3.ToString(resistantToMagicalDamage)); if(type == 1 and resistantToPhysicalDamage) then print("Physical damage vs. target that is resistant to physical damage - aborting") return 0; elseif(type ~= 1 and resistantToMagicalDamage) then print("Magical damage vs. target that is resistant to magical damage - aborting") return 0; end -- get base damage damageMultiplier = 1; if (isTrap == true) then damage = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, DamageStat_Right, sourceCharacter, targetCharacter); elseif attackerclass ~= CLASS_INVALID then damageOverride = DA3.GetAbilityProperty(TimelineContext, AbilityDamageOverride); if(damageOverride ~= nil and damageOverride > 0) then print("Using ability damage override for base damage " .. DA3.ToString(damageOverride)); damage = damageOverride; elseif (hand == 0) then -- EquipSlot_Mainhand damage = damage + DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, DamageStat_Right, sourceCharacter, targetCharacter); else damage = damage + DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, DamageStat_Left, sourceCharacter, targetCharacter); end else -- creatures if(type == 1) then -- physical damage = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, DamageStat_Right, sourceCharacter, targetCharacter); else -- elemental damage = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, DamageStat_Left, sourceCharacter, targetCharacter); end end print("base damage = " .. damage); if (attackerclass ~= CLASS_INVALID) and (targetclass ~= CLASS_INVALID) and (type == 1) then -- physical party friendly fire hax to compensate for player armor damage = damage * 2.5 print("after physical FF compensation = " .. damage); end -- apply a bit of random range damageMin = math.floor(math.max(damage * 0.95, 1) + 0.5) damageMax = math.floor(math.max(damage * 1.05, 1) + 0.5) damage = math.random(damageMin, damageMax); print("base damage after random variation = " .. damage); flanking = DA3.WedgeTest(targetCharacter, sourceCharacter, 180, 180); flankingbonus = 0; print("flanking = " .. DA3.ToString(flanking)); barrier = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, Barrier, sourceCharacter, targetCharacter) or 0; print("Early barrier check: " .. barrier); -- Physical Damage Reduction due to Armor Rating if (type == 1 and damage > 0 and IsRune == false) then -- physical (if its a physical damage AND rune then it must be vs-race rune, so we skip armor) -- get armor value of defender: Armor Rating if flanking and Armor_Rating_Front if not flanking if(flanking or targetclass == CLASS_INVALID) then -- creatures always use normal armor, no fancy front armor for them armor = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatArmor, sourceCharacter, targetCharacter); else armor = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatArmorFront, sourceCharacter, targetCharacter); end print("armor = " .. armor); -- get flat and % armor penetration of attacker armorPenetration = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, StatArmorPenetration, sourceCharacter, targetCharacter); armorPenetrationPercentage = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, StatArmorPenetrationPercentage, sourceCharacter, targetCharacter); print("armorPenetration = " .. armorPenetration); print("armorPenetrationPercentage = " .. armorPenetrationPercentage); -- calculate effective armor based on penetration of attacker effectiveArmor = math.max(0,(armor-armorPenetration)) * ((100-armorPenetrationPercentage)/100); -- reduce damage by Effective Armor -- minimum 2 damage when critting, even against massive armor -- commenting out this section as I get bad parameter error for the ArmorPercentageForMagicDefense param... not sure why --if (type ~= 1) then -- effectiveArmor = effectiveArmor * DA3.EvaluateFloatProvider(ArmorPercentageForMagicDefense, TimelineContext) --end print("effectiveArmor = " .. effectiveArmor); barrierCompensateValue = math.min(effectiveArmor, barrier); print("barrier compensate value: " .. barrierCompensateValue); armorAdjustedDamage = damage - effectiveArmor; if (critical) then damage = math.max(2, armorAdjustedDamage) + barrierCompensateValue; else damage = math.max(1, armorAdjustedDamage) + barrierCompensateValue; end print("damage (after effectiveArmor) = " .. damage); end -- The script parameter name is still StatCreatureDamageMultiplier, but it can be used by players as well damageMultiplier = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, StatCreatureDamageMultiplier, sourceCharacter, targetCharacter); damageMultiplier = (damageMultiplier * 100) - 100; -- convert to % print("damage multiplier = " .. damageMultiplier); difficultyMultiplier = 0; rankMultiplier = 0; if(attackerclass == CLASS_INVALID) then -- creature -- Difficulty difficultyMultiplier = (DA3.EvaluateFloatProvider(DifficultyDamageMultiplierProvider, TimelineContext)); difficultyMultiplier = (difficultyMultiplier * 100) - 100; -- convert to % print("Difficulty multiplier: " .. difficultyMultiplier); -- Rank rank = DA3.GetStatTotalValue(sourceCharacter, RankStat) or 0; rankDamageTable = { 0, 12.5, 25 } rankIndex = math.max(1,math.min(3,rank+1)) rankMultiplier = rankDamageTable[rankIndex] print("Rank Multiplier: " .. rankMultiplier); end -- ability damage multiplier print("Caclulating Ability Damage Multiplier (ADM)"); abilityBonus = DA3.GetAbilityProperty(TimelineContext, AbilityDamageMultiplier) or AbilityDamageMultiplierDefault; -- need to turn it into a percentage-friendly number: if(abilityBonus > 0) then abilityBonus = (abilityBonus * 100) - 100; print("Ability bonus (will be applied along with all other bonuses) = " .. abilityBonus); end -- get damage type bonus if (type == 7) then -- plot damage = math.floor(damage + 0.5); return math.max(0, damage); end bonusStat = DA3.GetDamageTypeAttackerBonusStat(DamageModifiers, type); print("bonusStat = " .. DA3.ToString(bonusStat)); typebonus = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, bonusStat, sourceCharacter, targetCharacter); print ("bonus from damage type = " .. typebonus .. "%"); -- get critical hit bonus critical = DA3.IsCriticalActivation(TimelineContext); critical_chance_override = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, Stat_Critical_Chance_Override, sourceCharacter, targetCharacter); print("critical = " .. DA3.ToString(critical) .. ", critical chance override: " .. critical_chance_override); if(critical_chance_override ~= nil and critical_chance_override > 0) then critical = false; roll = math.random(100); if(roll <= critical_chance_override) then critical = true; end print("critical result after overrride roll: " .. critical); end criticalbonus = 0; if (critical) and (attackerclass ~= CLASS_INVALID) and not(isTrap) then criticalbonus = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, CriticalBonus, sourceCharacter, targetCharacter); criticalResistance = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatResistanceCritical, sourceCharacter, targetCharacter); print ("attacker bonus from critical = " .. criticalbonus .. "%"); print ("defender resistance from critical = " .. criticalResistance .. "%"); criticalbonus = criticalbonus - criticalResistance; print ("final bonus from critical = " .. criticalbonus .. "%"); end -- attack bonus attackbonus = 0; --ability = DA3.GetContextAbility(TimelineContext); --if (attackerclass ~= CLASS_INVALID and ability ~= nil and DA3.IsAbilityWithTag(ability, BasicAttackTag) == false) then attackbonus = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, Attack, sourceCharacter, targetCharacter); print ("bonus from attack stat = " .. attackbonus .. "%"); --end -- attack bonus based on level difference (SP only) if(DA3.IsMultiplayer() == false) then if (attackerclass == CLASS_INVALID) then difference = attackerlevel - targetlevel; print("level difference = " .. difference); if (difference > 3) then attackbonus = attackbonus + (difference * 25); end print("level adjusted attack bonus = " .. attackbonus); end end -- flanking if (flanking) and not(isTrap) then flankingbonus = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, FlankingBonusStat, sourceCharacter, targetCharacter); if(isMeleeRange == false) then flankingbonus = flankingbonus - 25; -- no default, but can increase by bonuses if(flankingbonus < 0) then flankingbonus = 0 end end print("flanking bonus = " .. flankingbonus); end print("target damage before bonus adjustments = " .. damage); --[[ FINAL INCOMING DAMAGE CALCULATION ]]-- if (attackerclass == CLASS_INVALID) then damage = damage * (100 + damageMultiplier + abilityBonus + typebonus + criticalbonus + flankingbonus + attackbonus + rankMultiplier + difficultyMultiplier) / 100; else damage = damage * (100 + abilityBonus) / 100; damage = damage * (100 + damageMultiplier + typebonus + attackbonus) / 100; damage = damage * (100 + criticalbonus + flankingbonus) / 100; end print("target adjusted damage = " .. damage); --[[ TARGET SECTION ]]-- -- Damage Reduction due to Resistances resistanceStat = DA3.GetDamageTypeVictimResistanceStat(DamageModifiers, type); print("resistanceStat = " .. DA3.ToString(resistanceStat)); resistance = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, resistanceStat, sourceCharacter, targetCharacter); print ("resistance to damage type = " .. resistance); damage = damage * ((100 - resistance) / 100); damage = math.max(0, damage); print ("damage after resistance = " .. damage); -- defense resistance -- Melee defense: physical or nature damage at short range -- Ranged defense: physical or nature damage not at short range -- Magic defense: all other damage types defense = 0; if (targetclass ~= CLASS_INVALID) then defenseMelee = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatDefenseMelee, sourceCharacter, targetCharacter); defenseRanged = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatDefenseRanged, sourceCharacter, targetCharacter); defenseMagic = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatDefenseMagic, sourceCharacter, targetCharacter); defenseAllFront = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, Stat_Defense_All_Front, sourceCharacter, targetCharacter); print ("Melee Defense = " .. defenseMelee); print ("Ranged Defense = " .. defenseRanged); print ("Magic Defense = " .. defenseMagic); print ("Front Defense = " .. defenseAllFront); if(flanking == false) then print ("Being attacked from front - adding front defense to all other defenses"); defenseMelee = math.min(80,(defenseMelee + defenseAllFront)); defenseRanged = math.min(80,(defenseRanged + defenseAllFront)); defenseMagic = math.min(80,(defenseMagic + defenseAllFront)); end naturalDamageType = false; if (type == 1 or type == 5) then naturalDamageType = true; end; print ("naturalDamageType = " .. DA3.ToString(naturalDamageType)); print ("isMeleeRange = " .. DA3.ToString(isMeleeRange)); if (naturalDamageType == true and isMeleeRange == true) then defense = defenseMelee; elseif(naturalDamageType == true and isMeleeRange == false) then defense = defenseRanged; else defense = defenseMagic; end print ("defense value used for this attack = " .. defense); end damage = damage * ((100 - defense) / 100); print ("damage after defence = " .. damage); -- get shielded shielded = DA3.CharacterHasTimelineTag(targetCharacter, ShieldedTag); print("shielded = " .. DA3.ToString(shielded)); if (shielded) and (flanking == false) then print("attacker within front arc of shielded target"); damage = damage * 0.5; print("damage after shielding = " .. damage); end remaining = damage; -- barrier damage bonus if (remaining > 0) then print ("barrier = " .. barrier); if (barrier > 0) then barriermodifier = DA3.GetAbilityProperty(TimelineContext, BarrierDamageModifier); if barriermodifier ~= nil then barriermodifier = barriermodifier*100 else barriermodifier = 0; end barrierPenetration = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, StatDamageBonusBarrier, sourceCharacter, targetCharacter) or 0; barrierResistance = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatResistanceBarrier, sourceCharacter, targetCharacter) or 0; print ("barriermodifier = " .. barriermodifier); print ("barrierPenetration = " .. barrierPenetration); print ("barrierResistance = " .. barrierResistance); barriermodifier = barriermodifier + barrierPenetration - barrierResistance; barrierDamage = remaining + remaining * (barriermodifier / 100) print ("barriermodifier (total) = " .. barriermodifier .. ", barrier damage total: " .. barrierDamage); if ( barrierDamage > barrier ) then -- if the total barrier damage inflicted is bigger than the barrier value remaining damage = math.max(barrier, damage); -- should be just enough to remove the barrier or more only if the base damage, without barrier bonuses, was more than the barrier. remaining = damage - barrier; -- the remaining damage to handle past the barrier print("Barrier damage is greater than what's left of the barrier. Update total damage: " .. damage .. ", remaining damage to handle past barrier: " .. remaining); else -- the total damage reduced the barrier, but did not eliminate it damage = barrierDamage; remaining = 0; print("Barrier damage was enough to take only part of the barrier. Total barrier damage: " .. damage); print("There should be no need to handle further damage logic beyond this point"); end end end -- guard damage bonus if (remaining > 0) then guard = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatGuard, sourceCharacter, targetCharacter) or 0; print ("guard = " .. guard); if (guard > 0) then guardModifier = DA3.GetAbilityProperty(TimelineContext, GuardDamageModifier) or 0; guardModifier = guardModifier * 100; guardDamageBonus = DA3.GetStatValueWithDamageCalculationModifiers(sourceCharacter, StatDamageBonusGuard, sourceCharacter, targetCharacter) or 0; guardResist = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, StatResistanceGuard, sourceCharacter, targetCharacter) or 0; print ("guardDamageBonus = " .. guardDamageBonus); print ("guardResist = " .. guardResist); guardModifier = guardModifier + guardDamageBonus - guardResist; guardDamage = remaining + remaining * (guardModifier / 100) print ("guardModifier = " .. guardModifier); if ( guardDamage > guard ) then -- if the total guard damage inflicted is bigger than the guard value remaining damage = math.max(guard, damage); -- should be just enough to remove the guard or more only if the base damage, without barrier bonuses, was more than the barrier. remaining = damage - guard; -- the remaining damage to handle past the guard print("Guard damage is greater than what's left of the guard. Update total damage: " .. damage .. ", remaining damage to handle past guard: " .. remaining); else -- the total damage reduced the guard, but did not eliminate it damage = guardDamage; remaining = 0; print("Guard damage was enough to take only part of the guard. Total guard damage: " .. damage); print("There should be no need to handle further damage logic beyond this point"); end end end -- health damage bonus if (remaining > 0) then healthmodifier = DA3.GetAbilityProperty(TimelineContext, HealthDamageModifier) or 0; print ("healthmodifier = " .. healthmodifier); if (healthmodifier > 0) then damage = damage + (remaining * (healthmodifier / 100)); remaining = 0; end end -- damage rounding -- this is important for making sure that at least 1 damage is applied always by the player against enemies damage = math.floor(damage + 0.5); damage = math.max(1, damage); --// IMPORTANT: unkillable check should be done last, after rounding. unkillable = DA3.CharacterHasTimelineTag(targetCharacter, Unkillable); print("unkillable = " .. DA3.ToString(unkillable)); if (unkillable) then health = DA3.GetCharacterHealth(targetCharacter); print("health = " .. DA3.ToString(health)); if (health - damage <= 0) then -- if its a killing blow then inflict damage up to 1 health damage = health - 1; end if (damage < 1) then damage = 0 end end print("returned damage = " .. damage); -- Allows for specific instances where creatures take 0 damage and to not show a floaty damageTakenMultiplier = DA3.GetStatValueWithDamageCalculationModifiers(targetCharacter, DamageTakenMultiplier, sourceCharacter, targetCharacter); damage = damage * damageTakenMultiplier return damage; end