diff --git a/game/modules/tome/data/birth/classes/afflicted.lua b/game/modules/tome/data/birth/classes/afflicted.lua index 2ee563e649a7c6665d37394698ea0ca01f49c9f7..01f9d06df0934d2b4a0020267f4c3ddf38be1552 100644 --- a/game/modules/tome/data/birth/classes/afflicted.lua +++ b/game/modules/tome/data/birth/classes/afflicted.lua @@ -107,7 +107,7 @@ newBirthDescriptor{ }, talents = { [ActorTalents.T_UNNATURAL_BODY] = 1, - [ActorTalents.T_FEED_HATE] = 1, + [ActorTalents.T_FEED] = 1, [ActorTalents.T_WILLFUL_STRIKE] = 1, }, copy = { diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua index 90ef988a7b6413e4cab1c16f7d869a433588c61d..7d2579e50059555aaefc2768e794bda6bd3995a5 100644 --- a/game/modules/tome/data/damage_types.lua +++ b/game/modules/tome/data/damage_types.lua @@ -111,6 +111,11 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) if target.knowTalent and target:knowTalent(target.T_RESOLVE) then local t = target:getTalentFromId(target.T_RESOLVE) t.on_absorb(target, t, type, dam) end + if not target.dead and dam > 0 and type == DamageType.MIND and src and src.knowTalent and src:knowTalent(src.T_MADNESS) then + local t = src:getTalentFromId(src.T_MADNESS) + t.doMadness(target, t, src) + end + return dam end return 0 diff --git a/game/modules/tome/data/gfx/particles/feed_hate.lua b/game/modules/tome/data/gfx/particles/feed_hate.lua index 2d10b95609aadf2c82490c47c01d29602265a826..da4b3b74622d7fc67a0f1c5e8d884a7290018d48 100644 --- a/game/modules/tome/data/gfx/particles/feed_hate.lua +++ b/game/modules/tome/data/gfx/particles/feed_hate.lua @@ -45,7 +45,7 @@ return { generator = function() r = 32 / 255, rv = 0, ra = 0, g = 32 / 255, gv = 0, ga = 0, b = 32 / 255, bv = 0, ba = 0, - a = rng.range(60, 120) / 255, av = 0, aa = 0, + a = rng.range(80, 150) / 255, av = 0, aa = 0, } end, }, function(self) diff --git a/game/modules/tome/data/talents/cursed/dark-sustenance.lua b/game/modules/tome/data/talents/cursed/dark-sustenance.lua index 96f14d6185343382959c52c527653963c18bd89b..de1f9a270a66a9e38f66613916b44835473ee3eb 100644 --- a/game/modules/tome/data/talents/cursed/dark-sustenance.lua +++ b/game/modules/tome/data/talents/cursed/dark-sustenance.lua @@ -18,19 +18,16 @@ -- darkgod@te4.org newTalent{ - name = "Feed Hate", + name = "Feed", type = {"cursed/dark-sustenance", 1}, require = cursed_wil_req1, points = 5, random_ego = "attack", - cooldown = 8, + cooldown = 6, range = 15, requires_target = true, getHateGain = function(self, t) - return self:getWil(0.3) + self:getTalentLevel(t) * 0.1 - end, - getExtension = function(self, t) - return math.floor(self:getTalentLevel(t) - 1) + return math.sqrt(self:getTalentLevel(t)) * 0.2 + self:getWil(0.15) end, action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} @@ -46,38 +43,48 @@ newTalent{ if self:hasEffect(self.EFF_FEED_HATE) then self:removeEffect(self.EFF_FEED_HATE) end - + local hateGain = t.getHateGain(self, t) - local extension = t.getExtension(self, t) - self:setEffect(self.EFF_FEED_HATE, 99999, { target=target, hateGain=hateGain, extension=extension }) + local constitutionGain = 0 + local lifeRegenGain = 0 + local damageGain = 0 + local resistGain = 0 + + local tFeedHealth = self:getTalentFromId(self.T_FEED_HEALTH) + if tFeedHealth and self:getTalentLevelRaw(tFeedHealth) > 0 then + constitutionGain = tFeedHealth.getConstitutionGain(self, tFeedHealth, target) + lifeRegenGain = tFeedHealth.getLifeRegenGain(self, tFeedHealth) + end + + local tFeedPower = self:getTalentFromId(self.T_FEED_POWER) + if tFeedPower and self:getTalentLevelRaw(tFeedPower) > 0 then + damageGain = tFeedPower.getDamageGain(self, tFeedPower, target) + end + + local tFeedStrengths = self:getTalentFromId(self.T_FEED_STRENGTHS) + if tFeedStrengths and self:getTalentLevelRaw(tFeedStrengths) > 0 then + resistGain = tFeedStrengths.getResistGain(self, tFeedStrengths, target) + end + + self:setEffect(self.EFF_FEED, 99999, { target=target, hateGain=hateGain, constitutionGain=constitutionGain, lifeRegenGain=lifeRegenGain, damageGain=damageGain, resistGain=resistGain, extension=0 }) return true end, info = function(self, t) local hateGain = t.getHateGain(self, t) - local extension = t.getExtension(self, t) - local extensionText = "" - if extension > 0 then - return ([[Draws %0.2f hate per turn from a targeted foe as long as the foe remains in your line of sight. You will continue to gain hate for %d turns after the link is severed. - Improves with the Willpower stat.]]):format(hateGain, extension) - else - return ([[Draws %0.2f hate per turn from a targeted foe as long as the foe remains in your line of sight. - Improves with the Willpower stat.]]):format(hateGain) - end + return ([[Feed from the essence of your enemy. Draws %0.2f hate per turn from a targeted foe as long as the foe remains in your line of sight. + Improves with the Willpower stat.]]):format(hateGain) end, } newTalent{ name = "Feed Health", type = {"cursed/dark-sustenance", 2}, + mode = "passive", require = cursed_wil_req2, points = 5, - random_ego = "attack", - cooldown = 8, - range = 15, - requires_target = true, getConstitutionGain = function(self, t, target) - local gain = 2 + math.floor(self:getWil(18) * (0.3 + self:getTalentLevel(t) * 0.2)) + local gain = math.floor((6 + self:getWil(6)) * math.sqrt(self:getTalentLevel(t)) * 0.392) if target then -- return capped gain return math.min(gain, math.floor(target:getCon() * 0.75)) @@ -87,141 +94,47 @@ newTalent{ end end, getLifeRegenGain = function(self, t, target) - return self.max_life * (0.003 + self:getWil(0.005) + self:getTalentLevel(t) * 0.005) - end, - getExtension = function(self, t) - return math.floor(self:getTalentLevel(t) - 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 self:reactionToward(target) >= 0 or target.summoner == self then - game.logPlayer(self, "You can only gain sustenance from your foes!"); - return nil - end - - -- remove old effect - if self:hasEffect(self.EFF_FEED_HEALTH) then - self:removeEffect(self.EFF_FEED_HEALTH) - end - - local constitutionGain = t.getConstitutionGain(self, t, target) - local lifeRegenGain = t.getLifeRegenGain(self, t) - local extension = t.getExtension(self, t) - self:setEffect(self.EFF_FEED_HEALTH, 99999, { target=target, constitutionGain=constitutionGain, lifeRegenGain=lifeRegenGain, extension=extension }) - - return true + return self.max_life * (math.sqrt(self:getTalentLevel(t)) * 0.012 + self:getWil(0.01)) end, info = function(self, t) local constitutionGain = t.getConstitutionGain(self, t) local lifeRegenGain = t.getLifeRegenGain(self, t) - local extension = t.getExtension(self, t) - if extension > 0 then - return ([[Transfers %d constitution and %0.1f life per turn from a targeted foe to you as long as the foe remains in your line of sight. You will continue to gain life for %d turns after the link is severed. - Improves with the Willpower stat.]]):format(constitutionGain, lifeRegenGain, extension) - else - return ([[Transfers %d constitution and %0.1f life per turn from a targeted foe to you as long as the foe remains in your line of sight. - Improves with the Willpower stat.]]):format(constitutionGain, lifeRegenGain) - end + return ([[Enhances your feeding by transfering %d constitution and %0.1f life per turn from a targeted foe to you. + Improves with the Willpower stat.]]):format(constitutionGain, lifeRegenGain) end, } newTalent{ name = "Feed Power", type = {"cursed/dark-sustenance", 3}, + mode = "passive", require = cursed_wil_req3, points = 5, - random_ego = "attack", - cooldown = 8, - range = 15, - requires_target = true, getDamageGain = function(self, t) - return self:getWil(10) + self:getTalentLevel(t) * 5 - end, - getExtension = function(self, t) - return math.floor(self:getTalentLevel(t) - 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 self:reactionToward(target) >= 0 or target.summoner == self then - game.logPlayer(self, "You can only gain sustenance from your foes!"); - return nil - end - - -- remove old effect - if self:hasEffect(self.EFF_FEED_POWER) then - self:removeEffect(self.EFF_FEED_POWER) - end - - local damageGain = t.getDamageGain(self, t) - local extension = t.getExtension(self, t) - self:setEffect(self.EFF_FEED_POWER, 99999, { target=target, damageGain=damageGain, extension=extension }) - - return true + return math.sqrt(self:getTalentLevel(t)) * 5 + self:getWil(5) end, info = function(self, t) local damageGain = t.getDamageGain(self, t) - local extension = t.getExtension(self, t) - if extension > 0 then - return ([[Reduces your targeted foe's damage by %d%% and increases yours by the same amount as long as the foe remains in your line of sight. You will continue to gain power for %d turns after the link is severed. - Improves with the Willpower stat.]]):format(damageGain, extension) - else - return ([[Reduces your targeted foe's damage by %d%% and increases yours by the same amount as long as the foe remains in your line of sight. - Improves with the Willpower stat.]]):format(damageGain) - end + return ([[Enhances your feeding by reducing your targeted foe's damage by %d%% and increasing yours by the same amount. + Improves with the Willpower stat.]]):format(damageGain) end, } newTalent{ name = "Feed Strengths", type = {"cursed/dark-sustenance", 4}, + mode = "passive", require = cursed_wil_req4, points = 5, - random_ego = "attack", - cooldown = 8, - range = 15, - requires_target = true, getResistGain = function(self, t) - return 20 + self:getWil(10) + self:getTalentLevel(t) * 7 + return math.sqrt(self:getTalentLevel(t)) * 22 + self:getWil(15) end, getExtension = function(self, t) return math.floor(self:getTalentLevel(t) - 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 self:reactionToward(target) >= 0 or target.summoner == self then - game.logPlayer(self, "You can only gain sustenance from your foes!"); - return nil - end - - -- remove old effect - if self:hasEffect(self.EFF_FEED_STRENGTHS) then - self:removeEffect(self.EFF_FEED_STRENGTHS) - end - - local resistGain = t.getResistGain(self, t) - local extension = t.getExtension(self, t) - self:setEffect(self.EFF_FEED_STRENGTHS, 99999, { target=target, resistGain=resistGain, extension=extension }) - - return true - end, info = function(self, t) local resistGain = t.getResistGain(self, t) - local extension = t.getExtension(self, t) - if extension > 0 then - return ([[Reduces your targeted foe's positive resistances by %d%% and increases yours by the same amount as long as the foe remains in your line of sight. You will continue to gain power for %d turns after the link is severed. - Improves with the Willpower stat.]]):format(resistGain, extension) - else - return ([[Reduces your targeted foes positive resistances by %d%% and increases yours by the same amount as long as the foe remains in your line of sight. - Improves with the Willpower stat.]]):format(resistGain) - end + return ([[Enhances your feeding by reducing your targeted foe's positive resistances by %d%% and increasing yours by the same amount. + Improves with the Willpower stat.]]):format(resistGain) end, } diff --git a/game/modules/tome/data/talents/cursed/darkness.lua b/game/modules/tome/data/talents/cursed/darkness.lua index 8442675cea2ded118cb9933e98a66d550b3388c8..acb4ac363a310ad96ef828260cfc302ab9f6171b 100644 --- a/game/modules/tome/data/talents/cursed/darkness.lua +++ b/game/modules/tome/data/talents/cursed/darkness.lua @@ -19,130 +19,13 @@ local Object = require "engine.Object" local Map = require "engine.Map" -local canCreep, doCreep, createDark local function combatTalentDamage(self, t, min, max) return self:combatTalentSpellDamage(t, min, max, self.level + self:getMag()) end -function canCreep(x, y, ignoreCreepingDark) - -- not on map - if not game.level.map:isBound(x, y) then return false end - -- already dark - if not ignoreCreepingDark then - if game.level.map:checkAllEntities(x, y, "creepingDark") then return false end - end - -- allow objects and terrain to block, but not actors - if game.level.map:checkAllEntities(x, y, "block_move") and not game.level.map(x, y, Map.ACTOR) then return false end - - return true -end - -function doCreep(self, useCreep) - local start = rng.range(0, 8) - for i = start, start + 8 do - local x = self.x + (i % 3) - 1 - local y = self.y + math.floor((i % 9) / 3) - 1 - if not (x == self.x and y == self.y) and canCreep(x, y) then - -- add new dark - local newCreep - if useCreep then - -- transfer some of our creep to the new dark - newCreep = math.ceil(self.creep / 2) - self.creep = self.creep - newCreep - else - -- just clone our creep - newCreep = self.creep - end - createDark(self.summoner, x, y, self.damage, self.originalDuration, newCreep, self.creepChance, 0) - return true - end - - -- nowhere to creep - return false - end -end - -function createDark(summoner, x, y, damage, duration, creep, creepChance, initialCreep) - local e = Object.new{ - name = "creeping dark", - block_sight=true, - canAct = false, - canCreep = true, - x = x, y = y, - damage = damage, - originalDuration = duration, - duration = duration, - creep = creep, - creepChance = creepChance, - summoner = summoner, - summoner_gain_exp = true, - act = function(self) - local Map = require "engine.Map" - - self:useEnergy() - - -- apply damage to anything inside the darkness - local actor = game.level.map(self.x, self.y, Map.ACTOR) - if actor and actor ~= self.summoner and (not actor.summoner or actor.summoner ~= self.summoner) then - self.summoner:project(actor, actor.x, actor.y, engine.DamageType.DARKNESS, self.damage) - --DamageType:get(DamageType.DARKNESS).projector(self.summoner, actor.x, actor.y, DamageType.DARKNESS, damage) - end - - if self.duration <= 0 then - -- remove - if self.particles then game.level.map:removeParticleEmitter(self.particles) end - game.level.map:remove(self.x, self.y, Map.TERRAIN+3) - game.level:removeEntity(self) - --game.level.map:redisplay() - else - self.duration = self.duration - 1 - - if self.canCreep and self.creep > 0 and rng.percent(self.creepChance) then - if not doCreep(self, true) then - -- doCreep failed..pass creep on to a neighbor and stop creeping - self.canCreep = false - local start = rng.range(0, 8) - for i = start, start + 8 do - local x = self.x + (i % 3) - 1 - local y = self.y + math.floor((i % 9) / 3) - 1 - if not (x == self.x and y == self.y) and canCreep(x, y) then - local dark = game.level.map:checkAllEntities(x, y, "creepingDark") - if dark and dark.canCreep then - -- transfer creep - dark.creep = dark.creep + self.creep - self.creep = 0 - return - end - end - end - end - end - end - end, - } - e.creepingDark = e -- used for checkAllEntities to return the dark Object itself - game.level:addEntity(e) - game.level.map(x, y, Map.TERRAIN+3, e) - - -- add particles - e.particles = Particles.new("creeping_dark", 1, { }) - e.particles.x = x - e.particles.y = y - game.level.map:addParticleEmitter(e.particles) - - -- do some initial creeping - while initialCreep > 0 do - if not doCreep(e, false) then - e.canCreep = false - e.initialCreep = 0 - break - end - initialCreep = initialCreep - 1 - end -end - local function createDarkTendrils(summoner, x, y, target, damage, duration, pinDuration) + if not summoner:getTalentFromId(summoner.T_CREEPING_DARKNESS) then return end local e = Object.new{ name = "dark tendril", @@ -163,6 +46,8 @@ local function createDarkTendrils(summoner, x, y, target, damage, duration, pinD local done = false local hitTarget = false + local tCreepingDarkness = self.summoner:getTalentFromId(summoner.T_CREEPING_DARKNESS) + if self.finalizing then if self.duration <= 0 or self.target.dead or self.x ~= self.target.x or self.y ~= self.target.y then game.logSeen(self, "The dark tendrils dissipate.") @@ -181,7 +66,7 @@ local function createDarkTendrils(summoner, x, y, target, damage, duration, pinD for i = start, start + 8 do local nextX = self.x + (i % 3) - 1 local nextY = self.y + math.floor((i % 9) / 3) - 1 - if not (nextX == self.x and nextY == self.y) and canCreep(nextX, nextY, true) then + if not (nextX == self.x and nextY == self.y) and tCreepingDarkness.canCreep(nextX, nextY, true) then local distance = core.fov.distance(nextX, nextY, target.x, target.y) if distance < bestDistance then bestDistance, bestX, bestY = distance, nextX, nextY @@ -193,7 +78,7 @@ local function createDarkTendrils(summoner, x, y, target, damage, duration, pinD if bestX and bestY then self.x, self.y = bestX, bestY if not game.level.map:checkAllEntities(self.x, self.y, "creepingDark") then - createDark(self.summoner, self.x, self.y, damage, 3, 2, 33, 0) + tCreepingDarkness.createDark(self.summoner, self.x, self.y, damage, 3, 2, 33, 0) end if self.x == target.x and self.y == target.y then @@ -218,7 +103,7 @@ local function createDarkTendrils(summoner, x, y, target, damage, duration, pinD if dark then dark.duration = math.max(dark.duration, self.pinDuration + 1) for i = 1, 4 do - if rng.chance(50) then doCreep(dark, false) end + if rng.chance(50) then tCreepingDarkness.doCreep(tCreepingDarkness, dark, false) end end end @@ -263,6 +148,128 @@ newTalent{ hate = 1.5, range = 5, requires_target = true, + + -- implementation of creeping darkness..used in various locations, but stored here + canCreep = function(x, y, ignoreCreepingDark) + -- not on map + if not game.level.map:isBound(x, y) then return false end + -- already dark + if not ignoreCreepingDark then + if game.level.map:checkAllEntities(x, y, "creepingDark") then return false end + end + -- allow objects and terrain to block, but not actors + if game.level.map:checkAllEntities(x, y, "block_move") and not game.level.map(x, y, Map.ACTOR) then return false end + + return true + end, + doCreep = function(tCreepingDarkness, self, useCreep) + local start = rng.range(0, 8) + for i = start, start + 8 do + local x = self.x + (i % 3) - 1 + local y = self.y + math.floor((i % 9) / 3) - 1 + if not (x == self.x and y == self.y) and tCreepingDarkness.canCreep(x, y) then + -- add new dark + local newCreep + if useCreep then + -- transfer some of our creep to the new dark + newCreep = math.ceil(self.creep / 2) + self.creep = self.creep - newCreep + else + -- just clone our creep + newCreep = self.creep + end + tCreepingDarkness.createDark(self.summoner, x, y, self.damage, self.originalDuration, newCreep, self.creepChance, 0) + return true + end + + -- nowhere to creep + return false + end + end, + createDark = function(summoner, x, y, damage, duration, creep, creepChance, initialCreep) + local e = Object.new{ + name = "creeping dark", + block_sight=true, + canAct = false, + canCreep = true, + x = x, y = y, + damage = damage, + originalDuration = duration, + duration = duration, + creep = creep, + creepChance = creepChance, + summoner = summoner, + summoner_gain_exp = true, + act = function(self) + local Map = require "engine.Map" + + self:useEnergy() + + -- apply damage to anything inside the darkness + local actor = game.level.map(self.x, self.y, Map.ACTOR) + if actor and actor ~= self.summoner and (not actor.summoner or actor.summoner ~= self.summoner) then + self.summoner:project(actor, actor.x, actor.y, engine.DamageType.DARKNESS, self.damage) + --DamageType:get(DamageType.DARKNESS).projector(self.summoner, actor.x, actor.y, DamageType.DARKNESS, damage) + end + + if self.duration <= 0 then + -- remove + if self.particles then game.level.map:removeParticleEmitter(self.particles) end + game.level.map:remove(self.x, self.y, Map.TERRAIN+3) + game.level:removeEntity(self) + --game.level.map:redisplay() + else + self.duration = self.duration - 1 + + local tCreepingDarkness = self.summoner:getTalentFromId(self.summoner.T_CREEPING_DARKNESS) + + if self.canCreep and self.creep > 0 and rng.percent(self.creepChance) then + if not tCreepingDarkness.doCreep(tCreepingDarkness, self, true) then + -- doCreep failed..pass creep on to a neighbor and stop creeping + self.canCreep = false + local start = rng.range(0, 8) + for i = start, start + 8 do + local x = self.x + (i % 3) - 1 + local y = self.y + math.floor((i % 9) / 3) - 1 + if not (x == self.x and y == self.y) and tCreepingDarkness.canCreep(x, y) then + local dark = game.level.map:checkAllEntities(x, y, "creepingDark") + if dark and dark.canCreep then + -- transfer creep + dark.creep = dark.creep + self.creep + self.creep = 0 + return + end + end + end + end + end + end + end, + } + e.creepingDark = e -- used for checkAllEntities to return the dark Object itself + game.level:addEntity(e) + game.level.map(x, y, Map.TERRAIN+3, e) + + -- add particles + e.particles = Particles.new("creeping_dark", 1, { }) + e.particles.x = x + e.particles.y = y + game.level.map:addParticleEmitter(e.particles) + + -- do some initial creeping + if initialCreep > 0 then + local tCreepingDarkness = self.summoner:getTalentFromId(summoner.T_CREEPING_DARKNESS) + while initialCreep > 0 do + if not tCreepingDarkness.doCreep(tCreepingDarkness, e, false) then + e.canCreep = false + e.initialCreep = 0 + break + end + initialCreep = initialCreep - 1 + end + end + end, + getRadius = function(self, t) return 3 end, @@ -287,7 +294,7 @@ newTalent{ local locations = {} for darkX = x - dist, x + dist do for darkY = y - dist, y + dist do - if canCreep(darkX, darkY) then + if t.canCreep(darkX, darkY) then locations[#locations+1] = {darkX, darkY} end end @@ -300,7 +307,7 @@ newTalent{ local selection = rng.range(i, #locations) locations[i], locations[selection] = locations[selection], locations[i] - createDark(self, locations[i][1], locations[i][2], damage, 8, 4, 70, 0) + t.createDark(self, locations[i][1], locations[i][2], damage, 8, 4, 70, 0) end game:playSoundNear(self, "talents/breath") @@ -348,7 +355,7 @@ newTalent{ points = 5, random_ego = "attack", hate = 0.8, - cooldown = 3, + cooldown = 6, tactical = { ATTACK = 10, }, diff --git a/game/modules/tome/data/talents/cursed/punishments.lua b/game/modules/tome/data/talents/cursed/punishments.lua index 8546a93a5f5f32af6c708fdc34c4fea16ee4a12a..59f65b248c2a54b67e200be6630bfc4078858081 100644 --- a/game/modules/tome/data/talents/cursed/punishments.lua +++ b/game/modules/tome/data/talents/cursed/punishments.lua @@ -195,7 +195,7 @@ newTalent{ require = cursed_wil_req3, points = 5, random_ego = "attack", - cooldown = 3, + cooldown = 6, hate = 0.5, range = 12, getDuration = function(self, t) @@ -234,6 +234,54 @@ newTalent{ end, } +newTalent{ + name = "Madness", + type = {"cursed/punishments", 4}, + mode = "passive", + require = cursed_wil_req4, + points = 5, + getMindpower = function(self, t) + return math.sqrt(self:getTalentLevel(t)) * 0.4 * combatPower(self, t) + end, + getChance = function(self, t) + return 25 + end, + doMadness = function(self, t, src) + local mindpower = t.getMindpower(src, t) + local chance = t.getChance(src, t) + + if self and src and self:reactionToward(src) < 0 and self:checkHit(mindpower, self:combatMentalResist(), 0, chance, 5) then + local effect = rng.range(1, 3) + if effect == 1 then + -- confusion + if self:canBe("confusion") and not self:hasEffect(self.EFF_MADNESS_CONFUSED) then + self:setEffect(self.EFF_MADNESS_CONFUSED, 2, {power=70}) + hit = true + end + elseif effect == 2 then + -- stun + if self:canBe("stun") and not self:hasEffect(self.EFF_MADNESS_STUNNED) then + self:setEffect(self.EFF_MADNESS_STUNNED, 2, {}) + hit = true + end + elseif effect == 3 then + -- slow + if self:canBe("slow") and not self:hasEffect(self.EFF_MADNESS_SLOW) then + self:setEffect(self.EFF_MADNESS_SLOW, 2, {power=0.3}) + hit = true + end + end + end + end, + info = function(self, t) + local mindpower = t.getMindpower(self, t) + local chance = t.getChance(self, t) + return ([[Every time you inflict mental damage there is a %d%% chance that your foe must save against your mindpower or go mad. Madness can briefly cause them to become confused, slowed or stunned. (%d mindpower vs mental resistance). + The mindpower will increase with the Willpower stat.]]):format(chance, mindpower) + end, +} + +--[[ newTalent{ name = "Tortured Sanity", type = {"cursed/punishments", 4}, @@ -265,7 +313,7 @@ newTalent{ local target = game.level.map(x, y, Map.ACTOR) if target and self:reactionToward(target) < 0 then if target:canBe("stun") and rng.percent(chance) then - if target:checkHit(self:combatMindpower(), target:combatMentalResist(), 0, 95, 5) then + if target:checkHit(mindpower, target:combatMentalResist(), 0, 95, 5) then target:setEffect(target.EFF_DAZED, duration, {src=self}) game.level.map:particleEmitter(x, y, 1, "cursed_ground", {}) else @@ -282,8 +330,8 @@ newTalent{ local mindpower = t.getMindpower(self, t) local duration = t.getDuration(self, t) local chance = t.getChance(self, t) - return ([[Your will reaches into the minds of all nearby enemies and tortures their sanity. Anyone within range who fails a mental save has a %d%% chance of being dazed for %d turns (%d mindpower vs mental resistance). - The mindpower will increase with the Willpower stat.]]):format(chance, duration, mindpower) + return ([Your will reaches into the minds of all nearby enemies and tortures their sanity. Anyone within range who fails a mental save has a %d%% chance of being dazed for %d turns (%d mindpower vs mental resistance). + The mindpower will increase with the Willpower stat.]):format(chance, duration, mindpower) end, } - +]] diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua index 018d59dee2805526728fcbe0c46ed06297fc07b2..5a132ce26b803f98f512276b88ee67e1ccd20eb9 100644 --- a/game/modules/tome/data/timed_effects.lua +++ b/game/modules/tome/data/timed_effects.lua @@ -368,7 +368,7 @@ newEffect{ newEffect{ name = "DAZED", desc = "Dazed", - long_desc = function(self, eff) return "The target is dazed, redering it unable to act. Any damage will remove the daze." end, + long_desc = function(self, eff) return "The target is dazed, rendering it unable to act. Any damage will remove the daze." end, type = "physical", status = "detrimental", parameters = {}, @@ -1564,7 +1564,7 @@ newEffect{ newEffect{ name = "GLOOM_STUNNED", desc = "Stunned by the gloom", - long_desc = function(self, eff) return "The gloom has stunned the target, redering it unable to act." end, + long_desc = function(self, eff) return "The gloom has stunned the target, rendering it unable to act." end, type = "mental", status = "detrimental", parameters = {}, @@ -2354,245 +2354,74 @@ newEffect{ } newEffect{ - name = "FEED_HATE", - desc = "Feeding Hate", - long_desc = function(self, eff) return ("%s is feeding %0.2f hate from %s."):format(self.name:capitalize(), eff.hateGain, eff.target.name) end, + name = "FEED", + desc = "Feeding", + long_desc = function(self, eff) return ("%s is feeding from %s."):format(self.name:capitalize(), eff.target.name) end, type = "mental", status = "beneficial", parameters = { }, activate = function(self, eff) - eff.hateGainId = self:addTemporaryValue("hate_regen", eff.hateGain) - - eff.extension = eff.extension or 0 - eff.isSevered = false - end, - deactivate = function(self, eff) - if eff.hateGainId then self:removeTemporaryValue("hate_regen", eff.hateGainId) end - - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - eff.particles = nil + -- hate + if eff.hateGain and eff.hateGain > 0 then + eff.hateGainId = self:addTemporaryValue("hate_regen", eff.hateGain) end - end, - on_timeout = function(self, eff) - if eff.isSevered then - eff.extension = eff.extension - 1 - if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_HATE) - end - elseif eff.target.dead or not self:hasLOS(eff.target.x, eff.target.y) then - eff.isSevered = true - - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - eff.particles = nil - end - - if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_HATE) - end - else - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - end - -- add updated particle emitter - local dx, dy = eff.target.x - self.x, eff.target.y - self.y - eff.particles = Particles.new("feed_hate", math.max(math.abs(dx), math.abs(dy)), { tx=dx, ty=dy }) - eff.particles.x = self.x - eff.particles.y = self.y - game.level.map:addParticleEmitter(eff.particles) - end - end, -} - -newEffect{ - name = "FEED_HEALTH", - desc = "Feeding Health", - long_desc = function(self, eff) - if eff.lifeRegenGain and eff.lifeRegenGain > 0 then - return ("#Target# is feeding %d constitution and %0.1f life per turn from %s."):format(eff.constitutionGain, eff.lifeRegenGain, eff.target.name) - else - return ("#Target# is feeding %d constitution from %s."):format(eff.constitutionGain, eff.target.name) + + -- health + if eff.constitutionGain and eff.constitutionGain > 0 then + eff.constitutionGainId = self:addTemporaryValue("inc_stats", + { + [Stats.STAT_CON] = eff.constitutionGain, + }) + eff.constitutionLossId = eff.target:addTemporaryValue("inc_stats", + { + [Stats.STAT_CON] = -eff.constitutionGain, + }) end - end, - type = "mental", - status = "beneficial", - parameters = { }, - activate = function(self, eff) - eff.constitutionGainId = self:addTemporaryValue("inc_stats", - { - [Stats.STAT_CON] = eff.constitutionGain, - }) - eff.constitutionLossId = eff.target:addTemporaryValue("inc_stats", - { - [Stats.STAT_CON] = -eff.constitutionGain, - }) - if eff.lifeRegenGain and eff.lifeRegenGain > 0 then eff.lifeRegenGainId = self:addTemporaryValue("life_regen", eff.lifeRegenGain) eff.lifeRegenLossId = eff.target:addTemporaryValue("life_regen", -eff.lifeRegenGain) end - - eff.extension = eff.extension or 0 - eff.isSevered = false - end, - deactivate = function(self, eff) - if eff.constitutionGainId then self:removeTemporaryValue("inc_stats", eff.constitutionGainId) end - if eff.constitutionLossId then eff.target:removeTemporaryValue("inc_stats", eff.constitutionLossId) end - if eff.lifeRegenGainId then self:removeTemporaryValue("life_regen", eff.lifeRegenGainId) end - if eff.lifeRegenLossId then eff.target:removeTemporaryValue("life_regen", eff.lifeRegenLossId) end - - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - eff.particles = nil + + -- power + if eff.damageGain and eff.damageGain > 0 then + eff.damageGainId = self:addTemporaryValue("inc_damage", {all=eff.damageGain}) + eff.damageLossId = eff.target:addTemporaryValue("inc_damage", {all=eff.damageLoss}) end - end, - on_timeout = function(self, eff) - if eff.isSevered then - eff.extension = eff.extension - 1 - if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_HEALTH) - end - elseif eff.target.dead or not self:hasLOS(eff.target.x, eff.target.y) then - eff.isSevered = true - - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - eff.particles = nil + + -- strengths + if eff.resistGain and eff.resistGain > 0 then + local gainList = {} + local lossList = {} + for id, resist in pairs(eff.target.resists) do + if resist > 0 then + local amount = eff.resistGain * 0.01 * resist + gainList[id] = amount + lossList[id] = -amount + end end - eff.target:removeTemporaryValue("inc_stats", eff.constitutionLossId) - eff.constitutionLossId = nil - eff.target:removeTemporaryValue("life_regen", eff.lifeRegenLossId) - eff.lifeRegenLossId = nil - - if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_HEALTH) - end - else - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - end - -- add updated particle emitter - local dx, dy = eff.target.x - self.x, eff.target.y - self.y - eff.particles = Particles.new("feed_health", math.max(math.abs(dx), math.abs(dy)), { tx=dx, ty=dy }) - eff.particles.x = self.x - eff.particles.y = self.y - game.level.map:addParticleEmitter(eff.particles) + eff.resistGainId = self:addTemporaryValue("resists", gainList) + eff.resistLossId = eff.target:addTemporaryValue("resists", lossList) end - end, -} -newEffect{ - name = "FEED_POWER", - desc = "Feeding Power", - long_desc = function(self, eff) - return ("%s is feeding %d%% increased damage from %s."):format(self.name:capitalize(), eff.damageGain, eff.target.name) - end, - type = "mental", - status = "beneficial", - parameters = { }, - activate = function(self, eff) - eff.damageGainId = self:addTemporaryValue("inc_damage", {all=eff.damageGain}) - eff.damageLossId = eff.target:addTemporaryValue("inc_damage", {all=eff.damageLoss}) eff.extension = eff.extension or 0 eff.isSevered = false end, deactivate = function(self, eff) + -- hate + if eff.hateGainId then self:removeTemporaryValue("hate_regen", eff.hateGainId) end + + -- health + if eff.constitutionGainId then self:removeTemporaryValue("inc_stats", eff.constitutionGainId) end + if eff.constitutionLossId then eff.target:removeTemporaryValue("inc_stats", eff.constitutionLossId) end + if eff.lifeRegenGainId then self:removeTemporaryValue("life_regen", eff.lifeRegenGainId) end + if eff.lifeRegenLossId then eff.target:removeTemporaryValue("life_regen", eff.lifeRegenLossId) end + + -- power if eff.damageGainId then self:removeTemporaryValue("inc_damage", eff.damageGainId) end if eff.damageLossId then eff.target:removeTemporaryValue("inc_damage", eff.damageLossId) end - - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - eff.particles = nil - end - end, - on_timeout = function(self, eff) - if eff.isSevered then - eff.extension = eff.extension - 1 - if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_POWER) - end - elseif eff.target.dead or not self:hasLOS(eff.target.x, eff.target.y) then - eff.isSevered = true - - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - eff.particles = nil - end - - eff.target:removeTemporaryValue("inc_damage", eff.damageLossId) - eff.damageLossId = nil - - if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_POWER) - end - else - if eff.particles then - -- remove old particle emitter - eff.particles.x = nil - eff.particles.y = nil - game.level.map:removeParticleEmitter(eff.particles) - end - -- add updated particle emitter - local dx, dy = eff.target.x - self.x, eff.target.y - self.y - eff.particles = Particles.new("feed_power", math.max(math.abs(dx), math.abs(dy)), { tx=dx, ty=dy }) - eff.particles.x = self.x - eff.particles.y = self.y - game.level.map:addParticleEmitter(eff.particles) - end - end, -} - -newEffect{ - name = "FEED_STRENGTHS", - desc = "Feeding Strengths", - long_desc = function(self, eff) return ("%s is feeding %d%% or resistances %s."):format(self.name:capitalize(), eff.resistGain, eff.target.name) end, - type = "mental", - status = "beneficial", - parameters = { }, - activate = function(self, eff) - local gainList = {} - local lossList = {} - for id, resist in pairs(eff.target.resists) do - if resist > 0 then - local amount = eff.resistGain * 0.01 * resist - gainList[id] = amount - lossList[id] = -amount - end - end - - eff.resistGainId = self:addTemporaryValue("resists", gainList) - eff.resistLossId = eff.target:addTemporaryValue("resists", lossList) - - eff.extension = eff.extension or 0 - eff.isSevered = false - end, - deactivate = function(self, eff) + + -- strengths if eff.resistGainId then self:removeTemporaryValue("resists", eff.resistGainId) end if eff.resistLossId then eff.target:removeTemporaryValue("resists", eff.resistLossId) end @@ -2608,7 +2437,7 @@ newEffect{ if eff.isSevered then eff.extension = eff.extension - 1 if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_STRENGTHS) + self:removeEffect(self.EFF_FEED) end elseif eff.target.dead or not self:hasLOS(eff.target.x, eff.target.y) then eff.isSevered = true @@ -2621,11 +2450,8 @@ newEffect{ eff.particles = nil end - if eff.resistLossId then eff.target:removeTemporaryValue("resists", eff.resistLossId) end - eff.resistLossId = nil - if eff.extension <= 0 then - self:removeEffect(self.EFF_FEED_STRENGTHS) + self:removeEffect(self.EFF_FEED) end else if eff.particles then @@ -2636,7 +2462,7 @@ newEffect{ end -- add updated particle emitter local dx, dy = eff.target.x - self.x, eff.target.y - self.y - eff.particles = Particles.new("feed_strengths", math.max(math.abs(dx), math.abs(dy)), { tx=dx, ty=dy }) + eff.particles = Particles.new("feed_hate", math.max(math.abs(dx), math.abs(dy)), { tx=dx, ty=dy }) eff.particles.x = self.x eff.particles.y = self.y game.level.map:addParticleEmitter(eff.particles) @@ -2688,6 +2514,68 @@ newEffect{ end, } +newEffect{ + name = "MADNESS_SLOW", + desc = "Slowed by madness", + long_desc = function(self, eff) return ("Madness reduces the target's global speed by %d%%."):format((1 / (1 - eff.power) - 1) * 100) end, + type = "mental", + status = "detrimental", + parameters = { power=0.1 }, + on_gain = function(self, err) return "#F53CBE##Target# slows in the grip of madness!", "+Slow" end, + on_lose = function(self, err) return "#Target# overcomes the madness.", "-Slow" end, + activate = function(self, eff) + eff.particle = self:addParticles(Particles.new("gloom_slow", 1)) + eff.tmpid = self:addTemporaryValue("energy", {mod=-eff.power}) + eff.dur = self:updateEffectDuration(eff.dur, "slow") + end, + deactivate = function(self, eff) + self:removeTemporaryValue("energy", eff.tmpid) + self:removeParticles(eff.particle) + end, +} + +newEffect{ + name = "MADNESS_STUNNED", + desc = "Stunned by madness", + long_desc = function(self, eff) return "Madness has stunned the target, rendering it unable to act." end, + type = "mental", + status = "detrimental", + parameters = {}, + on_gain = function(self, err) return "#F53CBE##Target# is paralyzed by madness!", "+Stunned" end, + on_lose = function(self, err) return "#Target# overcomes the madness", "-Stunned" end, + activate = function(self, eff) + eff.particle = self:addParticles(Particles.new("gloom_stunned", 1)) + eff.tmpid = self:addTemporaryValue("stunned", 1) + -- Start the stun counter only if this is the first stun + if self.stunned == 1 then self.stunned_counter = (self:attr("stun_immune") or 0) * 100 end + eff.dur = self:updateEffectDuration(eff.dur, "stun") + end, + deactivate = function(self, eff) + self:removeParticles(eff.particle) + self:removeTemporaryValue("stunned", eff.tmpid) + if not self:attr("stunned") then self.stunned_counter = nil end + end, +} + +newEffect{ + name = "MADNESS_CONFUSED", + desc = "Confused by madness", + long_desc = function(self, eff) return ("Madness has confused the target, making it act randomly (%d%% chance) and unable to perform complex actions."):format(eff.power) end, + type = "mental", + status = "detrimental", + parameters = {}, + on_gain = function(self, err) return "#F53CBE##Target# is lost in madness!", "+Confused" end, + on_lose = function(self, err) return "#Target# overcomes the madness", "-Confused" end, + activate = function(self, eff) + eff.particle = self:addParticles(Particles.new("gloom_confused", 1)) + eff.tmpid = self:addTemporaryValue("confused", eff.power) + eff.dur = self:updateEffectDuration(eff.dur, "confusion") + end, + deactivate = function(self, eff) + self:removeParticles(eff.particle) + self:removeTemporaryValue("confused", eff.tmpid) + end, +} newEffect{ name = "TOTALITY", diff --git a/game/modules/tome/data/zones/vor-pride/npcs.lua b/game/modules/tome/data/zones/vor-pride/npcs.lua index 82b629477f928002007436765abfe9a456a8d5d0..8ec80fec15b52e3f8cad9d4411b087e0491acfff 100644 --- a/game/modules/tome/data/zones/vor-pride/npcs.lua +++ b/game/modules/tome/data/zones/vor-pride/npcs.lua @@ -52,7 +52,7 @@ newEntity{ base="BASE_NPC_ORC_VOR", define_as = "VOR", }, resolvers.drops{chance=100, nb=1, {defined="ORB_ELEMENTS"} }, resolvers.drops{chance=20, nb=1, {defined="JEWELER_TOME"} }, - resolvers.drops{chance=1020, nb=1, {defined="NOTE_LORE"} }, + resolvers.drops{chance=100, nb=1, {defined="NOTE_LORE"} }, resolvers.drops{chance=100, nb=5, {ego_chance=100} }, resolvers.talents{ diff --git a/game/xmpp/init.lua b/game/xmpp/init.lua index 3d544f5f24283144065ca6cd2f425e81eb54c71d..98076226639784149252aa42e7f628743e58599d 100644 --- a/game/xmpp/init.lua +++ b/game/xmpp/init.lua @@ -1,56 +1,6 @@ -print("XMPP thread starting...") +print("TE4Online starting...") -local jid, password = "test@online.te4.org", "test" +require "socket" -require "verse" -- Verse main library -require "verse.client" -- XMPP client library - --- We always connect at least to the general channel -local channels = { general = true } - -c = verse.new() -c:add_plugin("version") -c:add_plugin("disco") -c:add_plugin("pep") - --- Add some hooks for debugging -c:hook("opened", function() - print("Stream opened!") -end) -c:hook("closed", function() - print("Stream closed!") -end) -c:hook("stanza", function(stanza) --- print("Stanza:", stanza) -end) - --- This one prints all received data ---c:hook("incoming-raw", print, 1000) - --- Print a message after authentication -c:hook("authentication-success", function() print("Logged in!") end) -c:hook("authentication-failure", function(err) print("Failed to log in! Error: "..tostring(err.condition)) end) - --- Print a message and exit when disconnected -c:hook("disconnected", function() - print("Disconnected!") -end) - --- Now, actually start the connection: -c:connect_client(jid, password) - --- Catch the "ready" event to know when the stream is ready to use -c:hook("ready", function() - print("Stream ready!") - c.version:set{name = "T-Engine4 XMPP Client"} - - c:hook_pep("http://jabber.org/protocol/te4chat", function(event) - if channels[event.item.tags[1][1]] then - print(event.from, "says", event.item.tags[2][1]) - end --- core.xmpp. - end) - c:send(verse.presence():add_child(c:caps())) -end) - -verse.loop() +local sock = socket.connect("te4.org", 5122) +if not sock then return end