diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index 6bc3231d79bb65a3426233873479faff16f81b6c..b299fb5b71ef76c7b97e066833c32dfcbfd211c1 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -575,8 +575,8 @@ function _M:playerFOV() -- For each entity, generate lite local uid, e = next(game.level.entities) while uid do - if e ~= self and e.lite and e.lite > 0 and e.computeFOV then - e:computeFOV(e.lite, "block_sight", function(x, y, dx, dy, sqdist) game.level.map:applyExtraLite(x, y, fovdist[sqdist]) end, true, true) + if e ~= self and ((e.lite and e.lite > 0) or (e.radiance_aura and e.radiance_aura > 0)) and e.computeFOV then + e:computeFOV(math.max(e.lite or 0, e.radiance_aura or 0), "block_sight", function(x, y, dx, dy, sqdist) game.level.map:applyExtraLite(x, y, fovdist[sqdist]) end, true, true) end uid, e = next(game.level.entities, uid) end diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index 836f0ae2327d57e517ab33d7ce2b3da8c0068256..3785bc27d2d3395f3dbe00e3931b0219f5cc3fd2 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -2251,26 +2251,36 @@ end -- Starts the grapple function _M:startGrapple(target) -- pulls boosted grapple effect from the clinch talent if known + local grappledParam = {src = self, apply_power = 1, silence = 0, power = 1, slow = 0, reduction = 0} + local grappleParam = {sharePct = 0, drain = 0, trgt = target } + local duration = 5 if self:knowTalent(self.T_CLINCH) then local t = self:getTalentFromId(self.T_CLINCH) - power = t.getPower(self, t) + if self:knowTalent(self.T_CRUSHING_HOLD) then + local t2 = self:getTalentFromId(self.T_CRUSHING_HOLD) + grappledParam = t2.getBonusEffects(self, t2) -- get the 4 bonus parameters first + end + local power = self:physicalCrit(t.getPower(self, t), nil, target, self:combatAttack(), target:combatDefense()) + grappledParam["power"] = power -- damage/turn set by Clinch duration = t.getDuration(self, t) - hitbonus = self:getTalentLevel(t)/2 - else - power = 5 - duration = 4 - hitbonus = 0 + + grappleParam["drain"] = t.getDrain(self, t) -- stamina/turn set by Clinch + grappleParam["sharePct"] = t.getSharePct(self, t) -- damage shared with grappled set by Clinch + end + -- oh for the love of god why didn't I rewrite this entire structure + grappledParam["src"] = self + grappledParam["apply_power"] = self:combatPhysicalpower() -- Breaks the grapple before reapplying if self:hasEffect(self.EFF_GRAPPLING) then self:removeEffect(self.EFF_GRAPPLING, true) - target:setEffect(target.EFF_GRAPPLED, duration, {src=self, power=power}, true) - self:setEffect(self.EFF_GRAPPLING, duration, {trgt=target}, true) + target:setEffect(target.EFF_GRAPPLED, duration, grappledParam, true) + self:setEffect(self.EFF_GRAPPLING, duration, grappleParam, true) return true elseif target:canBe("pin") then - target:setEffect(target.EFF_GRAPPLED, duration, {src=self, power=power, apply_power=self:combatPhysicalpower()}) + target:setEffect(target.EFF_GRAPPLED, duration, grappledParam) target:crossTierEffect(target.EFF_GRAPPLED, self:combatPhysicalpower()) - self:setEffect(self.EFF_GRAPPLING, duration, {trgt=target}) + self:setEffect(self.EFF_GRAPPLING, duration, grappleParam) return true else game.logSeen(target, "%s resists the grapple!", target.name:capitalize()) diff --git a/game/modules/tome/data/general/objects/egos/light-armor.lua b/game/modules/tome/data/general/objects/egos/light-armor.lua index df797062e050f75e341b3485bb97f63dca0d5fc0..529a85d68325b11ece6367a2a5cac96e32fe6f3a 100644 --- a/game/modules/tome/data/general/objects/egos/light-armor.lua +++ b/game/modules/tome/data/general/objects/egos/light-armor.lua @@ -32,6 +32,7 @@ newEntity{ rarity = 12, cost = 14, wielder = { + healing_factor = resolvers.mbonus_material(20, 10, function(e, v) v=v/100 return 0, v end), -- copied from robe.lua life_regen = resolvers.mbonus_material(120, 30, function(e, v) v=v/10 return 0, v end), }, } @@ -45,12 +46,13 @@ newEntity{ rarity = 22, cost = 35, wielder = { - combat_def = resolvers.mbonus_material(8, 2), - combat_def_ranged = resolvers.mbonus_material(8, 2), - movement_speed = 0.1, + combat_def = resolvers.mbonus_material(14, 2), + combat_def_ranged = resolvers.mbonus_material(14, 2), + movement_speed = 0.2, inc_stats = { [Stats.STAT_DEX] = resolvers.mbonus_material(3, 2), }, }, } + newEntity{ power_source = {technique=true}, name = "marauder's ", prefix=true, instant_resolve=true, @@ -109,15 +111,13 @@ newEntity{ power_source = {technique=true}, name = " of the wind", suffix=true, instant_resolve=true, keywords = {wind=true}, - level_range = {40, 50}, + level_range = {30, 50}, greater_ego = 1, rarity = 30, cost = 80, resolvers.charmt(Talents.T_SECOND_WIND, {3,4,5}, 35), wielder = { - max_life = resolvers.mbonus_material(60, 40, function(e, v) return 0, -v end), - combat_armor = resolvers.mbonus_material(7, 3, function(e, v) return 0, -v end), - + cancel_damage_chance = resolvers.mbonus_material(8, 2), combat_physcrit = resolvers.mbonus_material(7, 3), combat_apr = resolvers.mbonus_material(15, 5), combat_def = resolvers.mbonus_material(10, 5), @@ -168,3 +168,37 @@ newEntity{ }, } +newEntity{ + power_source = {technique=true}, + name = " of alacrity", suffix=true, instant_resolve=true, + keywords = {alacrity=true}, + level_range = {40, 50}, + greater_ego = 1, + rarity = 30, + cost = 80, + wielder = { + combat_physspeed = 0.15, + combat_mindspeed = 0.15, + combat_spellspeed = 0.15, + }, +} + +-- you are so going to veto this ^^; +newEntity{ + power_source = {technique=true, arcane=true, nature=true}, + name = " of the hero ", suffix=true, instant_resolve=true, + keywords = {hero=true}, + level_range = {25, 50}, + greater_ego = 1, + rarity = 30, + cost = 35, + wielder = { + inc_stats = { + [Stats.STAT_STR] = resolvers.mbonus_material(4, 3), + [Stats.STAT_DEX] = resolvers.mbonus_material(4, 3), + [Stats.STAT_WIL] = resolvers.mbonus_material(4, 3), + [Stats.STAT_CUN] = resolvers.mbonus_material(4, 3), + [Stats.STAT_MAG] = resolvers.mbonus_material(4, 3), + }, + }, +} diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index 1b65ba3c756993ba0c568862b2f21b0ca7edb16e..7ac5c41d549917b55c621e70018fdbd79978b5cc 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -41,9 +41,9 @@ newEntity{ base = "BASE_GEM", identified = false, material_level = 4, wielder = { - inc_stats = {[Stats.STAT_DEX] = 10, [Stats.STAT_CUN] = 10 }, + inc_stats = {[Stats.STAT_DEX] = 8, [Stats.STAT_CUN] = 8 }, inc_damage = {[DamageType.LIGHTNING] = 20 }, - cancel_damage_chance = 10, -- add to tooltip + cancel_damage_chance = 8, -- add to tooltip damage_affinity={ [DamageType.LIGHTNING] = 20, }, diff --git a/game/modules/tome/data/talents/cursed/cursed-aura.lua b/game/modules/tome/data/talents/cursed/cursed-aura.lua index be0a924a3e2d830690479810aa6364463cda62e1..71f12438b71d06a70bd38095dd13c9ea796a18b6 100644 --- a/game/modules/tome/data/talents/cursed/cursed-aura.lua +++ b/game/modules/tome/data/talents/cursed/cursed-aura.lua @@ -435,7 +435,7 @@ newTalent{ no_breath = 1, disarm_immune = 1, never_move = 1, - no_drops = true, -- remove to drop the weapon + --no_drops = true, -- remove to drop the weapon resolvers.talents{ [Talents.T_WEAPON_COMBAT]={base=1, every=10}, @@ -504,6 +504,6 @@ newTalent{ local duration = t.getDuration(self, t) local attackSpeed = 100/t.getAttackSpeed(self, t) - return ([[Instill a part of your living curse into a weapon in your inventory, and toss it nearby. This nearly impervious sentry will attack all nearby enemies for %d turns. When the curse ends, the weapon will crumble to dust, worn through by your hatred. Attack Speed: %d%%]]):format(duration, attackSpeed) + return ([[Instill a part of your living curse into a weapon in your inventory, and toss it nearby. This nearly impervious sentry will attack all nearby enemies for %d turns. When the curse ends, the weapon will drop to the ground. Attack Speed: %d%%]]):format(duration, attackSpeed) end, } diff --git a/game/modules/tome/data/talents/cursed/endless-hunt.lua b/game/modules/tome/data/talents/cursed/endless-hunt.lua index 1fd54aea2d6c8cc97095a6c73e2c82976c03f663..b2ded74824324502c5a086cf47072ae0b39dfe44 100644 --- a/game/modules/tome/data/talents/cursed/endless-hunt.lua +++ b/game/modules/tome/data/talents/cursed/endless-hunt.lua @@ -151,7 +151,7 @@ newTalent{ return getHateMultiplier(self, 0.35, 0.67, false, hate) end, getTargetDamageChange = function(self, t) - return -self:combatLimit(self:combatTalentStatDamage(t, "wil", 0.7, 0.9), 1, 0, 0, 0.75, 0.87) -- Limit < 100% + return -self:combatLimit(self:combatTalentStatDamage(t, "wil", 0.7, 0.9), 1, 0, 0, 0.75, 0.87)*100 -- Limit < 100% end, getDuration = function(self, t) return 2 @@ -196,7 +196,7 @@ newTalent{ local targetDamageChange = t.getTargetDamageChange(self, t) local duration = t.getDuration(self, t) return ([[Harass your stalked victim with two quick attacks for %d%% (at 0 Hate) to %d%% (at 100+ Hate) damage each. Each attack that scores a hit disrupts one talent, rune or infusion for %d turns. Your opponent will be unnerved by the attacks, reducing the damage they deal by %d%% for %d turns. - Damage reduction increases with the Willpower stat.]]):format(t.getDamageMultiplier(self, t, 0) * 100, t.getDamageMultiplier(self, t, 100) * 100, cooldownDuration, -targetDamageChange * 100, duration) + Damage reduction increases with the Willpower stat.]]):format(t.getDamageMultiplier(self, t, 0) * 100, t.getDamageMultiplier(self, t, 100) * 100, cooldownDuration, -targetDamageChange, duration) end, } diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua index 53458c08d7107e85be30498bf3358350a71a0c28..853e43cbb653f9dc8beb0a3944e7ec2dba402b8b 100644 --- a/game/modules/tome/data/talents/misc/npcs.lua +++ b/game/modules/tome/data/talents/misc/npcs.lua @@ -2174,3 +2174,61 @@ newTalent{ format(defense, saves) end, } + +newTalent{ +name = "Maim", +type = {"technique/other", 1}, +points = 5, +random_ego = "attack", +cooldown = 12, +stamina = 10, +tactical = { ATTACK = { PHYSICAL = 2 }, DISABLE = 2 }, +requires_target = true, +getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, +getDamage = function(self, t) return self:combatTalentPhysicalDamage(t, 10, 100) * getUnarmedTrainingBonus(self) end, +getMaim = function(self, t) return self:combatTalentPhysicalDamage(t, 5, 30) 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 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 + +-- start the grapple; this will automatically hit and reapply the grapple if we're already grappling the target +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, self:combatAttack(), target:combatDefense())) +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, self:combatAttack(), target:combatDefense())) +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 damage by %d and global speed by 30%% for %d turns. +The grapple effects will be based off your grapple talent, if you have it, and the damage will scale with your Physical Power.]]) +:format(damDesc(self, DamageType.PHYSICAL, (damage)), maim, duration) +end, +} diff --git a/game/modules/tome/data/talents/techniques/finishing-moves.lua b/game/modules/tome/data/talents/techniques/finishing-moves.lua index f1e1d104796f533e3d8c2e327cd686a5a56d3c1e..6d4995058b1560a97c86a6426f62d1f3e9353cf6 100644 --- a/game/modules/tome/data/talents/techniques/finishing-moves.lua +++ b/game/modules/tome/data/talents/techniques/finishing-moves.lua @@ -101,7 +101,8 @@ newTalent{ if hit then local tg = {type="ball", range=1, radius=self:getTalentRadius(t), selffire=false, talent=t} - local damage = t.getAreaDamage(self, t) * (0.25 + (self:getCombo(combo) /5)) + local damage = self:physicalCrit(t.getAreaDamage(self, t) * (0.25 + (self:getCombo(combo) /5)), nil, target, self:combatAttack(), target:combatDefense()) + --local damage = self:physicalCrit(t.getAreaDamage(self, t) * (0.25 + (self:getCombo(combo) /5))) self:project(tg, x, y, DamageType.PHYSICAL, damage) game.level.map:particleEmitter(x, y, tg.radius, "ball_earth", {radius=tg.radius}) game:playSoundNear(self, "talents/breath") diff --git a/game/modules/tome/data/talents/techniques/grappling.lua b/game/modules/tome/data/talents/techniques/grappling.lua index dae486f58cfa4117feafc87b0e0dda0b6b053faf..b82f8719e6c1424bfad56ec932e7903ff6aafa12 100644 --- a/game/modules/tome/data/talents/techniques/grappling.lua +++ b/game/modules/tome/data/talents/techniques/grappling.lua @@ -16,7 +16,7 @@ -- -- Nicolas Casalini "DarkGod" -- darkgod@te4.org - +-- Obsolete but undeleted incase something uses it newTalent{ name = "Grappling Stance", type = {"technique/unarmed-other", 1}, @@ -62,20 +62,11 @@ newTalent{ stamina = 5, tactical = { ATTACK = 2, DISABLE = 2 }, requires_target = true, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 5, 8)) end, - getPower = function(self, t) return self:combatTalentPhysicalDamage(t, 5, 25) end, - getDrain = function(self, t) return 6 - math.max(1, 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, nil, {no_unlearn=true}) - end - end, - on_unlearn = function(self, t) - if not self:knowTalent(t) then - self:unlearnTalent(self.T_GRAPPLING_STANCE) - end - end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, + getPower = function(self, t) return self:combatTalentPhysicalDamage(t, 20, 60) end, + getDrain = function(self, t) return 6 end, + getSharePct = function(self, t) return math.min(0.35, self:combatTalentScale(t, 0.05, 0.25)) end, + getDamage = function(self, t) return 1 end, action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} @@ -85,11 +76,6 @@ newTalent{ 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 @@ -103,196 +89,219 @@ newTalent{ end -- start the grapple; this will automatically hit and reapply the grapple if we're already grappling the target - local hit = self:startGrapple(target) - - local duration = t.getDuration(self, t) - - -- do crushing hold or strangle if we're already grappling the target - if hit and self:knowTalent(self.T_CRUSHING_HOLD) then - local t = self:getTalentFromId(self.T_CRUSHING_HOLD) - if grappled and not target:attr("no_breath") and not target:attr("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 - + local hit self:attackTarget(target, nil, t.getDamage(self, t), true) + local hit2 = self:startGrapple(target) + 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 a target up to one size category larger then yourself for %d turns. A grappled opponent will be unable to move, and its Accuracy 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 Accuracy and Defense reduction, as well as the grapple success chance, will scale with your Physical Power. - Performing this action will switch your stance to Grappling Stance.]]) - :format(duration, power, drain) + local share = t.getSharePct(self, t)*100 + local damage = t.getDamage(self, t)*100 + return ([[Make a melee attack for %d%% damage and then attempt to grapple a target up to one size category larger then yourself for %d turns. A grappled opponent will be unable to move, take %d damage each turn, and %d%% of the damage you receive from any source will be redirected to them. 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.]]) + :format(damage, duration, power, share, drain) end, } +-- I tried to keep this relatively consistent with the existing Grappling code structure, but it wound up pretty awkward as a result newTalent{ - name = "Maim", + name = "Crushing Hold", type = {"technique/grappling", 2}, require = techs_req2, + mode = "passive", points = 5, - random_ego = "attack", - cooldown = 12, - stamina = 10, - tactical = { ATTACK = { PHYSICAL = 2 }, DISABLE = 2 }, + tactical = { ATTACK = { PHYSICAL = 2 }, DISABLE = { silence = 2 } }, requires_target = true, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, - getDamage = function(self, t) return self:combatTalentPhysicalDamage(t, 10, 100) * getUnarmedTrainingBonus(self) end, - getMaim = function(self, t) return self:combatTalentPhysicalDamage(t, 5, 30) 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 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 + getDamage = function(self, t) return self:combatTalentPhysicalDamage(t, 5, 50) * getUnarmedTrainingBonus(self) end, -- this function shouldn't be used any more but I left it in to be safe, Clinch now handles the damage + getSlow = function(self, t) + if self:getTalentLevel(self.T_CRUSHING_HOLD) >= 5 then + return self:combatTalentPhysicalDamage(t, 0.05, 0.65) else - self:breakGrapples() + return 0 end - - -- end the talent without effect if the target is to big - if self:grappleSizeCheck(target) then - return true - end - - -- start the grapple; this will automatically hit and reapply the grapple if we're already grappling the target - 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, self:combatAttack(), target:combatDefense())) - 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, self:combatAttack(), target:combatDefense())) - end + end, + getDamageReduction = function(self, t) + return self:combatTalentPhysicalDamage(t, 1, 15) + end, + getSilence = function(self, t) -- this is a silence without an immunity check by design, if concerned about NPC use this is the talent to block + if self:getTalentLevel(self.T_CRUSHING_HOLD) >= 3 then + return 1 + else + return 0 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 damage by %d and global speed by 30%% for %d turns. - The grapple effects will be based off your grapple talent, if you have it, and the damage will scale with your Physical Power.]]) - :format(damDesc(self, DamageType.PHYSICAL, (damage)), maim, duration) + getBonusEffects = function(self, t) -- used by startGrapple in Combat.lua, essentially merges these properties and the Clinch bonuses + return {silence = t.getSilence(self, t), slow = t.getSlow(self, t), reduce = t.getDamageReduction(self, t)} end, -} - -newTalent{ - name = "Crushing Hold", - type = {"technique/grappling", 3}, - require = techs_req3, - mode = "passive", - points = 5, - tactical = { ATTACK = { PHYSICAL = 2 }, DISABLE = { silence = 2 } }, - requires_target = true, - getDamage = function(self, t) return self:combatTalentPhysicalDamage(t, 5, 50) * getUnarmedTrainingBonus(self) 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 stranglehold, silencing the target and inflicting %0.2f physical damage each turn. - Undead, targets immune to silence, and creatures that do not breathe are immune to the strangle effect, and will only be affected by the crushing hold. - The damage will scale with your Physical Power.]]) - :format(damDesc(self, DamageType.PHYSICAL, (damage)), damDesc(self, DamageType.PHYSICAL, (damage * 1.5))) + local reduction = t.getDamageReduction(self, t) + local slow = t.getSlow(self, t) + + return ([[Enhances your grapples with additional effects. All additional effects will apply to every grapple with no additional save or resist check. + #RED#Talent Level 1: Reduces base weapon damage by %d + Talent Level 3: Silences + Talent Level 5: Reduces global action speed by %d%%]]) + :format(reduction, slow*100) end, } newTalent{ name = "Take Down", - type = {"technique/grappling", 4}, - require = techs_req4, + type = {"technique/grappling", 3}, + require = techs_req3, points = 5, random_ego = "attack", - cooldown = 24, - stamina = 12, - tactical = { ATTACK = { PHYSICAL = 1, stun = 1}, DISABLE = { stun = 2 }, CLOSEIN = 2 }, + cooldown = 10, + stamina = 15, + tactical = { ATTACK = { PHYSICAL = 1}, CLOSEIN = 2 }, requires_target = true, range = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.7)) end, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, getTakeDown = function(self, t) return self:combatTalentPhysicalDamage(t, 10, 100) * getUnarmedTrainingBonus(self) end, - getSlam = function(self, t) return self:combatTalentPhysicalDamage(t, 15, 150) * getUnarmedTrainingBonus(self) end, - -- Learn the appropriate stance + getSlam = function(self, t) return self:combatTalentPhysicalDamage(t, 10, 400) * getUnarmedTrainingBonus(self) end, + getDamage = function(self, t) + return self:combatTalentWeaponDamage(t, .1, 1) + 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 core.fov.distance(self.x, self.y, x, y) > self:getTalentRange(t) then return nil end - local grappled = false + -- if the target is grappled then do an attack+AoE project + if self:hasEffect(self.EFF_GRAPPLING) then + local target = self:hasEffect(self.EFF_GRAPPLING)["trgt"] + local tg = {type="ball", range=1, radius=5, selffire=false} - -- do the rush - local block_actor = function(_, bx, by) return game.level.map:checkEntity(bx, by, Map.TERRAIN, "block_move", self) end - local l = self:lineFOV(x, y, block_actor) - local lx, ly, is_corner_blocked = l:step() - local tx, ty = self.x, self.y - while lx and ly do - if is_corner_blocked or game.level.map:checkAllEntities(lx, ly, "block_move", self) then break end - tx, ty = lx, ly - lx, ly, is_corner_blocked = l:step() - end + local hit = self:attackTarget(target, nil, t.getDamage(self, t), true) + local slam = self:physicalCrit(t.getSlam(self, t), nil, target, self:combatAttack(), target:combatDefense()) + self:project(tg, self.x, self.y, DamageType.PHYSICAL, slam, {type="bones"}) + + self:breakGrapples() + + return true + else + 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 core.fov.distance(self.x, self.y, x, y) > self:getTalentRange(t) then return nil 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 + local grappled = false - -- breaks active grapples if the target is not grappled - if target:isGrappled(self) then - grappled = true - else - self:breakGrapples() - end + -- do the rush + local block_actor = function(_, bx, by) return game.level.map:checkEntity(bx, by, Map.TERRAIN, "block_move", self) end + local l = self:lineFOV(x, y, block_actor) + local lx, ly, is_corner_blocked = l:step() + local tx, ty = self.x, self.y + while lx and ly do + if is_corner_blocked or game.level.map:checkAllEntities(lx, ly, "block_move", self) then break end + tx, ty = lx, ly + lx, ly, is_corner_blocked = l:step() + end - if 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 + 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 - -- start the grapple; this will automatically hit and reapply the grapple if we're already grappling the target - 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, self:combatAttack(), target:combatDefense())) - if target:canBe("stun") then - target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {apply_power=self:combatPhysicalpower()}) - 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, self:combatAttack(), target:combatDefense())) - if target:canBe("stun") then - target:setEffect(target.EFF_DAZED, t.getDuration(self, t), {apply_power=self:combatPhysicalpower()}) - else - game.logSeen(target, "%s resists the daze!", target.name:capitalize()) - end + -- breaks active grapples if the target is not grappled + if target:isGrappled(self) then + grappled = true + else + self:breakGrapples() + end + + if 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 + + -- start the grapple; this will automatically hit and reapply the grapple if we're already grappling the target + local hit = self:attackTarget(target, nil, t.getDamage(self, t), true) + local hit2 = self:startGrapple (target) + end - end - return true + return true + end end, info = function(self, t) - local duration = t.getDuration(self, t) - local takedown = t.getTakeDown(self, t) + local takedown = t.getDamage(self, t)*100 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. + return ([[Rushes forward and attempts to take the target to the ground, making a melee attack for %d%% damage then attempting to grapple them. If you're already grappling the target you'll instead slam them into the ground creating a radius 5 shockwave for %d physical damage and breaking your grapple. The grapple effects and duration will be based off your grapple talent, if you have it, and the damage will scale with your Physical Power.]]) - :format(damDesc(self, DamageType.PHYSICAL, (takedown)), duration, damDesc(self, DamageType.PHYSICAL, (slam)), duration) + :format(damDesc(self, DamageType.PHYSICAL, (takedown)), damDesc(self, DamageType.PHYSICAL, (slam))) end, } +newTalent{ + name = "Hurricane Throw", + type = {"technique/grappling", 4}, + require = techs_str_req4, + points = 5, + random_ego = "attack", + requires_target = true, + cooldown = function(self, t) + return 8 + end, + stamina = 20, + range = function(self, t) + return 8 + end, + radius = function(self, t) + return 1 + end, + getDamage = function(self, t) + return self:combatTalentWeaponDamage(t, 1, 3.5) -- no interaction with Striking Stance so we make the base damage higher to compensate + end, + target = function(self, t) + return {type="ball", range=self:getTalentRange(t), selffire=false, radius=self:getTalentRadius(t)} + end, + action = function(self, t) + + if self:hasEffect(self.EFF_GRAPPLING) then + local grappled = self:hasEffect(self.EFF_GRAPPLING)["trgt"] + + local tg = self:getTalentTarget(t) + local x, y, target = self:getTarget(tg) + if not x or not y then return nil end + local _ _, x, y = self:canProject(tg, x, y) + + -- if the target square is an actor, find a free grid around it instead + if game.level.map(x, y, Map.ACTOR) then + x, y = util.findFreeGrid(x, y, 1, true, {[Map.ACTOR]=true}) + if not x then return end + end + + if game.level.map:checkAllEntities(x, y, "block_move") then return end + + local ox, oy = grappled.x, grappled.y + grappled:move(x, y, true) + if config.settings.tome.smooth_move > 0 then + grappled:resetMoveAnim() + grappled:setMoveAnim(ox, oy, 8, 5) + end + + -- pick all targets around the landing point and do a melee attack + self:project(tg, grappled.x, grappled.y, function(px, py, tg, self) + local target = game.level.map(px, py, Map.ACTOR) + if target and target ~= self then + + local hit = self:attackTarget(target, nil, t.getDamage(self, t), true) + self:breakGrapples() + end + end) + return true + + else + -- only usable if you have something Grappled + return false + end + end, + info = function(self, t) + return ([[In a mighty show of strength you whirl your grappled victim around and throw them into the air causing %d%% damage to them and any nearby enemies they collide with on landing.]]):format(t.getDamage(self, t)*100) + end, +} diff --git a/game/modules/tome/data/talents/techniques/techniques.lua b/game/modules/tome/data/talents/techniques/techniques.lua index 87619c76d77791d3db0f5753db1bbf5aeb9fb611..0094ad6d818b1f0e637c5fba3cb5eb7fae11c8b6 100644 --- a/game/modules/tome/data/talents/techniques/techniques.lua +++ b/game/modules/tome/data/talents/techniques/techniques.lua @@ -47,7 +47,7 @@ newTalentType{ is_unarmed=true, allow_random=true, type="technique/pugilism", na newTalentType{ is_unarmed=true, allow_random=true, type="technique/finishing-moves", name = "finishing moves", description = "Finishing moves that use combo points and may not be practiced in massive armor or while a weapon or shield is equipped." } newTalentType{ is_unarmed=true, allow_random=true, type="technique/grappling", name = "grappling", description = "Grappling techniques that may not be practiced in massive armor or while a weapon or shield is equipped." } newTalentType{ is_unarmed=true, allow_random=true, type="technique/unarmed-discipline", name = "unarmed discipline", description = "Advanced unarmed techniques including kicks and throw that may not be practiced in massive armor or while a weapon or shield is equipped." } -newTalentType{ is_unarmed=true, allow_random=true, type="technique/unarmed-training", name = "unarmed training", description = "Teaches various martial arts techniques that may not be practiced in massive armor or while a weapon or shield is equipped." } +newTalentType{ is_unarmed=true, allow_random=true, generic = true, type="technique/unarmed-training", name = "unarmed training", description = "Teaches various martial arts techniques that may not be practiced in massive armor or while a weapon or shield is equipped." } 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." } diff --git a/game/modules/tome/data/timed_effects/physical.lua b/game/modules/tome/data/timed_effects/physical.lua index b446f1eab835e856e60341faf0f5a438ae393b19..020e838ae74c42d72dab5b1e41df3dc09bc3c590 100644 --- a/game/modules/tome/data/timed_effects/physical.lua +++ b/game/modules/tome/data/timed_effects/physical.lua @@ -43,6 +43,8 @@ newEffect{ end, } + + newEffect{ name = "CUT", image = "effects/cut.png", desc = "Bleeding", @@ -1084,54 +1086,67 @@ newEffect{ newEffect{ name = "GRAPPLING", image = "talents/clinch.png", 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, + long_desc = function(self, eff) return ("Engaged in a grapple draining %d stamina per turn and redirecting %d%% of damage taken to %s. Any movement will break the effect as will some unarmed talents."):format(eff.drain, eff.sharePct*100, eff.trgt.name or "unknown") end, type = "physical", subtype = { grapple=true, }, status = "beneficial", - parameters = {}, + parameters = {trgt, sharePct = 0.1, drain = 0}, 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) local p = eff.trgt:hasEffect(eff.trgt.EFF_GRAPPLED) - local drain = 6 - (self:getTalentLevelRaw(self.T_CLINCH) or 0) if not p or p.src ~= self or core.fov.distance(self.x, self.y, eff.trgt.x, eff.trgt.y) > 1 or eff.trgt.dead or not game.level:hasEntity(eff.trgt) then self:removeEffect(self.EFF_GRAPPLING) else - self:incStamina(-drain) + self:incStamina(-eff.drain) end end, activate = function(self, eff) end, deactivate = function(self, eff) end, + callbackOnHit = function(self, eff, cb, src) + if not src then return cb.value end + local share = cb.value * eff.sharePct + + -- deal the redirected damage as physical because I don't know how to preserve the damage type in a callback + DamageType:get(DamageType.PHYSICAL).projector(self or eff.src, eff.trgt.x, eff.trgt.y, DamageType.PHYSICAL, share) + + return cb.value - share + end, } newEffect{ name = "GRAPPLED", image = "talents/grab.png", desc = "Grappled", - long_desc = function(self, eff) return ("The target is grappled, unable to move, and has its defense and attack reduced by %d."):format(eff.power) end, + long_desc = function(self, eff) return ("The target is grappled, unable to move, and limited in its offensive capabilities.\n#RED#Silenced\nPinned\n%s\n%s\n%s"):format("Damage reduced by " .. eff.reduce, "Slowed by " .. eff.slow, "Damage per turn " .. math.ceil(eff.power) ) end, type = "physical", subtype = { grapple=true, pin=true }, status = "detrimental", - parameters = {}, + parameters = {silence = 0, slow = 0, reduce = 1, power = 1}, remove_on_clone = true, 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) + self:effectTemporaryValue(eff, "never_move", 1) + self:effectTemporaryValue(eff, "combat_dam", -eff.reduce) + if (eff.silence > 0) then + self:effectTemporaryValue(eff, "silence", 1) + end + if (eff.slow > 0) then + self:effectTemporaryValue(eff, "global_speed_add", -eff.slow) + end end, on_timeout = function(self, eff) if not self.x or not eff.src or not eff.src.x or core.fov.distance(self.x, self.y, eff.src.x, eff.src.y) > 1 or eff.src.dead or not game.level:hasEntity(eff.src) then self:removeEffect(self.EFF_GRAPPLED) + else + DamageType:get(DamageType.PHYSICAL).projector(eff.src or self, self.x, self.y, DamageType.PHYSICAL, eff.power) end end, deactivate = function(self, eff) - self:removeTemporaryValue("combat_atk", eff.atk) - self:removeTemporaryValue("combat_def", eff.def) - self:removeTemporaryValue("never_move", eff.tmpid) - end, + + end, } newEffect{