diff -r 88f08638aa28 -r d2255241678c game/modules/tome/class/Actor.lua --- a/game/modules/tome/class/Actor.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/class/Actor.lua Fri Mar 25 19:27:01 2011 -0500 @@ -174,7 +174,7 @@ mod.class.interface.ActorInscriptions.init(self, t) -- Default melee barehanded damage - self.combat = self.combat or { dam=1, atk=1, apr=0, dammod={str=1} } + self.combat = self.combat or { dam=1, atk=1, apr=0, physcrit=0, physspeed =1, dammod={str=1} } self.talents[self.T_ATTACK] = self.talents[self.T_ATTACK] or 1 self:resetCanSeeCache() @@ -401,7 +401,7 @@ if moved and self:isTalentActive(self.T_BODY_OF_STONE) then self:forceUseTalent(self.T_BODY_OF_STONE, {ignore_energy=true}) end - + if moved and not force and ox and oy and (ox ~= self.x or oy ~= self.y) and config.settings.tome.smooth_move > 0 then local blur = 0 if self:attr("lightning_speed") or self:attr("step_up") or self:attr("wild_speed") then blur = 3 end @@ -1037,6 +1037,13 @@ self:forceUseTalent(self.T_SECOND_LIFE, {ignore_energy=true}) end + -- Unflinching Resolve + if self:knowTalent(self.T_UNFLINCHING_RESOLVE) and value >= (self.max_life / 10) then + local t = self:getTalentFromId(self.T_UNFLINCHING_RESOLVE) + local dam = value + t.on_hit(self, t, dam) + end + if value >= self.life and self.ai_state and self.ai_state.can_reform then local t = self:getTalentFromId(self.T_SHADOW_REFORM) if rng.percent(t.getChance(self, t)) then @@ -1400,6 +1407,9 @@ self.resists.all = self.resists.all - self.temp_con_perc end local inc = self:getCon() / 7 + if self:knowTalent(self.T_IRON_SKIN) then + inc = inc * (1 + (self:getTalentLevel(self.T_IRON_SKIN) * 0.2)) + end self.temp_con_perc = inc self.resists.all = self.resists.all + inc end @@ -1640,6 +1650,20 @@ if not silent then game.logSeen(self, "The spell fizzles.") end return false end + + -- when using unarmed techniques check for weapons and heavy armor + if ab.is_unarmed then + -- first check for heavy and massive armor + if self:hasMassiveArmor() then + if not silent then game.logSeen(self, "You are to heavily armored to use this talent.") end + return false + -- next make sure we're unarmed + elseif not self:isUnarmed() then + if not silent then game.logSeen(self, "You can't use this talent while holding a weapon or shield.") end + return false + end + end + if not self:enoughEnergy() and not fake then return false end @@ -1953,6 +1977,9 @@ if self:hasEffect(self.EFF_WILD_SPEED) then self:removeEffect(self.EFF_WILD_SPEED) end + if self:hasEffect(self.EFF_REFLEXIVE_DODGING) then + self:removeEffect(self.EFF_REFLEXIVE_DODGING) + end end --- Breaks lightning speed if active @@ -2277,6 +2304,10 @@ p.energy.mod = p.energy.mod * (100 - self.slow_projectiles) / 100 print("Projectile slowing down to", p.energy.mod) end + if self:knowTalent(self.T_HEIGHTENED_REFLEXES) then + local t = self:getTalentFromId(self.T_HEIGHTENED_REFLEXES) + t.do_reflexes(self, t) + end end --- Call when added to a level diff -r 88f08638aa28 -r d2255241678c game/modules/tome/class/Object.lua --- a/game/modules/tome/class/Object.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/class/Object.lua Fri Mar 25 19:27:01 2011 -0500 @@ -410,6 +410,7 @@ if w.fear_immune then desc:add(("Increases fear immunity: %d%%."):format(w.fear_immune * 100), true) end if w.knockback_immune then desc:add(("Increases knockback immunity: %d%%."):format(w.knockback_immune * 100), true) end if w.instakill_immune then desc:add(("Increases instant-death immunity: %d%%."):format(w.instakill_immune * 100), true) end + if w.teleport_immune then desc:add(("Increases teleport immunity: %d%%."):format(w.teleport_immune * 100), true) end if w.life_regen then desc:add(("Regenerates %0.2f hitpoints each turn."):format(w.life_regen), true) end if w.stamina_regen then desc:add(("Regenerates %0.2f stamina each turn."):format(w.stamina_regen), true) end @@ -447,7 +448,7 @@ desc:add({"color","YELLOW"}, "When used to attack (with talents):", {"color", "LAST"}, true) desc_combat(self.special_combat) end - + if self.no_teleport then desc:add("It is immune to teleportation, if you teleport it will fall on the ground.", true) end diff -r 88f08638aa28 -r d2255241678c game/modules/tome/class/interface/Combat.lua --- a/game/modules/tome/class/interface/Combat.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/class/interface/Combat.lua Fri Mar 25 19:27:01 2011 -0500 @@ -107,7 +107,7 @@ end local break_stealth = false - if not self:attr("disarmed") then + if not self:attr("disarmed") and not self:isUnarmed() then -- All weapons in main hands if self:getInven(self.INVEN_MAINHAND) then for i, o in ipairs(self:getInven(self.INVEN_MAINHAND)) do @@ -373,6 +373,12 @@ local t = self:getTalentFromId(self.T_CONDUIT) t.do_combat(self, t, target) end + + -- Exploit Weakness + if hitted and not target.dead and self:knowTalent(self.T_EXPLOIT_WEAKNESS) and self:isTalentActive(self.T_EXPLOIT_WEAKNESS) then + local t = self:getTalentFromId(self.T_EXPLOIT_WEAKNESS) + t.do_weakness(self, t, target) + end -- Special effect if hitted and not target.dead and weapon.special_on_hit and weapon.special_on_hit.fct then @@ -399,6 +405,27 @@ game.logSeen(self, "%s ripostes!", target.name:capitalize()) target:attackTarget(self, nil, nil, true) end + + -- Set Up + 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 + local t = target:getTalentFromId(target.T_SET_UP) + local power = t.getPower(target, t) + self:setEffect(self.EFF_SET_UP, 2, {src = target, power=power}) + end + + -- Defensive Throw! + 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:knowTalent(target.T_DEFENSIVE_THROW) and rng.percent(target:getTalentLevel(target.T_DEFENSIVE_THROW) * (5 + target:getCun(5))) then + local t = target:getTalentFromId(target.T_DEFENSIVE_THROW) + t.do_throw(target, self, t) + end + + -- Counter Attack! + 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:knowTalent(target.T_COUNTER_ATTACK) and rng.percent(target:getTalentLevel(target.T_COUNTER_ATTACK) * (5 + target:getCun(5))) then + game.logSeen(self, "%s counters the attack!", target.name:capitalize()) + local t = target:getTalentFromId(target.T_COUNTER_ATTACK) + local damage = t.getDamage(target, t) + local hit = target:attackTarget(self, nil, damage, true) + end -- Greater Weapon Focus local gwf = self:hasEffect(self.EFF_GREATER_WEAPON_FOCUS) @@ -439,6 +466,14 @@ if self:hasDualWeapon() and self:knowTalent(self.T_DUAL_WEAPON_DEFENSE) then add = add + 4 + (self:getTalentLevel(self.T_DUAL_WEAPON_DEFENSE) * self:getDex()) / 12 end + if self:knowTalent(self.T_TACTICAL_EXPERT) then + local t = self:getTalentFromId(self.T_TACTICAL_EXPERT) + add = add + t.do_tact_update(self, t) + end + if self:knowTalent(self.T_STEADY_MIND) then + local t = self:getTalentFromId(self.T_STEADY_MIND) + add = add + t.getDefense(self, t) + end return self.combat_def + (self:getDex() - 10) * 0.35 + add + (self:getLck() - 50) * 0.4 end @@ -456,6 +491,10 @@ if self:hasMassiveArmor() and self:knowTalent(self.T_MASSIVE_ARMOUR_TRAINING) then add = add + self:getTalentLevel(self.T_MASSIVE_ARMOUR_TRAINING) * 1.6 end + if self:knowTalent(self.T_PHYSICAL_CONDITIONING) then + local t = self:getTalentFromId(self.T_PHYSICAL_CONDITIONING) + add = add + t.getArmor(self, t) + end if self:knowTalent(self.T_CARBON_SPIKES) and self:isTalentActive(self.T_CARBON_SPIKES) then add = add + self.carbon_armor end @@ -547,8 +586,18 @@ if self:isTalentActive(Talents.T_BLOOD_FRENZY) then add = add + self.blood_frenzy end - - local talented_mod = math.sqrt(self:combatCheckTraining(weapon) / 10) + 1 + if self:knowTalent(self.T_EMPTY_HAND) and weapon == self.combat then + local t = self:getTalentFromId(self.T_EMPTY_HAND) + add = add + t.getDamage(self, t) + end + + if weapon == self.combat then + -- Handles unarmed mastery + talented_mod = math.sqrt(self:getTalentLevel(Talents.T_UNARMED_MASTERY) / 10) + 1 or 0 + else + talented_mod = math.sqrt(self:combatCheckTraining(weapon) / 10) + 1 + end + local power = math.max(self.combat_dam + (weapon.dam or 1) + add, 1) power = (math.sqrt(power / 10) - 1) * 0.8 + 1 print(("[COMBAT DAMAGE] power(%f) totstat(%f) talent_mod(%f)"):format(power, totstat, talented_mod)) @@ -636,7 +685,14 @@ if target:attr("combat_critical") then chance = chance + target:attr("combat_critical") end - if target:knowTalent(target.T_PROBABILITY_WEAVING) and target:isTalentActive(T_PROBABILIT_WEAVING) then + if target:hasEffect(target.EFF_SET_UP) then + local p = target:hasEffect(target.EFF_SET_UP) + if p and p.src == self then + chance = chance + p.power + end + end + + if target:knowTalent(target.T_PROBABILITY_WEAVING) and target:isTalentActive(T_PROBABILITY_WEAVING) then chance = chance - target:getTalentLevel(target.T_PROBABILITY_WEAVING) end if target:hasHeavyArmor() and target:knowTalent(target.T_HEAVY_ARMOUR_TRAINING) then @@ -652,6 +708,7 @@ if rng.percent(chance) then dam = dam * (1.5 + (self.combat_critical_power or 0) / 100) crit = true + end return dam, crit end @@ -723,17 +780,28 @@ --- Computes physical resistance function _M:combatPhysicalResist() - return self.combat_physresist + (self:getCon() + self:getStr() + (self:getLck() - 50) * 0.5) * 0.35 + local add = 0 + if self:knowTalent(self.T_PHYSICAL_CONDITIONING) then + local t = self:getTalentFromId(self.T_PHYSICAL_CONDITIONING) + add = add + t.getPhysical(self, t) + end + return self.combat_physresist + add + (self:getCon() + self:getStr() + (self:getLck() - 50) * 0.5) * 0.35 end --- Computes spell resistance function _M:combatSpellResist() - return self.combat_spellresist + (self:getMag() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 + local add = 0 + return self.combat_spellresist + add + (self:getMag() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 end --- Computes mental resistance function _M:combatMentalResist() - return self.combat_mentalresist + (self:getCun() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 + local add = 0 + if self:knowTalent(self.T_STEADY_MIND) then + local t = self:getTalentFromId(self.T_STEADY_MIND) + add = add + t.getMental(self, t) + end + return self.combat_mentalresist + add + (self:getCun() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 end --- Computes movement speed @@ -807,6 +875,18 @@ return shield end +-- Check if actor is unarmed +function _M:isUnarmed() + local unarmed = true + if not self:getInven("MAINHAND") or not self:getInven("OFFHAND") then return end + local weapon = self:getInven("MAINHAND")[1] + local offweapon = self:getInven("OFFHAND")[1] + if weapon or offweapon then + unarmed = false + end + return unarmed +end + --- Check if the actor dual wields function _M:hasDualWeapon() if self:attr("disarmed") then @@ -851,3 +931,106 @@ end return mount end + +-- Unarmed Combat; this handles grapple checks and building combo points + +-- Builds Comob; reduces the cooldown on all unarmed abilities on cooldown by one +function _M:buildCombo() + + local duration = 3 + local power = 1 + -- Combo String bonuses + if self:knowTalent(self.T_COMBO_STRING) then + local t= self:getTalentFromId(self.T_COMBO_STRING) + if rng.percent(t.getChance(self, t)) then + power = 2 + end + duration = 3 + math.ceil(t.getDuration(self, t)) + end + -- Relentless Strike bonus + if self:hasEffect(self.EFF_RELENTLESS_STRIKES) then + for tid, cd in pairs(self.talents_cd) do + local tt = self:getTalentFromId(tid) + if tt.type[1]:find("^technique/") then + self.talents_cd[tid] = cd - 1 + end + end + end + + self:setEffect(self.EFF_COMBO, duration, {power=power}) + +end + +function _M:getCombo(combo) + local combo = 0 + local p = self:hasEffect(self.EFF_COMBO) + if p then + combo = p.cur_power + end + return combo +end + +function _M:clearCombo() + if self:hasEffect(self.EFF_COMBO) then + self:removeEffect(self.EFF_COMBO) + end +end + +-- Check to see if the target is already being grappled; many talents have extra effects on grappled targets +function _M:isGrappled(source) + local p = self:hasEffect(self.EFF_GRAPPLED) + if p and p.src == source then + return true + else + return false + end +end + +-- Breaks active grapples; called by a few talents that involve a lot of movement +function _M:breakGrapples() + if self:hasEffect(self.EFF_GRAPPLING) then + -- deactivating GRAPPLING will clear the target's Grappled effect as well + self:removeEffect(self.EFF_GRAPPLING) + end +end + +-- grapple size check; compares attackers size and targets size +function _M:grappleSizeCheck(target) + size = target.size_category - self.size_category + if size > 1 then + game.logSeen(target, "%s fails because %s is too big!", self.name:capitalize(), target.name:capitalize()) + return true + else + return false + end +end + +-- Starts the grapple +function _M:startGrapple(target) + -- pulls boosted grapple effect from the clinch talent if known + if self:knowTalent(self.T_CLINCH) then + local t = self:getTalentFromId(self.T_CLINCH) + power = t.getPower(self, t) + duration = t.getDuration(self, t) + hitbonus = self:getTalentLevel(t)/2 + else + power = 5 + duration = 4 + hitbonus = 0 + end + -- Breaks the grapple before reapplying + if self:hasEffect(self.EFF_GRAPPLING) then + -- deactivating GRAPPLING will clear the targets Grappled effect and various holds + self:removeEffect(self.EFF_GRAPPLING, true) + target:setEffect(target.EFF_GRAPPLED, duration, {src=self, power=power}, true) + self:setEffect(self.EFF_GRAPPLING, duration, {src=target}, true) + return true + elseif target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5 - hitbonus) and target:canBe("pin") then + target:setEffect(target.EFF_GRAPPLED, duration, {src=self, power=power}) + self:setEffect(self.EFF_GRAPPLING, duration, {src=target}, true) + return true + else + game.logSeen(target, "%s resists the grapple!", target.name:capitalize()) + return false + end +end \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/birth/classes/warrior.lua --- a/game/modules/tome/data/birth/classes/warrior.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/birth/classes/warrior.lua Fri Mar 25 19:27:01 2011 -0500 @@ -30,6 +30,7 @@ __ALL__ = "disallow", Fighter = "allow", Berserker = "allow", + Brawler = "allow", ["Arcane Blade"] = "allow", }, }, @@ -174,3 +175,51 @@ life_rating = 2, }, } + +newBirthDescriptor{ + type = "subclass", + name = "Brawler", + desc = { + "Rather a pit-fighter, a boxer, or just a practitioner the brawler has learned to use his armored fists as weapons just as deadly as any blade.", + "Many of the brawler's abilities will earn combo points which they can use on finishing moves that will have added effect.", + "The unarmed fighting styles the brawler uses rely on maneuverability and having both hands available, as such they may not be practiced in massive armor or while a weapon or shield is equipped.", + "Their most important stats are: Strength, Dexterity, and Cunning", + "#GOLD#Stat modifiers:", + "#LIGHT_BLUE# * +3 Strength, +3 Dexterity, +0 Constitution", + "#LIGHT_BLUE# * +0 Magic, +0 Willpower, +3 Cunning", + }, + stats = { str=3, dex=3, cun=3}, + talents_types = { + ["cunning/survival"]={false, 0}, + ["technique/combat-training"]={true, 0.1}, + ["technique/field-control"]={false, 0.3}, + ["technique/combat-techniques-active"]={false, 0}, + ["technique/pugilism"]={true, 0.3}, + -- ["technique/kick-boxing"]={false, 0.3}, + ["technique/finishing-moves"]={true, 0.3}, + ["technique/grappling"]={true, 0.3}, + ["technique/martial-arts"]={false, 0.3}, + ["cunning/tactical"]={true, 0.3}, + ["technique/unarmed-training"]={true, 0.3}, + ["technique/conditioning"]={true, 0.3}, + + }, + talents = { + [ActorTalents.T_UPPERCUT] = 1, + [ActorTalents.T_DOUBLE_STRIKE] = 1, + [ActorTalents.T_WEAPON_COMBAT] = 1, + [ActorTalents.T_HEAVY_ARMOUR_TRAINING] = 1, + + -- base monk attack + [ActorTalents.T_EMPTY_HAND] = 1, + }, + copy = { + resolvers.equip{ id=true, + {type="armor", subtype="hands", name="iron gauntlets", autoreq=true}, + {type="armor", subtype="heavy", name="iron mail armour", autoreq=true}, + }, + }, + copy_add = { + life_rating = 2, + }, +} diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/damage_types.lua --- a/game/modules/tome/data/damage_types.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/damage_types.lua Fri Mar 25 19:27:01 2011 -0500 @@ -635,7 +635,7 @@ target:knockback(src.x, src.y, dam.dist) game.logSeen(target, "%s is knocked back!", target.name:capitalize()) else - game.logSeen(target, "%s resists the punch!", target.name:capitalize()) + game.logSeen(target, "%s resists the knockback!", target.name:capitalize()) end end end, diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/general/objects/boss-artifacts.lua --- a/game/modules/tome/data/general/objects/boss-artifacts.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/general/objects/boss-artifacts.lua Fri Mar 25 19:27:01 2011 -0500 @@ -753,6 +753,15 @@ inc_stats = { [Stats.STAT_WIL] = 3, }, resists = { [DamageType.FIRE]= 10, }, inc_damage = { [DamageType.FIRE]= 5, }, + combat = { + dam = 10, + apr = 1, + physcrit = 7, + physspeed = -0.4, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damtype = DamageType.FIRE, + damrange = 1.2, + }, }, max_power = 24, power_regen = 1, use_talent = { id = Talents.T_RITCH_FLAMESPITTER_BOLT, level = 2, power = 6 }, diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/general/objects/egos/gloves.lua --- a/game/modules/tome/data/general/objects/egos/gloves.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/general/objects/egos/gloves.lua Fri Mar 25 19:27:01 2011 -0500 @@ -18,6 +18,7 @@ -- darkgod@te4.org local Stats = require "engine.interface.ActorStats" local DamageType = require "engine.DamageType" +local Talents = require("engine.interface.ActorTalents") newEntity{ power_source = {technique=true}, @@ -39,6 +40,9 @@ wielder = { combat_spellcrit = resolvers.mbonus_material(15, 5, function(e, v) return v * 1.4 end), combat_physcrit = resolvers.mbonus_material(15, 5, function(e, v) return v * 1.4 end), + combat = { + physcrit = resolvers.mbonus_material(10, 4, function(e, v) return v * 0.4 end), + }, }, } @@ -51,6 +55,9 @@ cost = 25, wielder = { combat_critical_power = resolvers.mbonus_material(35, 5, function(e, v) return v * 2, v end), + combat = { + physcrit = resolvers.mbonus_material(10, 4, function(e, v) return v * 0.4 end), + }, }, } @@ -62,6 +69,9 @@ cost = 5, wielder = { combat_atk = resolvers.mbonus_material(15, 10, function(e, v) return v * 1 end), + combat = { + atk = resolvers.mbonus_material(10, 4, function(e, v) return v * 0.4 end), + }, }, } @@ -73,6 +83,9 @@ cost = 10, wielder = { combat_dam = resolvers.mbonus_material(15, 5, function(e, v) return v * 3 end), + combat = { + dam = resolvers.mbonus_material(7, 3, function(e, v) return v * 3 end), + }, }, } @@ -85,6 +98,9 @@ wielder = { inc_damage={ [DamageType.FIRE] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, resists = { [DamageType.FIRE] = resolvers.mbonus_material(5, 5, function(e, v) return v * 0.15 end), }, + combat = { + melee_project={[DamageType.FIRE] = resolvers.mbonus_material(25, 4, function(e, v) return v * 0.64 end)}, + }, }, } @@ -97,6 +113,9 @@ wielder = { inc_damage={ [DamageType.COLD] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, resists = { [DamageType.COLD] = resolvers.mbonus_material(5, 5, function(e, v) return v * 0.15 end), }, + combat = { + melee_project={[DamageType.ICE] = resolvers.mbonus_material(15, 4, function(e, v) return v * 0.64 end)}, + }, }, } @@ -109,6 +128,9 @@ wielder = { inc_damage={ [DamageType.ACID] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, resists = { [DamageType.ACID] = resolvers.mbonus_material(5, 5, function(e, v) return v * 0.15 end), }, + combat = { + melee_project={[DamageType.ACID] = resolvers.mbonus_material(25, 4, function(e, v) return v * 0.64 end)}, + }, }, } @@ -121,6 +143,9 @@ wielder = { inc_damage={ [DamageType.LIGHTNING] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, resists = { [DamageType.LIGHTNING] = resolvers.mbonus_material(5, 5, function(e, v) return v * 0.15 end), }, + combat = { + melee_project={[DamageType.LIGHTNING] = resolvers.mbonus_material(25, 4, function(e, v) return v * 0.64 end)}, + }, }, } newEntity{ @@ -132,6 +157,9 @@ wielder = { inc_damage={ [DamageType.TEMPORAL] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, resists = { [DamageType.TEMPORAL] = resolvers.mbonus_material(5, 5, function(e, v) return v * 0.15 end), }, + combat = { + melee_project={[DamageType.TEMPORAL] = resolvers.mbonus_material(15, 4, function(e, v) return v * 0.64 end)}, + }, }, } @@ -144,6 +172,9 @@ wielder = { inc_damage={ [DamageType.NATURE] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, resists = { [DamageType.NATURE] = resolvers.mbonus_material(5, 5, function(e, v) return v * 0.15 end), }, + combat = { + melee_project={[DamageType.SLIME] = resolvers.mbonus_material(25, 4, function(e, v) return v * 0.64 end)}, + }, }, } @@ -156,6 +187,9 @@ wielder = { inc_damage={ [DamageType.BLIGHT] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, resists = { [DamageType.BLIGHT] = resolvers.mbonus_material(5, 5, function(e, v) return v * 0.15 end), }, + combat = { + melee_project={[DamageType.BLIGHT] = resolvers.mbonus_material(25, 4, function(e, v) return v * 0.64 end)}, + }, }, } @@ -167,6 +201,9 @@ cost = 5, wielder = { inc_damage={ [DamageType.PHYSICAL] = resolvers.mbonus_material(8, 3, function(e, v) return v * 0.8 end), }, + combat = { + dam = resolvers.mbonus_material(7, 3, function(e, v) return v * 3 end), + }, }, } @@ -178,6 +215,9 @@ cost = 4, wielder = { inc_stats = { [Stats.STAT_STR] = resolvers.mbonus_material(4, 2, function(e, v) return v * 3 end) }, + combat = { + dam = resolvers.mbonus_material(7, 3, function(e, v) return v * 3 end), + }, }, } @@ -187,8 +227,14 @@ level_range = {1, 50}, rarity = 6, cost = 4, + unarmed_combat = { + physcrit = resolvers.mbonus_material(10, 4, function(e, v) return v * 0.4 end), + }, wielder = { inc_stats = { [Stats.STAT_DEX] = resolvers.mbonus_material(4, 2, function(e, v) return v * 3 end) }, + combat = { + physcrit = resolvers.mbonus_material(10, 4, function(e, v) return v * 0.4 end), + }, }, } @@ -210,6 +256,7 @@ rarity = 9, cost = 15, wielder = { + talent_cd_reduction={[Talents.T_CLINCH]=2}, inc_stats = { [Stats.STAT_STR] = resolvers.mbonus_material(2, 2, function(e, v) return v * 3 end) }, disarm_immune = resolvers.mbonus_material(4, 4, function(e, v) v=v/10 return v * 8, v end), }, @@ -244,6 +291,10 @@ [Stats.STAT_DEX] = resolvers.mbonus_material(3, 2, function(e, v) return v * 3 end), }, combat_apr = resolvers.mbonus_material(4, 4, function(e, v) return v * 0.3 end), + combat = { + physcrit = resolvers.mbonus_material(10, 4, function(e, v) return v * 0.4 end), + atk = resolvers.mbonus_material(10, 2, function(e, v) return v * 0.3 end), + }, }, } @@ -276,6 +327,10 @@ }, max_life=resolvers.mbonus_material(40, 40, function(e, v) return v * 0.1 end), combat_armor = resolvers.mbonus_material(3, 3, function(e, v) return v * 1 end), + combat = { + dam = resolvers.mbonus_material(7, 3, function(e, v) return v * 0.3 end), + atk = resolvers.mbonus_material(10, 2, function(e, v) return v * 0.3 end), + }, }, } @@ -309,7 +364,31 @@ [Stats.STAT_CUN] = resolvers.mbonus_material(3, 2, function(e, v) return v * 3 end), }, combat_atk = resolvers.mbonus_material(5, 5, function(e, v) return v * 1 end), + combat = { + apr = resolvers.mbonus_material(8, 1, function(e, v) return v * 0.3 end), + atk = resolvers.mbonus_material(10, 2, function(e, v) return v * 0.3 end), + }, }, - } +newEntity{ + power_source = {technique=true}, + name = "brawler's ", prefix=true, instant_resolve=true, + level_range = {40, 50}, + greater_ego = 1, + rarity = 20, + cost = 75, + wielder = { + talent_cd_reduction={ [Talents.T_DOUBLE_STRIKE]=1, }, + inc_stats = { + [Stats.STAT_STR] = resolvers.mbonus_material(3, 2, function(e, v) return v * 3 end), + [Stats.STAT_DEX] = resolvers.mbonus_material(3, 2, function(e, v) return v * 3 end), + [Stats.STAT_CUN] = resolvers.mbonus_material(3, 2, function(e, v) return v * 3 end), + }, + combat = { + physcrit = resolvers.mbonus_material(10, 4, function(e, v) return v * 0.4 end), + atk = resolvers.mbonus_material(10, 2, function(e, v) return v * 0.3 end), + physspeed = -0.1, + }, + }, +} diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/general/objects/gauntlets.lua --- a/game/modules/tome/data/general/objects/gauntlets.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/general/objects/gauntlets.lua Fri Mar 25 19:27:01 2011 -0500 @@ -30,6 +30,7 @@ encumber = 1.5, rarity = 9, metallic = true, + combat = { talented = "unarmed" ,}, desc = [[Metal gloves protecting the hands up to the middle of the lower arm.]], randart_able = { attack=10, physical=10, spell=10, def=40, misc=30 }, egos = "/data/general/objects/egos/gloves.lua", egos_chance = { prefix=resolvers.mbonus(40, 5), suffix=resolvers.mbonus(40, 5) }, @@ -42,6 +43,14 @@ material_level = 1, wielder = { combat_armor = 1, + combat = { + dam = resolvers.rngavg(7, 12), + apr = 4, + physcrit = 1, + physspeed = -0.2, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damrange = 1.4, + }, }, } @@ -52,6 +61,14 @@ material_level = 3, wielder = { combat_armor = 2, + combat = { + dam = resolvers.rngavg(16, 22), + apr = 7, + physcrit = 1, + physspeed = -0.2, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damrange = 1.4, + }, }, } @@ -62,5 +79,13 @@ material_level = 5, wielder = { combat_armor = 3, + unarmed_combat = { + dam = resolvers.rngavg(25, 32), + apr = 10, + physcrit = 3, + physspeed = -0.2, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damrange = 1.4, + }, }, } diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/general/objects/gloves.lua --- a/game/modules/tome/data/general/objects/gloves.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/general/objects/gloves.lua Fri Mar 25 19:27:01 2011 -0500 @@ -28,6 +28,7 @@ image = resolvers.image_material("gloves", "leather"), encumber = 1, rarity = 9, + unarmed_combat = { talented = "unarmed", damrange = 1.4, sound = "actions/melee", sound_miss = "actions/melee_miss", }, desc = [[Light gloves which do not seriously hinder finger movements, while still protecting the hands somewhat.]], randart_able = { attack=10, physical=10, spell=10, def=30, misc=10 }, egos = "/data/general/objects/egos/gloves.lua", egos_chance = { prefix=resolvers.mbonus(40, 5), suffix=resolvers.mbonus(40, 5) }, @@ -40,6 +41,14 @@ material_level = 1, wielder = { combat_armor = 1, + combat = { + dam = resolvers.rngavg(5, 8), + apr = 1, + physcrit = 4, + physspeed = -0.4, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damrange = 1.2, + }, }, } @@ -50,6 +59,14 @@ material_level = 3, wielder = { combat_armor = 2, + combat = { + dam = resolvers.rngavg(14, 18), + apr = 1, + physcrit = 7, + physspeed = -0.4, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damrange = 1.2, + }, }, } @@ -60,5 +77,13 @@ material_level = 5, wielder = { combat_armor = 3, + combat = { + dam = resolvers.rngavg(23, 28), + apr = 3, + physcrit = 10, + physspeed = -0.4, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damrange = 1.2, + }, }, } diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/general/objects/world-artifacts.lua --- a/game/modules/tome/data/general/objects/world-artifacts.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/general/objects/world-artifacts.lua Fri Mar 25 19:27:01 2011 -0500 @@ -1091,10 +1091,19 @@ cost = 150, material_level = 3, wielder = { + talent_cd_reduction={[Talents.T_CLINCH]=2}, inc_stats = { [Stats.STAT_CON] = 4 }, disarm_immune=0.3, knockback_immune=0.3, stun_immune=0.3, + combat = { + dam = 18, + apr = 1, + physcrit = 7, + physspeed = -0.4, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + damrange = 1.2 + }, }, } @@ -1114,6 +1123,15 @@ combat_physcrit = 10, combat_spellcrit = 10, combat_critical_power = 50, + combat = { + dam = 35, + apr = 10, + physcrit = 10, + physspeed = -0.2, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + melee_project={[DamageType.ARCANE] = 20}, + damrange = 1.4 + }, }, } @@ -1132,6 +1150,15 @@ resists = { [DamageType.COLD]= 10, [DamageType.LIGHTNING] = 10, }, knockback_immune = 0.5, max_life = 60, + combat = { + dam = 16, + apr = 1, + physcrit = 4, + physspeed = -0.4, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + melee_project={ [DamageType.COLD] = 10, [DamageType.LIGHTNING] = 10, }, + damrange = 1.2 + }, }, max_power = 6, power_regen = 1, use_talent = { id = Talents.T_THROW_BOULDER, level = 2, power = 6 }, @@ -1155,6 +1182,16 @@ resists_cap = { [DamageType.LIGHTNING] = 5 }, combat_spellcrit = 5, combat_critical_power = 20, + combat = { + dam = 22, + apr = 10, + physcrit = 4, + physspeed = -0.2, + dammod = {dex=0.4, str=-0.6, cun=0.4 }, + melee_project={ [DamageType.LIGHTNING] = 20, }, + talent_on_hit = { [Talents.T_LIGHTNING] = {level=3, chance=10} }, + damrange = 1.4 + }, }, max_power = 16, power_regen = 1, use_talent = { id = Talents.T_CHAIN_LIGHTNING, level = 3, power = 16 }, @@ -1838,16 +1875,28 @@ desc = [[Fashioned in ancient times by cultists of Harkor'Zun, these heavy granite gauntlets were designed to protect the wearer from the wrath of their dark master.]], level_range = {26, 31}, rarity = 210, - encumber = 14, + encumber = 7, metallic = false, cost = 150, - material_level = 4, + material_level = 3, wielder = { - fatigue = 20, - resists = {[DamageType.ACID] = 20}, - resists = {[DamageType.PHYSICAL] = 10}, - resists_cap = {[DamageType.ACID] = 5}, - resists_cap = {[DamageType.PHYSICAL] = 5}, + talent_cd_reduction={ + [Talents.T_CLINCH]=2, + [Talents.T_CRUSHING_HOLD]=2, + }, + fatigue = 10, + inc_damage = { [DamageType.PHYSICAL]=5, [DamageType.ACID]=10, }, + resists = {[DamageType.ACID] = 20, [DamageType.PHYSICAL] = 10, }, + resists_cap = {[DamageType.ACID] = 10, [DamageType.PHYSICAL] = 5, }, + combat = { + dam = 26, + apr = 15, + physcrit = 5, + physspeed = 1, + dammod = {dex=0.3, str=-0.4, cun=0.3 }, + melee_project={[DamageType.ACID] = 10}, + damrange = 1.4, + }, }, } diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents.lua --- a/game/modules/tome/data/talents.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/talents.lua Fri Mar 25 19:27:01 2011 -0500 @@ -25,6 +25,7 @@ if engine.interface.ActorTalents.talents_types_def[t.type[1]].generic then t.generic = true end if engine.interface.ActorTalents.talents_types_def[t.type[1]].no_silence then t.no_silence = true end if engine.interface.ActorTalents.talents_types_def[t.type[1]].is_spell then t.is_spell = true end + if engine.interface.ActorTalents.talents_types_def[t.type[1]].is_unarmed then t.is_unarmed = true end if t.tactical then local tacts = {} diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/chronomancy/energy.lua --- a/game/modules/tome/data/talents/chronomancy/energy.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/talents/chronomancy/energy.lua Fri Mar 25 19:27:01 2011 -0500 @@ -137,7 +137,7 @@ -- Go through all sustained spells for tid, act in pairs(target.sustain_talents) do local talent = target:getTalentFromId(tid) - if talent.is_spell then + if talent.is_spell then effs[#effs+1] = {"talent", tid} end end diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/chronomancy/timetravel.lua --- a/game/modules/tome/data/talents/chronomancy/timetravel.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua Fri Mar 25 19:27:01 2011 -0500 @@ -34,8 +34,8 @@ target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, - getDamage = function(self, t) return (self:combatTalentSpellDamage(t, 10, 120)*getParadoxModifier(self, pm)) end, - getPercent = function(self, t) return (30 + (self:combatTalentSpellDamage(t, 10, 30)*getParadoxModifier(self, pm))) / 100 end, + getDamage = function(self, t) return (self:combatTalentSpellDamage(t, 18, 160)*getParadoxModifier(self, pm)) end, + getPercent = function(self, t) return (10 + (self:combatTalentSpellDamage(t, 1, 10))) / 100 end, action = function(self, t) local tg = self:getTalentTarget(t) self:project(tg, self.x, self.y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/cunning/cunning.lua --- a/game/modules/tome/data/talents/cunning/cunning.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/talents/cunning/cunning.lua Fri Mar 25 19:27:01 2011 -0500 @@ -26,6 +26,7 @@ newTalentType{ allow_random=true, type="cunning/lethality", name = "lethality", description = "How to make your foes feel the pain." } newTalentType{ allow_random=true, type="cunning/shadow-magic", name = "shadow magic", description = "Blending magic and shadows." } newTalentType{ allow_random=true, type="cunning/survival", name = "survival", generic = true, description = "The knowledge of the dangers of the world, and how to best avoid them." } +newTalentType{ allow_random=true, type="cunning/tactical", name = "tactical", description = "Tactical combat abilities." } -- Generic requires for cunning based on talent level cuns_req1 = { @@ -53,5 +54,6 @@ load("/data/talents/cunning/traps.lua") load("/data/talents/cunning/dirty.lua") load("/data/talents/cunning/lethality.lua") +load("/data/talents/cunning/tactical.lua") load("/data/talents/cunning/survival.lua") load("/data/talents/cunning/shadow-magic.lua") diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/cunning/tactical.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/cunning/tactical.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,159 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +local function getStrikingStyle(self, dam) + local dam = 0 + if self:isTalentActive(self.T_STRIKING_STANCE) then + local t = self:getTalentFromId(self.T_STRIKING_STANCE) + dam = t.getDamage(self, t) + end + return dam / 100 +end + +newTalent{ + name = "Tactical Expert", + type = {"cunning/tactical", 1}, + require = cuns_req1, + mode = "passive", + points = 5, + getDefense = function(self, t) return self:getTalentLevel(t) * 2 end, + getMaximum = function(self, t) return (4 + (self:getTalentLevel(t) * self:getCun()) / 6) end, + do_tact_update = function (self, t) + local nb_foes = 0 + local act + for i = 1, #self.fov.actors_dist do + act = self.fov.actors_dist[i] + if self:reactionToward(act) < 0 and self:canSee(act) and act["__sqdist"] <= 2 then nb_foes = nb_foes + 1 end + end + + local defense = nb_foes * t.getDefense(self, t) + + if defense <= t.getMaximum(self, t) then + defense = defense + else + defense = t.getMaximum(self, t) + end + + return defense + end, + info = function(self, t) + local defense = t.getDefense(self, t) + local maximum = t.getMaximum(self, t) + return ([[Your defense is increased by %d for every visible foe that is adjacent too you up to a maximum of +%d defense. + The maximum defense increase will scale with the cunning stat.]]):format(defense, maximum) + end, +} + +newTalent{ + name = "Counter Attack", + type = {"cunning/tactical", 2}, + require = cuns_req2, + mode = "passive", + points = 5, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.8, 1.3) + getStrikingStyle(self, dam) end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[When you avoid a melee blow you have a %d%% chance to get a free, automatic melee attack against your foe for %d%% damage. + The is considered a strike for the purpose of stance damage bonuses (if you have any). + The chance of countering increases with the cunning stat.]]):format(self:getTalentLevel(t) * (5 + self:getCun(5)), damage) + end, +} + +newTalent{ + name = "Set Up", + type = {"cunning/tactical", 3}, + require = cuns_req3, + points = 5, + random_ego = "utility", + cooldown = 12, + stamina = 12, + tactical = { DISABLE = 1, DEFEND = 2 }, + getPower = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 1, 25) end, + getDuration = function(self, t) return 2 + math.ceil(self:getTalentLevel(t)) end, + getDefense = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 1, 50) end, + action = function(self, t) + + self:setEffect(self.EFF_DEFENSIVE_MANEUVER, t.getDuration(self, t), {power=t.getDefense(self, t)}) + + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + local power = t.getPower(self, t) + local defense = t.getDefense(self, t) + return ([[Increases defense by %d for %d turns. When you avoid a melee blow you set the target up, increasing the chance of you landing a critical strike on them by %d%% and reducing their saving throws by %d. + The defense increase will scale with the cunning stat.]]) + :format(defense, duration, power, power) + end, +} + +--[=[newTalent{ + name = "Sprint", + type = {"cunning/tactical", 2}, + require = cuns_req2, + points = 5, + cooldown = 24, + stamina = 12, + tactical = { CLOSEIN = 2, ESCAPE = 2 }, + no_energy = true, + getMovement = function(self, t) return 1 - 1 / (1 + (100)/ 100) end, + getDuration = function(self, t) return 4 + math.ceil(self:getTalentLevel(t)) end, + action = function(self, t) + + self:setEffect(self.EFF_SPRINT, t.getDuration(self, t), {power=t.getMovement(self, t)}) + + return true + end, + info = function(self, t) + local movement = t.getMovement(self, t) + local duration = t.getDuration(self, t) + return ([[Increases movement speed by %d%% for %d turns. + Activating this talent takes no time]]): + format(movement * 100, duration) + end, +}]=] + +newTalent{ + name = "Exploit Weakness", + type = {"cunning/tactical", 4}, + require = cuns_req4, + mode = "sustained", + points = 5, + cooldown = 30, + sustain_stamina = 30, + tactical = { BUFF = 2 }, + getReductionMax = function(self, t) return 7 * self:getTalentLevel(t) end, + do_weakness = function(self, t, target) + target:setEffect(target.EFF_WEAKENED_DEFENSES, 3, {inc = - 5, max = - t.getReductionMax(self, t)}) + end, + activate = function(self, t) + return { + dam = self:addTemporaryValue("inc_damage", {[DamageType.PHYSICAL]=-10}), + } + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("inc_damage", p.dam) + return true + end, + info = function(self, t) + local reduction = t.getReductionMax(self, t) + return ([[Systematically find the weaknesses in your opponents physical resists at the cost of 10%% of your physical damage. Each time you hit an opponenet with a melee attack you reduce their physical resistance by 5%% up to a maximum of %d%%. + ]]):format(reduction) + end, +} \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/conditioning.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/techniques/conditioning.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,103 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newTalent{ + name = "Physical Conditioning", + type = {"technique/conditioning", 1}, + require = techs_con_req1, + mode = "passive", + points = 5, + getArmor = function(self, t) return 4 + self:combatTalentStatDamage(t, "con", 1, 20) end, + getPhysical = function(self, t) return 4 + self:combatTalentStatDamage(t, "con", 1, 20) end, + info = function(self, t) + local armor = t.getArmor(self, t) + local saves = t.getPhysical(self, t) + return ([[Physical conditioning that increases armor by %d and physical saves by %d. + The bonuses will scale with your Constitution stat.]]): + format(armor, saves) + end, +} + +newTalent{ + name = "Firm Footing", + type = {"technique/conditioning", 2}, + require = techs_con_req2, + mode = "passive", + points = 5, + getResists = function(self, t) return self:getTalentLevelRaw(t) * 15 end, + on_learn = function(self, t) + self:attr("knockback_immune", 0.15) + self:attr("pin_immune", 0.15) + end, + on_unlearn = function(self, t) + self:attr("knockback_immune", -0.15) + self:attr("pin_immune", -0.15) + end, + info = function(self, t) + local resists = t.getResists(self, t) + return ([[Increases Knockback and Pin resistance by %d%%.]]): + format(resists) + end, +} + +newTalent{ + name = "Iron Skin", + type = {"technique/conditioning", 3}, + require = techs_con_req3, + mode = "passive", + points = 5, + getPercent = function (self, t) return self:getTalentLevel(t) * 20 end, + on_learn = function(self, t) + self:updateConDamageReduction() + end, + on_unlearn = function(self, t) + self:updateConDamageReduction() + end, + info = function(self, t) + local percent = t.getPercent(self, t) + return ([[You've learned to shrug off more damage then is normal. Increases your effective constitution for resist all bonuses by %d%%.]]): + format(percent) + end, +} + +newTalent{ + name = "Unflinching Resolve", + type = {"technique/conditioning", 4}, + require = techs_con_req4, + mode = "passive", + points = 5, + getRegen = function(self, t) return self:getTalentLevel(t) * 0.05 end, + getResist = function(self, t) return self:getTalentLevelRaw(t) * 15 end, + on_hit = function(self, t, dam) + local power = (dam * self:getRegen(t)) / 3 + self:setEffect(self.EFF_RECOVERY, 3, {power = power}) + end, + on_learn = function(self, t) + self:attr("stun_immune", 0.15) + end, + on_unlearn = function(self, t) + self:attr("stun_immune", -0.15) + end, + info = function(self, t) + local resist = t.getResist(self, t) + local regen = t.getRegen(self, t) + return ([[After being hit for 10%% or more of your maximum life in a single blow you recover %d%% of the damage over three turns. Additionally your stun immunity is increased by %d%%.]]): + format(regen * 100, resist) + end, +} diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/finishing-moves.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/techniques/finishing-moves.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,216 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newTalent{ + name = "Uppercut", + type = {"technique/finishing-moves", 1}, + require = techs_dex_req1, + points = 5, + random_ego = "attack", + cooldown = function(self, t) return math.floor(16 - self:getTalentLevelRaw(t) * 2) end, + stamina = 10, + message = "@Source@ throws a finishing uppercut.", + tactical = { ATTACK = 2, DISABLE = 2 }, + requires_target = true, + on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_COMBO) then if not silent then game.logPlayer(self, "You must have a combo going to use this ability.") end return false end return true end, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.1, 1.5) end, + getDuration = function(self, t) return 2 + (self:getCombo(combo)) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + local hit = self:attackTarget(target, nil, t.getDamage(self, t), true) + + if hit then + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("stun") then + target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {}) + else + game.logSeen(target, "%s resists the stun!", target.name:capitalize()) + end + end + + self:clearCombo() + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[A finishing uppercut that deals %d%% damage and attempts to stun your target for 2 turns + 1 turn per combo point you have. + The stun chance will improve with the strength stat. + Using this talent removes your combo points.]]) + :format(damage) + end, +} + +newTalent{ + name = "Spinning Backhand", + type = {"technique/finishing-moves", 2}, + require = techs_dex_req2, + points = 5, + random_ego = "attack", + cooldown = function(self, t) return math.floor(16 - self:getTalentLevelRaw(t) * 2) end, + stamina = 12, + range = function(self, t) return 1 + (self:getCombo(combo) or 0) end, + message = "@Source@ lashes out with a spinning backhand.", + tactical = { ATTACKAREA = 2, CLOSEIN = 1 }, + requires_target = true, + on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_COMBO) then if not silent then game.logPlayer(self, "You must have a combo going to use this ability.") end return false end return true end, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.2, 1.7) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > self:getTalentRange(t) then return nil end + + -- bonus damage for charging + local charge = math.floor((core.fov.distance(self.x, self.y, x, y)) -1) / 10 + local damage = t.getDamage(self, t) + charge + + -- do the rush + local l = line.new(self.x, self.y, x, y) + local tx, ty = self.x, self.y + lx, ly = l() + while lx and ly do + if game.level.map:checkAllEntities(lx, ly, "block_move", self) then break end + tx, ty = lx, ly + lx, ly = l() + end + + local ox, oy = self.x, self.y + self:move(tx, ty, true) + if config.settings.tome.smooth_move > 0 then + self:resetMoveAnim() + self:setMoveAnim(ox, oy, 8, 5) + end + + -- do the backhand + if math.floor(core.fov.distance(self.x, self.y, x, y)) == 1 then + -- get left and right side + local dir = util.getDir(x, y, self.x, self.y) + local lx, ly = util.coordAddDir(self.x, self.y, dir_sides[dir].left) + local rx, ry = util.coordAddDir(self.x, self.y, dir_sides[dir].right) + local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR) + + local hit = self:attackTarget(target, nil, damage, true) + + --left hit + if lt then + hit2 = self:attackTarget(lt, nil, damage, true) + end + --right hit + if rt then + hit3 = self:attackTarget(rt, nil, damage, true) + end + end + + -- remove grappls + self:breakGrapples() + + self:clearCombo() + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[Attack your foes in a frontal arc with a spinning backhand doing %d%% damage. If your not adjacent to the target you'll step forward as you spin, gaining 10%% bonus damage for each tile you move. + The range of this attack will increase by one per combo point you have. + This attack will remove any grapples you're maintaining and remove your combo points.]]) + :format(damage) + end, +} + +newTalent{ + name = "Relentless Strikes", + type = {"technique/finishing-moves", 3}, + require = techs_dex_req3, + points = 5, + random_ego = "utility", + cooldown = function(self, t) return math.floor(26 - self:getTalentLevelRaw(t) * 2) end, + stamina = 20, + tactical = { BUFF = 2 }, + on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_COMBO) then if not silent then game.logPlayer(self, "You must have a combo going to use this ability.") end return false end return true end, + getStamina = function(self, t) return self:getTalentLevel(t) * 2 end, + getDuration = function(self, t) return self:getCombo(combo) * 2 end, + action = function(self, t) + + self:setEffect(self.EFF_RELENTLESS_STRIKES, t.getDuration(self, t), {power=t.getStamina(self, t)}) + + self:clearCombo() + + return true + end, + info = function(self, t) + local stamina = t.getStamina(self, t) + return ([[Increases your stamina regen by %d per turn for a number of turns equal to double your combo points. Additionally every combo point you earn during this time will reduce the cooldown on all your techniques on cooldown by 1. + Using this talent removes your combo points.]]) + :format(stamina) + end, +} + +newTalent{ + name = "Haymaker", + type = {"technique/finishing-moves", 4}, + require = techs_dex_req4, + points = 5, + random_ego = "attack", + cooldown = function(self, t) return math.floor(16 - self:getTalentLevelRaw(t) * 2) end, + stamina = 12, + message = "@Source@ throws a wild haymaker!", + tactical = { ATTACK = 2 }, + requires_target = true, + on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_COMBO) then if not silent then game.logPlayer(self, "You must have a combo going to use this ability.") end return false end return true end, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.3, 1.7) end, + getBonusDamage = function(self, t) return (self:getCombo(combo))/10 end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + local damage = t.getDamage(self, t) * (1 + (t.getBonusDamage(self, t) or 0)) + + self:attackTarget(target, nil, damage, true) + + self:clearCombo() + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[A vicious finishing strike that deals %d%% damage + 10%% damage per combo point you have. + Using this talent removes your combo points.]]) + :format(damage) + end, +} \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/grappling.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/techniques/grappling.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,297 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newTalent{ + name = "Grappling Stance", + type = {"technique/unarmed-other", 1}, + mode = "sustained", + hide = true, + points = 1, + cooldown = 12, + tactical = { BUFF = 2 }, + type_no_req = true, + getSave = function(self, t) return 5 + self:getStr(20) end, + getDamage = function(self, t) return 20 + self:getStr(20) end, + activate = function(self, t) + cancelStances(self) + local ret = { + phys = self:addTemporaryValue("combat_physresist", t.getSave(self, t)), + } + return ret + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("combat_physresist", p.phys) + return true + end, + info = function(self, t) + local save = t.getSave(self, t) + local damage = t.getDamage(self, t) + return ([[Increases your physical saves by %d and the damage of your grappling talents by %d%%. + The bonuses will scale with the strength stat.]]) + :format(save, damage) + end, +} + +newTalent{ + name = "Clinch", + type = {"technique/grappling", 1}, + require = techs_req1, + points = 5, + random_ego = "attack", + cooldown = 6, + stamina = 5, + tactical = { ATTACK = 2, DISABLE = 2 }, + requires_target = true, + getDuration = function(self, t) return 4 + math.floor(self:getTalentLevel(t)) end, + getPower = function(self, t) return 5 + self:combatTalentStatDamage(t, "str", 1, 50) end, + getDrain = function(self, t) return 6 - (self:getTalentLevelRaw(t) or 0) end, + -- Learn the appropriate stance + on_learn = function(self, t) + if not self:knowTalent(self.T_GRAPPLING_STANCE) then + self:learnTalent(self.T_GRAPPLING_STANCE, true) + end + end, + on_unlearn = function(self, t) + if not self:knowTalent(t) then + self:unlearnTalent(self.T_GRAPPLING_STANCE) + end + end, + action = function(self, t) + + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + local grappled = false + + -- force stance change + if target and not self:isTalentActive(self.T_GRAPPLING_STANCE) then + self:forceUseTalent(self.T_GRAPPLING_STANCE, {ignore_energy=true, ignore_cd = true}) + end + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + -- end the talent without effect if the target is to big + if self:grappleSizeCheck(target) then + return true + end + + -- start the grapple + local hit = self:startGrapple(target) + + local duration = t.getDuration(self, t) + + -- do crushing hold if we know it + if hit and self:knowTalent(self.T_CRUSHING_HOLD) then + local t = self:getTalentFromId(self.T_CRUSHING_HOLD) + if grappled and not target.no_breath and not target.undead and target:canBe("silence") then + target:setEffect(target.EFF_STRANGLE_HOLD, duration, {src=self, power=t.getDamage(self, t) * 1.5}) + else + target:setEffect(target.EFF_CRUSHING_HOLD, duration, {src=self, power=t.getDamage(self, t)}) + end + + end + + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + local power = t.getPower(self, t) + local drain = t.getDrain(self, t) + return ([[Grapples the target for %d turns. A grappled opponent will be unable to move and it's attack and defense will be reduced by %d. Any movement from the target or you will break the grapple. Maintaining a grapple drains %d stamina per turn. + You may only grapple a single target at a time and using any targeted unarmed talent on a target that you're not grappling will break the grapple. + The grapple attack and defense reduction as well as success chance will scale with the strength stat. + Performing this action will switch your stance to Grappling Stance.]]) + :format(duration, power, drain) + end, +} + +newTalent{ + name = "Maim", + type = {"technique/grappling", 2}, + require = techs_req2, + points = 5, + random_ego = "attack", + cooldown = 12, + stamina = 10, + tactical = { ATTACK = 2, DISABLE = 2 }, + requires_target = true, + getDuration = function(self, t) return 2 + math.floor(self:getTalentLevel(t)) end, + getDamage = function(self, t) return 10 + self:combatTalentStatDamage(t, "str", 20, 400) * (1 + getGrapplingStyle(self, dam)) end, + getMaim = function(self, t) return 10 + self:combatTalentStatDamage(t, "str", 5, 20) * (1 + getGrapplingStyle(self, dam)) end, + -- Learn the appropriate stance + action = function(self, t) + + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + local grappled = false + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + -- end the talent without effect if the target is to big + if self:grappleSizeCheck(target) then + return true + end + + local hit = self:startGrapple (target) + + -- deal damage and maim if appropriate + if hit then + + if grappled then + self:project(target, x, y, DamageType.PHYSICAL, self:physicalCrit(t.getDamage(self, t), nil, target)) + target:setEffect(target.EFF_MAIMED, t.getDuration(self, t), {power=t.getMaim(self, t)}) + else + self:project(target, x, y, DamageType.PHYSICAL, self:physicalCrit(t.getDamage(self, t), nil, target)) + end + + end + + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + local damage = t.getDamage(self, t) + local maim = t.getMaim(self, t) + return ([[Grapples the target and inflicts %0.2f physical damage. If the target is already grappled the target will be maimed as well, reducing attack and damage by %d and global speed by 30%% for %d turns. + The grapple effects will be based off your grapple talent effect if you have it and the damage will scale with the strength stat.]]) + :format(damDesc(self, DamageType.PHYSICAL, (damage)), maim, duration) + end, +} + +newTalent{ + name = "Crushing Hold", + type = {"technique/grappling", 3}, + require = techs_req3, + mode = "passive", + points = 5, + tactical = { ATTACK = 2, DISABLE = 2 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentStatDamage(t, "str", 1, 200) * (1 + getGrapplingStyle(self, dam)) end, + info = function(self, t) + local damage = t.getDamage(self, t) + return ([[Your clinch talent now starts a crushing hold that deals %0.2f physical damage each turn. If the target is already grappled the hold will instead become a strangle hold, silencing the target and inflicting %0.2f physical damage each turn. + The damage will scale with the strength stat.]]) + :format(damDesc(self, DamageType.PHYSICAL, (damage)), damDesc(self, DamageType.PHYSICAL, (damage * 1.5))) + end, +} + +newTalent{ + name = "Take Down", + type = {"technique/grappling", 4}, + require = techs_req4, + points = 5, + random_ego = "attack", + cooldown = 24, + stamina = 12, + tactical = { ATTACK = 2, DISABLE = 2, CLOSEIN = 2 }, + requires_target = true, + range = function(self, t) return 2 + math.floor(self:getTalentLevel(t)/3) end, + getDuration = function(self, t) return 2 + math.floor(self:getTalentLevel(t)) end, + getTakeDown = function(self, t) return 10 + self:combatTalentStatDamage(t, "str", 15, 250) * (1 + getGrapplingStyle(self, dam)) end, + getSlam = function(self, t) return 20 + self:combatTalentStatDamage(t, "str", 30, 500) * (1 + getGrapplingStyle(self, dam)) end, + -- Learn the appropriate stance + action = function(self, t) + + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > self:getTalentRange(t) then return nil end + + local grappled = false + + -- do the rush + local l = line.new(self.x, self.y, x, y) + local tx, ty = self.x, self.y + lx, ly = l() + while lx and ly do + if game.level.map:checkAllEntities(lx, ly, "block_move", self) then break end + tx, ty = lx, ly + lx, ly = l() + end + + local ox, oy = self.x, self.y + self:move(tx, ty, true) + if config.settings.tome.smooth_move > 0 then + self:resetMoveAnim() + self:setMoveAnim(ox, oy, 8, 5) + end + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + if math.floor(core.fov.distance(self.x, self.y, x, y)) == 1 then + -- end the talent without effect if the target is to big + if self:grappleSizeCheck(target) then + return true + end + + local hit = self:startGrapple (target) + + -- takedown or slam as appropriate + if hit then + + if grappled then + self:project(target, x, y, DamageType.PHYSICAL, self:physicalCrit(t.getSlam(self, t), nil, target)) + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("stun") then + target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {}) + else + game.logSeen(target, "%s resists the stun!", target.name:capitalize()) + end + else + self:project(target, x, y, DamageType.PHYSICAL, self:physicalCrit(t.getTakeDown(self, t), nil, target)) + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("stun") then + target:setEffect(target.EFF_DAZED, t.getDuration(self, t), {}) + else + game.logSeen(target, "%s resists the daze!", target.name:capitalize()) + end + end + + end + end + + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + local takedown = t.getTakeDown(self, t) + local slam = t.getSlam(self, t) + return ([[Rushes forward and attempts to take the target to the ground, starting a grapple, inflicting %0.2f physical damage, and dazing the target for %d turns. If you're already grappling the target you'll instead slam them into the ground for %0.2f physical damage and potentially stun them for %d turns. + The grapple effects and duration will be based off your grapple talent effect if you have it and the damage will scale with the strength stat.]]) + :format(damDesc(self, DamageType.PHYSICAL, (takedown)), duration, damDesc(self, DamageType.PHYSICAL, (slam)), duration) + end, +} \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/kick-boxing.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/techniques/kick-boxing.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,237 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newTalent{ + name = "Push Kick", + type = {"unarmed/kick-boxing", 1}, + require = mart_dex_req1, + points = 5, + cooldown = 6, + stamina = 6, + tactical = { ATTACK = 2, ESCAPE = 2 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentStatDamage(t, "str", 20, 200) * (1 + getStrikingStyle(self, dam)) end, + getPush = function(self, t) return 1 + math.ceil(self:getTalentLevel(t)/4) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + local hit = target:checkHit(self:combatAttack(), target:combatDefense(), 0, 95, 5 - self:getTalentLevel(t) / 2) + -- local hit = self:attackTarget(target, nil, nil, true) + + -- Try to knockback ! + if hit then + local can = function(target) + if target:checkHit(self:combatAttack(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("knockback") then + self:project(target, target.x, target.y, DamageType.PHYSICAL, t.getDamage(self, t), nil, target) + return true + else + self:project(target, target.x, target.y, DamageType.PHYSICAL, t.getDamage(self, t), nil, target) + game.logSeen(target, "%s resists the knockback!", target.name:capitalize()) + end + + end + + if can(target) then target:knockback(self.x, self.y, t.getPush(self, t), can) end + + -- move the attacker back and build combo point + self:knockback(target.x, target.y, 1) + self:buildCombo() + else + game.logSeen(target, "%s misses %s.", self.name:capitalize(), target.name:capitalize()) + end + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local push =t.getPush(self, t) + return ([[A push kick that knocks the target back %d tiles, moves you back 1 tile, and inflicts %0.2f physical damage. If another creature is in the way that creature will be affected too. Targets knocked into other targets may take extra damage. + The damage will scale with the Strength stat. + Builds one combo point.]]) + :format(push, damDesc(self, DamageType.PHYSICAL, (damage))) + end, +} + +newTalent{ + name = "Uppercut3", + type = {"unarmed/kick-boxing", 2}, + require = mart_dex_req2, + points = 5, + random_ego = "attack", + cooldown = 12, + stamina = 10, + tactical = { ATTACK = 2 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.1, 1.5) + getStrikingStyle(self, dam) end, + getDamageTwo = function(self, t) return self:combatTalentWeaponDamage(t, 1.5, 2.1) + getStrikingStyle(self, dam) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + -- extra damage vs. grappled targets + if target:isGrappled(self) then + hit = self:attackTarget(target, nil, t.getDamageTwo(self, t), true) + else + hit = self:attackTarget(target, nil, t.getDamage(self, t), true) + end + + -- combo point + if hit then + self:buildCombo() + end + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + local damagetwo = t.getDamageTwo(self, t) * 100 + return ([[Attack the target with a rising knee strike that deals %d%% damage or %d%% damage against grappled targets. + If the attack lands it will build one combo point.]]) + :format(damage, damagetwo) + end, +} + +newTalent{ + name = "Spinning Backhand3", + type = {"unarmed/kick-boxing", 3}, + require = mart_dex_req3, + points = 5, + random_ego = "attack", + cooldown = 18, + stamina = 20, + range = function(self, t) return 2 + math.floor(self:getTalentLevel(t)/3) end, + tactical = { ATTACKAREA = 2, CLOSEIN = 1 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.7) + getStrikingStyle(self, dam) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > self:getTalentRange(t) then return nil end + + -- bonus damage for charging + local charge = math.floor((core.fov.distance(self.x, self.y, x, y)) -1) / 10 + + -- do the rush + local l = line.new(self.x, self.y, x, y) + local tx, ty = self.x, self.y + lx, ly = l() + while lx and ly do + if game.level.map:checkAllEntities(lx, ly, "block_move", self) then break end + tx, ty = lx, ly + lx, ly = l() + end + + local ox, oy = self.x, self.y + self:move(tx, ty, true) + if config.settings.tome.smooth_move > 0 then + self:resetMoveAnim() + self:setMoveAnim(ox, oy, 8, 5) + end + + -- do the backhand + if math.floor(core.fov.distance(self.x, self.y, x, y)) == 1 then + -- get left and right side + local dir = util.getDir(x, y, self.x, self.y) + local lx, ly = util.coordAddDir(self.x, self.y, dir_sides[dir].left) + local rx, ry = util.coordAddDir(self.x, self.y, dir_sides[dir].right) + local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR) + + local hit = self:attackTarget(target, nil, t.getDamage(self, t) + charge, true) + --combo point? + if hit then + self:buildCombo() + end + + --left hit + if lt then + hit2 = self:attackTarget(lt, nil, t.getDamage(self, t) + charge, true) + --combo point? + if hit2 then + self:buildCombo() + end + end + --right hit + if rt then + hit3 = self:attackTarget(rt, nil, t.getDamage(self, t) + charge, true) + --combo point? + if hit3 then + self:buildCombo() + end + end + end + + -- remove grappls + self:breakGrapples() + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[Attack your foes in a frontal arc with a spinning backhand doing %d%% damage. If your not adjacent to the target you'll step forward as you spin, gaining 10%% bonus damage for each tile you move. + This attack will remove any grapples you're maintaining. + Earns one combo point for each target hit.]]) + :format(damage) + end, +} + +newTalent{ + name = "Expert Strikes3", + type = {"unarmed/kick-boxing", 4}, + require = mart_dex_req4, + mode = "sustained", + points = 5, + cooldown = 24, + sustain_stamina = 50, + tactical = { BUFF = 2 }, + no_energy = true, + getResistPenetration = function(self, t) return 10 + self:combatTalentStatDamage(t, "dex", 10, 50) end, + getStaminaDrain = function(self, t) return 1 + self:getTalentLevelRaw(t) end, + getComboChance = function(self, t) return 20 * self:getTalentLevelRaw(t) end, + on_crit = function(self, t) + if rng.percent(t.getComboChance(self,t)) then + self:buildCombo() + end + end, + activate = function(self, t) + local ret = { + resist = self:addTemporaryValue("resists_pen", {[DamageType.PHYSICAL] = t.getResistPenetration(self, t)}), + stamina_regen = self:addTemporaryValue("stamina_regen", - t.getStaminaDrain(self, t)), + } + return ret + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("resists_pen", p.resist) + self:removeTemporaryValue("stamina_regen", p.stamina_regen) + return true + end, + info = function(self, t) + local resistpen = t.getResistPenetration(self, t) + local drain = t.getStaminaDrain(self, t) + local combo = t.getComboChance(self, t) + return ([[Increases your physical resist penetration by %d%% and has a %d%% chance to grant you a combo point whenever one of your attacks is a critical hit but drains stamina quickly (-%d stamina/turn). + The resist penetration will scale with the Dexterity stat.]]): + format(resistpen, combo, drain) + end, +} \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/martial-arts.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/techniques/martial-arts.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,189 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +-- strength, cunning, and dexterity damage combined +local function getTriStat(self, t, low, high) + low = low / 3 + high = high / 3 + return self:combatTalentStatDamage(t, "str", low, high) + self:combatTalentStatDamage(t, "cun", low, high) + self:combatTalentStatDamage(t, "dex", low, high) +end + +newTalent{ + name = "Push Kick", + type = {"technique/martial-arts", 1}, + require = techs_dex_req1, + points = 5, + cooldown = 6, + stamina = 12, + tactical = { ATTACK = 2, ESCAPE = 2 }, + requires_target = true, + getDamage = function(self, t) return getTriStat(self, t, 30, 300) * (1 + getStrikingStyle(self, dam)) end, + getPush = function(self, t) return 1 + math.ceil(self:getTalentLevel(t)/4) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + local hit = target:checkHit(self:combatAttack(), target:combatDefense(), 0, 95, 5 - self:getTalentLevel(t) / 2) + -- local hit = self:attackTarget(target, nil, nil, true) + + -- Try to knockback ! + if hit then + local can = function(target) + if target:checkHit(self:combatAttack(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("knockback") then + self:project(target, target.x, target.y, DamageType.PHYSICAL, t.getDamage(self, t)) + return true + else + self:project(target, target.x, target.y, DamageType.PHYSICAL, t.getDamage(self, t)) + game.logSeen(target, "%s resists the knockback!", target.name:capitalize()) + end + + end + + if can(target) then target:knockback(self.x, self.y, t.getPush(self, t), can) end + + -- move the attacker back + self:knockback(target.x, target.y, 1) + self:breakGrapples() + self:buildCombo() + + else + game.logSeen(target, "%s misses %s.", self.name:capitalize(), target.name:capitalize()) + end + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local push =t.getPush(self, t) + return ([[A push kick that knocks the target back %d tiles, moves you back 1 tile, and inflicts %0.2f physical damage. If another creature is in the way that creature will be affected too. Targets knocked into other targets may take extra damage. + This is considered a strike for the purposes of stance damage bonuses, will earn one combo point, and will break any grapples you're maintaining. + The damage will scale with the strength, dexterity, and cunning stats.]]) + :format(push, damDesc(self, DamageType.PHYSICAL, (damage))) + end, +} + +newTalent{ + name = "Defensive Throw", + type = {"technique/martial-arts", 2}, + require = techs_dex_req2, + mode = "passive", + points = 5, + getDamage = function(self, t) return getTriStat(self, t, 10, 100) * (1 + getGrapplingStyle(self, dam)) end, + getDamageTwo = function(self, t) return getTriStat(self, t, 10, 100) * (1.5 + getGrapplingStyle(self, dam)) end, + do_throw = function(self, target, t) + + local hit = self:checkHit(self:combatAttack(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) + + -- if grappled stun + if hit and target:canBe("knockback") and target:isGrappled(self) then + self:project(target, target.x, target.y, DamageType.PHYSICAL, self:physicalCrit(t.getDamageTwo(self, t), nil, target)) + game.logSeen(target, "%s has been slammed into the ground!", target.name:capitalize()) + -- see if the throw stuns the enemy + if hit and target:canBe("stun")then + target:setEffect(target.EFF_STUNNED, 2, {}) + end + -- if not grappled daze + elseif hit and target:canBe("knockback") then + self:project(target, target.x, target.y, DamageType.PHYSICAL, self:physicalCrit(t.getDamage(self, t), nil, target)) + game.logSeen(target, "%s has been thrown to the ground!", target.name:capitalize()) + -- see if the throw dazes the enemy + if hit and target:canBe("stun")then + target:setEffect(target.EFF_DAZED, 2, {}) + end + end + + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local damagetwo = t.getDamageTwo(self, t) + return ([[When you avoid a melee blow you have a %d%% chance to throw the target to the ground. If the throw lands the target will take %0.2f damage and be dazed for 2 turns or %0.2f damage and be stunned for 2 turns if grappled. + The chance of throwing increases with the cunning stat and the damage will scale with the strength stat, dexterity, and cunning stats. + This is considered a grapple for the purposes of stance damage bonuses.]]):format(self:getTalentLevel(t) * (5 + self:getCun(5)), damDesc(self, DamageType.PHYSICAL, (damage)), damDesc(self, DamageType.PHYSICAL, (damagetwo))) + end, +} + +newTalent{ + name = "Breath Control", + type = {"technique/martial-arts", 3}, + require = techs_dex_req2, + mode = "sustained", + points = 5, + cooldown = 30, + sustain_stamina = 30, + tactical = { BUFF = 1, STAMINA = 2 }, + getSpeed = function(self, t) return 0.1 end, + getStamina = function(self, t) return self:getTalentLevel(t) * 1.5 end, + activate = function(self, t) + return { + speed = self:addTemporaryValue("energy", {mod = -t.getSpeed(self, t)}), + stamina = self:addTemporaryValue("stamina_regen", t.getStamina(self, t)), + } + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("energy", p.speed) + self:removeTemporaryValue("stamina_regen", p.stamina) + return true + end, + info = function(self, t) + local speed = t.getSpeed(self, t) + local stamina = t.getStamina(self, t) + return ([[You focus your breathing, increasing stamina regeneration by %0.2f per turn at the cost of %d%% global speed.]]): + format(stamina, speed * 100) + end, +} + +newTalent{ + name = "Roundhouse Kick", + type = {"technique/martial-arts", 4}, + require = techs_dex_req3, + points = 5, + random_ego = "attack", + cooldown = 12, + stamina = 18, + range = 0, + radius = function(self, t) return 1 end, + tactical = { ATTACKAREA = 2, DISABLE = 2 }, + requires_target = true, + getDamage = function(self, t) return getTriStat(self, t, 20, 600) * (1 + getStrikingStyle(self, dam)) end, + target = function(self, t) + return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} + end, + action = function(self, t) + local tg = self:getTalentTarget(t) + local x, y, target = self:getTarget(tg) + if not x or not y then return nil end + + self:breakGrapples() + + self:project(tg, x, y, DamageType.PHYSKNOCKBACK, {dam=self:physicalCrit(t.getDamage(self, t), nil, target), dist=4}) + + self:buildCombo() + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + return ([[Attack your foes in a frontal arc with a roundhouse kick that deals %0.2f physical damage and knocks your foes back. + This is considered a strike for the purposes of stance damage bonuses, will earn one combo point, and break any grapples you're maintaining. + The knockback chance will increase with the strength stat and the damage will scale with the strength, dexterity, and cunning stats.]]) + :format(damDesc(self, DamageType.PHYSICAL, (damage))) + end, +} \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/pugilism.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/techniques/pugilism.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,297 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newTalent{ + name = "Striking Stance", + type = {"technique/unarmed-other", 1}, + mode = "sustained", + hide = true, + points = 1, + cooldown = 12, + tactical = { BUFF = 2 }, + type_no_req = true, + getCriticalPower = function(self, t) return 10 + self:getDex(20) end, + getDamage = function(self, t) return 20 + self:getDex(20) end, + activate = function(self, t) + cancelStances(self) + local ret = { + critpower = self:addTemporaryValue("combat_critical_power", t.getCriticalPower(self, t)), + } + return ret + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("combat_critical_power", p.critpower) + return true + end, + info = function(self, t) + local critpower = t.getCriticalPower(self, t) + local damage = t.getDamage(self, t) + return ([[Increases your critical damage multiplier by %d%% and the damage multiplier of your pugilism talents by %d%%. + The bonuses will scale with the Dexterity stat.]]): + format(critpower, damage) + end, +} + +newTalent{ + name = "Double Strike", + type = {"technique/pugilism", 1}, + require = techs_dex_req1, + points = 5, + random_ego = "attack", + cooldown = 3, + stamina = 5, + message = "@Source@ throws two quick punches.", + tactical = { ATTACK = 2 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.3, 0.7) + getStrikingStyle(self, dam) end, + -- Learn the appropriate stance + on_learn = function(self, t) + if not self:knowTalent(self.T_STRIKING_STANCE) then + self:learnTalent(self.T_STRIKING_STANCE, true) + end + end, + on_unlearn = function(self, t) + if not self:knowTalent(t) then + self:unlearnTalent(self.T_STRIKING_STANCE) + end + end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + -- force stance change + if target and not self:isTalentActive(self.T_STRIKING_STANCE) then + self:forceUseTalent(self.T_STRIKING_STANCE, {ignore_energy=true, ignore_cd = true}) + end + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + local hit1 = self:attackTarget(target, nil, t.getDamage(self, t), true) + local hit2 = self:attackTarget(target, nil, t.getDamage(self, t), true) + + if hit1 then + self:buildCombo() + end + + if hit2 then + self:buildCombo() + end + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[Two quick punches that deal %d%% damage each. + Each jab that connects will earn one combo point.]]) + :format(damage) + end, +} + +newTalent{ + name = "Body Shot", + type = {"technique/pugilism", 2}, + require = techs_dex_req2, + points = 5, + random_ego = "attack", + cooldown = 8, + stamina = 10, + message = "@Source@ throws a body shot.", + tactical = { ATTACK = 2, DISABLE = 2 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.1, 1.5) + getStrikingStyle(self, dam) end, + getDuration = function(self, t) return 1 + math.floor(self:getTalentLevel(t)) end, + getDrain = function(self, t) return self:getTalentLevel(t) * 10 end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + local hit = self:attackTarget(target, nil, t.getDamage(self, t), true) + + if hit then + -- try to daze + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("stun") then + target:setEffect(target.EFF_DAZED, t.getDuration(self, t), {}) + else + game.logSeen(target, "%s resists the body shot!", target.name:capitalize()) + end + + target:incStamina(- t.getDrain(self, t)) + self:buildCombo() + + end + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + local drain = t.getDrain(self, t) + local daze = t.getDuration(self, t) + return ([[A punch to the body that deals %d%% damage, drains %d of the target's stamina, and potentially dazes the target for %d turns. + The daze chance will increase with the strength stat. + If the blow connects it will earn one combo point.]]) + :format(damage, drain, daze) + end, +} + +newTalent{ + name = "Rushing Strike", + type = {"technique/pugilism", 3}, + require = techs_dex_req3, + points = 5, + random_ego = "attack", + cooldown = 6, + message = "@Source@ throws a rushing punch!", + range = function(self, t) return 2 + math.ceil(self:getTalentLevel(t)/2) end, + stamina = 8, + tactical = { ATTACK = 2, DISABLE = 2, CLOSEIN = 2 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.8, 1.4) + getStrikingStyle(self, dam) end, + getDuration = function(self, t) return 1 + math.ceil(self:getTalentLevel(t)) end, + action = function(self, t) + if self:attr("never_move") then game.logPlayer(self, "You can not do that currently.") return end + + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > self:getTalentRange(t) then return nil end + + -- bonus damage for charging + local charge = math.floor((core.fov.distance(self.x, self.y, x, y)) -1) / 5 + + -- do the rush + local l = line.new(self.x, self.y, x, y) + local tx, ty = self.x, self.y + lx, ly = l() + while lx and ly do + if game.level.map:checkAllEntities(lx, ly, "block_move", self) then break end + tx, ty = lx, ly + lx, ly = l() + end + + local ox, oy = self.x, self.y + self:move(tx, ty, true) + if config.settings.tome.smooth_move > 0 then + self:resetMoveAnim() + self:setMoveAnim(ox, oy, 8, 5) + end + + -- force stance change + if target and not self:isTalentActive(self.T_STRIKING_STANCE) then + self:forceUseTalent(self.T_STRIKING_STANCE, {ignore_energy=true, ignore_cd = true}) + end + + -- break grabs + self:breakGrapples() + + if math.floor(core.fov.distance(self.x, self.y, x, y)) == 1 then + local hit = self:attackTarget(target, nil, t.getDamage(self, t), true) + + -- Try to stun ! + if hit then + + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5 - self:getTalentLevel(t) / 2) and target:canBe("stun") then + target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {}) + else + game.logSeen(target, "%s resists the stun!", target.name:capitalize()) + end + + self:buildCombo() + end + + end + + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[Attacks the target with a vicious rushing strike that deals %d%% and may stun the target for %d turns. If the target is at range you'll rush towards them and deal 20%% bonus damage per tile traveled. + This attack will remove any grapples you're maintaining, switch your stance to Striking Stance, and earn one combo point if the blow connects. + The stun chance will increase with the strength stat.]]) + :format(damage, duration) + end, +} + +newTalent{ + name = "Flurry of Fists", + type = {"technique/pugilism", 4}, + require = techs_dex_req4, + points = 5, + random_ego = "attack", + cooldown = 24, + stamina = 15, + message = "@Source@ lashes out with a flurry of fists.", + tactical = { ATTACK = 2 }, + requires_target = true, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.4, 1) + getStrikingStyle(self, dam) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end + + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + + local hit1 = self:attackTarget(target, nil, t.getDamage(self, t), true) + local hit2 = self:attackTarget(target, nil, t.getDamage(self, t), true) + local hit3 = self:attackTarget(target, nil, t.getDamage(self, t), true) + + if hit1 then + self:buildCombo() + end + + if hit2 then + self:buildCombo() + end + + if hit3 then + self:buildCombo() + end + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) * 100 + return ([[Lashes out at the target with three quick punches that each deal %d%% damage. + Each punch that connects will earn one combo point.]]) + :format(damage) + end, +} \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/techniques.lua --- a/game/modules/tome/data/talents/techniques/techniques.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/talents/techniques/techniques.lua Fri Mar 25 19:27:01 2011 -0500 @@ -39,6 +39,19 @@ newTalentType{ allow_random=true, type="technique/combat-training", name = "combat training", generic = true, description = "Teaches to use various armors and improves health." } newTalentType{ allow_random=true, type="technique/magical-combat", name = "magical combat", description = "The blending together of magic and melee prowess." } +-- Unarmed Combat +newTalentType{ is_unarmed=true, allow_random=true, type="technique/pugilism", name = "pugilism", description = "Boxing techniques." } +--newTalentType{ is_unarmed=true, allow_random=true, type="technique/kick-boxing", name = "kick boxing", description = "Kick Boxing techniques." } +newTalentType{ is_unarmed=true, allow_random=true, type="technique/finishing-moves", name = "finishing moves", description = "Finishing moves that use combo points." } +newTalentType{ is_unarmed=true, allow_random=true, type="technique/grappling", name = "grappling", description = "Grappling techniques." } +newTalentType{ is_unarmed=true, allow_random=true, type="technique/martial-arts", name = "martial arts", description = "Advanced unarmed techniques including kicks and throws." } +newTalentType{ is_unarmed=true, allow_random=true, type="technique/unarmed-training", name = "unarmed training", generic = true, description = "Teaches various martial arts techniques." } +newTalentType{ allow_random=true, type="technique/conditioning", name = "conditioning", generic = true, description = "Physical conditioning." } + +newTalentType{ is_unarmed=true, type="technique/unarmed-other", name = "unarmed other", generic = true, description = "Base martial arts attack and stances." } + + + -- Generic requires for techs based on talent level -- Uses STR techs_req1 = function(self, t) local stat = "str"; return { @@ -126,6 +139,27 @@ level = function(level) return 16 + (level-1) end, } end +-- Generic requires for techs_con based on talent level +techs_con_req1 = { + stat = { con=function(level) return 12 + (level-1) * 2 end }, + level = function(level) return 0 + (level-1) end, +} +techs_con_req2 = { + stat = { con=function(level) return 20 + (level-1) * 2 end }, + level = function(level) return 4 + (level-1) end, +} +techs_con_req3 = { + stat = { con=function(level) return 28 + (level-1) * 2 end }, + level = function(level) return 8 + (level-1) end, +} +techs_con_req4 = { + stat = { con=function(level) return 36 + (level-1) * 2 end }, + level = function(level) return 12 + (level-1) end, +} +techs_con_req5 = { + stat = { con=function(level) return 44 + (level-1) * 2 end }, + level = function(level) return 16 + (level-1) end, +} -- Archery range talents archery_range = function(self, t) @@ -134,6 +168,35 @@ return weapon.combat.range or 6 end +-- Unarmed stance changes and stance damage bonuses + +getGrapplingStyle = function(self, dam) + local dam = 0 + if self:isTalentActive(self.T_GRAPPLING_STANCE) then + local t = self:getTalentFromId(self.T_GRAPPLING_STANCE) + dam = t.getDamage(self, t) + end + return dam / 100 +end + +getStrikingStyle = function(self, dam) + local dam = 0 + if self:isTalentActive(self.T_STRIKING_STANCE) then + local t = self:getTalentFromId(self.T_STRIKING_STANCE) + dam = t.getDamage(self, t) + end + return dam / 100 +end + +cancelStances = function(self) + local stances = {self.T_STRIKING_STANCE, self.T_GRAPPLING_STANCE} + for i, t in ipairs(stances) do + if self:isTalentActive(t) then + self:forceUseTalent(t, {ignore_energy=true, ignore_cd=true}) + end + end +end + load("/data/talents/techniques/2hweapon.lua") load("/data/talents/techniques/dualweapon.lua") load("/data/talents/techniques/weaponshield.lua") @@ -148,3 +211,11 @@ load("/data/talents/techniques/sling.lua") load("/data/talents/techniques/archery.lua") load("/data/talents/techniques/magical-combat.lua") + +load("/data/talents/techniques/pugilism.lua") +load("/data/talents/techniques/martial-arts.lua") +load("/data/talents/techniques/finishing-moves.lua") +load("/data/talents/techniques/grappling.lua") +--load("/data/talents/techniques/throws.lua") +load("/data/talents/techniques/unarmed-training.lua") +load("/data/talents/techniques/conditioning.lua") diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/talents/techniques/unarmed-training.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game/modules/tome/data/talents/techniques/unarmed-training.lua Fri Mar 25 19:27:01 2011 -0500 @@ -0,0 +1,103 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +-- Empty Hand adds extra scaling to gauntlet and glove attacks based on character level. + +newTalent{ + name = "Empty Hand", + type = {"technique/unarmed-other", 1}, + innate = true, + hide = true, + mode = "passive", + points = 1, + getDamage = function(self, t) return self.level * 0.5 end, + info = function(self, t) + local damage = t.getDamage(self, t) + return ([[Adds %d damage to all glove and gauntlet strikes. + This talent's effects will scale with your level.]]): + format(damage) + end, +} + +-- generic unarmed training + +newTalent{ + name = "Unarmed Mastery", + type = {"technique/unarmed-training", 1}, + points = 10, + require = { stat = { dex=function(level) return 12 + level * 3 end }, }, + mode = "passive", + getDamage = function(self, t) return math.sqrt(self:getTalentLevel(t) / 10) end, + info = function(self, t) + local damage = t.getDamage(self, t) + return ([[Increases damage done with unarmed attacks by %d%%.]]): + format(100 * damage) + end, +} + +newTalent{ + name = "Steady Mind", + type = {"technique/unarmed-training", 2}, + mode = "passive", + points = 5, + require = techs_dex_req2, + getDefense = function(self, t) return 4 + self:combatTalentStatDamage(t, "dex", 1, 20) end, + getMental = function(self, t) return 4 + self:combatTalentStatDamage(t, "cun", 1, 20) end, + info = function(self, t) + local defense = t.getDefense(self, t) + local saves = t.getMental(self, t) + return ([[Superior cunning and training allows you to out think and out wit your opponents physical and mental assualts. Increases defense by %d and mental saves by %d. + The defense bonus will scale with the Dexterity stat and the save bonus with the Cunning stat.]]): + format(defense, saves) + end, +} + +newTalent{ + name = "Heightened Reflexes", + type = {"technique/unarmed-training", 3}, + require = techs_dex_req3, + mode = "passive", + points = 5, + getDuration = function(self, t) return 1 + math.floor(self:getTalentLevel(t)) end, + do_reflexes = function(self, t) + self:setEffect(self.EFF_REFLEXIVE_DODGING, t.getDuration(self, t), {power=1}) + end, + info = function(self, t) + local duration = t.getDuration(self, t) + return ([[When you're targeted by a projectile your global speed is increased by 100%% for %d turns. Taking any action other then movement will break the effect.]]): + format(duration) + end, +} + +newTalent{ + name = "Combo String", + type = {"technique/unarmed-training", 4}, + require = techs_dex_req4, + mode = "passive", + points = 5, + getDuration = function(self, t) return math.floor(self:getTalentLevel(t)/2) end, + getChance = function(self, t) return self:getTalentLevel(t) * (5 + self:getCun(5)) end, + info = function(self, t) + local duration = t.getDuration(self, t) + local chance = t.getChance(self, t) + return ([[When building a combo point you have a %d%% chance to gain an extra combo point. Additionally your combo points will last %d turns longer before expiring. + The chance of building a second combo point will improve with the cunning stat.]]): + format(chance, duration) + end, +} \ No newline at end of file diff -r 88f08638aa28 -r d2255241678c game/modules/tome/data/timed_effects.lua --- a/game/modules/tome/data/timed_effects.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/data/timed_effects.lua Fri Mar 25 19:27:01 2011 -0500 @@ -3452,4 +3452,292 @@ end, deactivate = function(self, eff) end, -} \ No newline at end of file +} + +-- Grappling stuff +newEffect{ + name = "GRAPPLING", + desc = "Grappling", + long_desc = function(self, eff) return ("The target is engaged in a grapple. Any movement will break the effect as will some unarmed talents."):format() end, + type = "physical", + status = "beneficial", + parameters = {}, + on_gain = function(self, err) return "#Target# is engaged in a grapple!", "+Grappling" end, + on_lose = function(self, err) return "#Target# has released the hold.", "-Grappling" end, + on_timeout = function(self, eff) + if math.floor(core.fov.distance(self.x, self.y, eff.src.x, eff.src.y)) > 1 or eff.src.dead or not eff.src:hasEffect(self.EFF_GRAPPLED) then + if eff.src:hasEffect(self.EFF_GRAPPLED) then + eff.src:removeEffect(self.EFF_GRAPPLED) + end + return true + end + end, + activate = function(self, eff) + local drain = 6 - (self:getTalentLevelRaw(self.T_CLINCH) or 0) + eff.tmpid = self:addTemporaryValue("stamina_regen", - drain) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("stamina_regen", eff.tmpid) + -- clears debuffs from the grappled opponent; grapple break is set to silent so it won't spam everytime a grapple is reapplied + if eff.src:hasEffect(self.EFF_GRAPPLED) then + eff.src:removeEffect(self.EFF_GRAPPLED, true) + end + if eff.src:hasEffect(self.EFF_CRUSHING_HOLD) then + eff.src:removeEffect(self.EFF_CRUSHING_HOLD) + end + if eff.src:hasEffect(self.EFF_STRANGLE_HOLD) then + eff.src:removeEffect(self.EFF_STRANGLE_HOLD) + end + end, +} + +newEffect{ + name = "GRAPPLED", + desc = "Grappled", + long_desc = function(self, eff) return ("The target is grappled, unable to move, and has it's defense and attack reduced by %d."):format(eff.power) end, + type = "physical", + status = "detrimental", + parameters = {}, + on_gain = function(self, err) return "#Target# is grappled!", "+Grappled" end, + on_lose = function(self, err) return "#Target# is free from the grapple.", "-Grappled" end, + activate = function(self, eff) + eff.tmpid = self:addTemporaryValue("never_move", 1) + eff.def = self:addTemporaryValue("combat_def", -eff.power) + eff.atk = self:addTemporaryValue("combat_atk", -eff.power) + end, + on_timeout = function(self, eff) + if math.floor(core.fov.distance(self.x, self.y, eff.src.x, eff.src.y)) > 1 or eff.src.dead or not eff.src:hasEffect(self.EFF_GRAPPLING) then + if eff.src:hasEffect(self.EFF_GRAPPLING) then + eff.src:removeEffect(self.EFF_GRAPPLING) + end + return true + end + end, + deactivate = function(self, eff) + -- clears the grappling effect from the attacker; this will in turn remove all other holds. + if eff.src:hasEffect(self.EFF_GRAPPLING) then + eff.src:removeEffect(self.EFF_GRAPPLING) + end + self:removeTemporaryValue("combat_atk", eff.atk) + self:removeTemporaryValue("combat_def", eff.def) + self:removeTemporaryValue("never_move", eff.tmpid) + end, +} + +newEffect{ + name = "CRUSHING_HOLD", + desc = "Crushing Hold", + long_desc = function(self, eff) return ("The target is being crushed and suffers %d damage each turn"):format(eff.power) end, + type = "physical", + status = "detrimental", + parameters = { power=1 }, + on_gain = function(self, err) return "#Target# is being crushed.", "+Crushing Hold" end, + on_lose = function(self, err) return "#Target# has escaped the crushing hold.", "-Crushing Hold" end, + on_timeout = function(self, eff) + DamageType:get(DamageType.PHYSICAL).projector(eff.src or self, self.x, self.y, DamageType.PHYSICAL, eff.power) + end, +} + +newEffect{ + name = "STRANGLE_HOLD", + desc = "Strangle Hold", + long_desc = function(self, eff) return ("The target is being strangled and may not cast spells and suffers %d damage each turn."):format(eff.power) end, + type = "physical", + status = "detrimental", + parameters = { power=1 }, + on_gain = function(self, err) return "#Target# is being strangled.", "+Strangle Hold" end, + on_lose = function(self, err) return "#Target# has escaped the strangle hold.", "-Strangle Hold" end, + on_timeout = function(self, eff) + DamageType:get(DamageType.PHYSICAL).projector(eff.src or self, self.x, self.y, DamageType.PHYSICAL, eff.power) + end, + activate = function(self, eff) + if self:canBe("silence") then + eff.tmpid = self:addTemporaryValue("silence", 1) + eff.dur = self:updateEffectDuration(eff.dur, "silence") + end + silenced = true + end, + deactivate = function(self, eff) + if silenced then + self:removeTemporaryValue("silence", eff.tmpid) + end + end, +} + +newEffect{ + name = "MAIMED", + desc = "Maimed", + long_desc = function(self, eff) return ("The target is maimed, reducing attack and damage by %d and global speed by 30%%."):format(eff.power) end, + type = "physical", + status = "detrimental", + parameters = { atk=10, dam=10 }, + on_gain = function(self, err) return "#Target# is maimed.", "+Maimed" end, + on_lose = function(self, err) return "#Target# has recovered from the maiming.", "-Maimed" end, + activate = function(self, eff) + eff.atkid = self:addTemporaryValue("combat_atk", -eff.atk) + eff.damid = self:addTemporaryValue("combat_dam", -eff.dam) + eff.tmpid = self:addTemporaryValue("energy", {mod=-0.3}) + eff.dur = self:updateEffectDuration(eff.dur, "slow") + end, + deactivate = function(self, eff) + self:removeTemporaryValue("combat_atk", eff.atkid) + self:removeTemporaryValue("combat_dam", eff.damid) + self:removeTemporaryValue("energy", eff.tmpid) + end, +} + +newEffect{ + name = "COMBO", + desc = "Combo", + long_desc = function(self, eff) return ("The target is in the middle of a combo chain and has earned %d combo points."):format(eff.cur_power) end, + type = "physical", + status = "beneficial", + parameters = { power=1, max=5 }, + on_merge = function(self, old_eff, new_eff) + self:removeTemporaryValue("combo", old_eff.tmpid) + old_eff.cur_power = math.min(old_eff.cur_power + new_eff.power, new_eff.max) + old_eff.tmpid = self:addTemporaryValue("combo", {power = old_eff.cur_power}) + + old_eff.dur = new_eff.dur + return old_eff + end, + activate = function(self, eff) + eff.cur_power = eff.power + eff.tmpid = self:addTemporaryValue("combo", {eff.power}) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("combo", eff.tmpid) + end, +} + +newEffect{ + name = "RELENTLESS_STRIKES", + desc = "Relentless Strikes", + long_desc = function(self, eff) return ("The target is attacking relentlessly, increasing it's stamina regen by %d and gaining increased effect from combo point gains."):format(eff.power) end, + type = "physical", + status = "beneficial", + parameters = { power=10 }, + on_gain = function(self, err) return "#Target# is striking relentlessly!", "+Relentless Strikes" end, + on_lose = function(self, err) return "#Target#'s striking ability has returned to normal.", "-Relentless Strikes" end, + activate = function(self, eff) + eff.tmpid = self:addTemporaryValue("stamina_regen", eff.power) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("stamina_regen", eff.tmpid) + end, +} + +newEffect{ + name = "DEFENSIVE_MANEUVER", + desc = "Defensive Maneuver", + long_desc = function(self, eff) return ("The target's defense is increased by %d."):format(eff.power) end, + type = "physical", + status = "beneficial", + parameters = {power = 1}, + on_gain = function(self, err) return "#Target# is moving defensively!", "+Defensive Maneuver" end, + on_lose = function(self, err) return "#Target# isn't moving as defensively anymore.", "-Defensive Maneuver" end, + activate = function(self, eff) + eff.defense = self:addTemporaryValue("combat_def", eff.power) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("combat_def", eff.defense) + end, +} + +newEffect{ + name = "SET_UP", + desc = "Set Up", + long_desc = function(self, eff) return ("The target is off balance and is %d%% more likely to be crit by the target that set it up. In addition all it's saves are reduced by %d."):format(eff.power, eff.power) end, + type = "physical", + status = "detrimental", + parameters = {power = 1}, + on_gain = function(self, err) return "#Target# has been set up!", "+Set Up" end, + on_lose = function(self, err) return "#Target# has survived the set up.", "-Set Up" end, + activate = function(self, eff) + eff.mental = self:addTemporaryValue("combat_mentalresist", -eff.power) + eff.spell = self:addTemporaryValue("combat_spellresist", -eff.power) + eff.physical = self:addTemporaryValue("combat_physresist", -eff.power) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("combat_mentalresist", eff.mental) + self:removeTemporaryValue("combat_spellresist", eff.spell) + self:removeTemporaryValue("combat_physresist", eff.physical) + end, +} + +newEffect{ + name = "RECOVERY", + desc = "Recovery", + long_desc = function(self, eff) return ("The target is recovering from a critical hit and regaining %d life each turn."):format(eff.power) end, + type = "physical", + status = "beneficial", + parameters = { power=10 }, + on_gain = function(self, err) return "#Target# is recovering from the attack!", "+Recovery" end, + on_lose = function(self, err) return "#Target# has finished recovering from the attack.", "-Recovery" end, + activate = function(self, eff) + eff.tmpid = self:addTemporaryValue("life_regen", eff.power) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("life_regen", eff.tmpid) + end, +} + +newEffect{ + name = "REFLEXIVE_DODGING", + desc = "Reflexive Dodging", + long_desc = function(self, eff) return ("Increases global action speed by %d%%."):format(eff.power * 100) end, + type = "physical", + status = "beneficial", + parameters = { power=0.1 }, + on_gain = function(self, err) return "#Target# speeds up.", "+Reflexive Dodging" end, + on_lose = function(self, err) return "#Target# slows down.", "-Reflexive Dodging" end, + activate = function(self, eff) + eff.tmpid = self:addTemporaryValue("energy", {mod=eff.power}) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("energy", eff.tmpid) + end, +} + +newEffect{ + name = "WEAKENED_DEFENSES", + desc = "Weakened Defenses", + long_desc = function(self, eff) return ("The target's physical resistance has been reduced by %d%%."):format(eff.inc) end, + type = "physical", + status = "detrimental", + parameters = { inc=1, max=5 }, + on_merge = function(self, old_eff, new_eff) + self:removeTemporaryValue("resists", old_eff.tmpid) + old_eff.cur_inc = math.max(old_eff.cur_inc + new_eff.inc, new_eff.max) + old_eff.tmpid = self:addTemporaryValue("resists", {[DamageType.PHYSICAL] = old_eff.cur_inc}) + + old_eff.dur = new_eff.dur + return old_eff + end, + activate = function(self, eff) + eff.cur_inc = eff.inc + eff.tmpid= self:addTemporaryValue("resists", { + [DamageType.PHYSICAL]= eff.inc, + }) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("resists", eff.tmpid) + end, +} + +newEffect{ + name = "SPRINT", + desc = "Sprint", + long_desc = function(self, eff) return ("Increases movement speed by %d%%."):format(eff.power*100) end, + type = "physical", + status = "beneficial", + parameters = { power=0.1 }, + on_gain = function(self, err) return "#Target# speeds up.", "+Sprint" end, + on_lose = function(self, err) return "#Target# slows down.", "-Sprint" end, + activate = function(self, eff) + eff.tmpid = self:addTemporaryValue("movement_speed", -eff.power) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("movement_speed", eff.tmpid) + end, +} diff -r 88f08638aa28 -r d2255241678c game/modules/tome/dialogs/CharacterSheet.lua --- a/game/modules/tome/dialogs/CharacterSheet.lua Thu Mar 03 01:35:51 2011 +0000 +++ b/game/modules/tome/dialogs/CharacterSheet.lua Fri Mar 25 19:27:01 2011 -0500 @@ -136,22 +136,35 @@ h = basey w = 200 -- All weapons in main hands - if player:getInven(player.INVEN_MAINHAND) then + local mainhand = player:getInven(player.INVEN_MAINHAND) + if mainhand and (#mainhand > 0) then for i, o in ipairs(player:getInven(player.INVEN_MAINHAND)) do local mean, dam = o.combat, o.combat if o.archery and mean then dam = (player:getInven("QUIVER")[1] and player:getInven("QUIVER")[1].combat) or o.basic_ammo end if mean and dam then - self:mouseTooltip(self.TOOLTIP_COMBAT_ATTACK, s:drawColorStringBlended(self.font, ("Attack(Main Hand): #00ff00#%3d"):format(player:combatAttack(mean)), w, h, 255, 255, 255)) h = h + self.font_h - self:mouseTooltip(self.TOOLTIP_COMBAT_DAMAGE, s:drawColorStringBlended(self.font, ("Damage(Main Hand): #00ff00#%3d"):format(player:combatDamage(dam)), w, h, 255, 255, 255)) h = h + self.font_h - self:mouseTooltip(self.TOOLTIP_COMBAT_APR, s:drawColorStringBlended(self.font, ("APR (Main Hand): #00ff00#%3d"):format(player:combatAPR(dam)), w, h, 255, 255, 255)) h = h + self.font_h - self:mouseTooltip(self.TOOLTIP_COMBAT_CRIT, s:drawColorStringBlended(self.font, ("Crit (Main Hand): #00ff00#%3d%%"):format(player:combatCrit(dam)), w, h, 255, 255, 255)) h = h + self.font_h - self:mouseTooltip(self.TOOLTIP_COMBAT_SPEED, s:drawColorStringBlended(self.font, ("Speed (Main Hand): #00ff00#%0.2f"):format(player:combatSpeed(mean)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_ATTACK, s:drawColorStringBlended(self.font, ("Accuracy(Main Hand): #00ff00#%3d"):format(player:combatAttack(mean)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_DAMAGE, s:drawColorStringBlended(self.font, ("Damage (Main Hand): #00ff00#%3d"):format(player:combatDamage(dam)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_APR, s:drawColorStringBlended(self.font, ("APR (Main Hand): #00ff00#%3d"):format(player:combatAPR(dam)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_CRIT, s:drawColorStringBlended(self.font, ("Crit (Main Hand): #00ff00#%3d%%"):format(player:combatCrit(dam)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_SPEED, s:drawColorStringBlended(self.font, ("Speed (Main Hand): #00ff00#%0.2f"):format(player:combatSpeed(mean)), w, h, 255, 255, 255)) h = h + self.font_h end if mean and mean.range then self:mouseTooltip(self.TOOLTIP_COMBAT_RANGE, s:drawColorStringBlended(self.font, ("Range (Main Hand): #00ff00#%3d"):format(mean.range), w, h, 255, 255, 255)) h = h + self.font_h end end + -- Handle bare-handed combat + else + local mean, dam = player.combat, player.combat + if mean and dam then + self:mouseTooltip(self.TOOLTIP_COMBAT_ATTACK, s:drawColorStringBlended(self.font, ("Accuracy(Unarmed): #00ff00#%3d"):format(player:combatAttack(mean)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_DAMAGE, s:drawColorStringBlended(self.font, ("Damage (Unarmed): #00ff00#%3d"):format(player:combatDamage(dam)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_APR, s:drawColorStringBlended(self.font, ("APR (Unarmed): #00ff00#%3d"):format(player:combatAPR(dam)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_CRIT, s:drawColorStringBlended(self.font, ("Crit (Unarmed): #00ff00#%3d%%"):format(player:combatCrit(dam)), w, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(self.TOOLTIP_COMBAT_SPEED, s:drawColorStringBlended(self.font, ("Speed (Unarmed): #00ff00#%0.2f"):format(player:combatSpeed(mean)), w, h, 255, 255, 255)) h = h + self.font_h + end + if mean and mean.range then self:mouseTooltip(self.TOOLTIP_COMBAT_RANGE, s:drawColorStringBlended(self.font, ("Range (Main Hand): #00ff00#%3d"):format(mean.range), w, h, 255, 255, 255)) h = h + self.font_h end end + h = h + self.font_h -- All weapons in off hands -- Offhand attacks are with a damage penalty, that can be reduced by talents @@ -172,6 +185,7 @@ if mean and mean.range then self:mouseTooltip(self.TOOLTIP_COMBAT_RANGE, s:drawColorStringBlended(self.font, ("Range (Off Hand): #00ff00#%3d"):format(mean.range), w, h, 255, 255, 255)) h = h + self.font_h end end end + h = h + self.font_h self:mouseTooltip(self.TOOLTIP_SPELL_POWER, s:drawColorStringBlended(self.font, ("Spellpower: #00ff00#%3d"):format(player:combatSpellpower()), w, h, 255, 255, 255)) h = h + self.font_h self:mouseTooltip(self.TOOLTIP_SPELL_CRIT, s:drawColorStringBlended(self.font, ("Spell Crit: #00ff00#%3d%%"):format(player:combatSpellCrit()), w, h, 255, 255, 255)) h = h + self.font_h