Commit 219b4d778486f03dbdf5c3cec05ca1681bf947de

Authored by dg
1 parent c1037087

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
... ... @@ -143,6 +143,9 @@ function _M:init(t, no_default)
143 143 t.resists = t.resists or {}
144 144 t.resists_cap = t.resists_cap or { all = 100 }
145 145 t.resists_pen = t.resists_pen or {}
  146 +
  147 + -- Absorbs a percentage of damage
  148 + t.damage_affinity = t.damage_affinity or {}
146 149
147 150 -- % Increase damage
148 151 t.inc_damage = t.inc_damage or {}
... ...
... ... @@ -133,7 +133,14 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
133 133 target:setEffect(target.EFF_SMEARED, 6, {src=src, power=smear/6})
134 134 dam = 0
135 135 end
136   -
  136 +
  137 + -- affinity healing, we store it to apply it after damage is resolved
  138 + local affinity_heal = 0
  139 + if target.damage_affinity then
  140 + local aff = (target.damage_affinity[type] or 0) / 100
  141 + affinity_heal = (dam * aff)
  142 + end
  143 +
137 144 -- Reduce damage with resistance
138 145 if target.resists then
139 146 local pen = 0
... ... @@ -198,6 +205,12 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
198 205 target:setEffect(target.EFF_ELEMENTAL_HARMONY, 5 + math.ceil(target:attr("elemental_harmony")), {power=target:attr("elemental_harmony"), type=type})
199 206 end
200 207 end
  208 +
  209 + -- damage affinity healing
  210 + if not target.dead and affinity_heal > 0 then
  211 + target:heal(affinity_heal)
  212 + 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)
  213 + end
