Commit 12bbad2a19d3a60a660bf1426b6b9d8089f58d17

Authored by yutio888
1 parent 3a06daa0

refactor melee hit proc:

1. add two callbacks: callbackOnMeleeProject used for effects similar to melee-project and should be triggered before weapon's on-kill effects; callbackOnMeleeHitProcs used by the victim to trigger on-hit effects
2. move many melee procs in attackTargetHitProcs into related talents/effects by callback and thus greatly simplify this function
3. remove the obselete attributes "damage_backfire","movespeed_on_hit"
4. remove several unique talent attribute "shattering_impact", "knockback_on_hit", "onslaught"
5. slightly reorders the proc order, which should not make much difference though.
... ... @@ -6057,6 +6057,8 @@ local sustainCallbackCheck = {
6057 6057 callbackOnCombatAttack = "talents_on_combat_attack",
6058 6058 callbackOnMeleeAttackBonuses = "talents_on_melee_attack_bonus",
6059 6059 callbackOnMeleeAttack = "talents_on_melee_attack",
  6060 + callbackOnMeleeProject = "talents_on_melee_attack_project",
  6061 + callbackOnMeleeHitProcs = "talents_on_melee_hit_proc",
6060 6062 callbackOnMeleeHit = "talents_on_melee_hit",
6061 6063 callbackOnMeleeMiss = "talents_on_melee_miss",
6062 6064 callbackOnArcheryAttack = "talents_on_archery_attack",
... ...
... ... @@ -1883,8 +1883,6 @@ function _M:getTextualDesc(compare_with, use_actor)
1883 1883
1884 1884 compare_fields(w, compare_with, field, "paradox_reduce_anomalies", "%+d", _t"Reduces paradox anomalies(equivalent to willpower): ")
1885 1885
1886   - compare_fields(w, compare_with, field, "damage_backfire", "%+d%%", _t"Damage Backlash: ", nil, true)
1887   -
1888 1886 compare_fields(w, compare_with, field, "resist_unseen", "%-d%%", _t"Reduce all damage from unseen attackers: ")
1889 1887
1890 1888 if w.undead and w.undead > 0 then
... ...
... ... @@ -701,6 +701,8 @@ end
701 701
702 702 --- handle various on hit procs for melee combat
703 703 function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult, atk, def, hitted, crit, evaded, repelled, old_target_life)
  704 + local old_global_accuracy_damage_bonus = self.__global_accuracy_damage_bonus
  705 + self.__global_accuracy_damage_bonus = nil
704 706 if self:isAccuracyEffect(weapon, "staff") then
705 707 local bonus = 1 + self:getAccuracyEffect(weapon, atk, def, 0.02, 2) -- +200% proc damage at 100 accuracy
706 708 print("[ATTACK] staff accuracy bonus", atk, def, "=", bonus)
... ... @@ -715,39 +717,6 @@ function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult,
715 717 self.__global_accuracy_damage_bonus = self.__global_accuracy_damage_bonus / self.__attacktargetwith_recursing_procs_reduce
716 718 end
717 719
718   - if self:attr("unharmed_attack_on_hit") then
719   - local v = self:attr("unharmed_attack_on_hit")
720   - self:attr("unharmed_attack_on_hit", -v)
721   - if rng.percent(50) then self:attackTarget(target, nil, 1, true, true) end
722   - self:attr("unharmed_attack_on_hit", v)
723   - end
724   -
725   - -- handle stalk targeting for hits (also handled in Actor for turn end effects)
726   - if hitted and target ~= self then
727   - local effStalker = self:hasEffect(self.EFF_STALKER)
728   - if effStalker then
729   - -- mark if stalkee was hit
730   - effStalker.hit = effStalker.hit or effStalker.target == target
731   - elseif self:isTalentActive(self.T_STALK) then
732   - local stalk = self:isTalentActive(self.T_STALK)
733   -
734   - if not stalk.hit then
735   - -- mark a new target
736   - stalk.hit = true
737   - stalk.hit_target = target
738   - elseif stalk.hit_target ~= target then
739   - -- more than one target; clear it
740   - stalk.hit_target = nil
741   - end
742   - end
743   - end
744   -
745   - -- Spread diseases
746   - if hitted and self:knowTalent(self.T_CARRIER) and rng.percent(self:callTalent(self.T_CARRIER, "getDiseaseSpread")) then
747   - -- Use epidemic talent spreading
748   - self:callTalent(self.T_EPIDEMIC, "do_spread", target, dam)
749   - end
750   -
751 720 -- Melee project
752 721 if hitted and not target.dead and weapon and weapon.melee_project then for typ, dam in pairs(weapon.melee_project) do
753 722 if dam > 0 then
... ... @@ -760,30 +729,6 @@ function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult,
760 729 end
761 730 end end
762 731
763   - -- Shadow cast
764   - if hitted and not target.dead and self:knowTalent(self.T_SHADOW_COMBAT) and self:isTalentActive(self.T_SHADOW_COMBAT) then
765   - local dam = self:callTalent(self.T_SHADOW_COMBAT, "getDamage")
766   - DamageType:get(DamageType.DARKNESS).projector(self, target.x, target.y, DamageType.DARKNESS, dam)
767   - end
768   -
769   - -- Ruin
770   - if hitted and not target.dead and self:knowTalent(self.T_RUIN) and self:isTalentActive(self.T_RUIN) then
771   - local t = self:getTalentFromId(self.T_RUIN)
772   - local dam = {dam=t.getDamage(self, t), healfactor=0.4, source=t}
773   - DamageType:get(DamageType.DRAINLIFE).projector(self, target.x, target.y, DamageType.DRAINLIFE, dam)
774   - end
775   -
776   - -- Temporal Cast
777   - if hitted and self:knowTalent(self.T_WEAPON_FOLDING) and self:isTalentActive(self.T_WEAPON_FOLDING) then
778   - self:callTalent(self.T_WEAPON_FOLDING, "doWeaponFolding", target)
779   - end
780   -
781   - -- Autospell cast
782   - if hitted and not target.dead and self:knowTalent(self.T_ARCANE_COMBAT) and self:isTalentActive(self.T_ARCANE_COMBAT) then
783   - local t = self:getTalentFromId(self.T_ARCANE_COMBAT)
784   - t.do_trigger(self, t, target)
785   - end
786   -
787 732 -- On hit talent
788 733 -- Disable friendly fire for procs since players can't control when they happen or where they hit
789 734 local old_ff = self.nullify_all_friendlyfire
... ... @@ -807,23 +752,6 @@ function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult,
807 752 end
808 753 self.nullify_all_friendlyfire = old_ff
809 754
810   - -- Shattering Impact
811   - if hitted and self:attr("shattering_impact") and (not self.shattering_impact_last_turn or self.shattering_impact_last_turn < game.turn) then
812   - local dam = dam * self.shattering_impact
813   - game.logSeen(target, "The shattering blow creates a shockwave!")
814   - self:project({type="ball", radius=1, selffire=false, act_exclude={[target.uid]=true}}, target.x, target.y, DamageType.PHYSICAL, dam) -- don't hit target with the AOE
815   - self:incStamina(-8)
816   - self.shattering_impact_last_turn = game.turn
817   - end
818   -
819   - -- Damage Backlash
820   - if dam > 0 and self:attr("damage_backfire") then
821   - local hurt = math.min(dam, old_target_life) * self.damage_backfire / 100
822   - if hurt > 0 then
823   - self:takeHit(hurt, self, {cant_die=true})
824   - end
825   - end
826   -
827 755 -- Burst on Hit
828 756 if hitted and weapon and weapon.burst_on_hit then
829 757 for typ, dam in pairs(weapon.burst_on_hit) do
... ... @@ -842,40 +770,7 @@ function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult,
842 770 end
843 771 end
844 772
845   - -- Arcane Destruction
846   - if hitted and crit and weapon and self:knowTalent(self.T_ARCANE_DESTRUCTION) then
847   - local chance = 100
848   - if self:hasShield() then chance = 50
849   - elseif self:hasDualWeapon() then chance = 50
850   - end
851   - if rng.percent(chance) then
852   - local t = self:getTalentFromId(self.T_ARCANE_DESTRUCTION)
853   - self:project({type="ball", radius=self:getTalentRadius(t), friendlyfire=false}, target.x, target.y, DamageType.ARCANE, t.getDamage(self, t))
854   - game.level.map:particleEmitter(target.x, target.y, self:getTalentRadius(t), "ball_arcane", {radius=2, tx=target.x, ty=target.y})
855   - end
856   - end
857   -
858   - -- Onslaught
859   - if hitted and self:attr("onslaught") then
860   - local dir = util.getDir(target.x, target.y, self.x, self.y) or 6
861   - local lx, ly = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).left)
862   - local rx, ry = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).right)
863   - local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR)
864   -
865   - if target:checkHit(self:combatAttack(weapon), target:combatPhysicalResist(), 0, 95, 10) and target:canBe("knockback") then
866   - target:knockback(self.x, self.y, self:attr("onslaught"))
867   - target:crossTierEffect(target.EFF_OFFBALANCE, self:combatAttack())
868   - end
869   - if lt and lt:checkHit(self:combatAttack(weapon), lt:combatPhysicalResist(), 0, 95, 10) and lt:canBe("knockback") then
870   - lt:knockback(self.x, self.y, self:attr("onslaught"))
871   - target:crossTierEffect(target.EFF_OFFBALANCE, self:combatAttack())
872   - end
873   - if rt and rt:checkHit(self:combatAttack(weapon), rt:combatPhysicalResist(), 0, 95, 10) and rt:canBe("knockback") then
874   - rt:knockback(self.x, self.y, self:attr("onslaught"))
875   - target:crossTierEffect(target.EFF_OFFBALANCE, self:combatAttack())
876   - end
877   - end
878   -
  773 + self:fireTalentCheck("callbackOnMeleeProject", target, hitted, crit, weapon, damtype, mult, dam)
