From 0f9e22a7c39b928f2e8a0de0c6d29726aa673939 Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Sat, 26 Mar 2011 20:26:24 +0000 Subject: [PATCH] brawlers git-svn-id: http://svn.net-core.org/repos/t-engine4@3069 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/modules/tome/class/Actor.lua | 35 ++- game/modules/tome/class/Object.lua | 3 +- game/modules/tome/class/PlayerDisplay.lua | 6 +- game/modules/tome/class/interface/Combat.lua | 190 ++++++++++- .../tome/data/birth/classes/warrior.lua | 51 +++ game/modules/tome/data/damage_types.lua | 2 +- .../data/general/objects/boss-artifacts.lua | 9 + .../tome/data/general/objects/egos/gloves.lua | 81 ++++- .../tome/data/general/objects/gauntlets.lua | 25 ++ .../tome/data/general/objects/gloves.lua | 25 ++ .../data/general/objects/world-artifacts.lua | 63 +++- game/modules/tome/data/talents.lua | 1 + .../tome/data/talents/chronomancy/energy.lua | 2 +- .../data/talents/chronomancy/timetravel.lua | 4 +- .../tome/data/talents/cunning/cunning.lua | 2 + .../tome/data/talents/cunning/tactical.lua | 159 ++++++++++ .../data/talents/techniques/conditioning.lua | 103 ++++++ .../talents/techniques/finishing-moves.lua | 216 +++++++++++++ .../data/talents/techniques/grappling.lua | 297 ++++++++++++++++++ .../data/talents/techniques/kick-boxing.lua | 237 ++++++++++++++ .../tome/data/talents/techniques/pugilism.lua | 297 ++++++++++++++++++ .../data/talents/techniques/techniques.lua | 69 ++++ .../talents/techniques/unarmed-discipline.lua | 189 +++++++++++ .../talents/techniques/unarmed-training.lua | 103 ++++++ .../unlock-chronomancer_paradox_mage.lua | 35 +++ .../data/texts/unlock-warrior_brawler.lua | 35 +++ game/modules/tome/data/timed_effects.lua | 290 +++++++++++++++++ game/modules/tome/dialogs/CharacterSheet.lua | 16 +- 28 files changed, 2523 insertions(+), 22 deletions(-) create mode 100644 game/modules/tome/data/talents/cunning/tactical.lua create mode 100644 game/modules/tome/data/talents/techniques/conditioning.lua create mode 100644 game/modules/tome/data/talents/techniques/finishing-moves.lua create mode 100644 game/modules/tome/data/talents/techniques/grappling.lua create mode 100644 game/modules/tome/data/talents/techniques/kick-boxing.lua create mode 100644 game/modules/tome/data/talents/techniques/pugilism.lua create mode 100644 game/modules/tome/data/talents/techniques/unarmed-discipline.lua create mode 100644 game/modules/tome/data/talents/techniques/unarmed-training.lua create mode 100644 game/modules/tome/data/texts/unlock-chronomancer_paradox_mage.lua create mode 100644 game/modules/tome/data/texts/unlock-warrior_brawler.lua diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 49511d3b83..975fea9525 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -177,7 +177,7 @@ function _M:init(t, no_default) 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() @@ -430,7 +430,7 @@ function _M:move(x, y, force) 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 @@ -1069,6 +1069,13 @@ function _M:onTakeHit(value, src) 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 @@ -1442,6 +1449,9 @@ function _M:updateConDamageReduction() 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 @@ -1682,6 +1692,20 @@ function _M:preUseTalent(ab, silent, fake) 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 @@ -1995,6 +2019,9 @@ function _M:breakStepUp() 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 @@ -2319,6 +2346,10 @@ function _M:on_projectile_target(x, y, p) 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 --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua index c4b004f88e..535c5dc55b 100644 --- a/game/modules/tome/class/Object.lua +++ b/game/modules/tome/class/Object.lua @@ -413,6 +413,7 @@ function _M:getTextualDesc() 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 @@ -450,7 +451,7 @@ function _M:getTextualDesc() 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 --git a/game/modules/tome/class/PlayerDisplay.lua b/game/modules/tome/class/PlayerDisplay.lua index 4e500ef949..4de1ce7897 100644 --- a/game/modules/tome/class/PlayerDisplay.lua +++ b/game/modules/tome/class/PlayerDisplay.lua @@ -291,11 +291,13 @@ function _M:display() for eff_id, p in pairs(player.tmp) do local e = player.tempeffect_def[eff_id] local dur = p.dur + 1 + local name = e.desc + if e.display_desc then name = e.display_desc(self, p) end local desc = e.long_desc(player, p) if e.status == "detrimental" then - self:mouseTooltip(desc, self:makeTexture(("#LIGHT_RED#%s(%d)"):format(e.desc,dur), x, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(desc, self:makeTexture(("#LIGHT_RED#%s(%d)"):format(name, dur), x, h, 255, 255, 255)) h = h + self.font_h else - self:mouseTooltip(desc, self:makeTexture(("#LIGHT_GREEN#%s(%d)"):format(e.desc,dur), x, h, 255, 255, 255)) h = h + self.font_h + self:mouseTooltip(desc, self:makeTexture(("#LIGHT_GREEN#%s(%d)"):format(name, dur), x, h, 255, 255, 255)) h = h + self.font_h end end if game.level and game.level.arena then diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index 88d081f8b4..0f962e8a78 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -107,7 +107,7 @@ function _M:attackTarget(target, damtype, mult, noenergy) 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 @@ -382,6 +382,12 @@ function _M:attackTargetWith(target, weapon, damtype, mult) 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 @@ -408,6 +414,27 @@ function _M:attackTargetWith(target, weapon, damtype, mult) 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) @@ -448,6 +475,14 @@ function _M:combatDefense() 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 @@ -465,6 +500,10 @@ function _M:combatArmor() 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 @@ -564,8 +603,18 @@ function _M:combatDamage(weapon) 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)) @@ -653,7 +702,14 @@ function _M:physicalCrit(dam, weapon, target) 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 @@ -669,6 +725,7 @@ function _M:physicalCrit(dam, weapon, target) if rng.percent(chance) then dam = dam * (1.5 + (self.combat_critical_power or 0) / 100) crit = true + end return dam, crit end @@ -741,6 +798,10 @@ end --- Computes physical resistance function _M:combatPhysicalResist() 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 if self:knowTalent(self.T_POWER_IS_MONEY) then add = add + util.bound(self.money / (80 - self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 5), 0, self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 10) end @@ -758,10 +819,16 @@ end --- Computes mental resistance function _M:combatMentalResist() + 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 local add = 0 if self:knowTalent(self.T_POWER_IS_MONEY) then add = add + util.bound(self.money / (60 - self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 5), 0, self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 10) end + add = add + t.getMental(self, t) return self.combat_mentalresist + (self:getCun() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 + add end @@ -839,6 +906,18 @@ function _M:hasShield() 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 @@ -883,3 +962,106 @@ function _M:hasMount() 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 --git a/game/modules/tome/data/birth/classes/warrior.lua b/game/modules/tome/data/birth/classes/warrior.lua index 675f245b2f..3e035f3bd3 100644 --- a/game/modules/tome/data/birth/classes/warrior.lua +++ b/game/modules/tome/data/birth/classes/warrior.lua @@ -30,6 +30,7 @@ newBirthDescriptor{ __ALL__ = "disallow", Fighter = "allow", Berserker = "allow", + Brawler = function() return profile.mod.allow_build.warrior_brawler and "allow" or "disallow" end, ["Arcane Blade"] = "allow", }, }, @@ -174,3 +175,53 @@ newBirthDescriptor{ life_rating = 2, }, } + +newBirthDescriptor{ + type = "subclass", + name = "Brawler", + desc = { + "The ravages of the Spellblaze stretched armies thin and left many unprotected. Not everyone could afford the luxury of a weapon.", + "Without steel or iron, poor communities of all races turned to the strength of their own bodies for defense against the darkness.", + "Rather a pit-fighter, a boxer, or just a practitioner the occupation known as brawler still exists today.", + "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/dirty"]={true, 0}, + ["cunning/tactical"]={true, 0.3}, + ["cunning/survival"]={false, 0}, + ["technique/combat-training"]={true, 0.1}, + ["technique/field-control"]={false, 0.3}, + ["technique/combat-techniques-active"]={false, 0}, + ["technique/combat-techniques-passive"]={true, 0.1}, + ["technique/pugilism"]={true, 0.3}, + ["technique/finishing-moves"]={true, 0.3}, + ["technique/grappling"]={false, 0.3}, + ["technique/unarmed-discipline"]={false, 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, ego_chance=-1000, ego_chance=-1000}, + {type="armor", subtype="light", name="rough leather armour", autoreq=true, ego_chance=-1000, ego_chance=-1000}, + }, + }, + copy_add = { + life_rating = 2, + }, +} diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua index e8ba8470c2..a73c407255 100644 --- a/game/modules/tome/data/damage_types.lua +++ b/game/modules/tome/data/damage_types.lua @@ -642,7 +642,7 @@ newDamageType{ 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 --git a/game/modules/tome/data/general/objects/boss-artifacts.lua b/game/modules/tome/data/general/objects/boss-artifacts.lua index 65265a1606..00cd29aa62 100644 --- a/game/modules/tome/data/general/objects/boss-artifacts.lua +++ b/game/modules/tome/data/general/objects/boss-artifacts.lua @@ -754,6 +754,15 @@ newEntity{ base = "BASE_GLOVES", define_as = "FLAMEWROUGHT", 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 --git a/game/modules/tome/data/general/objects/egos/gloves.lua b/game/modules/tome/data/general/objects/egos/gloves.lua index 8f436aef1c..6a4e42182c 100644 --- a/game/modules/tome/data/general/objects/egos/gloves.lua +++ b/game/modules/tome/data/general/objects/egos/gloves.lua @@ -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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ 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 @@ newEntity{ [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 @@ newEntity{ }, 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 @@ newEntity{ [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 --git a/game/modules/tome/data/general/objects/gauntlets.lua b/game/modules/tome/data/general/objects/gauntlets.lua index dc5246ae1f..19f813c0c9 100644 --- a/game/modules/tome/data/general/objects/gauntlets.lua +++ b/game/modules/tome/data/general/objects/gauntlets.lua @@ -30,6 +30,7 @@ newEntity{ 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 @@ newEntity{ base = "BASE_GAUNTLETS", 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 @@ newEntity{ base = "BASE_GAUNTLETS", 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 @@ newEntity{ base = "BASE_GAUNTLETS", 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 --git a/game/modules/tome/data/general/objects/gloves.lua b/game/modules/tome/data/general/objects/gloves.lua index 4cc5844b34..b140abbfd2 100644 --- a/game/modules/tome/data/general/objects/gloves.lua +++ b/game/modules/tome/data/general/objects/gloves.lua @@ -28,6 +28,7 @@ newEntity{ 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 @@ newEntity{ base = "BASE_GLOVES", 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 @@ newEntity{ base = "BASE_GLOVES", 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 @@ newEntity{ base = "BASE_GLOVES", 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 --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index 64931d1da8..49717e4dd9 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -1098,10 +1098,19 @@ newEntity{ base = "BASE_GLOVES", 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 + }, }, } @@ -1121,6 +1130,15 @@ newEntity{ base = "BASE_GAUNTLETS", 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 + }, }, } @@ -1139,6 +1157,15 @@ newEntity{ base = "BASE_GLOVES", 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 }, @@ -1162,6 +1189,16 @@ newEntity{ base = "BASE_GAUNTLETS", 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 }, @@ -1847,16 +1884,28 @@ newEntity{ base = "BASE_GAUNTLETS", 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 --git a/game/modules/tome/data/talents.lua b/game/modules/tome/data/talents.lua index 0e52aa51f9..26559fc82b 100644 --- a/game/modules/tome/data/talents.lua +++ b/game/modules/tome/data/talents.lua @@ -25,6 +25,7 @@ newTalent = function(t) 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 --git a/game/modules/tome/data/talents/chronomancy/energy.lua b/game/modules/tome/data/talents/chronomancy/energy.lua index edac1b4ec7..de1655087d 100644 --- a/game/modules/tome/data/talents/chronomancy/energy.lua +++ b/game/modules/tome/data/talents/chronomancy/energy.lua @@ -137,7 +137,7 @@ newTalent{ -- 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 --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua index 2e3fa89c89..280f827deb 100644 --- a/game/modules/tome/data/talents/chronomancy/timetravel.lua +++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua @@ -34,8 +34,8 @@ newTalent{ 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 --git a/game/modules/tome/data/talents/cunning/cunning.lua b/game/modules/tome/data/talents/cunning/cunning.lua index e1ed04e9ca..1d8af9853d 100644 --- a/game/modules/tome/data/talents/cunning/cunning.lua +++ b/game/modules/tome/data/talents/cunning/cunning.lua @@ -26,6 +26,7 @@ newTalentType{ allow_random=true, type="cunning/dirty", name = "dirty fighting", 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/stealth.lua") 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 --git a/game/modules/tome/data/talents/cunning/tactical.lua b/game/modules/tome/data/talents/cunning/tactical.lua new file mode 100644 index 0000000000..4594af6d1c --- /dev/null +++ b/game/modules/tome/data/talents/cunning/tactical.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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.5, 0.9) + 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 --git a/game/modules/tome/data/talents/techniques/conditioning.lua b/game/modules/tome/data/talents/techniques/conditioning.lua new file mode 100644 index 0000000000..6a31cd8d49 --- /dev/null +++ b/game/modules/tome/data/talents/techniques/conditioning.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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 * t.getRegen(self, 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 --git a/game/modules/tome/data/talents/techniques/finishing-moves.lua b/game/modules/tome/data/talents/techniques/finishing-moves.lua new file mode 100644 index 0000000000..9c5b31ad23 --- /dev/null +++ b/game/modules/tome/data/talents/techniques/finishing-moves.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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 --git a/game/modules/tome/data/talents/techniques/grappling.lua b/game/modules/tome/data/talents/techniques/grappling.lua new file mode 100644 index 0000000000..363bf07daa --- /dev/null +++ b/game/modules/tome/data/talents/techniques/grappling.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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 --git a/game/modules/tome/data/talents/techniques/kick-boxing.lua b/game/modules/tome/data/talents/techniques/kick-boxing.lua new file mode 100644 index 0000000000..e455e35bb9 --- /dev/null +++ b/game/modules/tome/data/talents/techniques/kick-boxing.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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 --git a/game/modules/tome/data/talents/techniques/pugilism.lua b/game/modules/tome/data/talents/techniques/pugilism.lua new file mode 100644 index 0000000000..6ac2a1f10c --- /dev/null +++ b/game/modules/tome/data/talents/techniques/pugilism.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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) * 5 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 --git a/game/modules/tome/data/talents/techniques/techniques.lua b/game/modules/tome/data/talents/techniques/techniques.lua index 81d582748a..c2456b12d9 100644 --- a/game/modules/tome/data/talents/techniques/techniques.lua +++ b/game/modules/tome/data/talents/techniques/techniques.lua @@ -39,6 +39,18 @@ newTalentType{ allow_random=true, type="technique/combat-techniques-passive", na 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/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/unarmed-discipline", name = "unarmed discipline", 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 +138,27 @@ techs_strdex_req5 = function(self, t) local stat = self:getStr() >= self:getDex( 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 +167,35 @@ archery_range = function(self, t) 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 +210,10 @@ load("/data/talents/techniques/bow.lua") 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/unarmed-discipline.lua") +load("/data/talents/techniques/finishing-moves.lua") +load("/data/talents/techniques/grappling.lua") +load("/data/talents/techniques/unarmed-training.lua") +load("/data/talents/techniques/conditioning.lua") diff --git a/game/modules/tome/data/talents/techniques/unarmed-discipline.lua b/game/modules/tome/data/talents/techniques/unarmed-discipline.lua new file mode 100644 index 0000000000..893f1f45f8 --- /dev/null +++ b/game/modules/tome/data/talents/techniques/unarmed-discipline.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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/unarmed-discipline", 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/unarmed-discipline", 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/unarmed-discipline", 3}, + require = techs_dex_req3, + 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/unarmed-discipline", 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 --git a/game/modules/tome/data/talents/techniques/unarmed-training.lua b/game/modules/tome/data/talents/techniques/unarmed-training.lua new file mode 100644 index 0000000000..5b0c254c99 --- /dev/null +++ b/game/modules/tome/data/talents/techniques/unarmed-training.lua @@ -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 <http://www.gnu.org/licenses/>. +-- +-- 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 --git a/game/modules/tome/data/texts/unlock-chronomancer_paradox_mage.lua b/game/modules/tome/data/texts/unlock-chronomancer_paradox_mage.lua new file mode 100644 index 0000000000..79f528513a --- /dev/null +++ b/game/modules/tome/data/texts/unlock-chronomancer_paradox_mage.lua @@ -0,0 +1,35 @@ +-- 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 <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +return "New Class: #LIGHT_GREEN#Paradox Mage (Chronomancer)", +[[Time isn't a linear thing, it only appears to be. In truth it's a fabric, the very fabric that holds the universe together. +Some seek to weave this fabric, to bend it and shape it to their own will. Only the truely dedicated or the truely mad manage to unlock the secrets of time and space. + +You have learned such secrets and can now create new characters with the #LIGHT_GREEN#Paradox Mage class#WHITE#. + +Paradox Mages are non-traditional spell casters who use chronomancy to reshape the universe. +Class features:#YELLOW# +- Control the flow of time around you +- Bend and manipulate the timestream or even pull yourself from the future +- Manipulate space and gravity to smash your foes to pieces or keep them in place +- See into the future or even travel into the past#WHITE# + +Paradox Mages use Paradox for their powers. It represents how much damage they've done to the spacetime continuum. +The higher their paradox the greater the effect of their powers but the harder they'll be to control. +]] \ No newline at end of file diff --git a/game/modules/tome/data/texts/unlock-warrior_brawler.lua b/game/modules/tome/data/texts/unlock-warrior_brawler.lua new file mode 100644 index 0000000000..963a362b6c --- /dev/null +++ b/game/modules/tome/data/texts/unlock-warrior_brawler.lua @@ -0,0 +1,35 @@ +-- 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 <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +return "New Class: #LIGHT_GREEN#Brawler (Warrior)", +[[The ravages of the Spellblaze stretched armies thin and left many unprotected. Not everyone could afford the luxury of a weapon. +Without steel or iron, poor communities of all races turned to the strength of their own bodies for defense against the darkness. +These unarmed techniques still exist today. + +You have learned these techniques and can now create new characters with the #LIGHT_GREEN#Brawler class#WHITE#. + +Brawlers are warriors who fight with little more then their own bodies as weapons. +Class features:#YELLOW# +- Build deadly combination attacks with your strikes +- Wear gauntlets or gloves to boost your damage +- Outwit your foes and set them up for deadly counter attacks +- Control your enemies and break their bones with fierce grappling techniques#WHITE# + +Brawlers use stamina and must remain at least semi-mobile. As such they can not perform their unarmed talents in massive armor. +]] \ No newline at end of file diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua index a31cb4aab5..42c2aa13a8 100644 --- a/game/modules/tome/data/timed_effects.lua +++ b/game/modules/tome/data/timed_effects.lua @@ -3552,3 +3552,293 @@ newEffect{ self:removeTemporaryValue("combat_mentalresist", eff.mindid) end, } + +-- 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) + self:removeTemporaryValue("combat_mindpower", eff.mindid) + end, +} + +newEffect{ + name = "COMBO", + desc = "Combo", + display_desc = function(self, eff) return eff.cur_power.." Combo" end, + 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 damaging blow 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 --git a/game/modules/tome/dialogs/CharacterSheet.lua b/game/modules/tome/dialogs/CharacterSheet.lua index 2146f955c0..4894efa3ce 100644 --- a/game/modules/tome/dialogs/CharacterSheet.lua +++ b/game/modules/tome/dialogs/CharacterSheet.lua @@ -136,7 +136,8 @@ function _M:drawDialog(s) 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 @@ -151,7 +152,19 @@ function _M:drawDialog(s) 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 @@ function _M:drawDialog(s) 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 -- GitLab