201 214
202 215 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
203 216 local t = src:getTalentFromId(src.__projecting_for.project_type.talent or src.__projecting_for.project_type.talent_id)
... ... @@ -1546,17 +1559,6 @@ newDamageType{
1546 1559 end,
1547 1560 }
1548 1561
1549   --- Dredge Haste
1550   -newDamageType{
1551   - name = "dredge frenzy", type = "DREDGE_FRENZY",
1552   - projector = function(src, x, y, type, dam)
1553   - local target = game.level.map(x, y, Map.ACTOR)
1554   - if target and target.dredge then
1555   - target:setEffect(target.EFF_SPEED, 3, {power=0.6})
1556   - end
1557   - end,
1558   -}
1559   -
1560 1562 newDamageType{
1561 1563 name = "devour life", type = "DEVOUR_LIFE",
1562 1564 projector = function(src, x, y, type, dam)
... ...
... ... @@ -44,7 +44,8 @@ newEntity{
44 44
45 45 newEntity{ base = "BASE_NPC_HORROR",
46 46 name = "worm that walks", color=colors.SANDY_BROWN,
47   - desc = [[A maggot-filled robe with a vaguely humanoid shape.]],
  47 + 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.
  48 + Each swing drips pestulant fluid before it, and each droplet writhes and wriggles in the air before splashing against the ground.]],
48 49 level_range = {25, nil}, exp_worth = 1,
49 50 rarity = 5,
50 51 max_life = resolvers.rngavg(150,170),
... ... @@ -52,8 +53,9 @@ newEntity{ base = "BASE_NPC_HORROR",
52 53 rank = 3,
53 54 hate_regen = 1,
54 55 vim_regen = 1,
55   -
56   - ai = "tactical", ai_state = { ai_move="move_dmap", talent_in=1, },
  56 +
  57 + autolevel = "warriormage",
  58 + ai = "tactical", ai_state = { ai_move="move_dmap", talent_in=1, ally_compassion=0 },
57 59 ai_tactic = resolvers.tactic "melee",
58 60
59 61 see_invisible = 100,
... ... @@ -61,10 +63,11 @@ newEntity{ base = "BASE_NPC_HORROR",
61 63 stun_immune = 1,
62 64 blind_immune = 1,
63 65 disease_immune = 1,
64   - move_others=true,
65   -
66   - resists = { [DamageType.PHYSICAL] = 50, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50},
67 66
  67 + resists = { [DamageType.PHYSICAL] = 50, [DamageType.ACID] = 100, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50},
  68 + damage_affinity = { [DamageType.BLIGHT] = 50 },
  69 + no_auto_resists = true,
  70 +
68 71 body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
69 72 resolvers.drops{chance=20, nb=1, {} },
70 73 resolvers.equip{
... ... @@ -78,15 +81,15 @@ newEntity{ base = "BASE_NPC_HORROR",
78 81 resolvers.inscriptions(1, {"regeneration infusion"}),
79 82
80 83 resolvers.talents{
81   - --[Talents.T_BONE_GRAB]={base=4, every=10},
82   - [Talents.T_BLINDSIDE]={base=5, every=12},
  84 + [Talents.T_WORM_ROT]={base=4, every=8},
  85 + [Talents.T_EPIDEMIC]={base=4, every=8},
  86 + [Talents.T_REND]={base=4, every=8},
  87 + [Talents.T_ACID_STRIKE]={base=4, every=8},
  88 + [Talents.T_BLOODLUST]={base=4, every=8},
  89 + [Talents.T_RUIN]={base=4, every=8},
83 90 [Talents.T_CORRUPTED_STRENGTH]={base=3, every=15},
84   - [Talents.T_SLASH]={base=4, every=12},
85   - [Talents.T_FRENZY]={base=4, every=15},
86   - [Talents.T_REND]={base=4, every=12},
87   - [Talents.T_BLOODLUST]={base=3, every=12},
88   - [Talents.T_RUIN]={base=2, every=12},
89   - [Talents.T_SUMMON]=1,
  91 +
  92 + [Talents.T_BLINDSIDE]={base=3, every=12},
90 93
91 94 [Talents.T_WEAPON_COMBAT]={base=5, every=10, max=10},
92 95 [Talents.T_WEAPONS_MASTERY]={base=3, every=10, max=10},
... ... @@ -96,14 +99,12 @@ newEntity{ base = "BASE_NPC_HORROR",
96 99
97 100 on_takehit = function(self, value, src)
98 101 if value >= (self.max_life * 0.1) then
99   - self:forceUseTalent(self.T_SUMMON, {ignore_energy=true, ignore_cd=true})
  102 + local t = self:getTalentFromId(self.T_WORM_ROT)
  103 + t.spawn_carrion_worm(self, self, t)
  104 + game.logSeen(self, "#LIGHT_RED#A carrion worm mass has spawned from %s' wounds!", self.name)
100 105 end
101 106 return value
102 107 end,
103   -
104   - summon = {
105   - {type="vermin", subtype="worms", name="carrion worm mass", number=1, hasxp=false},
106   - },
107 108 }
108 109
109 110 newEntity{ base = "BASE_NPC_HORROR",
... ... @@ -474,11 +475,9 @@ newEntity{ base = "BASE_NPC_HORROR",
474 475 },
475 476 }
476 477
477   --- Temporal Horrors
478   -
479 478 newEntity{ base = "BASE_NPC_HORROR",
480   - subtype = "temporal",
481   - name = "temporal devourer", color=colors.CRIMSON,
  479 + subtype = "eldritch",
  480 + name = "devourer", color=colors.CRIMSON,
482 481 desc = "A headless, round creature with stubby legs and arms. Its body seems to be all teeth.",
483 482 level_range = {10, nil}, exp_worth = 1,
484 483 rarity = 2,
... ... @@ -486,17 +485,27 @@ newEntity{ base = "BASE_NPC_HORROR",
486 485 movement_speed = 0.8,
487 486 size_category = 2,
488 487 autolevel = "warrior",
489   - max_life = resolvers.rngavg(50, 80),
490   - combat_armor = 1, combat_def = 10,
491   - combat = { dam=resolvers.levelup(resolvers.rngavg(20,30), 1, 1.2), atk=resolvers.rngavg(10,20), apr=5, dammod={str=1} },
  488 + max_life = resolvers.rngavg(80, 100),
  489 + life_rating = 14,
  490 + life_regen = 4,
  491 + combat_armor = 16, combat_def = 1,
  492 + combat = { dam=resolvers.levelup(resolvers.rngavg(25,40), 1, 0.6), atk=resolvers.rngavg(25,50), apr=25, dammod={str=1.1}, physcrit = 10 },
  493 + ai_state = { talent_in=2, },
492 494
493   - resists = { [DamageType.TEMPORAL] = 5},
  495 + resolvers.talents{
  496 + [Talents.T_BLOODBATH]={base=1, every=5, max=7},
  497 + [Talents.T_GNASHING_TEETH]={base=1, every=5, max=7},
  498 + -- talents only usable while frenzied
  499 + [Talents.T_FRENZIED_LEAP]={base=1, every=5, max=7},
  500 + [Talents.T_FRENZIED_BITE]={base=1, every=5, max=7},
  501 + },
494 502
495 503 make_escort = {
496   - {type="horror", subtype="temporal", name="temporal devourer", number=2, no_subescort=true},
  504 + {type="horror", subtype="eldritch", name="devourer", number=2, no_subescort=true},
497 505 },
498 506 }
499 507
  508 +-- temporal horrors
500 509 newEntity{ base = "BASE_NPC_HORROR",
501 510 subtype = "temporal",
502 511 dredge = 1,
... ... @@ -511,7 +520,7 @@ newEntity{ base = "BASE_NPC_HORROR",
511 520 combat_armor = 1, combat_def = 10,
512 521 combat = { dam=resolvers.levelup(resolvers.rngavg(15,20), 1, 1.1), atk=resolvers.rngavg(5,15), apr=5, dammod={str=1} },
513 522
514   - resists = { [DamageType.TEMPORAL] = 5},
  523 + resists = { [DamageType.TEMPORAL] = 25},
515 524
516 525 resolvers.talents{
517 526 [Talents.T_DUST_TO_DUST]={base=1, every=7, max=5},
... ... @@ -523,7 +532,7 @@ newEntity{ base = "BASE_NPC_HORROR",
523 532 newEntity{ base = "BASE_NPC_HORROR",
524 533 subtype = "temporal",
525 534 dredge = 1,
526   - name = "temporal dredge", color=colors.PINK,
  535 + name = "dredge", color=colors.PINK,
527 536 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.",
528 537 level_range = {15, nil}, exp_worth = 1,
529 538 rarity = 2,
... ... @@ -536,7 +545,7 @@ newEntity{ base = "BASE_NPC_HORROR",
536 545 combat_armor = 1, combat_def = 0,
537 546 combat = { dam=resolvers.levelup(resolvers.rngavg(25,150), 1, 1.2), atk=resolvers.rngavg(25,130), apr=1, dammod={str=1.1} },
538 547
539   - resists = {all = 10, [DamageType.TEMPORAL] = 50, [DamageType.PHYSICAL] = 25},
  548 + resists = {all = 10, [DamageType.TEMPORAL] = 25, [DamageType.PHYSICAL] = 25},
540 549
541 550 resolvers.talents{
542 551 [Talents.T_STUN]={base=3, every=7, max=7},
... ... @@ -565,7 +574,7 @@ newEntity{ base = "BASE_NPC_HORROR",
565 574 resists = {all = 10, [DamageType.TEMPORAL] = 50},
566 575
567 576 make_escort = {
568   - {type="horror", subtype="temporal", name="temporal dredge", number=3, no_subescort=true},
  577 + {type="horror", subtype="temporal", name="dredge", number=3, no_subescort=true},
569 578 },
570 579
571 580 resolvers.inscriptions(1, {"shielding rune"}),
... ...
... ... @@ -66,15 +66,18 @@ newEntity{ base = "BASE_NPC_WORM",
66 66
67 67 newEntity{ base = "BASE_NPC_WORM",
68 68 name = "carrion worm mass", color=colors.SANDY_BROWN,
  69 + can_multiply = 2,
69 70 level_range = {20, nil}, exp_worth = 1,
70 71 rarity = 4,
71 72 max_life = resolvers.rngavg(5,9),
72 73 combat = { dam=1, atk=15, apr=100 },
73   -
74   - resists = {[DamageType.BLIGHT] = 100},
  74 +
  75 + disease_immune = 1,
  76 + resists = { [DamageType.PHYSICAL] = 50, [DamageType.ACID] = 100, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50},
  77 + no_auto_resists = true,
75 78
76 79 autolevel = "warriormage",
77   - resolvers.talents{ [Talents.T_CRAWL_POISON]=2, [Talents.T_ROTTING_DISEASE]=4, [Talents.T_MULTIPLY]=1 },
  80 + resolvers.talents{ [Talents.T_CRAWL_ACID]=2, [Talents.T_ROTTING_DISEASE]=4, [Talents.T_MULTIPLY]=1 },
78 81
79 82 on_die = function(self, src)
80 83 game.level.map:addEffect(self,
... ... @@ -82,7 +85,7 @@ newEntity{ base = "BASE_NPC_WORM",
82 85 engine.DamageType.BLIGHT, self:getStr(90, true),
83 86 2,
84 87 5, nil,
85   - engine.Entity.new{alpha=100, display='', color_br=30, color_bg=180, color_bb=60}
  88 + engine.Entity.new{alpha=50, display='', color_br=30, color_bg=180, color_bb=60}
86 89 )
87 90 game.logSeen(self, "%s exudes a corrupted gas as it dies.", self.name:capitalize())
88 91 end,
... ...
... ... @@ -212,7 +212,11 @@ newTalent{
212 212 local disease = rng.table(diseases)
213 213 local params = disease.params
214 214 params.src = self
215   - 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})
  215 + if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 12 - self:getTalentLevel(t)) and target:canBe("disease") then
  216 + 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})
  217 + else
  218 + game.logSeen(target, "%s resists the disease!", target.name:capitalize())
  219 + end
216 220 game.level.map:particleEmitter(px, py, 1, "slime")
217 221 end)
218 222 end,
... ...
... ... @@ -19,11 +19,139 @@
19 19
20 20 newTalentType{ type="technique/horror", name = "horror techniques", hide = true, description = "Physical talents of the various horrors of the world." }
21 21 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." }
  22 +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." }
22 23 newTalentType{ type="other/horror", name = "horror powers", hide = true, description = "Unclassified talents of the various horrors of the world." }
23 24
24 25 local oldTalent = newTalent
25 26 local newTalent = function(t) if type(t.hide) == "nil" then t.hide = true end return oldTalent(t) end
  27 +-- Bloated Horror Powers
  28 +-- Ideas; Mind Shriek (confusion, plus mental damge each turn), Hallucination (Clones that die on hit if the target makes a mental save)
26 29
  30 +-- Devourer Powers
  31 +newTalent{
  32 + name = "Frenzied Bite",
  33 + type = {"technique/horror", 3},
  34 + points = 5,
  35 + cooldown = 12,
  36 + stamina = 24,
  37 + tactical = { ATTACK = 1, DISABLE = 2 },
  38 + message = "In a frenzy @Source@ bites at @Target@!",
  39 + on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_FRENZY) then return false end return true end,
  40 + action = function(self, t)
  41 + local tg = {type="hit", range=self:getTalentRange(t)}
  42 + local x, y, target = self:getTarget(tg)
  43 + if not x or not y or not target then return nil end
  44 + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end
  45 +
  46 + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1, 1.7), true)
  47 + if hit then
  48 + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5) and target:canBe("cut") then
  49 + target:setEffect(target.EFF_DEEP_WOUND, 5, {src=self, heal_factor=self:getTalentLevel(t) * 10, power=self:combatTalentWeaponDamage(t, 1.5, 3)/5})
  50 + end
  51 + end
  52 + return true
  53 + end,
  54 + info = function(self, t)
  55 + return ([[A nasty bite that causes a deep wound, only usable in a frenzy.]])
  56 + end,
  57 +}
  58 +
  59 +newTalent{
  60 + name = "Frenzied Leap", -- modified ghoulish leap, only usable while in a frenzy
  61 + type = {"technique/horror", 1},
  62 + points = 5,
  63 + cooldown = 5,
  64 + tactical = { CLOSEIN = 3 },
  65 + direct_hit = true,
  66 + message = "@Source@ leaps forward in a frenzy!",
  67 + range = function(self, t) return math.floor(2 + self:getTalentLevel(t)) end,
  68 + requires_target = true,
  69 + 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,
  70 + action = function(self, t)
  71 + local tg = {type="hit", range=self:getTalentRange(t)}
  72 + local x, y, target = self:getTarget(tg)
  73 + if not x or not y then return nil end
  74 + if math.floor(core.fov.distance(self.x, self.y, x, y)) > self:getTalentRange(t) then return nil end
  75 +
  76 + local l = line.new(self.x, self.y, x, y)
  77 + local lx, ly = l()
  78 + local tx, ty = lx, ly
  79 + lx, ly = l()
  80 + while lx and ly do
  81 + if game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move", self) then break end
  82 + tx, ty = lx, ly
  83 + lx, ly = l()
  84 + end
  85 +
  86 + -- Find space
  87 + if game.level.map:checkEntity(tx, ty, Map.TERRAIN, "block_move", self) then return nil end
  88 + local fx, fy = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true})
  89 + if not fx then
  90 + return
  91 + end
  92 + self:move(fx, fy, true)
  93 +
  94 + return true
  95 + end,
  96 + info = function(self, t)
  97 + return ([[Leap toward your target.]])
  98 + end,
  99 +}
  100 +
  101 +newTalent{
  102 + name = "Gnashing Teeth",
  103 + type = {"technique/horror", 1},
  104 + points = 5,
  105 + cooldown = 3,
  106 + stamina = 8,
  107 + message = "@Source@ tries to bite @Target@ with razor sharp teeth!",
  108 + requires_target = true,
  109 + tactical = { ATTACK = 2 },
  110 + do_devourer_frenzy = function(self, target, t)
  111 + game.logSeen(self, "The scent of blood sends the %ss into a frenzy!", self.name:capitalize())
  112 + -- frenzy devourerers
  113 + local tg = {type="ball", range=0, radius=3, selffire=true, talent=t}
  114 + self:project(tg, target.x, target.y, function(px, py)
  115 + local target = game.level.map(px, py, engine.Map.ACTOR)
  116 + local reapplied = false
  117 + if target then
  118 + local actor_frenzy = false
  119 + if target.name == "devourer" then
  120 + actor_frenzy = true
  121 + end
  122 + if actor_frenzy then
  123 + -- silence the apply message if the target already has the effect
  124 + for eff_id, p in pairs(target.tmp) do
  125 + local e = target.tempeffect_def[eff_id]
  126 + if e.name == "Frenzy" then
  127 + reapplied = true
  128 + end
  129 + end
  130 + 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)
  131 + end
  132 + end
  133 + end)
  134 + end,
  135 + action = function(self, t)
  136 + local tg = {type="hit", range=self:getTalentRange(t)}
  137 + local x, y, target = self:getTarget(tg)
  138 + if not x or not y or not target then return nil end
  139 + if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end
  140 + local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true)
  141 +
  142 + if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 8 - self:getTalentLevel(t) / 2) and target:canBe("cut") then
  143 + target:setEffect(target.EFF_CUT, 5, {power=self:combatTalentWeaponDamage(t, 0.8, 1.5), src=self})
  144 + t.do_devourer_frenzy(self, target, t)
  145 + else
  146 + game.logSeen(target, "%s resists the cut!", target.name:capitalize())
  147 + end
  148 +
  149 + return true
  150 + end,
  151 + info = function(self, t)
  152 + 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).]])
  153 + end,
  154 +}