879 774 -- Reactive target on_melee_hit damage
880 775 if hitted then
881 776 local dr, fa, pct = 0
... ... @@ -925,53 +820,6 @@ function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult,
925 820 target.__project_source = old
926 821
927 822 end
928   - -- Acid splash
929   - if hitted and not target.dead and target:knowTalent(target.T_ACID_BLOOD) then
930   - local t = target:getTalentFromId(target.T_ACID_BLOOD)
931   - t.do_splash(target, t, self)
932   - end
933   -
934   - -- Bloodbath
935   - if hitted and crit and self:knowTalent(self.T_BLOODBATH) then
936   - local t = self:getTalentFromId(self.T_BLOODBATH)
937   - t.do_bloodbath(self, t)
938   - end
939   -
940   - -- Mortal Terror
941   - if hitted and not target.dead and self:knowTalent(self.T_MORTAL_TERROR) then
942   - local t = self:getTalentFromId(self.T_MORTAL_TERROR)
943   - t.do_terror(self, t, target, dam)
944   - end
945   -
946   - -- Psi Auras
947   - local psiweapon = self:getInven("PSIONIC_FOCUS") and self:getInven("PSIONIC_FOCUS")[1]
948   - if psiweapon and psiweapon.combat and psiweapon.subtype ~= "mindstar" then
949   - if hitted and not target.dead and self:knowTalent(self.T_KINETIC_AURA) and self:isTalentActive(self.T_KINETIC_AURA) and self.use_psi_combat then
950   - local t = self:getTalentFromId(self.T_KINETIC_AURA)
951   - t.do_combat(self, t, target)
952   - end
953   - if hitted and not target.dead and self:knowTalent(self.T_THERMAL_AURA) and self:isTalentActive(self.T_THERMAL_AURA) and self.use_psi_combat then
954   - local t = self:getTalentFromId(self.T_THERMAL_AURA)
955   - t.do_combat(self, t, target)
956   - end
957   - if hitted and not target.dead and self:knowTalent(self.T_CHARGED_AURA) and self:isTalentActive(self.T_CHARGED_AURA) and self.use_psi_combat then
958   - local t = self:getTalentFromId(self.T_CHARGED_AURA)
959   - t.do_combat(self, t, target)
960   - end
961   - end
962   -
963   - -- Static dis-Charge
964   - if hitted and not target.dead and self:hasEffect(self.EFF_STATIC_CHARGE) then
965   - local eff = self:hasEffect(self.EFF_STATIC_CHARGE)
966   - DamageType:get(DamageType.LIGHTNING).projector(self, target.x, target.y, DamageType.LIGHTNING, eff.power)
967   - self:removeEffect(self.EFF_STATIC_CHARGE)
968   - end
969   -
970   - -- Exploit Weakness
971   - if hitted and not target.dead and self:knowTalent(self.T_EXPLOIT_WEAKNESS) and self:isTalentActive(self.T_EXPLOIT_WEAKNESS) then
972   - local t = self:getTalentFromId(self.T_EXPLOIT_WEAKNESS)
973   - t.do_weakness(self, t, target)
974   - end
975 823
976 824 -- Special weapon effects (passing the special definition to facilitate encapsulating multiple special effects)
977 825 if hitted and weapon and weapon.special_on_hit then
... ... @@ -1019,128 +867,18 @@ function _M:attackTargetHitProcs(target, weapon, dam, apr, armor, damtype, mult,
1019 867 if hitted and self:attr("psi_regen_on_hit") then self:incPsi(self.psi_regen_on_hit) end
1020 868 end
1021 869
1022   - -- Ablative armor
1023   - if hitted and not target.dead and target:attr("carbon_spikes") then
1024   - if target.carbon_armor >= 1 then
1025   - target.carbon_armor = target.carbon_armor - 1
1026   - else
1027   - -- Deactivate without loosing energy
1028   - target:forceUseTalent(target.T_CARBON_SPIKES, {ignore_energy=true})
1029   - end
1030   - end
1031   -
1032   - if hitted and not target.dead and target:knowTalent(target.T_STONESHIELD) then
1033   - local t = target:getTalentFromId(target.T_STONESHIELD)
1034   - local m, mm, e, em = t.getValues(self, t)
1035   - target:incMana(math.min(dam * m, mm))
1036   - target:incEquilibrium(-math.min(dam * e, em))
1037   - end
1038   -
1039   - -- Set Up
1040   - if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:hasEffect(target.EFF_DEFENSIVE_MANEUVER) then
1041   - local t = target:getTalentFromId(target.T_SET_UP)
1042   - local power = t.getPower(target, t)
1043   - self:setEffect(self.EFF_SET_UP, 2, {src = target, power=power})
1044   - end
1045   -
1046   - -- Counter Attack!
1047   - if not hitted and not target.dead and target:knowTalent(target.T_COUNTER_ATTACK) and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:knowTalent(target.T_COUNTER_ATTACK) and self:isNear(target.x,target.y, 1) then --Adjacency check
1048   - local cadam = target:callTalent(target.T_COUNTER_ATTACK,"checkCounterAttack")
1049   - if cadam then
1050   - local t = target:getTalentFromId(target.T_COUNTER_ATTACK)
1051   - t.do_counter(target, self, t)
1052   - end
1053   - end
1054   -
1055   - -- Gesture of Guarding counterattack
1056   - if hitted and not target.dead and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:hasEffect(target.EFF_GESTURE_OF_GUARDING) then
1057   - local t = target:getTalentFromId(target.T_GESTURE_OF_GUARDING)
1058   - t.on_hit(target, t, self)
1059   - end
1060   -
1061   - -- Defensive Throw!
1062   - if not hitted and not target.dead and target:knowTalent(target.T_DEFENSIVE_THROW) and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:isNear(self.x,self.y,1) then
1063   - local t = target:getTalentFromId(target.T_DEFENSIVE_THROW)
1064   - t.do_throw(target, self, t)
1065   - end
1066   -
1067 870 -- Zero gravity
1068 871 if hitted and game.level.data.zero_gravity and rng.percent(util.bound(dam, 0, 100)) then
1069 872 target:knockback(self.x, self.y, math.ceil(math.log(dam)))
1070 873 end
1071 874
1072   - -- Roll with it
1073   - if hitted and target:attr("knockback_on_hit") and not target.turn_procs.roll_with_it and rng.percent(util.bound(dam, 0, 100)) then
1074   - local ox, oy = self.x, self.y
1075   - game:onTickEnd(function()
1076   - target:knockback(ox, oy, 1)
1077   - if not target:hasEffect(target.EFF_WILD_SPEED) then target:setEffect(target.EFF_WILD_SPEED, 1, {power=200}) end
1078   - end)
1079   - target.turn_procs.roll_with_it = true
1080   - end
1081   -
1082   - -- Weakness hate bonus
1083   - local effGloomWeakness = target:hasEffect(target.EFF_GLOOM_WEAKNESS)
1084   - if hitted and effGloomWeakness and effGloomWeakness.hateBonus or 0 > 0 then
1085   - self:incHate(effGloomWeakness.hateBonus)
1086   - game.logPlayer(self, "#F53CBE#You revel in attacking a weakened foe! (+%d hate)", effGloomWeakness.hateBonus)
1087   - effGloomWeakness.hateBonus = nil
1088   - end
1089   -
1090   - -- Rampage
1091   - if hitted and crit then
1092   - local eff = self:hasEffect(self.EFF_RAMPAGE)
1093   - if eff and not eff.critHit and eff.actualDuration < eff.maxDuration and self:knowTalent(self.T_BRUTALITY) then
1094   - game.logPlayer(self, "#F53CBE#Your rampage is invigorated by your fierce attack! (+1 duration)")
1095   - eff.critHit = true
1096   - eff.actualDuration = eff.actualDuration + 1
1097   - eff.dur = eff.dur + 1
1098   - end
1099   - end
1100   -
1101   - if hitted and crit and target:hasEffect(target.EFF_DISMAYED) then
1102   - target:removeEffect(target.EFF_DISMAYED)
1103   - end
1104   -
1105   - if hitted and not target.dead then
1106   - -- Curse of Madness: Twisted Mind
1107   - --[[if self.hasEffect and self:hasEffect(self.EFF_CURSE_OF_MADNESS) then
1108   - local eff = self:hasEffect(self.EFF_CURSE_OF_MADNESS)
1109   - local def = self.tempeffect_def[self.EFF_CURSE_OF_MADNESS]
1110   - def.doConspirator(self, eff, target)
1111   - end
1112   - if target.hasEffect and target:hasEffect(target.EFF_CURSE_OF_MADNESS) then
1113   - local eff = target:hasEffect(target.EFF_CURSE_OF_MADNESS)
1114   - local def = target.tempeffect_def[target.EFF_CURSE_OF_MADNESS]
1115   - def.doConspirator(target, eff, self)
1116   - end]]
1117   -
1118   - -- Curse of Nightmares: Suffocate
1119   - --[[if self.hasEffect and self:hasEffect(self.EFF_CURSE_OF_NIGHTMARES) then
1120   - local eff = self:hasEffect(self.EFF_CURSE_OF_NIGHTMARES)
1121   - local def = self.tempeffect_def[self.EFF_CURSE_OF_NIGHTMARES]
1122   - def.doSuffocate(self, eff, target)
1123   - end
1124   - if target.hasEffect and target:hasEffect(target.EFF_CURSE_OF_NIGHTMARES) then
1125   - local eff = target:hasEffect(target.EFF_CURSE_OF_NIGHTMARES)
1126   - local def = target.tempeffect_def[target.EFF_CURSE_OF_NIGHTMARES]
1127   - def.doSuffocate(target, eff, self)
1128   - end]]
1129   - end
1130   -
1131   - if target:isTalentActive(target.T_SHARDS) and hitted and not target.dead and not target.turn_procs.shield_shards then
1132   - local t = target:getTalentFromId(target.T_SHARDS)
1133   - target.turn_procs.shield_shards = true
1134   - self.logCombat(target, self, "#Source# counter attacks #Target# with %s shield shards!", string.his_her(target))
1135   - target:attr("ignore_counterstrike", 1)
1136   - target:attackTarget(self, DamageType.NATURE, self:combatTalentWeaponDamage(t, 0.4, 1), true)
1137   - target:attr("ignore_counterstrike", -1)
1138   - end
  875 + local missed = not hitted and not evaded and not repelled
1139 876 -- post melee attack hooks/callbacks, not included: apr, armor, atk, def, evaded, repelled, old_target_life
1140   - local hd = {"Combat:attackTargetWith", hitted=hitted, crit=crit, target=target, weapon=weapon, damtype=damtype, mult=mult, dam=dam}
  877 + local hd = {"Combat:attackTargetWith", hitted=hitted, crit=crit, target=target, weapon=weapon, damtype=damtype, mult=mult, dam=dam, missed = missed}
1141 878 if self:triggerHook(hd) then hitted = hd.hitted end
1142   - self:fireTalentCheck("callbackOnMeleeAttack", target, hitted, crit, weapon, damtype, mult, dam, hd)
1143   - self.__global_accuracy_damage_bonus = nil
  879 + target:fireTalentCheck("callbackOnMeleeHitProcs", self, hitted, crit, weapon, damtype, mult, dam, missed)
  880 + self:fireTalentCheck("callbackOnMeleeAttack", target, hitted, crit, weapon, damtype, mult, dam, hd, missed)
  881 + self.__global_accuracy_damage_bonus = old_global_accuracy_damage_bonus
1144 882
1145 883 return hitted
1146 884 end
... ...
... ... @@ -806,6 +806,17 @@ newTalent{
806 806 self.carbon_armor = self.carbon_armor + 1
807 807 end
808 808 end,
  809 + callbackOnMeleeHitProcs = function(self, t, target, hitted)
  810 + -- Ablative armor
  811 + if hitted and not self.dead and self:attr("carbon_spikes") then
  812 + if self.carbon_armor >= 1 then
  813 + self.carbon_armor = self.carbon_armor - 1
  814 + else
  815 + -- Deactivate without loosing energy
  816 + self:forceUseTalent(self.T_CARBON_SPIKES, {ignore_energy=true})
  817 + end
  818 + end
  819 + end,
809 820 do_carbonLoss = function(self, t)
810 821 if self.carbon_armor >= 1 then
811 822 self.carbon_armor = self.carbon_armor - 1
... ...
... ... @@ -211,6 +211,13 @@ newTalent{
211 211 DamageType:get(DamageType.TEMPORAL).projector(self, target.x, target.y, DamageType.TEMPORAL, dam)
212 212 end
213 213 end,
  214 + callbackPriorities = { callbackOnMeleeProject = -25 },
  215 + callbackOnMeleeProject = function(self, t, target, hitted)
  216 + -- Temporal Cast
  217 + if hitted then
  218 + t.doWeaponFolding(self, t, target)
  219 + end
  220 + end,
214 221 info = function(self, t)
215 222 local damage = t.getDamage(self, t)
216 223 local chance = t.getChance(self, t)
... ...
... ... @@ -75,6 +75,14 @@ newTalent{
75 75 passives = function(self, t, p)
76 76 self:talentTemporaryValue(p, "disease_immune", t.getDiseaseImmune(self, t))
77 77 end,
  78 + callbackPriorities = { callbackOnMeleeProject = - 1000 }, -- happens before most effect
  79 + callbackOnMeleeProject = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  80 + -- Spread diseases
  81 + if hitted and rng.percent(t.getDiseaseSpread(self, t)) then
  82 + -- Use epidemic talent spreading
  83 + self:callTalent(self.T_EPIDEMIC, "do_spread", target, dam)
  84 + end
  85 + end,
78 86 info = function(self, t)
79 87 return ([[You gain a %d%% resistance to diseases, and each of your melee attacks have a %d%% chance to spread any diseases on your target.
80 88 (As the Epidemic talent with the melee attack treated like blight damage.)]]):
... ... @@ -88,6 +96,12 @@ newTalent{
88 96 mode = "passive",
89 97 require = str_corrs_req4,
90 98 points = 5,
  99 + callbackPriorities = { callbackOnMeleeHitProcs = -20 },
  100 + callbackOnMeleeHitProcs = function(self, t, target, hitted)
  101 + if hitted and not self.dead then
  102 + t.do_splash(self, t, target)
  103 + end
  104 + end,
91 105 do_splash = function(self, t, target)
92 106 local dam = self:spellCrit(self:combatTalentSpellDamage(t, 5, 30))
93 107 local atk = self:combatTalentSpellDamage(t, 15, 35)
... ...
... ... @@ -110,6 +110,14 @@ newTalent{
110 110 deactivate = function(self, t, p)
111 111 return true
112 112 end,
  113 + callbackPriorities = { callbackOnMeleeProject = -25 },
  114 + callbackOnMeleeProject = function(self, t, target, hitted)
  115 + -- Ruin
  116 + if hitted and not target.dead and self:knowTalent(self.T_RUIN) and self:isTalentActive(self.T_RUIN) then
  117 + local dam = {dam=t.getDamage(self, t), healfactor=0.4, source=t}
  118 + DamageType:get(DamageType.DRAINLIFE).projector(self, target.x, target.y, DamageType.DRAINLIFE, dam)
  119 + end
  120 + end,
113 121 info = function(self, t)
114 122 local dam = damDesc(self, DamageType.BLIGHT, t.getDamage(self, t))
115 123 return ([[Concentrate on the corruption you bring, enhancing each of your melee strikes with %0.2f blight damage (which also heals you for %0.2f each hit).
... ...
... ... @@ -34,6 +34,13 @@ newTalent{
34 34 end,
35 35 getDamage = function(self, t) return self:combatTalentSpellDamage(t, 1, 70)+10 end, -- This doesn't crit or generally scale easily so its safe to be aggressive
36 36 getManaCost = function(self, t) return 0 end,
  37 + callbackPriorities = { callbackOnMeleeProject = -25 },
  38 + callbackOnMeleeProject = function(self, t, target, hitted)
  39 + if hitted and not target.dead then
  40 + local dam = self:callTalent(self.T_SHADOW_COMBAT, "getDamage")
  41 + DamageType:get(DamageType.DARKNESS).projector(self, target.x, target.y, DamageType.DARKNESS, dam)
  42 + end
  43 + end,
37 44 activate = function(self, t)
38 45 local ret = {}
39 46 if core.shader.active(4) then
... ...
... ... @@ -157,6 +157,14 @@ newTalent{
157 157 do_weakness = function(self, t, target)
158 158 target:setEffect(target.EFF_WEAKENED_DEFENSES, 3, {inc = - 5, max = - t.getReductionMax(self, t)})
159 159 end,
  160 + callbackPriorities = {callbackOnMeleeAttack = -10},
  161 + callbackOnMeleeAttack = function(self, t, target, hitted)
  162 + -- Exploit Weakness
  163 + if hitted and not target.dead then
  164 + local t = self:getTalentFromId(self.T_EXPLOIT_WEAKNESS)
  165 + t.do_weakness(self, t, target)
  166 + end
  167 + end,
160 168 activate = function(self, t)
161 169 return {
162 170 dam = self:addTemporaryValue("inc_damage", {[DamageType.PHYSICAL]=-10}),
... ...
... ... @@ -79,6 +79,23 @@ newTalent{
79 79 stalk.hit_turns = 0
80 80 end
81 81 end,
  82 + callbackPriorities = { callbackOnMeleeAttack = -99 },
  83 + callbackOnMeleeAttack = function(self, eff, target, hitted)
  84 + -- handle stalk targeting for hits (also handled in Actor for turn end effects)
  85 + if hitted and target ~= self and not self:hasEffect(self.EFF_STALKER) then
  86 + -- mark if stalkee was hit
  87 + local stalk = self:isTalentActive(self.T_STALK)
  88 +
  89 + if not stalk.hit then
  90 + -- mark a new target
  91 + stalk.hit = true
  92 + stalk.hit_target = target
  93 + elseif stalk.hit_target ~= target then
  94 + -- more than one target; clear it
  95 + stalk.hit_target = nil
  96 + end
  97 + end
  98 + end,
82 99 info = function(self, t)
83 100 local duration = t.getDuration(self, t)
84 101 return ([[When you focus your attacks on a single foe and strike them in melee for two consecutive turns, your hatred of them overcomes you and you begin to stalk them with single-minded purpose. The effect will last for %d turns, or until your prey is dead. Stalking gives you bonuses against your foe that grow each turn you hit them, and diminish each turn you don't.
... ...
... ... @@ -285,12 +285,15 @@ newTalent{
285 285 end
286 286 return deflected
287 287 end,
288   - -- Counterattack handled in _M:attackTargetWith function in mod.class.interface.Combat.lua (requires EFF_GESTURE_OF_GUARDING)
289   - on_hit = function(self, t, who)
290   - if rng.percent(t.getCounterAttackChance(self, t)) and self:isTalentActive(self.T_GESTURE_OF_PAIN) and canUseGestures(self) then
291   - self:logCombat(who, "#F53CBE##Source# lashes back at #Target#!")
292   - local tGestureOfPain = self:getTalentFromId(self.T_GESTURE_OF_PAIN)
293   - tGestureOfPain.attack(self, tGestureOfPain, who)
  288 + callbackPriorities = { callbackOnMeleeHitProcs = -1, },
  289 + callbackOnMeleeHitProcs = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  290 + -- Gesture of Guarding counterattack
  291 + if hitted and not self.dead and not self:attr("stunned") and not self:attr("dazed") and not self:attr("stoned") and self:hasEffect(self.EFF_GESTURE_OF_GUARDING) then
  292 + if rng.percent(t.getCounterAttackChance(self, t)) and self:isTalentActive(self.T_GESTURE_OF_PAIN) and canUseGestures(self) then
  293 + self:logCombat(target, "#F53CBE##Source# lashes back at #Target#!")
  294 + local tGestureOfPain = self:getTalentFromId(self.T_GESTURE_OF_PAIN)
  295 + tGestureOfPain.attack(self, tGestureOfPain, target)
  296 + end
294 297 end
295 298 end,
296 299 on_learn = gestures_gfx_update,
... ...
... ... @@ -37,6 +37,14 @@ newTalent{
37 37 self:combatTalentLimit(t, 0.5, 0.075, 0.2),
38 38 self:combatTalentScale(t, 5, 9, "log")
39 39 end,
  40 + callbackPriorities = { callbackOnMeleeHitProcs = -3, },
  41 + callbackOnMeleeHitProcs = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  42 + if hitted and not self.dead then
  43 + local m, mm, e, em = t.getValues(self, t)
  44 + self:incMana(math.min(dam * m, mm))
  45 + self:incEquilibrium(-math.min(dam * e, em))
  46 + end
  47 + end,
40 48 getDamage = function(self, t) return 30 end,
41 49 getPercentInc = function(self, t) return math.sqrt(self:getTalentLevel(t) / 5) / 1.5 end,
42 50 info = function(self, t)
... ... @@ -80,6 +88,17 @@ newTalent{
80 88 sustain_equilibrium = 15,
81 89 cooldown = 30,
82 90 tactical = { BUFF = 2 },
  91 + callbackPriorities={callbackOnMeleeHitProcs = 1 },
  92 + callbackOnMeleeHitProcs = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  93 + if self:isTalentActive(self.T_SHARDS) and hitted and not self.dead and not self.turn_procs.shield_shards then
  94 + local t = self:getTalentFromId(self.T_SHARDS)
  95 + self.turn_procs.shield_shards = true
  96 + self.logCombat(self, target, "#Source# counter attacks #Target# with %s shield shards!", string.his_her(self))
  97 + self:attr("ignore_counterstrike", 1)
  98 + self:attackTarget(target, DamageType.NATURE, self:combatTalentWeaponDamage(t, 0.4, 1), true)
  99 + self:attr("ignore_counterstrike", -1)
  100 + end
  101 + end,
83 102 activate = function(self, t)
84 103 return {}
85 104 end,
... ...
... ... @@ -3378,6 +3378,11 @@ newTalent{
3378 3378 getThrows = function(self, t)
3379 3379 return self:combatScale(self:getStr() + self:getDex()-20, 0, 0, 2.24, 180)
3380 3380 end,
  3381 + callbackOnMeleeHitProcs = function(self, t, target, hitted)
  3382 + if not hitted and not self.dead and not self:attr("stunned") and not self:attr("dazed") and not self:attr("stoned") and self:isNear(target.x, target.y,1) then
  3383 + t.do_throw(self, target, t)
  3384 + end
  3385 + end,
3381 3386 -- called by _M:attackTargetWith function in mod\class\interface\Combat.lua (includes adjacency check)
3382 3387 do_throw = function(self, target, t)
3383 3388 local ef = self:hasEffect(self.EFF_DEFENSIVE_GRAPPLING)
... ...
... ... @@ -190,6 +190,15 @@ newTalent{
190 190 getKnockback = function(self, t)
191 191 return 3 + math.floor(self:getTalentLevel(t))
192 192 end,
  193 + callbackPriorities = { callbackOnMeleeProject = -25 },
  194 + callbackOnMeleeProject = function(self, t, target, hitted)
  195 + if hitted and not target.dead and self.use_psi_combat then
  196 + local psiweapon = self:getInven("PSIONIC_FOCUS") and self:getInven("PSIONIC_FOCUS")[1]
  197 + if psiweapon and psiweapon.combat and psiweapon.subtype ~= "mindstar" then
  198 + t.do_combat(self, t, target)
  199 + end
  200 + end
  201 + end,
193 202 callbackOnActBase = function(self, t)
194 203 if not aura_should_proc(self, t) then return end
195 204 local mast = aura_mastery(self, t)
... ... @@ -310,6 +319,15 @@ newTalent{
310 319 getSpikeCost = function(self, t)
311 320 return t.sustain_psi*2/3
312 321 end,
  322 + callbackPriorities = { callbackOnMeleeProject = -25 },
  323 + callbackOnMeleeProject = function(self, t, target, hitted)
  324 + if hitted and not target.dead and self.use_psi_combat then
  325 + local psiweapon = self:getInven("PSIONIC_FOCUS") and self:getInven("PSIONIC_FOCUS")[1]
  326 + if psiweapon and psiweapon.combat and psiweapon.subtype ~= "mindstar" then
  327 + t.do_combat(self, t, target)
  328 + end
  329 + end
  330 + end,
313 331 callbackOnActBase = function(self, t)
314 332 if not aura_should_proc(self, t) then return end
315 333 local mast = aura_mastery(self, t)
... ... @@ -433,6 +451,15 @@ newTalent{
433 451 getNumSpikeTargets = function(self, t)
434 452 return 3 + math.floor(0.5*self:getTalentLevel(t))
435 453 end,
  454 + callbackPriorities = { callbackOnMeleeProject = -25 },
  455 + callbackOnMeleeProject = function(self, t, target, hitted)
  456 + if hitted and not target.dead and self.use_psi_combat then
  457 + local psiweapon = self:getInven("PSIONIC_FOCUS") and self:getInven("PSIONIC_FOCUS")[1]
  458 + if psiweapon and psiweapon.combat and psiweapon.subtype ~= "mindstar" then
  459 + t.do_combat(self, t, target)
  460 + end
  461 + end
  462 + end,
436 463 callbackOnActBase = function(self, t)
437 464 if not aura_should_proc(self, t) then return end
438 465 local mast = aura_mastery(self, t)
... ...
... ... @@ -40,6 +40,15 @@ newTalent{
40 40 game.logSeen(target, "%s resists the terror!", target:getName():capitalize())
41 41 end
42 42 end,
  43 + callbackPriorities = {
  44 + callbackOnMeleeAttack = -30,
  45 + },
  46 + callbackOnMeleeAttack = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  47 + -- Mortal Terror
  48 + if hitted and not target.dead then
  49 + t.do_terror(self, t, target, dam)
  50 + end
  51 + end,
43 52 passives = function(self, t, p)
44 53 self:talentTemporaryValue(p, "combat_physcrit", t.getCrit(self, t))
45 54 end,
... ... @@ -65,6 +74,11 @@ newTalent{
65 74 do_bloodbath = function(self, t)
66 75 self:setEffect(self.EFF_BLOODBATH, t.getDuration(self, t), {regen=t.getRegen(self, t), max=t.getMax(self, t), hp=t.getHealth(self,t)})
67 76 end,
  77 + callbackOnMeleeAttack = function(self, t, target, hitted, crit)
  78 + if hitted and crit then
  79 + t.do_bloodbath(self, t)
  80 + end
  81 + end,
68 82 info = function(self, t)
69 83 local regen = t.getRegen(self, t)
70 84 local max_regen = t.getMax(self, t)
... ...
... ... @@ -29,6 +29,12 @@ newTalent{
29 29 cooldown = 5,
30 30 tactical = { BUFF = 2 },
31 31 getChance = function(self, t) return self:combatLimit(self:getTalentLevel(t) * (1 + self:getCun(9, true)), 100, 20, 0, 70, 50) end, -- Limit < 100%
  32 + callbackPriorities = { callbackOnMeleeProject = -25 },
  33 + callbackOnMeleeProject = function(self, t, target, hitted)
  34 + if hitted and not target.dead then
  35 + t.do_trigger(self, t, target)
  36 + end
  37 + end,
32 38 callbackOnAITalentTactics = function(self, t, hd)
33 39 local p = self:isTalentActive(t.id)
34 40 if not p then return end
... ... @@ -237,6 +243,20 @@ newTalent{
237 243 radius = function(self, t) return self:getTalentLevel(t) < 5 and 1 or 2 end,
238 244 getDamage = function(self, t) return self:combatTalentSpellDamage(t, 1, 100) end,
239 245 getSPMult = function(self, t) return self:combatTalentScale(t, 1/7, 5/7) end,
  246 + callbackPriorities = { callbackOnMeleeProject = -20 },
  247 + callbackOnMeleeProject = function(self, t, target, hitted, crit)
  248 + -- Arcane Destruction
  249 + if hitted and crit then
  250 + local chance = (self:hasShield() or self:hasDualWeapon()) and 50 or 100
  251 + if rng.percent(chance) then
  252 + self:project({type="ball", radius=self:getTalentRadius(t), friendlyfire=false}, target.x, target.y, DamageType.ARCANE, t.getDamage(self, t))
  253 + if not self:hasProc("arcane_destruction_particle") then
  254 + self:setProc("arcane_destruction_particle")
  255 + game.level.map:particleEmitter(target.x, target.y, self:getTalentRadius(t), "ball_arcane", {radius=2, tx=target.x, ty=target.y})
  256 + end
  257 + end
  258 + end
  259 + end,
240 260 info = function(self, t)
241 261 return ([[Raw magical damage channels through the caster's weapon, increasing raw Physical Power by %d%% of your Magic (current bonus: %d).
242 262 Each time you crit with a melee blow, you will unleash a radius %d ball of arcane damage, doing %0.2f.
... ...
... ... @@ -54,16 +54,37 @@ newTalent{
54 54 range = function(self,t) return math.floor(self:combatTalentLimit(t, 10, 1, 5)) end, -- Limit KB range to <10
55 55 activate = function(self, t)
56 56 return {
57   - onslaught = self:addTemporaryValue("onslaught", t.range(self,t)),
58 57 stamina = self:addTemporaryValue("stamina_regen", -1),
59 58 }
60 59 end,
61 60
62 61 deactivate = function(self, t, p)
63   - self:removeTemporaryValue("onslaught", p.onslaught)
64 62 self:removeTemporaryValue("stamina_regen", p.stamina)
65 63 return true
66 64 end,
  65 + callbackOnMeleeAttack = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  66 + -- Onslaught
  67 + if hitted then
  68 + local dir = util.getDir(target.x, target.y, self.x, self.y) or 6
  69 + local lx, ly = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).left)
  70 + local rx, ry = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).right)
  71 + local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR)
  72 + local range = self:getTalentRange(t)
  73 +
  74 + if target:checkHit(self:combatAttack(weapon), target:combatPhysicalResist(), 0, 95, 10) and target:canBe("knockback") then
  75 + target:knockback(self.x, self.y, range)
  76 + target:crossTierEffect(target.EFF_OFFBALANCE, self:combatAttack())
  77 + end
  78 + if lt and lt:checkHit(self:combatAttack(weapon), lt:combatPhysicalResist(), 0, 95, 10) and lt:canBe("knockback") then
  79 + lt:knockback(self.x, self.y, range)
  80 + target:crossTierEffect(target.EFF_OFFBALANCE, self:combatAttack())
  81 + end
  82 + if rt and rt:checkHit(self:combatAttack(weapon), rt:combatPhysicalResist(), 0, 95, 10) and rt:canBe("knockback") then
  83 + rt:knockback(self.x, self.y, range)
  84 + target:crossTierEffect(target.EFF_OFFBALANCE, self:combatAttack())
  85 + end
  86 + end
  87 + end,
67 88 info = function(self, t)
68 89 return ([[Take an offensive stance. As you attack your foes, you knock your target and foes adjacent to them in a frontal arc back (up to %d grids).
69 90 This consumes stamina rapidly (-1 stamina/turn).]]):
... ... @@ -116,14 +137,20 @@ newTalent{
116 137 sustain_stamina = 40,
117 138 tactical = { BUFF = 2 },
118 139 weaponDam = function(self, t) return (self:combatTalentLimit(t, 1, 0.38, 0.6)) end, -- Limit < 100% weapon damage
119   - --Note: Shattering impact effect handled in mod.class.interface.Combat.lua : _M:attackTargetWith
  140 + callbackOnMeleeProject = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  141 + -- Shattering Impact
  142 + if hitted and (not self.shattering_impact_last_turn or self.shattering_impact_last_turn < game.turn) then
  143 + local dam = dam * t.weaponDam(self, t)
  144 + game.logSeen(target, "The shattering blow creates a shockwave!")
  145 + self:project({type="ball", radius=1, selffire=false, act_exclude={[target.uid]=true}}, target.x, target.y, DamageType.PHYSICAL, dam) -- don't hit target with the AOE
  146 + self:incStamina(-8)
  147 + self.shattering_impact_last_turn = game.turn
  148 + end
  149 + end,
120 150 activate = function(self, t)
121   - return {
122   - dam = self:addTemporaryValue("shattering_impact", t.weaponDam(self, t)),
123   - }
  151 + return {}
124 152 end,
125 153 deactivate = function(self, t, p)
126   - self:removeTemporaryValue("shattering_impact", p.dam)
127 154 return true
128 155 end,
129 156 info = function(self, t)
... ...
... ... @@ -28,6 +28,14 @@ uberTalent{
28 28 self:attr("unharmed_attack_on_hit", -1)
29 29 self:attr("show_gloves_combat", -1)
30 30 end,
  31 + callbackPriorities = { callbackOnMeleeAttack = 99999 }, -- called after any other effect.
  32 + callbackOnMeleeAttack = function(self, t, target, hitted)
  33 + if not self.__unarmed_attack_on_hit and rng.percent(50) then
  34 + self.__unarmed_attack_on_hit = true
  35 + self:attackTarget(target, nil, 1, true, true)
  36 + self.__unarmed_attack_on_hit = nil
  37 + end
  38 + end,
31 39 info = function(self, t)
32 40 return ([[Each time that you make a melee attack you have a 50%% chance to execute an additional unarmed strike.]])
33 41 :tformat()
... ... @@ -237,13 +245,21 @@ uberTalent{
237 245 getMult = function(self, t) return self:combatLimit(self:getDex(), 0.7, 0.9, 50, 0.85, 100) end, -- Limit > 70% damage taken
238 246 activate = function(self, t)
239 247 local ret = {}
240   - self:talentTemporaryValue(ret, "knockback_on_hit", 1)
241   - self:talentTemporaryValue(ret, "movespeed_on_hit", {speed=3, dur=1})
242 248 return ret
243 249 end,
244 250 deactivate = function(self, t, p)
245 251 return true
246 252 end,
  253 + callbackOnMeleeHitProcs = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
  254 + if hitted and not self.turn_procs.roll_with_it and rng.percent(util.bound(dam, 0, 100)) then
  255 + local ox, oy = self.x, self.y
  256 + game:onTickEnd(function()
  257 + self:knockback(ox, oy, 1)
  258 + if not self:hasEffect(self.EFF_WILD_SPEED) then self:setEffect(self.EFF_WILD_SPEED, 1, {power=200}) end
  259 + end)
  260 + self.turn_procs.roll_with_it = true
  261 + end
  262 + end,
247 263 info = function(self, t)
248 264 return ([[You have learned to take a few hits when needed and can flow with the tide of battle.
249 265 So long as you can move, you find a way to dodge, evade, deflect or otherwise reduce physical damage against you by %d%%.
... ...
... ... @@ -320,6 +320,13 @@ newEffect{
320 320 self:removeParticles(eff.particle)
321 321 self:removeTemporaryValue("inc_damage", eff.incDamageId)
322 322 end,
  323 + callbackOnMeleeHitProcs = function(self, eff, target, hitted, crit, weapon, damtype, mult, dam)
  324 + if hitted and eff.hateBonus and eff.hateBonus > 0 then
  325 + target:incHate(eff.hateBonus)
  326 + game.logPlayer(target, "#F53CBE#You revel in attacking a weakened foe! (+%d hate)", eff.hateBonus)
  327 + eff.hateBonus = nil
  328 + end
  329 + end,
323 330 }
324 331
325 332 newEffect{
... ... @@ -408,6 +415,11 @@ newEffect{
408 415 subtype = { gloom=true, confusion=true },
409 416 status = "detrimental",
410 417 parameters = {},
  418 + callbackOnMeleeHitProcs = function(self, eff, target, hitted, crit)
  419 + if hitted and crit then
  420 + self:removeEffect(self.EFF_DISMAYED)
  421 + end
  422 + end,
411 423 on_gain = function(self, err) return _t"#F53CBE##Target# is dismayed!", _t"+Dismayed" end,
412 424 on_lose = function(self, err) return _t"#Target# overcomes the dismay", _t"-Dismayed" end,
413 425 activate = function(self, eff)
... ... @@ -449,6 +461,14 @@ newEffect{
449 461 self:removeEffect(self.EFF_STALKER)
450 462 end
451 463 end,
  464 + callbackPriorities = { callbackOnMeleeAttack = -100 },
  465 + callbackOnMeleeAttack = function(self, eff, target, hitted)
  466 + -- handle stalk targeting for hits (also handled in Actor for turn end effects)
  467 + if hitted and target ~= self then
  468 + -- mark if stalkee was hit
  469 + eff.hit = eff.hit or eff.target == target
  470 + end
  471 + end,
452 472 }
453 473
454 474 newEffect{
... ... @@ -1984,6 +2004,18 @@ newEffect{
1984 2004
1985 2005 return true
1986 2006 end,
  2007 + callbackPriorities = {callbackOnMeleeAttack = -1},
  2008 + callbackOnMeleeAttack = function(self, eff, target, hitted, crit)
  2009 + -- Rampage
  2010 + if hitted and crit then
  2011 + if eff and not eff.critHit and eff.actualDuration < eff.maxDuration and self:knowTalent(self.T_BRUTALITY) then
  2012 + game.logPlayer(self, "#F53CBE#Your rampage is invigorated by your fierce attack! (+1 duration)")
  2013 + eff.critHit = true
  2014 + eff.actualDuration = eff.actualDuration + 1
  2015 + eff.dur = eff.dur + 1
  2016 + end
  2017 + end
  2018 + end,
1987 2019 do_postUseTalent = function(self, eff)
1988 2020 if eff.dur > 0 then
1989 2021 eff.dur = eff.dur - 1
... ... @@ -2824,6 +2856,14 @@ newEffect{
2824 2856 if eff.particle1 then self:removeParticles(eff.particle1) end
2825 2857 if eff.particle2 then self:removeParticles(eff.particle2) end
2826 2858 end,
  2859 + callbackPriorities = {callbackOnMeleeProject = -12},
  2860 + callbackOnMeleeProject = function(self, eff, target, hitted, crit)
  2861 + -- Static dis-Charge
  2862 + if hitted and not target.dead then
  2863 + DamageType:get(DamageType.LIGHTNING).projector(self, target.x, target.y, DamageType.LIGHTNING, eff.power)
  2864 + self:removeEffect(self.EFF_STATIC_CHARGE)
  2865 + end
  2866 + end,
2827 2867 }
2828 2868
2829 2869 newEffect{
... ...
... ... @@ -1625,6 +1625,14 @@ newEffect{
1625 1625 deactivate = function(self, eff)
1626 1626 self:removeTemporaryValue("combat_def", eff.defense)
1627 1627 end,
  1628 + callbackPriorities = { callbackOnMeleeHitProcs = -2, },
  1629 + callbackOnMeleeHitProcs = function(self, eff, target, hitted, crit, weapon, damtype, mult, dam, missed)
  1630 + if not hitted and not self.dead and missed and not self:attr("stunned") and not self:attr("dazed") and not self:attr("stoned") then
  1631 + local t = self:getTalentFromId(self.T_SET_UP)
  1632 + local power = t.getPower(self, t)
  1633 + target:setEffect(target.EFF_SET_UP, 2, {src = self, power=power})
  1634 + end
  1635 + end,
1628 1636 }
1629 1637
1630 1638 newEffect{
... ...