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
zcmZ8`WmH{F5-rZf-7QE6?s9RL;O+!>cjpj15D4z>!QI^n?hXNhyL))aH#2YMy<Vq#
z*Q%<nwa%ZtySttjAipj`qA1Bi!(c=F9hi|mu_(&Wf5C|JA4paE2QJwE<s^@vutY}s
zpY-@oklg>uAdfe+Lj2!Q>;-xJIV9%aXgmxQ?>{I4#SWY)nAgr{eq6~st&rb^q##F_
zr|N;oG26HLZS-a&WZP*9sS-!49VdcVK$D(XdKTWl`*UrQ-0@=1!NUl`9>Xj}JlVp0
z5e!Xp75**%aCf;X8#q9o{C#&Pv=K7`<8#pa;S*+FxCcL`(<L){2c>&#M+d*GrK`xh
zz7vW-a$^8*t?eCaL{bOSRR!}*OYMiRoA5u@X9piQh}WIwCu&c148R)MJa!r9r>bDh
zQ-)Q<g1t7yaO-_OTvuvwLxHd4Lqtkzu;3?eFRmT_&V$>%YcCIb4}{FSv1!XW+09Cb
zJB`!rU^pxK&Es5%krA!Qle7-ARdlBk#BAGicv?WFmo@K4$!B80JZm`&Ect%t_a>z%
z3Fmc#22HAFXRVoM!gbt1YN?YmgU98@&qsgeV<-8qI+7cbJHd$LLjy7){tFxw_5C=z
zMWtP3VtQtFo6K`XkoIRfKGWW;KpTS4*-`&}oymKyY}GN0anAcYu6x6u6)Xz9cG6g<
zV?dVWv`ACjuBIpzMvuE|wfFL|y2;)-@l%(OfAgg|O)y&2<!lrm*qB%nEhRS&#HV?5
zM-zdtOW!XymspjP)Q~oi-968d_x`wu($#dxwekfdK)Wljyqk_wY=q6Q><#<N*xwb8
zGQvy>AN1p60v14lO9ztKj$DH{=8X;Y0N|sVjSnO?Go0!sHkcT-2V2UUyjd`CE7(7i
z9z6^rEv<5=O6mx}Eju8L>@MRBuz;aOwHb(l!OehY);Zm#YY`1*4$_{P%h-}mB)*Gc
z_&p=GU&s%h<J0UXLYG0>K=KD>aq{NR7^_gj21asA$BcG$c+Vp37Q`WnJxi-0K+}Lr
ziX#O!-W~5Dm_E1&sHzyI+_SEQefVC8;|-SXJ+NJ7f2+nqilF?#YHTRVG-w=az-K}g
z&J35J$}FYFk!@mdq|{_X0L?77HTk&?jjN1l>1q8lCmme6KAx?4lf&|Wq03R=_*tu1
zF}|(6pJ!dzt8w-1ap3q7>Uv-`fN~UMXNX;JSS`3z?k_^ZR;t*AG8V|&&v67bahRqd
z63T8<hwSbFW{7B6kqcVqJPq77TrejJE=NUNhHD+&P-WYMkmDZ_OYDe4vj8bt^ont{
zo=IQL`3GQy2Fs2*fr1n|xr-6#pzcuvlY>_4t%H2vnG*t6Yv4G0#<$!7L2W_g+zVB!
z5231aBi`*&SSc{RCdw_aW(34!b>M(|Bv0J@5os)`Sp{idRuy4B-xvHjhAJoW&}&m{
z3Tl7kJ4I2e3LW#efC1($@?tPXosTq3dSEuLgD7#K)0i5=@Q~_O6VkrOM3*P?wT8pn
zRZ;R=%Toiw1ZhWrJFOakUA~=!uadeg3!F%lpVWWn$hmiu`H1<b@*UmV>kj2;^o=c=
zd@k9FJfGg!ni=LR%o6-2{$X}lw^XIQ$r{HG!v{sk$iw~@+gsaC!wOb}mxdS#uCs7M
zT**<UmSpX!-Q!a~Gh7;Bk2aqw!h+X%H}(}XZrAB|1I`w5;AC1LCS3f$7T%8k(t>Sd
zx(F)=Ve)<?YX*5F{Tt7h22A!-C$CTm-)|jaRvBCNm+m|TT%^O21|6FM{#H!OX5D4+
zb5d*7Ii9kkVpc8m_>~z+jEAW2*DLiz>ZFV1qJ|C_0wVQWzNos9;(jH?ApB8ZAt`Bq
zs*RI>PTIwhe=c(dsGTMf51m)v(X{JhHzAfbqQu#scYbd}RI=6lAxl4i_yjQtS=Bb<
z27CGRg3-Y4q17QWyrm^95XJkIO6^P}^=I3zZiF$6rjL3*JwA(Ce-dbEE1}o35puDw
z9urCVDMW`Kl{_;7EU0QGnS@DipP;~+x1L&DG2?k)2>I#`6f<RcQH}ZF446nL1-GhG
zGu(i#TBE$9IZD__l(OI^33t^cPsfE@*yu{51Bvj7=lV_6hvo`1AJP`8Xisb>0<FLN
zI6}wS#j%M<R@2scWS4!N9!Wxe8nL7}#uxOhnN(SIdQ6$jwoPX9S5?oOe>o2tq7L3p
zj$qWDp*z&zzr|~_kZnpkaH+3yoTS%nko?$Sn`kadN=Y~^(o0xyK`^+b!APzmdlq5V
zsTJxib$;bjS^B$OJLC#jh%W%KL;1zWFN3}VP1ysp+#LgGmqJ98^b_-%=}2&*cqD@9
zRy6YW(JTV-cD4Pvj<5%fTvWbqQi0I?MPLLQ@peb_5&jvL+!^0<jO+Tb+hDM+0)WPR
za>9jgO8bg(xWI6hL}-<Bpg<ikiT)4>{pMJYlCbw0?wc_WH!WOps>g}|`gQEUG`R-A
zqp$ri-_Scr9J;K4tFWf4E@*Y83*@8>;}Yu=)qCI0nk7C{2#qcmgB}(viQEo#AQA#e
z=GD7v${TZhBz`vA8L-QL$4HpZZ@Zg_I`34gPdQIsv=BeHBo!Ie^Q4Gc6fN-Ba#wiq
zC8GZgCL|`moCy(nktN2=^M<ASQmZOH0Fi|~AXX@>M^oG&V5a#aei&GkFvA@dNju7%
zC(aeAhS~)V49RLL=NU@_=Tf8K&GahY|G*Q!gQx^#e@d|=)JA>kN4|^_=Wb3NiHobC
zU9gQnl1@tFX(wbC9ik_XW1}>aqK3r^J+KL2pN<bqnCzqyb%Xm}@};B#X`_dRB6YZ-
zNVUw<9$x<aBqAkf12$%yL#bad4ck7urIu!8fi}?k)36WS4Lo#i!D4Wn(nBsN+)|SN
zF#-bEgIFJcymTwKcZ@M4Syf5I4N1&~?-VW-Q*PQ;$^^e;4F8G@A1OhTPSBzFtVJP*
z@%0Sy2AnI5dzj=w2|A{Ng<(fm?T!8L4$6^k;LHBHD4Jxd9TX_?($y5Cpc{v+pAHcQ
z(nApUCM<8IxJ59hqCkM=uDLlg5H@wc+5`a&`{DC}y^1t3tmNUUN!s2O#51%rTfu^p
zmR0R2x7BF&oyH^vr|I^BZiB9hx>HcLY7kB<Au6ne)XTx+{WGq?B*k`SvZFfYo({(4
zV&6FRm#lBp%VIhBK3KW?#ktEvq2}F`@m%DB9Hwl#YaNJM<$gxub}0s1J|3+8Fc<)?
zEOviQmMZ?<hz(c8grWFFJUY7GP5;iLqQkRB^(&)U;ITj=L8UkJ*l}>g`$i+P>%Jcu
zT8nsU<|jQ6(cq7DN+OP^zWkX*%hXxBW+N3Z0~QHXvjp%uYxOJlIZ3PC8)RH7&XcpJ
zBo*`ao%38#4G;~2Z5=-`Sh5jz{Ls#TkD_>_*E|=}{c(=uN8lfy*t~vDrDsA%y!6L2
z&kji+JEhGwJ{H2>C5wAPR5WuL=rx=#sA4ZD+4rG@!$ZFmU(~-%^)oZ8t*KN!r&<4)
z=y;b)--A>(&JVvNO>aR_@x&%e8^qEL)1(*R={{K<(<Ko{dmCWZeS9n(ozX}Pl!mkl
z8Ae279Z?(}NQdoWedZPPZX$PZ)71lnV`Sb`(H>v}d}!f`WQoR^RRSyHN5o|nUSSEw
zSDH{eu(qK)CHtT0Mp4ymAIL<O^!U`%Za4FFF^*(@zmcPxG@KY99AkHXiAU-qCNWBJ
zCot9SZv^{X7AHuvL1MK($vkcY9fLD7DZFuZH||iOG=&MSdr2a*Wg#%=0!;mN)iM!g
z*3ym{Xdy!0(xlmzLO5J}_E%IuFpV(b>1fS%aS>4tAbga6P<R3*U<Q5TXox!#b1?kM
zkCm^Qc3;TzK%uvqbV)S+u<h4sWunY)E7&JO<V4(mW7q9OVVgZjR0RQq#4@ABss#VG
z)i!vwc@Qj9Um9opi7VJ~Sq9O>!70@x*uG}eG$<4?T2{bi1bNap0CUWtBym;z)PHH^
zrfJPBobOD>{SiM7^zv-*h9vl7xTP}T2cC3~&@X*-JB7k&9-L_yiCP`CBrTqS_3`4O
z6Sd*1?7Vfq<8ba3jBHGxR$3}+v=*zDMn$@wVXyx6;>zr6bvL=(O+mFzC-cDlhZFR~
zy|#1mUG@tP%y%TK&$<gfUgergc)&L^eM%DbNhIIG->hGC9bUxSowR#pWN%9sNER<S
zZ#Ign8&E3jO5E7l3Tkk1rkwRh-G1oZI|di-wkKU5U4#I;;BGxYtWkCx=XZuurCGl4
zF-UVC6!}wnC^vfHao!Na-uR@+U%D@TTH9=HgE-2E4X0vfwFhZj)F2<Uj_HM#!Z0em
z+WQq{|Aios*WJ2P$=)EL(Z;KNn%}>nb>!>lENf@@G(VFEq|C~Qm<M|GT(19Rpc440
zS*9p=>Nd^(y9On&|3TkEa|NQH)n@ob{F5LYWMbHp25hg&AkU;hVla;`>FaZncuz<B
z2*o<ig+5U?N8ezFRE9Xz%)&6|%J2wZj25-|A%YI^;OKKq!HWos0?p1LhKqJ?K*z*N
zG&`Wb@!YEd_)ROg##ZKHPdf|+PnsUr=V3%pU>o<l4(|h2M58m9w+O7nh6Vd8XGNn4
zoyQ#Cym?tHy0mm&<4m%ot3Kc$G9Bk2y|GQz&EV1D2~GZD<m7JEyDX9&q}5GN=h@pp
z?uSgU&w5~mzEV;rlFYqhP>1z2AOm*P64ptxtRA16U)H<p0$;Hb5G$bOOre!W9>+v)
z%Q;DR84>`28PB6@0zJD9&A>#(=SIvm8Atjal+jM>R`(nnc?mlnFu6)b^nFz=<wbUO
zl^{0aV<e@YsAUSw#no3iCME~}*N0Pwb&oxXdwt;H+2cr?bX2PiLQzqDsW@CEXG%hB
z*|=&cN~fj_yUTCUX|#lEwz||QNM<YrsO36y9tK<&02Q)nJI&9I&ArMDYG`;fDA1uW
z;YgQBE1ySr&9`9XevzmV`(z?qVm8{gdJ+uv_zg)RE9U<>lGgine7teBTlEYtUqdI8
zH^jWaX3vZ|8Qv*PX@loLNyH#yf6T&AuOy!nDE_T(8Kz7gna{F|JM<hw8*+9~HugmG
ztr3uQP7IP9Rno{1tyCxejCOzv+s(gLikqg`XD1Z0S7(zo`Q1wpWwWciZ#yii2Ro#s
zzG9V*RmR}*jV6>%o%=Rsw0ur~tNnxCG;!Y=ai8^G^)s5w(yR5Jqw|fU%=dJ^44-b!
zn)ELuw=YH#_W7-s$_!R$s%(&gVn2~kIn)5Lx+fVdKwOZi=6l1h&M-8Vt9_P-(w&r;
zZ73j+Klh-Dh5Cf^kJ{7dDwvSje2eZ`Av&NTm^OPYO@bQmqrey<&oDkWyuEv{O|$i6
z?<*;>#epPxQQR6`Q4`0ORQkl%gQ%g$1oMa|ElZoJK}VMy@eSNh%IKz21xe5Ebpvd!
zg_+3VGr2WcK`$&BLeY@+BK*oy)#rYtX+;q#-Z%Z&+bu6gGvioOvE8ppdKV(N;F24$
zok0-lU`%46g(Uy@1^p9pgH9*QIlE4fH+~n4Bka{%)Tj2mmBhC3+fP@8!)c2MIFaRw
zn7`JkI4AC9^9s$s9UaMhVt(V-dbI?aK9|+<&iIftZ`bQoDQaY>K6f-kTqkwpUVDK8
z(kg-CH&H2xqnDCXNvQ#ZhK*!R!&vV#T6z|vN4~t31ku}x#Jg2NHO{WiV?k_-Iajej
zYhHa_D{u9;h|yWgLYp{EYMaE%vd)Ml3FPLaxTR8RQAG?Rw=Z)l0*xRp*<=7kK)`)o
zqh1B0tF=_4w3JMq^DSWq=ah31+d?qs_xyC~-Ec!RHfwqDQmiwFR?*xawqjOOorlN8
z0!Me9a;$D{Ao*;%MF|uu9B;a)ENkqZze&UJeWO(ny|9O)b$Dvr4fDHs=1LV5EX}EI
zXqXCU3vWIJb#0rc^&NR7>;NqlIPeKg_q*=*R#Tz*rxvFEk2jC2o2`p`=T-j4pR~Jx
zMIwCN`JxMF0js$pODu(ao23a$Qxv&;hqOIM%l*cc%(g)T!my>aEJ@YAEz<XMEAN$$
zTN^p+?N&MAli0^j6I>_t+nh=SOxrB2=6K&L^RbZF98~`H5Ab!g=LMp~Dl)|?^zp3M
z<4Q<)D-U^-j*(&P$9tfE^|=h$5nHeS`C64$nZtBu9i^gI$+TZTbK<vn$-AFL6aMjO
zsW!TidnjSHC~T6phFX0krthL(cb#}1p9Ps`IgGF1duU-c1@%n1!4E1Og#4yyBH1L)
zKua5Hewu`!C-+bTP+O9=nxl<~28XMq6n^uEOpAxu5ZMftGyO`F{CL6(RovU-#?U6@
z=&~^8JRvpSS7}qeT(?qflhjvfmm11Am&}R}req&#Yjm^Mz!u<7%&xxsrFoaFJ}Z6h
zJX>Kw?TutxmqKw+M<DvLwUzR4kJjb8{hrNXITrSOpv&QnluSt$9^}~*2J~P-K!}q;
zLj3Cu3<LrC`vw-zgi8uU&v9XQo~w#$BYd9w%3jw!fy$@dP^!c%6OwbYkq#z6`Vehv
zVP2f<mnEp4T`;6@@{wAID2I{JI9AK~C6f%;@81Wnt!?a&tzwbWGB^Hq#&{y7HO`u%
zSbaM8_aZ2NT|G!A$|~ZZP7fYZS3ty(b~6?C&e0o|?`XcISGEPfNow|p8CWgKm!Zi8
z)iUW^-X?nUm?pNBt&mL(I4znkiJXo3k>`6pTt6G#DK3mEYX_l<?QRn$)jN|Jq`lCu
z+xsK3B%CHKFa3|t6X>`>B&cBnTh2Wap-GF2i^Juu0VZMe^gYOQUoecs`Qj~Gv?wCS
zzx2B7%r3c0OC$oD7_8}p<&Ml{h3_!mRqN{~Un4iT@!mE}nrki!66Nx27_qiZAUEsM
zC6BzQw8*Wr@@TZhzEn=rYjFS_Plsxn6xqkJCVXlGfk|~u+WD(b87_FG8YKqqSZ8#|
z*?CVbA%b$fXn}MTIv1Xpq$aq0gI<E-gg=u&=#b0DaZP|_F1LX7BLQZ?FVPm^na41a
z>N@97aoBv^0SQPZ<WmuYnyXMV4mdM(c%6&HX%#`<yHa;*3OpIZw9y}9Vp2v9iu}_v
zmddi-GIZbgL|Cr{Xk3woY5B5#*{OG6nijkZgu1*EE<AZBQ))~zE&!+Rsqf2i`s7nu
zfC;m|Q6&x-o-;E25`Op1ab!0V(zb(zWLudoP6(TlaHk@6aeVyjJvnWq*6daJF15xi
zW?%}-p6<g(>;$MQTI>)>EBOJAq3Z7L?`uzGSSIgr!C1{}w+-DC;C-_grSFcpe2fKe
z+8NpPZU)EnX|HazSyw215*k~`rr%itQ#3;`t5cH!hZmOth#y#ZCaLUNHj|<sjWh}S
z+_%<OJH7no*qntXKSST^H@|nx!{U!=IVd`lk#{*NsM|Z(pAv%h8dp__48V;r%}Jg@
zHV(nYmxAWXjB3V&GXK64Q)brePqLza!V~1550CMljty75%d@Djt}OMZy44_$2?@`2
zt0l!GaN5IvXCk!ck)f@-zFo7V0Vx6e{vB55X--9`^?=^{Qe(|6F$P8^#<L>qoqVJY
z+LHHodRe&n)$Cd&6+7h}xMHFaJETZzb~LbnL&_w(P)*{Bh~;NonFTPOp;4XKIK9yi
zy!y5_+l>2%`j_y15!(1Y-F!tvQS2tb4b@x*xJ}*iDH9i^a&cRhvgFq^Sahq$qC<KJ
z8?(l-r+pPh<(Sq)(W4a%w3L|!bH3f_Bg2c<k;qNQlo!kglNmNrTihz2xPF;m9Ny68
zN=zn0Sjgu2oz*WV=NBp}EvEq!rztw=K-<wYxMeO<O^vnD19c$F8vpBa3p$Mu!TJ*`
zK-0@;<x1$PE(*-n?Odxonj~IZjo9Mr`(qb!k+{&{w0G{pI~E*VRQ37C9N4Bgkh(eb
zMWqdjc#JXMhL2ptYL?a&YnpZ5zze_T+?e5Tq5tBC6rhH3^Y}4>>h6(-&R3h@BE6~I
zdsEYH6Nk=JnZ;<d#_i0&re#bukEEXqSpL*MB+-=F3=#9N5Z)o@tnm$H*$QMwH{m9o
z8}0piQqfs(a+t`+g!$^ms26<fmf^D}e$OO&ZG!$@;Rvl6Lc6kpFnaUY{S(z4c2~IA
z>(J$_Xm@o??5FQHcTYkkg>Q5wXqsGv0ySwN#IhEmS#VU8b4f*#DR*rwj#KknfE4#$
zkk|4{5}&BGtE-QXPtZh2Q~^Kb7Q;R7w?;En1n9$>#T#q;jQd4z=J6oZHqwo}!rR5T
zThZzw-U7wY0B?Rl@zeCZou4puWu$eD&laswhXH~yw&zY!s%kY{1^)gslzBVfDcR1a
zQa$jCh7a^$XVaiz&F?~yB$|<ffD1M){m1De#E@iZ*)$psmZya}NU@vITk~627q8tw
zQo3w=-|UJa{yDfbBw1I#b-0CEpyY1qVl_JSO_u2i=`X|2Hn;gV!$LsJ5JCK#;s4#`
z;Ac^}7Gi>dfcQH>f1Gw^u0~9rcDDRNf0lRdX3lj&#9~m0+-j^~UN#P1cJBDEq#}@<
z@e`yhAWpC}3zs}6XB`}wCFI|>MxE0;G-2S6!T-1Xe+Kd&!^O<i)ym$&<!|XzomJ;;
zFuLDnRnAN6fT3I*K}j&J7}Q6&IK#8{k;ZO9(-Ha+{Ht4fmya0mANo@&#&EqrX0p8N
z)(r|xE3Gd)9!}<P1X2llw2uy@ai9D1xG3W33s=9J0W?CRfIcvDiLYI4sygaHj-ibj
z5~J@aiTa_v-nvu8S5gMsv0oOZIVL$+J&%+s-Kg+%qp+}gKAlI>)VmNY>RG7A1PXKz
z*&;HF*Xz_`*h{KNuf%<C!m+G5S9;$_BXby;<Jc{eTF4@=KIvR$@xD&Od6XkfWn;;R
z{q<^Br~BwbBn;pxwraXiB{pA)O<T($#^PC%R%^3%%qq|y`z3ec-GP$gGDk$n2O<m%
z8SRd-4%kO*N9gDQr<mtq+zBSjQ&GM*f0h?UaV(4CBMa+GDI<fIa-?<!3a$(>J~|}0
zHccxY$tOJBlYCa>vy<>NJ(fc#86Mj)KxP)5BhLa}I+EG9;NX~?`|SZl8Be}Bu13<`
zPdcG>OHh3B#hL2YC;St(mkm`fG_RCA?hTdDEZTT_V?rRU%}w`>^5=1qFnhB&H$-ZG
zij*u|=QOt&+-f(W(~a{x`&PM5kqRa9l5~oTaFr%XWtH0l^m_PeG70^fu(UZiLntw7
zKfG58a?jOVk<470p!5V|P`C>2SXf)EN4u|(V}FesNtEiJqewo+&GH81y)2t+@9=F1
zeygK@-T@$#nAZ?&<FS3f+&BGgEcB$zVp>P^_ZtP3pG>|Y@!tjeH6m{(Ejc94uW6w+
z7*jQ0o9(%0%!D<f20_bL`Q&+|5B08ENjOA=#m|_9p>fb_72l9_$No8-KtwY6>3Xv5
zTJQl)jTnh{ne+w3BVM5*rwv0m>j=Nw5E)nmWEvDyS4vg?VEn1#w)q|o#hyGS{S_>_
zf*LG}IgT-P{*ceiLQ&n}C?DZPBuQycC%OXTNn#DbWayeZyQEG3ldrS#l1x4jvPJLw
z;YL?p#zFZZljkA#!fvyE8u>Co5zV37gFbuCP2=K;`0_4L3pF1`pzn=|KnvJhUjHlx
z)VzIYpGV66?D$#en(-W)`?rNt{^#NP<7MIQ2P~VJyaQUQOwk!ys#gN?L&}e>%$7KY
zqeF3YGhsknu^P`e_(yb?fC?5m;^A5pLQ`GAZfOQo%=b;TWECAL0ez8mh!g>g3C;#x
zuYAQD%wvHfQbByv-|>*5J~0ZNObR@^1EoMimqzE}k)V(IC}U~PhAv0zb!x{jk$cx*
zPd9k;Xl3!XNP(Jp5=Y--<?4Pg>21XxgWxue*Y^0D?mL6<n_l8IbK~&qSS8<*=wp#~
zwYbNW%NVbQ(P+9h(;zh~aa**--ns86!&+IA?zQduvmGwohUF9kmu?n5>e-imARYDj
z(*_;BF;z`AFRz0}P>xf@8=<uX(~yExPy}M-{6NqdY!cT{<LXZBrpEIyk@dQ<>b=f7
z(ay@8C`o4eR<~nm^Y+*JPw>e6FW?U4-GoX^-j4k(iDWT%6d38g!=0+bIU4p@J2A{p
zrjSXXpxxYLcftTU-;2hn)(zzyAow<i?pExPwS<$>=$3#5Mw+9&QrU&<TU9p#1zY-;
z)KgLrcfY^KORn~FnEQIUZ(=KH5I-{R_vHSw2j=1MiNcC(Ut<Kalai}q6(cD2CEazf
zFO|C7BkNVP`ks!-+^O&)pMvEKYZ9#|KF8U2ZP@<62apvQwvpV$RL0Sj-=xr}n9skX
zQ*I?m0XLhhos!~iEY-&)mIKdYpz($IFNbq&{YVh}!{G(=|Kad|pVI3d%p(7x-}q3A
zf7;XW*_K4{ixzbM>Ns1f{DZ7kMDebcH2>JymJ<KqzNOT^V`Bd|C(i0$IUQCqsDGLK
zzn6n}9BX>AzpnpP6U6_p{6EMPZ(vOT`kV7tSK<S#afwKY|0^m=vXD^N5ZHf&=3m!~
z8fzjb=Xdb|?+D`$tdRx&?)%@r{3{&)9l?hGuZ;Zv#aL~KAYbC;ZD`=aE&lHMKO$XQ
A)c^nh

delta 8096
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
OLk;cd5In8^i2o1BYc3Z6

-- 
GitLab