27 155
28 156 -- Nightmare Horror Powers
29 157 newTalent{
... ... @@ -45,7 +173,7 @@ newTalent{
45 173 if not x then
46 174 return
47 175 end
48   -
  176 +
49 177 local m = target:clone{
50 178 shader = "shadow_simulacrum",
51 179 no_drops = true,
... ... @@ -63,7 +191,7 @@ newTalent{
63 191
64 192 mod.class.NPC.castAs(m)
65 193 engine.interface.ActorAI.init(m, m)
66   -
  194 +
67 195 m.energy.value = 0
68 196 m.player = nil
69 197 m.max_life = m.max_life / 2
... ... @@ -75,7 +203,7 @@ newTalent{
75 203 m.seen_by = nil
76 204 m.can_talk = nil
77 205 m.clone_on_hit = nil
78   -
  206 +
79 207 -- Remove some talents
80 208 local tids = {}
81 209 for tid, _ in pairs(m.talents) do
... ... @@ -86,10 +214,10 @@ newTalent{
86 214 if t.mode == "sustained" and m:isTalentActive(t.id) then m:forceUseTalent(t.id, {ignore_energy=true}) end
87 215 m.talents[t.id] = nil
88 216 end
89   -
  217 +
90 218 -- nil the Inner Demons effect to squelch combat log spam
91 219 m.tmp[m.EFF_INNER_DEMONS] = nil
92   -
  220 +
93 221 -- remove detrimental timed effects
94 222 local effs = {}
95 223 for eff_id, p in pairs(m.tmp) do
... ... @@ -98,7 +226,7 @@ newTalent{
98 226 effs[#effs+1] = {"effect", eff_id}
99 227 end
100 228 end
101   -
  229 +
102 230 while #effs > 0 do
103 231 local eff = rng.tableRemove(effs)
104 232 if eff[1] == "effect" then
... ... @@ -106,12 +234,12 @@ newTalent{
106 234 end
107 235 end
108 236
109   -
  237 +
110 238 game.zone:addEntity(game.level, m, "actor", x, y)
111 239 game.level.map:particleEmitter(x, y, 1, "shadow")
112 240
113 241 game.logSeen(target, "%s's Inner Demon manifests!", target.name:capitalize())
114   -
  242 +
115 243 end,
116 244 action = function(self, t)
117 245 local tg = {type="hit", range=self:getTalentRange(t), talent=t}
... ... @@ -121,13 +249,13 @@ newTalent{
121 249 if not x or not y then return nil end
122 250 local target = game.level.map(x, y, Map.ACTOR)
123 251 if not target then return nil end
124   -
  252 +
125 253 if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) and target:canBe("fear") then
126 254 target:setEffect(target.EFF_INNER_DEMONS, t.getDuration(self, t), {src = self, chance=t.getChance(self, t), dam=t.getDamage(self, t)})
127 255 else
128 256 game.logSeen(target, "%s resists the demons!", target.name:capitalize())
129 257 end
130   -
  258 +
131 259 return true
132 260 end,
133 261 info = function(self, t)
... ... @@ -156,13 +284,13 @@ newTalent{
156 284 if not x or not y then return nil end
157 285 local target = game.level.map(x, y, Map.ACTOR)
158 286 if not target then return nil end
159   -
  287 +
160 288 if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) and target:canBe("fear") then
161 289 target:setEffect(target.EFF_WAKING_NIGHTMARE, t.getDuration(self, t), {src = self, chance=t.getChance(self, t), dam=t.getDamage(self, t)})
162 290 else
163 291 game.logSeen(target, "%s resists the nightmare!", target.name:capitalize())
164 292 end
165   -
  293 +
166 294 return true
167 295 end,
168 296 info = function(self, t)
... ... @@ -211,7 +339,8 @@ newTalent{
211 339 return ([[An area of effect that causes darkness damage and reduces the lite radius and darkness resistance of those within.]])
212 340 end,
213 341 }
214   -
  342 +-- Temporal Stalker Powers
  343 +-- 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
215 344 -- Void Horror Powers
216 345 newTalent{
217 346 name = "Echoes From The Void",
... ... @@ -233,13 +362,13 @@ newTalent{
233 362 if not x or not y then return nil end
234 363 local target = game.level.map(x, y, Map.ACTOR)
235 364 if not target then return nil end
236   -
237   - if target:checkHit(self:combatMindpower(), target:combatMentalResist(), 0, 95, 15) and target:canBe("fear") then
238   - target:setEffect(target.EFF_VOID_ECHOES, 6, {src=self, power=t.getDamage(self, t)})
  365 +
  366 + if target:checkHit(src:combatMindpower(), target:combatMentalResist(), 0, 95, 15) and target:canBe("fear") then
  367 + target:setEffect(target.EFF_VOID_ECHOES, 6, {src= self, power=t.getDamage(self, t)})
239 368 else
240 369 game.logSeen(target, "%s resists the void!", target.name:capitalize())
241 370 end
242   -
  371 +
243 372 game:playSoundNear(self, "talents/arcane")
244 373 return true
245 374 end,
... ... @@ -269,9 +398,9 @@ newTalent{
269 398 local _ _, tx, ty = self:canProject(tg, tx, ty)
270 399 target = game.level.map(tx, ty, Map.ACTOR)
271 400 if target == self then target = nil end
272   -
  401 +
273 402 if self:getTalentLevel(t) < 5 then self:setEffect(self.EFF_SUMMON_DESTABILIZATION, 500, {power=5}) end
274   -
  403 +
275 404 for i = 1, self:getTalentLevelRaw(t) do
276 405 -- Find space
277 406 local x, y = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true})
... ... @@ -287,41 +416,41 @@ newTalent{
287 416 name = "void shard", faction = self.faction,
288 417 desc = [[It looks like a small hole in the fabric of spacetime.]],
289 418 stats = { str=22, dex=20, wil=15, con=15 },
290   -
291   - --level_range = {self.level, self.level},
  419 +
  420 + --level_range = {self.level, self.level},
292 421 exp_worth = 0,
293 422 max_life = resolvers.rngavg(5,10),
294 423 life_rating = 2,
295 424 rank = 2,
296 425 size_category = 1,
297   -
  426 +
298 427 autolevel = "summoner",
299   - ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_snake" },
  428 + ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_snake" },
300 429 combat_armor = 1, combat_def = 1,
301 430 combat = { dam=resolvers.levelup(resolvers.mbonus(40, 15), 1, 1.2), atk=15, apr=15, dammod={wil=0.8}, damtype=DamageType.TEMPORAL },
302 431 on_melee_hit = { [DamageType.TEMPORAL] = resolvers.mbonus(20, 10), },
303   -
  432 +
304 433 infravision = 10,
305 434 no_breath = 1,
306 435 fear_immune = 1,
307 436 stun_immune = 1,
308 437 confusion_immune = 1,
309 438 silence_immune = 1,
310   -
  439 +
311 440 ai_target = {actor=target}
312 441 }
313   -
  442 +
314 443 m.faction = self.faction
315   - m.summoner = self
  444 + m.summoner = self
316 445 m.summoner_gain_exp=true
317 446 m.summon_time = t.getSummonTime(self, t)
318   -
  447 +
319 448 m:resolve() m:resolve(nil, true)
320 449 m:forceLevelup(self.level)
321 450 game.zone:addEntity(game.level, m, "actor", x, y)
322 451 game.level.map:particleEmitter(x, y, 1, "summon")
323 452 m:setEffect(m.EFF_TEMPORAL_DESTABILIZATION, 5, {src=self, dam=t.getDamage(self, t), explosion=self:spellCrit(t.getExplosion(self, t))})
324   -
  453 +
325 454 end
326 455 game:playSoundNear(self, "talents/spell_generic")
327 456 return true
... ... @@ -329,4 +458,56 @@ newTalent{
329 458 info = function(self, t)
330 459 return ([[Summons shards of explosive doom!]])
331 460 end,
332   -}
\ No newline at end of file
  461 +}
  462 +
  463 +-- Worm that Walks Powers
  464 +newTalent{
  465 + name = "Worm Rot",
  466 + type = {"corruption/horror", 1},
  467 + points = 5,
  468 + cooldown = 8,
  469 + vim = 10,
  470 + range = 6,
  471 + requires_target = true,
  472 + tactical = { ATTACK = 2, DISABLE = 4 },
  473 + getBurstDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300) end,
  474 + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 5, 50) end,
  475 + getHealReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 70) end,
  476 + getDuration = function(self, t) return 4 + math.ceil(self:getTalentLevel(t)) end,
  477 + proj_speed = 6,
  478 + spawn_carrion_worm = function (self, target, t)
  479 + local x, y = util.findFreeGrid(target.x, target.y, 10, true, {[Map.ACTOR]=true})
  480 + if not x then
  481 + return
  482 + end
  483 +
  484 + local worm = {type="vermin", subtype="worms", name="carrion worm mass", number=1, hasxp=false}
  485 + local m = game.zone:makeEntity(game.level, "actor", worm, nil, true)
  486 + m:resolve()
  487 + m.faction = self.faction
  488 +
  489 + game.zone:addEntity(game.level, m, "actor", x, y)
  490 + end,
  491 + action = function(self, t)
  492 + local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="bolt_slime"}}
  493 + local x, y = self:getTarget(tg)
  494 + if not x or not y then return nil end
  495 +
  496 + self:project(tg, x, y, function(px, py)
  497 + local target = game.level.map(px, py, engine.Map.ACTOR)
  498 + if not target then return end
  499 + if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 12 - self:getTalentLevel(t)) and target:canBe("disease") then
  500 + target:setEffect(target.EFF_WORM_ROT, 5, {src=self, dam=t.getDamage(self, t), burst=t.getBurstDamage(self, t)})
  501 + else
  502 + game.logSeen(target, "%s resists the worm rot!", target.name:capitalize())
  503 + end
  504 + game.level.map:particleEmitter(px, py, 1, "slime")
  505 + end)
  506 + game:playSoundNear(self, "talents/slime")
  507 +
  508 + return true
  509 + end,
  510 + info = function(self, t)
  511 + 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.]])
  512 + end,
  513 +}
