From 219b4d778486f03dbdf5c3cec05ca1681bf947de Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Sat, 27 Aug 2011 16:28:47 +0000 Subject: [PATCH] Temporal Devourers renamed Devourer and subtype changed to eldritch Temporal Dredge renmed Dredge (subtype still temporal) Worm that Walks and Devourers now have unique talents Devourer hit points and damage improved (beware the land sharks!!) Carrion Worms multiply less but have better resists Epidemic now checks for disease immunity when spreading diseases Dredge Frenzy improved a bit git-svn-id: http://svn.net-core.org/repos/t-engine4@4271 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/modules/tome/class/Actor.lua | 3 + game/modules/tome/data/damage_types.lua | 26 +- .../modules/tome/data/general/npcs/horror.lua | 73 +++--- .../modules/tome/data/general/npcs/vermin.lua | 11 +- ...ourer.png => horror_eldritch_devourer.png} | Bin ..._dredge.png => horror_temporal_dredge.png} | Bin .../tome/data/talents/corruptions/plague.lua | 6 +- .../tome/data/talents/misc/horrors.lua | 241 +++++++++++++++--- game/modules/tome/data/talents/misc/npcs.lua | 27 +- game/modules/tome/data/timed_effects.lua | 97 ++++++- 10 files changed, 397 insertions(+), 87 deletions(-) rename game/modules/tome/data/gfx/shockbolt/npc/{horror_temporal_temporal_devourer.png => horror_eldritch_devourer.png} (100%) rename game/modules/tome/data/gfx/shockbolt/npc/{horror_temporal_temporal_dredge.png => horror_temporal_dredge.png} (100%) diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 444a00cea5..461afdf5b8 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -143,6 +143,9 @@ function _M:init(t, no_default) t.resists = t.resists or {} t.resists_cap = t.resists_cap or { all = 100 } t.resists_pen = t.resists_pen or {} + + -- Absorbs a percentage of damage + t.damage_affinity = t.damage_affinity or {} -- % Increase damage t.inc_damage = t.inc_damage or {} diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua index a9832dda37..58f344c83f 100644 --- a/game/modules/tome/data/damage_types.lua +++ b/game/modules/tome/data/damage_types.lua @@ -133,7 +133,14 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) target:setEffect(target.EFF_SMEARED, 6, {src=src, power=smear/6}) dam = 0 end - + + -- affinity healing, we store it to apply it after damage is resolved + local affinity_heal = 0 + if target.damage_affinity then + local aff = (target.damage_affinity[type] or 0) / 100 + affinity_heal = (dam * aff) + end + -- Reduce damage with resistance if target.resists then local pen = 0 @@ -198,6 +205,12 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) target:setEffect(target.EFF_ELEMENTAL_HARMONY, 5 + math.ceil(target:attr("elemental_harmony")), {power=target:attr("elemental_harmony"), type=type}) end end + + -- damage affinity healing + if not target.dead and affinity_heal > 0 then + target:heal(affinity_heal) + game.logSeen(target, "%s is healed by the %s%s#LAST# damage!", target.name:capitalize(), DamageType:get(type).text_color or "#aaaaaa#", DamageType:get(type).name) + end if dam > 0 and src.__projecting_for and src.__projecting_for.project_type and (src.__projecting_for.project_type.talent_id or src.__projecting_for.project_type.talent) and src.getTalentFromId and not src.__projecting_for.talent_on_hit_done then local t = src:getTalentFromId(src.__projecting_for.project_type.talent or src.__projecting_for.project_type.talent_id) @@ -1546,17 +1559,6 @@ newDamageType{ end, } --- Dredge Haste -newDamageType{ - name = "dredge frenzy", type = "DREDGE_FRENZY", - projector = function(src, x, y, type, dam) - local target = game.level.map(x, y, Map.ACTOR) - if target and target.dredge then - target:setEffect(target.EFF_SPEED, 3, {power=0.6}) - end - end, -} - newDamageType{ name = "devour life", type = "DEVOUR_LIFE", projector = function(src, x, y, type, dam) diff --git a/game/modules/tome/data/general/npcs/horror.lua b/game/modules/tome/data/general/npcs/horror.lua index a4a55676a6..82c4e22e8f 100644 --- a/game/modules/tome/data/general/npcs/horror.lua +++ b/game/modules/tome/data/general/npcs/horror.lua @@ -44,7 +44,8 @@ newEntity{ newEntity{ base = "BASE_NPC_HORROR", name = "worm that walks", color=colors.SANDY_BROWN, - desc = [[A maggot-filled robe with a vaguely humanoid shape.]], + desc = [[A bulging rotten robe seems to tear at the seams, with masses of bloated worms spilling from out and around the moving form. Two arm-like appendages, each made up of overlapping mucous-drenched maggots, grasp tightly around the handles of bile-coated waraxes. + Each swing drips pestulant fluid before it, and each droplet writhes and wriggles in the air before splashing against the ground.]], level_range = {25, nil}, exp_worth = 1, rarity = 5, max_life = resolvers.rngavg(150,170), @@ -52,8 +53,9 @@ newEntity{ base = "BASE_NPC_HORROR", rank = 3, hate_regen = 1, vim_regen = 1, - - ai = "tactical", ai_state = { ai_move="move_dmap", talent_in=1, }, + + autolevel = "warriormage", + ai = "tactical", ai_state = { ai_move="move_dmap", talent_in=1, ally_compassion=0 }, ai_tactic = resolvers.tactic "melee", see_invisible = 100, @@ -61,10 +63,11 @@ newEntity{ base = "BASE_NPC_HORROR", stun_immune = 1, blind_immune = 1, disease_immune = 1, - move_others=true, - - resists = { [DamageType.PHYSICAL] = 50, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50}, + resists = { [DamageType.PHYSICAL] = 50, [DamageType.ACID] = 100, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50}, + damage_affinity = { [DamageType.BLIGHT] = 50 }, + no_auto_resists = true, + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, resolvers.drops{chance=20, nb=1, {} }, resolvers.equip{ @@ -78,15 +81,15 @@ newEntity{ base = "BASE_NPC_HORROR", resolvers.inscriptions(1, {"regeneration infusion"}), resolvers.talents{ - --[Talents.T_BONE_GRAB]={base=4, every=10}, - [Talents.T_BLINDSIDE]={base=5, every=12}, + [Talents.T_WORM_ROT]={base=4, every=8}, + [Talents.T_EPIDEMIC]={base=4, every=8}, + [Talents.T_REND]={base=4, every=8}, + [Talents.T_ACID_STRIKE]={base=4, every=8}, + [Talents.T_BLOODLUST]={base=4, every=8}, + [Talents.T_RUIN]={base=4, every=8}, [Talents.T_CORRUPTED_STRENGTH]={base=3, every=15}, - [Talents.T_SLASH]={base=4, every=12}, - [Talents.T_FRENZY]={base=4, every=15}, - [Talents.T_REND]={base=4, every=12}, - [Talents.T_BLOODLUST]={base=3, every=12}, - [Talents.T_RUIN]={base=2, every=12}, - [Talents.T_SUMMON]=1, + + [Talents.T_BLINDSIDE]={base=3, every=12}, [Talents.T_WEAPON_COMBAT]={base=5, every=10, max=10}, [Talents.T_WEAPONS_MASTERY]={base=3, every=10, max=10}, @@ -96,14 +99,12 @@ newEntity{ base = "BASE_NPC_HORROR", on_takehit = function(self, value, src) if value >= (self.max_life * 0.1) then - self:forceUseTalent(self.T_SUMMON, {ignore_energy=true, ignore_cd=true}) + local t = self:getTalentFromId(self.T_WORM_ROT) + t.spawn_carrion_worm(self, self, t) + game.logSeen(self, "#LIGHT_RED#A carrion worm mass has spawned from %s' wounds!", self.name) end return value end, - - summon = { - {type="vermin", subtype="worms", name="carrion worm mass", number=1, hasxp=false}, - }, } newEntity{ base = "BASE_NPC_HORROR", @@ -474,11 +475,9 @@ newEntity{ base = "BASE_NPC_HORROR", }, } --- Temporal Horrors - newEntity{ base = "BASE_NPC_HORROR", - subtype = "temporal", - name = "temporal devourer", color=colors.CRIMSON, + subtype = "eldritch", + name = "devourer", color=colors.CRIMSON, desc = "A headless, round creature with stubby legs and arms. Its body seems to be all teeth.", level_range = {10, nil}, exp_worth = 1, rarity = 2, @@ -486,17 +485,27 @@ newEntity{ base = "BASE_NPC_HORROR", movement_speed = 0.8, size_category = 2, autolevel = "warrior", - max_life = resolvers.rngavg(50, 80), - combat_armor = 1, combat_def = 10, - combat = { dam=resolvers.levelup(resolvers.rngavg(20,30), 1, 1.2), atk=resolvers.rngavg(10,20), apr=5, dammod={str=1} }, + max_life = resolvers.rngavg(80, 100), + life_rating = 14, + life_regen = 4, + combat_armor = 16, combat_def = 1, + combat = { dam=resolvers.levelup(resolvers.rngavg(25,40), 1, 0.6), atk=resolvers.rngavg(25,50), apr=25, dammod={str=1.1}, physcrit = 10 }, + ai_state = { talent_in=2, }, - resists = { [DamageType.TEMPORAL] = 5}, + resolvers.talents{ + [Talents.T_BLOODBATH]={base=1, every=5, max=7}, + [Talents.T_GNASHING_TEETH]={base=1, every=5, max=7}, + -- talents only usable while frenzied + [Talents.T_FRENZIED_LEAP]={base=1, every=5, max=7}, + [Talents.T_FRENZIED_BITE]={base=1, every=5, max=7}, + }, make_escort = { - {type="horror", subtype="temporal", name="temporal devourer", number=2, no_subescort=true}, + {type="horror", subtype="eldritch", name="devourer", number=2, no_subescort=true}, }, } +-- temporal horrors newEntity{ base = "BASE_NPC_HORROR", subtype = "temporal", dredge = 1, @@ -511,7 +520,7 @@ newEntity{ base = "BASE_NPC_HORROR", combat_armor = 1, combat_def = 10, combat = { dam=resolvers.levelup(resolvers.rngavg(15,20), 1, 1.1), atk=resolvers.rngavg(5,15), apr=5, dammod={str=1} }, - resists = { [DamageType.TEMPORAL] = 5}, + resists = { [DamageType.TEMPORAL] = 25}, resolvers.talents{ [Talents.T_DUST_TO_DUST]={base=1, every=7, max=5}, @@ -523,7 +532,7 @@ newEntity{ base = "BASE_NPC_HORROR", newEntity{ base = "BASE_NPC_HORROR", subtype = "temporal", dredge = 1, - name = "temporal dredge", color=colors.PINK, + name = "dredge", color=colors.PINK, desc = "A hulking pink-skinned creature with long arms as thick as tree trunks. It drags its knuckles on the ground as it lumbers toward you.", level_range = {15, nil}, exp_worth = 1, rarity = 2, @@ -536,7 +545,7 @@ newEntity{ base = "BASE_NPC_HORROR", combat_armor = 1, combat_def = 0, combat = { dam=resolvers.levelup(resolvers.rngavg(25,150), 1, 1.2), atk=resolvers.rngavg(25,130), apr=1, dammod={str=1.1} }, - resists = {all = 10, [DamageType.TEMPORAL] = 50, [DamageType.PHYSICAL] = 25}, + resists = {all = 10, [DamageType.TEMPORAL] = 25, [DamageType.PHYSICAL] = 25}, resolvers.talents{ [Talents.T_STUN]={base=3, every=7, max=7}, @@ -565,7 +574,7 @@ newEntity{ base = "BASE_NPC_HORROR", resists = {all = 10, [DamageType.TEMPORAL] = 50}, make_escort = { - {type="horror", subtype="temporal", name="temporal dredge", number=3, no_subescort=true}, + {type="horror", subtype="temporal", name="dredge", number=3, no_subescort=true}, }, resolvers.inscriptions(1, {"shielding rune"}), diff --git a/game/modules/tome/data/general/npcs/vermin.lua b/game/modules/tome/data/general/npcs/vermin.lua index d3a6c6f16f..b01681782b 100644 --- a/game/modules/tome/data/general/npcs/vermin.lua +++ b/game/modules/tome/data/general/npcs/vermin.lua @@ -66,15 +66,18 @@ newEntity{ base = "BASE_NPC_WORM", newEntity{ base = "BASE_NPC_WORM", name = "carrion worm mass", color=colors.SANDY_BROWN, + can_multiply = 2, level_range = {20, nil}, exp_worth = 1, rarity = 4, max_life = resolvers.rngavg(5,9), combat = { dam=1, atk=15, apr=100 }, - - resists = {[DamageType.BLIGHT] = 100}, + + disease_immune = 1, + resists = { [DamageType.PHYSICAL] = 50, [DamageType.ACID] = 100, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50}, + no_auto_resists = true, autolevel = "warriormage", - resolvers.talents{ [Talents.T_CRAWL_POISON]=2, [Talents.T_ROTTING_DISEASE]=4, [Talents.T_MULTIPLY]=1 }, + resolvers.talents{ [Talents.T_CRAWL_ACID]=2, [Talents.T_ROTTING_DISEASE]=4, [Talents.T_MULTIPLY]=1 }, on_die = function(self, src) game.level.map:addEffect(self, @@ -82,7 +85,7 @@ newEntity{ base = "BASE_NPC_WORM", engine.DamageType.BLIGHT, self:getStr(90, true), 2, 5, nil, - engine.Entity.new{alpha=100, display='', color_br=30, color_bg=180, color_bb=60} + engine.Entity.new{alpha=50, display='', color_br=30, color_bg=180, color_bb=60} ) game.logSeen(self, "%s exudes a corrupted gas as it dies.", self.name:capitalize()) end, diff --git a/game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_devourer.png b/game/modules/tome/data/gfx/shockbolt/npc/horror_eldritch_devourer.png similarity index 100% rename from game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_devourer.png rename to game/modules/tome/data/gfx/shockbolt/npc/horror_eldritch_devourer.png diff --git a/game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_dredge.png b/game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_dredge.png similarity index 100% rename from game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_dredge.png rename to game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_dredge.png diff --git a/game/modules/tome/data/talents/corruptions/plague.lua b/game/modules/tome/data/talents/corruptions/plague.lua index 0ef613a0d1..d8fcb47202 100644 --- a/game/modules/tome/data/talents/corruptions/plague.lua +++ b/game/modules/tome/data/talents/corruptions/plague.lua @@ -212,7 +212,11 @@ newTalent{ local disease = rng.table(diseases) local params = disease.params params.src = self - target:setEffect(disease.id, 6, {src=self, dam=disease.params.dam, str=disease.params.str, dex=disease.params.dex, con=disease.params.con, heal_factor=disease.params.heal_factor}) + if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 12 - self:getTalentLevel(t)) and target:canBe("disease") then + target:setEffect(disease.id, 6, {src=self, dam=disease.params.dam, str=disease.params.str, dex=disease.params.dex, con=disease.params.con, heal_factor=disease.params.heal_factor, burst=disease.params.burst}) + else + game.logSeen(target, "%s resists the disease!", target.name:capitalize()) + end game.level.map:particleEmitter(px, py, 1, "slime") end) end, diff --git a/game/modules/tome/data/talents/misc/horrors.lua b/game/modules/tome/data/talents/misc/horrors.lua index 313a571f07..625a6cb146 100644 --- a/game/modules/tome/data/talents/misc/horrors.lua +++ b/game/modules/tome/data/talents/misc/horrors.lua @@ -19,11 +19,139 @@ newTalentType{ type="technique/horror", name = "horror techniques", hide = true, description = "Physical talents of the various horrors of the world." } newTalentType{ no_silence=true, is_spell=true, type="spell/horror", name = "horror spells", hide = true, description = "Spell talents of the various horrors of the world." } +newTalentType{ no_silence=true, is_spell=true, type="corruption/horror", name = "horror spells", hide = true, description = "Spell talents of the various horrors of the world." } newTalentType{ type="other/horror", name = "horror powers", hide = true, description = "Unclassified talents of the various horrors of the world." } local oldTalent = newTalent local newTalent = function(t) if type(t.hide) == "nil" then t.hide = true end return oldTalent(t) end +-- Bloated Horror Powers +-- Ideas; Mind Shriek (confusion, plus mental damge each turn), Hallucination (Clones that die on hit if the target makes a mental save) +-- Devourer Powers +newTalent{ + name = "Frenzied Bite", + type = {"technique/horror", 3}, + points = 5, + cooldown = 12, + stamina = 24, + tactical = { ATTACK = 1, DISABLE = 2 }, + message = "In a frenzy @Source@ bites at @Target@!", + on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_FRENZY) then return false end return true 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 = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1, 1.7), true) + if hit then + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5) and target:canBe("cut") then + target:setEffect(target.EFF_DEEP_WOUND, 5, {src=self, heal_factor=self:getTalentLevel(t) * 10, power=self:combatTalentWeaponDamage(t, 1.5, 3)/5}) + end + end + return true + end, + info = function(self, t) + return ([[A nasty bite that causes a deep wound, only usable in a frenzy.]]) + end, +} + +newTalent{ + name = "Frenzied Leap", -- modified ghoulish leap, only usable while in a frenzy + type = {"technique/horror", 1}, + points = 5, + cooldown = 5, + tactical = { CLOSEIN = 3 }, + direct_hit = true, + message = "@Source@ leaps forward in a frenzy!", + range = function(self, t) return math.floor(2 + self:getTalentLevel(t)) end, + requires_target = true, + on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_FRENZY) or self:attr("encased_in_ice") or self:attr("never_move") then return false end return true 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 then return nil end + if math.floor(core.fov.distance(self.x, self.y, x, y)) > self:getTalentRange(t) then return nil end + + local l = line.new(self.x, self.y, x, y) + local lx, ly = l() + local tx, ty = lx, ly + lx, ly = l() + while lx and ly do + if game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move", self) then break end + tx, ty = lx, ly + lx, ly = l() + end + + -- Find space + if game.level.map:checkEntity(tx, ty, Map.TERRAIN, "block_move", self) then return nil end + local fx, fy = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true}) + if not fx then + return + end + self:move(fx, fy, true) + + return true + end, + info = function(self, t) + return ([[Leap toward your target.]]) + end, +} + +newTalent{ + name = "Gnashing Teeth", + type = {"technique/horror", 1}, + points = 5, + cooldown = 3, + stamina = 8, + message = "@Source@ tries to bite @Target@ with razor sharp teeth!", + requires_target = true, + tactical = { ATTACK = 2 }, + do_devourer_frenzy = function(self, target, t) + game.logSeen(self, "The scent of blood sends the %ss into a frenzy!", self.name:capitalize()) + -- frenzy devourerers + local tg = {type="ball", range=0, radius=3, selffire=true, talent=t} + self:project(tg, target.x, target.y, function(px, py) + local target = game.level.map(px, py, engine.Map.ACTOR) + local reapplied = false + if target then + local actor_frenzy = false + if target.name == "devourer" then + actor_frenzy = true + end + if actor_frenzy then + -- silence the apply message if the target already has the effect + for eff_id, p in pairs(target.tmp) do + local e = target.tempeffect_def[eff_id] + if e.name == "Frenzy" then + reapplied = true + end + end + target:setEffect(target.EFF_FRENZY, math.floor(2 + self:getTalentLevel(t)), {crit = self:getTalentLevel(t) * 3, power=self:getTalentLevel(t) * 0.2, dieat=self:getTalentLevel(t) * 0.2}, reapplied) + end + end + 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 hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true) + + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 8 - self:getTalentLevel(t) / 2) and target:canBe("cut") then + target:setEffect(target.EFF_CUT, 5, {power=self:combatTalentWeaponDamage(t, 0.8, 1.5), src=self}) + t.do_devourer_frenzy(self, target, t) + else + game.logSeen(target, "%s resists the cut!", target.name:capitalize()) + end + + return true + end, + info = function(self, t) + return ([[Bites the target, potentially causing it to bleed. The blood will send the devourer into a frenzy (which in turn will frenzy nearby devourers).]]) + end, +} -- Nightmare Horror Powers newTalent{ @@ -45,7 +173,7 @@ newTalent{ if not x then return end - + local m = target:clone{ shader = "shadow_simulacrum", no_drops = true, @@ -63,7 +191,7 @@ newTalent{ mod.class.NPC.castAs(m) engine.interface.ActorAI.init(m, m) - + m.energy.value = 0 m.player = nil m.max_life = m.max_life / 2 @@ -75,7 +203,7 @@ newTalent{ m.seen_by = nil m.can_talk = nil m.clone_on_hit = nil - + -- Remove some talents local tids = {} for tid, _ in pairs(m.talents) do @@ -86,10 +214,10 @@ newTalent{ if t.mode == "sustained" and m:isTalentActive(t.id) then m:forceUseTalent(t.id, {ignore_energy=true}) end m.talents[t.id] = nil end - + -- nil the Inner Demons effect to squelch combat log spam m.tmp[m.EFF_INNER_DEMONS] = nil - + -- remove detrimental timed effects local effs = {} for eff_id, p in pairs(m.tmp) do @@ -98,7 +226,7 @@ newTalent{ effs[#effs+1] = {"effect", eff_id} end end - + while #effs > 0 do local eff = rng.tableRemove(effs) if eff[1] == "effect" then @@ -106,12 +234,12 @@ newTalent{ end end - + game.zone:addEntity(game.level, m, "actor", x, y) game.level.map:particleEmitter(x, y, 1, "shadow") game.logSeen(target, "%s's Inner Demon manifests!", target.name:capitalize()) - + end, action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t), talent=t} @@ -121,13 +249,13 @@ newTalent{ if not x or not y then return nil end local target = game.level.map(x, y, Map.ACTOR) if not target then return nil end - + if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) and target:canBe("fear") then target:setEffect(target.EFF_INNER_DEMONS, t.getDuration(self, t), {src = self, chance=t.getChance(self, t), dam=t.getDamage(self, t)}) else game.logSeen(target, "%s resists the demons!", target.name:capitalize()) end - + return true end, info = function(self, t) @@ -156,13 +284,13 @@ newTalent{ if not x or not y then return nil end local target = game.level.map(x, y, Map.ACTOR) if not target then return nil end - + if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) and target:canBe("fear") then target:setEffect(target.EFF_WAKING_NIGHTMARE, t.getDuration(self, t), {src = self, chance=t.getChance(self, t), dam=t.getDamage(self, t)}) else game.logSeen(target, "%s resists the nightmare!", target.name:capitalize()) end - + return true end, info = function(self, t) @@ -211,7 +339,8 @@ newTalent{ return ([[An area of effect that causes darkness damage and reduces the lite radius and darkness resistance of those within.]]) end, } - +-- Temporal Stalker Powers +-- Ideas; Damage Shift >:) Give more temporal effects, especially Warden effects, raise AP and add Temporal Damage on hit to mimic weapon folding, give invis rune -- Void Horror Powers newTalent{ name = "Echoes From The Void", @@ -233,13 +362,13 @@ newTalent{ if not x or not y then return nil end local target = game.level.map(x, y, Map.ACTOR) if not target then return nil end - - if target:checkHit(self:combatMindpower(), target:combatMentalResist(), 0, 95, 15) and target:canBe("fear") then - target:setEffect(target.EFF_VOID_ECHOES, 6, {src=self, power=t.getDamage(self, t)}) + + if target:checkHit(src:combatMindpower(), target:combatMentalResist(), 0, 95, 15) and target:canBe("fear") then + target:setEffect(target.EFF_VOID_ECHOES, 6, {src= self, power=t.getDamage(self, t)}) else game.logSeen(target, "%s resists the void!", target.name:capitalize()) end - + game:playSoundNear(self, "talents/arcane") return true end, @@ -269,9 +398,9 @@ newTalent{ local _ _, tx, ty = self:canProject(tg, tx, ty) target = game.level.map(tx, ty, Map.ACTOR) if target == self then target = nil end - + if self:getTalentLevel(t) < 5 then self:setEffect(self.EFF_SUMMON_DESTABILIZATION, 500, {power=5}) end - + for i = 1, self:getTalentLevelRaw(t) do -- Find space local x, y = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true}) @@ -287,41 +416,41 @@ newTalent{ name = "void shard", faction = self.faction, desc = [[It looks like a small hole in the fabric of spacetime.]], stats = { str=22, dex=20, wil=15, con=15 }, - - --level_range = {self.level, self.level}, + + --level_range = {self.level, self.level}, exp_worth = 0, max_life = resolvers.rngavg(5,10), life_rating = 2, rank = 2, size_category = 1, - + autolevel = "summoner", - ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_snake" }, + ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_snake" }, combat_armor = 1, combat_def = 1, combat = { dam=resolvers.levelup(resolvers.mbonus(40, 15), 1, 1.2), atk=15, apr=15, dammod={wil=0.8}, damtype=DamageType.TEMPORAL }, on_melee_hit = { [DamageType.TEMPORAL] = resolvers.mbonus(20, 10), }, - + infravision = 10, no_breath = 1, fear_immune = 1, stun_immune = 1, confusion_immune = 1, silence_immune = 1, - + ai_target = {actor=target} } - + m.faction = self.faction - m.summoner = self + m.summoner = self m.summoner_gain_exp=true m.summon_time = t.getSummonTime(self, t) - + m:resolve() m:resolve(nil, true) m:forceLevelup(self.level) game.zone:addEntity(game.level, m, "actor", x, y) game.level.map:particleEmitter(x, y, 1, "summon") m:setEffect(m.EFF_TEMPORAL_DESTABILIZATION, 5, {src=self, dam=t.getDamage(self, t), explosion=self:spellCrit(t.getExplosion(self, t))}) - + end game:playSoundNear(self, "talents/spell_generic") return true @@ -329,4 +458,56 @@ newTalent{ info = function(self, t) return ([[Summons shards of explosive doom!]]) end, -} \ No newline at end of file +} + +-- Worm that Walks Powers +newTalent{ + name = "Worm Rot", + type = {"corruption/horror", 1}, + points = 5, + cooldown = 8, + vim = 10, + range = 6, + requires_target = true, + tactical = { ATTACK = 2, DISABLE = 4 }, + getBurstDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 5, 50) end, + getHealReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 70) end, + getDuration = function(self, t) return 4 + math.ceil(self:getTalentLevel(t)) end, + proj_speed = 6, + spawn_carrion_worm = function (self, target, t) + local x, y = util.findFreeGrid(target.x, target.y, 10, true, {[Map.ACTOR]=true}) + if not x then + return + end + + local worm = {type="vermin", subtype="worms", name="carrion worm mass", number=1, hasxp=false} + local m = game.zone:makeEntity(game.level, "actor", worm, nil, true) + m:resolve() + m.faction = self.faction + + game.zone:addEntity(game.level, m, "actor", x, y) + end, + action = function(self, t) + local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="bolt_slime"}} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + + self:project(tg, x, y, function(px, py) + local target = game.level.map(px, py, engine.Map.ACTOR) + if not target then return end + if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 12 - self:getTalentLevel(t)) and target:canBe("disease") then + target:setEffect(target.EFF_WORM_ROT, 5, {src=self, dam=t.getDamage(self, t), burst=t.getBurstDamage(self, t)}) + else + game.logSeen(target, "%s resists the worm rot!", target.name:capitalize()) + end + game.level.map:particleEmitter(px, py, 1, "slime") + end) + game:playSoundNear(self, "talents/slime") + + return true + end, + info = function(self, t) + return ([[A terrible rotting disease that removes a beneficial physical effect and deals acid and blight damage each turn. If not cleared after a full five turn duration it will inflict extra damage and spawn a carrion worm mass.]]) + end, +} diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua index 49f125e157..9724983766 100644 --- a/game/modules/tome/data/talents/misc/npcs.lua +++ b/game/modules/tome/data/talents/misc/npcs.lua @@ -1119,7 +1119,7 @@ newTalent{ target = function(self, t) return {type="beam", range=self:getTalentRange(t), talent=t} end, - tactical = { ATTACK = 3 }, + tactical = { ATTACK = 2, ESCAPE = 2 }, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) @@ -1418,7 +1418,7 @@ newTalent{ points = 5, cooldown = 6, tactical = { - BUFF = 10, + BUFF = 4, }, direct_hit = true, range = 0, @@ -1428,9 +1428,30 @@ newTalent{ end, action = function(self, t) local tg = self:getTalentTarget(t) - local grids = self:project(tg, self.x, self.y, DamageType.DREDGE_FRENZY) + self:project(tg, self.x, self.y, function(px, py) + local target = game.level.map(px, py, engine.Map.ACTOR) + local reapplied = false + if target then + local actor_frenzy = false + if target.dredge then + actor_frenzy = true + end + if actor_frenzy then + -- silence the apply message if the target already has the effect + for eff_id, p in pairs(target.tmp) do + local e = target.tempeffect_def[eff_id] + if e.name == "Frenzy" then + reapplied = true + end + end + target:setEffect(target.EFF_FRENZY, self:getTalentLevel(t), {crit = self:getTalentLevel(t), power=self:getTalentLevel(t) * 0.1, dieat=self:getTalentLevel(t) * 0.1}, reapplied) + end + end + end) + game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_light", {radius=tg.radius}) game:playSoundNear(self, "talents/arcane") + return true end, info = function(self, t) diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua index 05339dd7a6..aaa1a959ac 100644 --- a/game/modules/tome/data/timed_effects.lua +++ b/game/modules/tome/data/timed_effects.lua @@ -1506,7 +1506,7 @@ newEffect{ newEffect{ name = "EPIDEMIC", desc = "Epidemic", - long_desc = function(self, eff) return ("The target is infected by a disease, doing %0.2f blight damage per turn.\nEach non-disease blight damage done to it will spread the disease."):format(eff.dam) end, + long_desc = function(self, eff) return ("The target is infected by a disease, doing %0.2f blight damage per turn and reducig healing received by %d%%.\nEach non-disease blight damage done to it will spread the disease."):format(eff.dam, eff.heal_factor) end, type = "disease", status = "detrimental", parameters = {}, @@ -2744,13 +2744,10 @@ newEffect{ end end - for i = 1, 1 do - if #effs == 0 then break end + if #effs > 0 then local eff = rng.tableRemove(effs) - if eff[1] == "effect" then self:removeEffect(eff[2]) - known = true end end end, @@ -4531,4 +4528,94 @@ newEffect{ end end end, +} + +newEffect{ + name = "WORM_ROT", + desc = "Worm Rot", + long_desc = function(self, eff) return ("The target is infected with carrion worm larvae. Each turn it will lose one beneficial physical effect and %0.2f blight and acid damage will be inflicted.\nAfter five turns the disease will inflict %0.2f blight damage and spawn a carrion worm mass."):format(eff.dam, eff.burst) end, + type = "disease", + status = "detrimental", + parameters = {}, + on_gain = function(self, err) return "#Target# is afflicted by a terrible worm rot!" end, + on_lose = function(self, err) return "#Target# is free from the worm rot." end, + -- Damage each turn + on_timeout = function(self, eff) + self.worm_rot_timer = self.worm_rot_timer - 1 + + -- disease damage + if self:attr("purify_disease") then + self:heal(eff.dam) + else + DamageType:get(DamageType.BLIGHT).projector(eff.src, self.x, self.y, DamageType.BLIGHT, eff.dam, {from_disease=true}) + end + -- acid damage from the larvae + DamageType:get(DamageType.ACID).projector(eff.src, self.x, self.y, DamageType.ACID, eff.dam) + + local effs = {} + -- Go through all physical effects + for eff_id, p in pairs(self.tmp) do + local e = self.tempeffect_def[eff_id] + if e.status == "beneficial" and e.type == "physical" then + effs[#effs+1] = {"effect", eff_id} + end + end + -- remove a random physical effect + if #effs > 0 then + local eff = rng.tableRemove(effs) + if eff[1] == "effect" then + self:removeEffect(eff[2]) + end + end + + -- burst and spawn a worm mass + if self.worm_rot_timer == 0 then + DamageType:get(DamageType.BLIGHT).projector(eff.src, self.x, self.y, DamageType.BLIGHT, eff.burst, {from_disease=true}) + local t = eff.src:getTalentFromId(eff.src.T_WORM_ROT) + t.spawn_carrion_worm(eff.src, self, t) + game.logSeen(self, "#LIGHT_RED#A carrion worm mass bursts out of %s!", self.name:capitalize()) + self:removeEffect(self.EFF_WORM_ROT) + end + end, + activate = function(self, eff) + self.worm_rot_timer = 5 + end, + deactivate = function(self, eff) + self.worm_rot_timer = nil + end, +} + +newEffect{ + name = "FRENZY", + desc = "Frenzy", + long_desc = function(self, eff) return ("Increases global action speed by %d%% and physical crit by %d%%.\nAdditionally the target will continue to fight until it's hit points reach -%d%%."):format(eff.power * 100, eff.crit, eff.dieat * 100) end, + type = "physical", + status = "beneficial", + parameters = { power=0.1 }, + on_gain = function(self, err) return "#Target# goes into a killing frenzy.", "+Frenzy" end, + on_lose = function(self, err) return "#Target# calms down.", "-Frenzy" end, + on_merge = function(self, old_eff, new_eff) + -- use on merge so reapplied frenzy doesn't kill off creatures with negative life + old_eff.dur = new_eff.dur + old_eff.power = new_eff.power + old_eff.crit = new_eff.crit + return old_eff + end, + activate = function(self, eff) + eff.tmpid = self:addTemporaryValue("global_speed", eff.power) + eff.critid = self:addTemporaryValue("combat_physcrit", eff.crit) + eff.dieatid = self:addTemporaryValue("die_at", -self.max_life * eff.dieat) + end, + deactivate = function(self, eff) + -- check negative life first incase the creature has healing + if self.life <= 0 then + local sx, sy = game.level.map:getTileToScreen(self.x, self.y) + game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, rng.float(-2.5, -1.5), "Falls dead!", {255,0,255}) + game.logSeen(self, "%s dies when it's frenzy ends!", self.name:capitalize()) + self:die(self) + end + self:removeTemporaryValue("global_speed", eff.tmpid) + self:removeTemporaryValue("combat_physcrit", eff.critid) + self:removeTemporaryValue("die_at", eff.dieatid) + end, } \ No newline at end of file -- GitLab