From 2b8070db3a5082c870e3b52ba0422f45f8688b32 Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Sat, 27 Mar 2010 17:39:53 +0000 Subject: [PATCH] all summoner talents done git-svn-id: http://svn.net-core.org/repos/t-engine4@466 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/engine/interface/ActorLife.lua | 3 + game/modules/tome/ai/heal.lua | 58 +++++++ .../tome/data/birth/classes/wilder.lua | 4 +- .../tome/data/general/objects/scrolls.lua | 2 +- .../modules/tome/data/talents/gifts/gifts.lua | 18 ++ .../talents/gifts/summon-augmentation.lua | 97 ++++++++++- .../data/talents/gifts/summon-distance.lua | 26 +-- .../tome/data/talents/gifts/summon-melee.lua | 20 ++- .../data/talents/gifts/summon-utility.lua | 161 ++++++++++++++---- ideas/gifts.ods | Bin 27515 -> 27532 bytes 10 files changed, 328 insertions(+), 61 deletions(-) create mode 100644 game/modules/tome/ai/heal.lua diff --git a/game/engine/interface/ActorLife.lua b/game/engine/interface/ActorLife.lua index bd3161e16c..e1e23fd6f6 100644 --- a/game/engine/interface/ActorLife.lua +++ b/game/engine/interface/ActorLife.lua @@ -129,6 +129,9 @@ function _M:project(t, x, y, damtype, dam, particles) for px, ys in pairs(grids) do for py, _ in pairs(ys) do damtype(px, py) + if particles then + game.level.map:particleEmitter(px, py, 1, particles.type) + end end end else diff --git a/game/modules/tome/ai/heal.lua b/game/modules/tome/ai/heal.lua new file mode 100644 index 0000000000..30a5f1b97f --- /dev/null +++ b/game/modules/tome/ai/heal.lua @@ -0,0 +1,58 @@ +-- Defines AIs that can heal + +-- Find an friendly target +-- this requires the ActorFOV interface, or an interface that provides self.fov.actors* +newAI("target_heal", function(self) + if self.ai_target.actor and not self.ai_target.actor.dead and rng.percent(90) then return true end + + -- Find closer ennemy and target it + -- Get list of actors ordered by distance + local arr = self.fov.actors_dist + local act + for i = 1, #arr do + act = self.fov.actors_dist[i] +-- print("AI looking for target", self.uid, self.name, "::", act.uid, act.name, self.fov.actors[act].sqdist) + -- find the closest ennemy + if act and self:reactionToward(act) >= 0 and not act.dead then + self.ai_target.actor = act + return true + end + end +end) + +-- Randomly use talents +newAI("dumb_heal", function(self) + -- Find available talents + local avail = {} + local target_dist = math.floor(core.fov.distance(self.x, self.y, self.ai_target.actor.x, self.ai_target.actor.y)) + for tid, _ in pairs(self.talents) do + local t = self:getTalentFromId(tid) +-- print(self.name, self.uid, "dumb ai talents can try use", t.name, tid, "::", t.mode, not self:isTalentCoolingDown(t), target_dist <= self:getTalentRange(t), self:preUseTalent(t, true), self:canProject({type="bolt"}, self.ai_target.actor.x, self.ai_target.actor.y)) + if t.mode == "activated" and not self:isTalentCoolingDown(t) and target_dist <= self:getTalentRange(t) and self:preUseTalent(t, true) and self:canProject({type="bolt"}, self.ai_target.actor.x, self.ai_target.actor.y) then + avail[#avail+1] = tid + print(self.name, self.uid, "dumb ai talents can use", t.name, tid) + elseif t.mode == "sustained" and not self:isTalentCoolingDown(t) and not self:isTalentActive(t) and self:preUseTalent(t, true) then + avail[#avail+1] = tid + print(self.name, self.uid, "dumb ai talents can activate", t.name, tid) + end + end + if #avail > 0 then + local tid = avail[rng.range(1, #avail)] + print("dumb ai uses", tid) + self:useTalent(tid) + return true + end +end) + +newAI("dumb_heal_simple", function(self) + if self:runAI("target_heal") then + -- One in "talent_in" chance of using a talent + if rng.chance(self.ai_state.talent_in or 6) and self:reactionToward(self.ai_target.actor) >= 0 then + self:runAI("dumb_heal") + end + if not self.energy.used then + self:runAI(self.ai_state.ai_move or "move_simple") + end + return true + end +end) diff --git a/game/modules/tome/data/birth/classes/wilder.lua b/game/modules/tome/data/birth/classes/wilder.lua index 4374343570..33b1844b6b 100644 --- a/game/modules/tome/data/birth/classes/wilder.lua +++ b/game/modules/tome/data/birth/classes/wilder.lua @@ -24,9 +24,9 @@ newBirthDescriptor{ desc = { "Summoners never fight alone, they are always ready to invoke one of their many summons to fight at their side.", "Summons can range from a combat hound to a fire drake.", - "Their most important stats are: Willpower and Constitution", + "Their most important stats are: Willpower and Cunning", }, - stats = { wil=3, con=2, cun=1, }, + stats = { wil=3, cun=2, con=1, }, talents_types = { ["wild-gift/call"]={true, 0.2}, ["wild-gift/summon-melee"]={true, 0.3}, diff --git a/game/modules/tome/data/general/objects/scrolls.lua b/game/modules/tome/data/general/objects/scrolls.lua index c8d4e165d4..8128b72af6 100644 --- a/game/modules/tome/data/general/objects/scrolls.lua +++ b/game/modules/tome/data/general/objects/scrolls.lua @@ -17,7 +17,7 @@ newEntity{ base = "BASE_SCROLL", cost = 1, use_simple = { name="light up the surrounding area", use = function(self, who) - who:project({type="ball", range=0, friendlyfire=false, radius=15}, who.x, who.y, DamageType.LIGHT, 1) + who:project({type="ball", range=0, friendlyfire=false, radius=15}, who.x, who.y, engine.DamageType.LIGHT, 1) game.logSeen(who, "%s reads a %s!", who.name:capitalize(), self:getName()) return "destroy", true end} diff --git a/game/modules/tome/data/talents/gifts/gifts.lua b/game/modules/tome/data/talents/gifts/gifts.lua index 805e72f83f..6d83b39451 100644 --- a/game/modules/tome/data/talents/gifts/gifts.lua +++ b/game/modules/tome/data/talents/gifts/gifts.lua @@ -33,10 +33,28 @@ gifts_req5 = { } load("/data/talents/gifts/call.lua") + load("/data/talents/gifts/slime.lua") + load("/data/talents/gifts/sand-drake.lua") load("/data/talents/gifts/fire-drake.lua") load("/data/talents/gifts/cold-drake.lua") + +function checkMaxSummon(self) + local nb = 0 + for _, e in pairs(game.level.entities) do + if e.summoner and e.summoner == self then nb = nb + 1 end + end + + local max = math.max(1, math.floor(self:getCun() / 10)) + if nb >= max then + game.logPlayer(self, "#PINK#You can not summon any more, you have too many summons already (%d). You can increase the limit with higher Cunning.", nb) + return true + else + return false + end +end + load("/data/talents/gifts/summon-melee.lua") load("/data/talents/gifts/summon-distance.lua") load("/data/talents/gifts/summon-utility.lua") diff --git a/game/modules/tome/data/talents/gifts/summon-augmentation.lua b/game/modules/tome/data/talents/gifts/summon-augmentation.lua index 25c30a12b2..0b63065fe4 100644 --- a/game/modules/tome/data/talents/gifts/summon-augmentation.lua +++ b/game/modules/tome/data/talents/gifts/summon-augmentation.lua @@ -4,17 +4,104 @@ newTalent{ require = gifts_req1, points = 5, equilibrium = 5, + cooldown = 15, + range = 20, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + local tx, ty, target = self:getTarget(tg) + if not tx or not ty or not target or not target.summoner or not target.summoner == self then return nil end + target:setEffect(target.EFF_ALL_STAT, 5, {power=2+math.floor(self:getTalentLevel(t) * 2)}) + return true + end, + info = function(self, t) + return ([[Induces a killing rage in one of your summons, increasing its stats by %d.]]):format(2+math.floor(self:getTalentLevel(t) * 2)) + end, +} + +newTalent{ + name = "Detonate", + type = {"wild-gift/summon-augmentation", 2}, + require = gifts_req2, + points = 5, + equilibrium = 5, + cooldown = 25, + range = 20, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + local tx, ty, target = self:getTarget(tg) + if not tx or not ty or not target or not target.summoner or not target.summoner == self then return nil end + + if target.name == "fire imp" then + local tg = {type="ball", range=self:getTalentRange(t), radius=1 + self:getTalentLevelRaw(t), talent=t} + target:project(tg, target.x, target.y, DamageType.FIRE, 28 + self:getWil(32) * self:getTalentLevel(t), {type="flame"}) + target:die() + elseif target.name == "black jelly" then + local tg = {type="ball", range=self:getTalentRange(t), radius=1 + self:getTalentLevelRaw(t), talent=t} + target:project(tg, target.x, target.y, DamageType.SLIME, 18 + self:getWil(22) * self:getTalentLevel(t), {type="slime"}) + target:die() + elseif target.name == "benevolent spirit" then + local tg = {type="ball", range=self:getTalentRange(t), radius=1 + self:getTalentLevelRaw(t), talent=t} + target:project(tg, target.x, target.y, function(tx, ty) + local a = game.level.map(tx, ty, Map.ACTOR) + if a and self:reactionToward(a) >= 0 then + a:heal(10 + self:getWil(30) * self:getTalentLevel(t)) + end + end, nil, {type="acid"}) + target:die() + else + game.logPlayer("You may not detonate this summon.") + return nil + end + return true + end, + info = function(self, t) + return ([[Destroys one of your summons, make it detonate. + Only some summons can be detonated: + - Fire Imp: Explodes into a fireball + - Jelly: Explodes into a ball of slowing slime + - Benevolent Spirit: Explodes into a ball of healing + The effect improves with your Willpower.]]):format() + end, +} + +newTalent{ + name = "Resilience", + type = {"wild-gift/summon-augmentation", 3}, + require = gifts_req3, + mode = "passive", + points = 5, + info = function(self, t) + return ([[Improves all your summons life time and Constitution.]]) + end, +} + +newTalent{ + name = "Phase Summon", + type = {"wild-gift/summon-augmentation", 4}, + require = gifts_req4, + points = 5, + equilibrium = 5, cooldown = 25, range = 20, action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t), nolock=true, talent=t} + local tg = {type="hit", range=self:getTalentRange(t), talent=t} local tx, ty, target = self:getTarget(tg) - if not tx or not ty or not target then return nil end - game.logPlayer("IMPLEMENT ME") + if not tx or not ty or not target or not target.summoner or not target.summoner == self then return nil end + + local dur = 1 + self:getTalentLevel(t) + self:setEffect(self.EFF_EVASION, dur, {chance=50}) + target:setEffect(target.EFF_EVASION, dur, {chance=50}) + + -- Displace + game.level.map:remove(self.x, self.y, Map.ACTOR) + game.level.map:remove(target.x, target.y, Map.ACTOR) + game.level.map(self.x, self.y, Map.ACTOR, target) + game.level.map(target.x, target.y, Map.ACTOR, self) + self.x, self.y, target.x, target.y = target.x, target.y, self.x, self.y + return true end, info = function(self, t) - return ([[Induces a killing rage in one of you melee summons, increase your target's damage by %d. - Damage increase improves with your Magic stat.]]):format(10 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) + return ([[Switches places with one of your summons. This disorients your foes, granting both you and your summon 50%% evasion for %d turns.]]):format(1 + self:getTalentLevel(t)) end, } diff --git a/game/modules/tome/data/talents/gifts/summon-distance.lua b/game/modules/tome/data/talents/gifts/summon-distance.lua index fddb2cc785..46ba232804 100644 --- a/game/modules/tome/data/talents/gifts/summon-distance.lua +++ b/game/modules/tome/data/talents/gifts/summon-distance.lua @@ -103,6 +103,7 @@ newTalent{ cooldown = 10, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -124,7 +125,7 @@ newTalent{ desc = [[]], autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=1, }, - stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=18, con=10 + self:getTalentLevel(t) * 2, }, + stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=10 + self:getTalentLevel(t) * 2, con=10+ self:getTalentLevelRaw(self.T_RESILIENCE)*2, }, level_range = {self.level, self.level}, exp_worth = 0, max_life = resolvers.rngavg(5,10), @@ -139,7 +140,7 @@ newTalent{ }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -166,6 +167,7 @@ newTalent{ cooldown = 10, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -187,7 +189,7 @@ newTalent{ desc = [[A strange reptilian creature with three smouldering heads.]], autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=1, }, - stats = { wil=15 + self:getWil() * self:getTalentLevel(t) / 5, str=18, }, + stats = { wil=15 + self:getWil() * self:getTalentLevel(t) / 5, str=18, con=10 + self:getTalentLevel(t) * 2 + self:getTalentLevelRaw(self.T_RESILIENCE)*2}, level_range = {self.level, self.level}, exp_worth = 0, max_life = resolvers.rngavg(5,10), @@ -204,7 +206,7 @@ newTalent{ }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -217,7 +219,7 @@ newTalent{ end, info = function(self, t) return ([[Summon a 3-headed Hydra to destroy your foes. 3-headed hydras are able to breath poison, acid and lightning. - It will get %d magic and %d willpower.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) + It will get %d willpower and %d constitution.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) end, } @@ -231,6 +233,7 @@ newTalent{ cooldown = 10, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -253,7 +256,7 @@ newTalent{ desc = [[It looks like a hole in reality, the Warper disrupts the normal flow of space and time.]], autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=1, }, - stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=18, con=10 + self:getTalentLevel(t) * 2, }, + stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=10 + self:getTalentLevel(t) * 2, con=10+self:getTalentLevelRaw(self.T_RESILIENCE) * 2, }, level_range = {self.level, self.level}, exp_worth = 0, max_life = resolvers.rngavg(5,10), @@ -270,7 +273,7 @@ newTalent{ }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -282,7 +285,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Summon a Fire Imp to burn your foes to death. Fire Imps are really weak in melee and die easily, but they can burn your foes from afar. + return ([[Summon a Warper to arras your foes. Warpers are really weak in melee and die easily, but they can blink around, throwing manathursts and time prisons at your foes. It will get %d magic and %d willpower.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) end, } @@ -297,6 +300,7 @@ newTalent{ cooldown = 10, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -318,7 +322,7 @@ newTalent{ desc = [[A mighty fire drake, an Uruloki.]], autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=1, }, - stats = { str=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=38, con=20 + self:getTalentLevel(t) * 3, }, + stats = { str=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=38, con=20 + self:getTalentLevel(t) * 3 + self:getTalentLevelRaw(self.T_RESILIENCE) * 2, }, level_range = {self.level, self.level}, exp_worth = 0, max_life = resolvers.rngavg(100, 150), @@ -335,7 +339,7 @@ newTalent{ }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 2, + summon_time = math.ceil(self:getTalentLevel(t)) + 2 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -348,6 +352,6 @@ newTalent{ end, info = function(self, t) return ([[Summon a Fire Drake to burn and crush your foes to death. Fire Drakes a behemoths that can burn your foes from afar with their fiery breath. - It will get %d strenght and %d willpower.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) + It will get %d strengh and %d constitution.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) end, } diff --git a/game/modules/tome/data/talents/gifts/summon-melee.lua b/game/modules/tome/data/talents/gifts/summon-melee.lua index c55672f355..102c68ebd8 100644 --- a/game/modules/tome/data/talents/gifts/summon-melee.lua +++ b/game/modules/tome/data/talents/gifts/summon-melee.lua @@ -8,6 +8,7 @@ newTalent{ cooldown = 15, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -29,7 +30,7 @@ newTalent{ desc = [[]], autolevel = "warrior", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=5, }, - stats = { str=10 + self:getWil() * self:getTalentLevel(t) / 5, dex=10 + self:getTalentLevel(t) * 2, mag=5, con=15 }, + stats = { str=10 + self:getWil() * self:getTalentLevel(t) / 5, dex=10 + self:getTalentLevel(t) * 2, mag=5, con=15 + self:getTalentLevelRaw(self.T_RESILIENCE)*2 }, level_range = {self.level, self.level}, exp_worth = 0, max_life = resolvers.rngavg(25,50), @@ -39,7 +40,7 @@ newTalent{ combat = { dam=resolvers.rngavg(12,25), atk=10, apr=10, dammod={str=0.8} }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -66,6 +67,7 @@ newTalent{ cooldown = 10, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -86,7 +88,7 @@ newTalent{ desc = "A strange blob on the dungeon floor.", name = "black jelly", autolevel = "none", faction=self.faction, - stats = { con=10 + self:getWil() * self:getTalentLevel(t) / 5, str=10 + self:getTalentLevel(t) * 2 }, + stats = { con=10 + self:getWil() * self:getTalentLevel(t) / 5 + self:getTalentLevelRaw(self.T_RESILIENCE) * 3, str=10 + self:getTalentLevel(t) * 2 }, resists = { [DamageType.LIGHT] = -50 }, ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=5, }, level_range = {self.level, self.level}, exp_worth = 0, @@ -100,7 +102,7 @@ newTalent{ combat = { dam=8, atk=15, apr=5, damtype=DamageType.ACID, dammod={str=0.7} }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -127,6 +129,7 @@ newTalent{ cooldown = 15, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -155,7 +158,7 @@ newTalent{ autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=5, }, energy = { mod=1.2 }, - stats = { str=25 + self:getWil() * self:getTalentLevel(t) / 5, dex=18, con=10 + self:getTalentLevel(t) * 2, }, + stats = { str=25 + self:getWil() * self:getTalentLevel(t) / 5, dex=18, con=10 + self:getTalentLevel(t) * 2 + self:getTalentLevelRaw(self.T_RESILIENCE)*2, }, resolvers.tmasteries{ ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3, ["technique/combat-training"]=0.3, }, desc = [[It is a cross between a human and a bull.]], @@ -167,7 +170,7 @@ newTalent{ faction = self.faction, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t) / 2) + 2, + summon_time = math.ceil(self:getTalentLevel(t) / 2) + 2 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -194,6 +197,7 @@ newTalent{ cooldown = 20, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -221,7 +225,7 @@ newTalent{ autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, }, - stats = { str=25 + self:getWil() * self:getTalentLevel(t) / 5, dex=18, con=10 + self:getTalentLevel(t) * 2, }, + stats = { str=25 + self:getWil() * self:getTalentLevel(t) / 5, dex=18, con=10 + self:getTalentLevel(t) * 2 + self:getTalentLevelRaw(self.T_RESILIENCE)*2, }, desc = [[It is a massive animated statue.]], level_range = {self.level, self.level}, exp_worth = 0, @@ -234,7 +238,7 @@ newTalent{ faction = self.faction, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } diff --git a/game/modules/tome/data/talents/gifts/summon-utility.lua b/game/modules/tome/data/talents/gifts/summon-utility.lua index e7341d3079..1d80e3d6a5 100644 --- a/game/modules/tome/data/talents/gifts/summon-utility.lua +++ b/game/modules/tome/data/talents/gifts/summon-utility.lua @@ -1,7 +1,6 @@ newTalent{ name = "Summon End", type = {"wild-gift/other",1}, - require = gifts_req1, points = 1, action = function(self, t) self:die() @@ -12,6 +11,98 @@ newTalent{ end, } +newTalent{ + name = "Taunt", + type = {"technique/other",1}, + points = 1, + cooldown = 5, + action = function(self, t) + local tg = {type="ball", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false, talent=t} + self:project(tg, self.x, self.y, function(tx, ty) + local a = game.level.map(tx, ty, Map.ACTOR) + if a and self:reactionToward(a) < 0 then + a:setTarget(self) + end + end) + return true + end, + info = function(self, t) + return ([[Forces hostile foes to attack you.]]) + end, +} + +newTalent{ short_name="SPIDER_WEB", + name = "Web", + type = {"wild-gift/other",1}, + points = 5, + equilibrium = 5, + cooldown = 3, + range=10, + action = function(self, t) + local tg = {type="bolt", range=self:getTalentRange(t), talent=t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, function(tx, ty) + local target = game.level.map(tx, ty, Map.ACTOR) + if target and target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 7) then + target:setEffect(target.EFF_PINNED, 3 + self:getTalentLevel(t), {}) + end + end) + return true + end, + info = function(self, t) + return ([[Spread a web and throw it toward your target. If caught it wont be able to move for %d turns.]]):format(3 + self:getTalentLevel(t)) + end, +} + +newTalent{ short_name="HEAL_OTHER", + name = "Heal Other", + type = {"wild-gift/other",1}, + points = 5, + equilibrium = 5, + cooldown = 4, + range=14, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, function(tx, ty) + local target = game.level.map(tx, ty, Map.ACTOR) + if target then + target:heal(15 + self:getMag(40) * self:getTalentLevel(t)) + end + end) + return true + end, + info = function(self, t) + return ([[Heal your target for %d life.]]):format(15 + self:getMag(40) * self:getTalentLevel(t)) + end, +} + +newTalent{ short_name="REGENERATE_OTHER", + name = "Regenerate Other", + type = {"wild-gift/other",1}, + points = 5, + equilibrium = 5, + cooldown = 10, + range=14, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t), talent=t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, function(tx, ty) + local target = game.level.map(tx, ty, Map.ACTOR) + if target then + target:setEffect(target.EFF_REGENERATION, 10, {power=(15 + self:getMag(40) * self:getTalentLevel(t)) / 10}) + end + end) + return true + end, + info = function(self, t) + return ([[Regenerate your target for %d life over 10 turns.]]):format(15 + self:getMag(40) * self:getTalentLevel(t)) + end, +} + newTalent{ name = "Turtle", type = {"wild-gift/summon-utility", 1}, @@ -22,6 +113,7 @@ newTalent{ cooldown = 10, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -37,28 +129,27 @@ newTalent{ local NPC = require "mod.class.NPC" local m = NPC.new{ - type = "demon", subtype = "lesser", - display = "u", color=colors.RED, - name = "fire imp", faction = self.faction, + type = "animal", subtype = "turle", + display = "R", color=colors.GREEN, + name = "turtle", faction = self.faction, desc = [[]], autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=1, }, - stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=18, con=10 + self:getTalentLevel(t) * 2, }, + stats = { con=15 + self:getWil() * self:getTalentLevel(t) / 5 + self:getTalentLevelRaw(self.T_RESILIENCE)*2, wil=18, dex=10 + self:getTalentLevel(t) * 2, }, level_range = {self.level, self.level}, exp_worth = 0, - max_life = resolvers.rngavg(5,10), - life_rating = 8, + max_life = 100, + life_rating = 14, - combat_armor = 0, combat_def = 0, + combat_armor = 10, combat_def = 0, combat = { dam=1, atk=1, }, - max_mana = 150, resolvers.talents{ - [self.T_FIRE_IMP_BOLT]=self:getTalentLevelRaw(t), + [self.T_TAUNT]=self:getTalentLevelRaw(t), }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -80,11 +171,12 @@ newTalent{ type = {"wild-gift/summon-utility", 2}, require = gifts_req2, points = 5, - message = "@Source@ summons a Qpider!", + message = "@Source@ summons a Spider!", equilibrium = 5, cooldown = 10, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -100,28 +192,28 @@ newTalent{ local NPC = require "mod.class.NPC" local m = NPC.new{ - type = "demon", subtype = "lesser", - display = "u", color=colors.RED, - name = "fire imp", faction = self.faction, + type = "animal", subtype = "spider", + display = "S", color=colors.LIGHT_DARK, + name = "giant spider", faction = self.faction, desc = [[]], autolevel = "none", ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=1, }, - stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=18, con=10 + self:getTalentLevel(t) * 2, }, + stats = { str=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=18, dex=10 + self:getTalentLevel(t) * 2, con=10 + self:getTalentLevelRaw(self.T_RESILIENCE)*2 }, level_range = {self.level, self.level}, exp_worth = 0, - max_life = resolvers.rngavg(5,10), - life_rating = 8, + max_life = 50, + life_rating = 10, combat_armor = 0, combat_def = 0, - combat = { dam=1, atk=1, }, + combat = { dam=10, atk=16, }, - max_mana = 150, resolvers.talents{ - [self.T_FIRE_IMP_BOLT]=self:getTalentLevelRaw(t), + [self.T_SPIDER_WEB]=self:getTalentLevelRaw(t), + [self.T_BITE_POISON]=self:getTalentLevelRaw(t), }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -133,8 +225,8 @@ newTalent{ return true end, info = function(self, t) - return ([[Summon a Fire Imp to burn your foes to death. Fire Imps are really weak in melee and die easily, but they can burn your foes from afar. - It will get %d magic and %d willpower.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) + return ([[Summon a Spider to arras your foes. Spiders can poison your foes and throw webs to pin them to the ground. + It will get %d strength and %d dexterity.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) end, } @@ -145,9 +237,10 @@ newTalent{ points = 5, message = "@Source@ summons a Benevolent Spirit!", equilibrium = 8, - cooldown = 10, + cooldown = 18, range = 20, action = function(self, t) + if checkMaxSummon(self) then return end local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} local tx, ty, target = self:getTarget(tg) if not tx or not ty then return nil end @@ -163,13 +256,13 @@ newTalent{ local NPC = require "mod.class.NPC" local m = NPC.new{ - type = "demon", subtype = "lesser", - display = "u", color=colors.RED, - name = "fire imp", faction = self.faction, + type = "spirit", subtype = "lesser", + display = "G", color=colors.LIGHT_GREEN, + name = "benevolent spirit", faction = self.faction, desc = [[]], autolevel = "none", - ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=1, }, - stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=18, con=10 + self:getTalentLevel(t) * 2, }, + ai = "summoned", ai_real = "dumb_heal_simple", ai_state = { talent_in=1, }, + stats = { mag=15 + self:getWil() * self:getTalentLevel(t) / 5, wil=10 + self:getTalentLevel(t) * 2, con=10 + self:getTalentLevelRaw(self.T_RESILIENCE)*2 }, level_range = {self.level, self.level}, exp_worth = 0, max_life = resolvers.rngavg(5,10), @@ -178,13 +271,13 @@ newTalent{ combat_armor = 0, combat_def = 0, combat = { dam=1, atk=1, }, - max_mana = 150, resolvers.talents{ - [self.T_FIRE_IMP_BOLT]=self:getTalentLevelRaw(t), + [self.T_HEAL_OTHER]=self:getTalentLevelRaw(t), + [self.T_REGENERATE_OTHER]=self:getTalentLevelRaw(t), }, summoner = self, summoner_gain_exp=true, - summon_time = math.ceil(self:getTalentLevel(t)) + 5, + summon_time = math.ceil(self:getTalentLevel(t)) + 5 + self:getTalentLevelRaw(self.T_RESILIENCE), ai_target = {actor=target} } @@ -196,7 +289,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Summon a Fire Imp to burn your foes to death. Fire Imps are really weak in melee and die easily, but they can burn your foes from afar. + return ([[Summon a Benevolent Spirit to heal your allies and yourself. It will get %d magic and %d willpower.]]):format(15 + self:getWil() * self:getTalentLevel(t) / 5, 10 + self:getTalentLevel(t) * 2) end, } diff --git a/ideas/gifts.ods b/ideas/gifts.ods index 323b9376ee6091d12959f91eb5c28910c2262650..2ea11020e4d33309b32718c4322c917170c2cc12 100644 GIT binary patch delta 8149 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	~m0+-j^~UN#P1cJBDEq#}@< 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 A)c^nh delta 8096 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 OLk;cd5In8^i2o1BYc3Z6 -- GitLab