... ...
... ... @@ -1119,7 +1119,7 @@ newTalent{
1119 1119 target = function(self, t)
1120 1120 return {type="beam", range=self:getTalentRange(t), talent=t}
1121 1121 end,
1122   - tactical = { ATTACK = 3 },
  1122 + tactical = { ATTACK = 2, ESCAPE = 2 },
1123 1123 action = function(self, t)
1124 1124 local tg = self:getTalentTarget(t)
1125 1125 local x, y = self:getTarget(tg)
... ... @@ -1418,7 +1418,7 @@ newTalent{
1418 1418 points = 5,
1419 1419 cooldown = 6,
1420 1420 tactical = {
1421   - BUFF = 10,
  1421 + BUFF = 4,
1422 1422 },
1423 1423 direct_hit = true,
1424 1424 range = 0,
... ... @@ -1428,9 +1428,30 @@ newTalent{
1428 1428 end,
1429 1429 action = function(self, t)
1430 1430 local tg = self:getTalentTarget(t)
1431   - local grids = self:project(tg, self.x, self.y, DamageType.DREDGE_FRENZY)
  1431 + self:project(tg, self.x, self.y, function(px, py)
  1432 + local target = game.level.map(px, py, engine.Map.ACTOR)
  1433 + local reapplied = false
  1434 + if target then
  1435 + local actor_frenzy = false
  1436 + if target.dredge then
  1437 + actor_frenzy = true
  1438 + end
  1439 + if actor_frenzy then
  1440 + -- silence the apply message if the target already has the effect
  1441 + for eff_id, p in pairs(target.tmp) do
  1442 + local e = target.tempeffect_def[eff_id]
  1443 + if e.name == "Frenzy" then
  1444 + reapplied = true
  1445 + end
  1446 + end
  1447 + 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)
  1448 + end
  1449 + end
  1450 + end)
  1451 +
1432 1452 game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_light", {radius=tg.radius})
1433 1453 game:playSoundNear(self, "talents/arcane")
  1454 +
1434 1455 return true
1435 1456 end,
1436 1457 info = function(self, t)
... ...
... ... @@ -1506,7 +1506,7 @@ newEffect{
1506 1506 newEffect{
1507 1507 name = "EPIDEMIC",
1508 1508 desc = "Epidemic",
1509   - 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,
  1509 + 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,
1510 1510 type = "disease",
1511 1511 status = "detrimental",
1512 1512 parameters = {},
... ... @@ -2744,13 +2744,10 @@ newEffect{
2744 2744 end
2745 2745 end
2746 2746
2747   - for i = 1, 1 do
2748   - if #effs == 0 then break end
  2747 + if #effs > 0 then
2749 2748 local eff = rng.tableRemove(effs)
2750   -
2751 2749 if eff[1] == "effect" then
2752 2750 self:removeEffect(eff[2])
2753   - known = true
2754 2751 end
2755 2752 end
2756 2753 end,
... ... @@ -4531,4 +4528,94 @@ newEffect{
4531 4528 end
4532 4529 end
4533 4530 end,
  4531 +}
  4532 +
  4533 +newEffect{
  4534 + name = "WORM_ROT",
  4535 + desc = "Worm Rot",
  4536 + 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,
  4537 + type = "disease",
  4538 + status = "detrimental",
  4539 + parameters = {},
  4540 + on_gain = function(self, err) return "#Target# is afflicted by a terrible worm rot!" end,
  4541 + on_lose = function(self, err) return "#Target# is free from the worm rot." end,
  4542 + -- Damage each turn
  4543 + on_timeout = function(self, eff)
  4544 + self.worm_rot_timer = self.worm_rot_timer - 1
  4545 +
  4546 + -- disease damage
  4547 + if self:attr("purify_disease") then
  4548 + self:heal(eff.dam)
  4549 + else
  4550 + DamageType:get(DamageType.BLIGHT).projector(eff.src, self.x, self.y, DamageType.BLIGHT, eff.dam, {from_disease=true})
  4551 + end
  4552 + -- acid damage from the larvae
  4553 + DamageType:get(DamageType.ACID).projector(eff.src, self.x, self.y, DamageType.ACID, eff.dam)
  4554 +
  4555 + local effs = {}
  4556 + -- Go through all physical effects
  4557 + for eff_id, p in pairs(self.tmp) do
  4558 + local e = self.tempeffect_def[eff_id]
  4559 + if e.status == "beneficial" and e.type == "physical" then
  4560 + effs[#effs+1] = {"effect", eff_id}
  4561 + end
  4562 + end
  4563 + -- remove a random physical effect
  4564 + if #effs > 0 then
  4565 + local eff = rng.tableRemove(effs)
  4566 + if eff[1] == "effect" then
  4567 + self:removeEffect(eff[2])
  4568 + end
  4569 + end
  4570 +
  4571 + -- burst and spawn a worm mass
  4572 + if self.worm_rot_timer == 0 then
  4573 + DamageType:get(DamageType.BLIGHT).projector(eff.src, self.x, self.y, DamageType.BLIGHT, eff.burst, {from_disease=true})
  4574 + local t = eff.src:getTalentFromId(eff.src.T_WORM_ROT)
  4575 + t.spawn_carrion_worm(eff.src, self, t)
  4576 + game.logSeen(self, "#LIGHT_RED#A carrion worm mass bursts out of %s!", self.name:capitalize())
  4577 + self:removeEffect(self.EFF_WORM_ROT)
  4578 + end
  4579 + end,
  4580 + activate = function(self, eff)
  4581 + self.worm_rot_timer = 5
  4582 + end,
  4583 + deactivate = function(self, eff)
  4584 + self.worm_rot_timer = nil
  4585 + end,
  4586 +}
  4587 +
  4588 +newEffect{
  4589 + name = "FRENZY",
  4590 + desc = "Frenzy",
  4591 + 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,
  4592 + type = "physical",
  4593 + status = "beneficial",
  4594 + parameters = { power=0.1 },
  4595 + on_gain = function(self, err) return "#Target# goes into a killing frenzy.", "+Frenzy" end,
  4596 + on_lose = function(self, err) return "#Target# calms down.", "-Frenzy" end,
  4597 + on_merge = function(self, old_eff, new_eff)
  4598 + -- use on merge so reapplied frenzy doesn't kill off creatures with negative life
  4599 + old_eff.dur = new_eff.dur
  4600 + old_eff.power = new_eff.power
  4601 + old_eff.crit = new_eff.crit
  4602 + return old_eff
  4603 + end,
  4604 + activate = function(self, eff)
  4605 + eff.tmpid = self:addTemporaryValue("global_speed", eff.power)
  4606 + eff.critid = self:addTemporaryValue("combat_physcrit", eff.crit)
  4607 + eff.dieatid = self:addTemporaryValue("die_at", -self.max_life * eff.dieat)
  4608 + end,
  4609 + deactivate = function(self, eff)
  4610 + -- check negative life first incase the creature has healing
  4611 + if self.life <= 0 then
  4612 + local sx, sy = game.level.map:getTileToScreen(self.x, self.y)
  4613 + game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, rng.float(-2.5, -1.5), "Falls dead!", {255,0,255})
  4614 + game.logSeen(self, "%s dies when it's frenzy ends!", self.name:capitalize())
  4615 + self:die(self)
  4616 + end
  4617 + self:removeTemporaryValue("global_speed", eff.tmpid)
  4618 + self:removeTemporaryValue("combat_physcrit", eff.critid)
  4619 + self:removeTemporaryValue("die_at", eff.dieatid)
  4620 + end,
4534 4621 }
\ No newline at end of file
... ...