summary refs log tree commit diff
diff options
context:
space:
mode:
authorRabbit Whispers <us@starfall.systems>2025-06-14 09:15:19 -0500
committerRabbit Whispers <us@starfall.systems>2025-06-14 13:08:50 -0500
commit7380279bedc3040cc4a923395368a5de8ffa7874 (patch)
tree0dc13f42fbdc820d273164c19f194d87c8ac4dc6
parent8d2cc446bf32b0f2105962def03b2a715d2c4c4c (diff)
bugfix: Adept and talents_mastery_bonus update passives
This change by yutio888 was merged to t-engine4 master here:
https://git.net-core.org/tome/t-engine4/-/commit/01ce59f64d6d6db3796f6186dbe5b67f02261174
but was never actually released in tome-1.7

Co-authored-by: yutio888 <yutio888@qq.com>
-rw-r--r--rabbit-bugfix/CHANGELOG.md12
-rw-r--r--rabbit-bugfix/init.lua10
-rw-r--r--rabbit-bugfix/overload/.keep0
-rw-r--r--rabbit-bugfix/overload/data/talents/uber/cun.lua544
-rw-r--r--rabbit-bugfix/superload/.keep0
-rw-r--r--rabbit-bugfix/superload/mod/class/Actor.lua11
6 files changed, 571 insertions, 6 deletions
diff --git a/rabbit-bugfix/CHANGELOG.md b/rabbit-bugfix/CHANGELOG.md
new file mode 100644
index 0000000..92b688e
--- /dev/null
+++ b/rabbit-bugfix/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+All ntoable changes to this project will be documented in this file.
+
+This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
+
+## [Unreleased]
+
+### Added
+
+- Passives update when taking Adept or equipping/unequipping talents\_mastery\_bonus items.
+  (This change by yutio888 was merged to the t-engine4 master branch but wasn't included in tome-1.7)
diff --git a/rabbit-bugfix/init.lua b/rabbit-bugfix/init.lua
index 0732d10..2b69454 100644
--- a/rabbit-bugfix/init.lua
+++ b/rabbit-bugfix/init.lua
@@ -3,15 +3,13 @@ short_name = "rabbit-bugfix"
 for_module = "tome"
 addon_version = {0,0,1}
 version = {1,7,6}
-weight = 0
+weight = 0 -- less than 10, since we need to overwrite Adept before orcs expansion patches Eye of the Tiger
 author = {'Rabbit Whispers'}
 homepage = {'starfall.systems'}
-description = [[
-
-]]
+description = [[Compilation of bugfixes for my own personal use.]]
 tags = {}
 
 data = false
 hooks = false
-overload = false
-superload = false
+overload = true
+superload = true
diff --git a/rabbit-bugfix/overload/.keep b/rabbit-bugfix/overload/.keep
deleted file mode 100644
index e69de29..0000000
--- a/rabbit-bugfix/overload/.keep
+++ /dev/null
diff --git a/rabbit-bugfix/overload/data/talents/uber/cun.lua b/rabbit-bugfix/overload/data/talents/uber/cun.lua
new file mode 100644
index 0000000..80a27a9
--- /dev/null
+++ b/rabbit-bugfix/overload/data/talents/uber/cun.lua
@@ -0,0 +1,544 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2019 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+uberTalent{
+	name = "Fast As Lightning",
+	mode = "passive",
+	not_listed = true,
+	trigger = function(self, t, ox, oy)
+		local dx, dy = (self.x - ox), (self.y - oy)
+		if dx ~= 0 then dx = dx / math.abs(dx) end
+		if dy ~= 0 then dy = dy / math.abs(dy) end
+		local dir = util.coordToDir(dx, dy, 0)
+
+		local eff = self:hasEffect(self.EFF_FAST_AS_LIGHTNING)
+		if eff and eff.blink then
+			if eff.dir ~= dir then
+				self:removeEffect(self.EFF_FAST_AS_LIGHTNING)
+			else
+				return
+			end
+		end
+
+		self:setEffect(self.EFF_FAST_AS_LIGHTNING, 1, {})
+		eff = self:hasEffect(self.EFF_FAST_AS_LIGHTNING)
+
+		if not eff.dir then eff.dir = dir eff.nb = 0 end
+
+		if eff.dir ~= dir then
+			self:removeEffect(self.EFF_FAST_AS_LIGHTNING)
+			self:setEffect(self.EFF_FAST_AS_LIGHTNING, 1, {})
+			eff = self:hasEffect(self.EFF_FAST_AS_LIGHTNING)
+			eff.dir = dir eff.nb = 0
+			game.logSeen(self, "#LIGHT_BLUE#%s slows from critical velocity!", self:getName():capitalize())
+		end
+
+		eff.nb = eff.nb + 1
+
+		if eff.nb >= 3 and not eff.blink then
+			self:effectTemporaryValue(eff, "prob_travel", 5)
+			game.logSeen(self, "#LIGHT_BLUE#%s reaches critical velocity!", self:getName():capitalize())
+			local sx, sy = game.level.map:getTileToScreen(self.x, self.y, true)
+			game.flyers:add(sx, sy, 30, rng.float(-3, -2), (rng.range(0,2)-1) * 0.5, "CRITICAL VELOCITY!", {0,128,255})
+			eff.particle = self:addParticles(Particles.new("megaspeed", 1, {angle=util.dirToAngle((dir == 4 and 6) or (dir == 6 and 4 or dir))}))
+			eff.blink = true
+			game:playSoundNear(self, "talents/thunderstorm")
+		end
+	end,
+	info = function(self, t)
+		return ([[When moving over 800%% speed for at least 3 steps in the same direction, you become so fast you can blink through obstacles as if they were not there.
+		While moving this fast you have 50%% chances to fully ignore an attack by displacing yourself (this may only happen once per turn).
+		Changing direction will break the effect.]])
+		:tformat()
+	end,
+}
+
+uberTalent{
+	name = "Tricky Defenses",
+	mode = "passive",
+	require = { special={desc=_t"Antimagic", fct=function(self) return self:knowTalentType("wild-gift/antimagic") end} },
+	-- called by getMax function in Antimagic shield talent definition mod.data.talents.gifts.antimagic.lua
+	shieldmult = function(self) return self:combatStatScale("cun", 0.1, 0.5) end,
+	info = function(self, t)
+		return ([[You are full of tricks and surprises; your Antimagic Shield can absorb %d%% more damage.
+		The increase scales with your Cunning.]])
+		:tformat(t.shieldmult(self)*100)
+	end,
+}
+
+uberTalent{
+	name = "Endless Woes",
+	mode = "passive",
+	require = { special={desc=_t"Have dealt over 10000 acid, blight, darkness, mind or temporal damage", fct=function(self) return 
+		self.damage_log and (
+			(self.damage_log[DamageType.ACID] and self.damage_log[DamageType.ACID] >= 10000) or
+			(self.damage_log[DamageType.BLIGHT] and self.damage_log[DamageType.BLIGHT] >= 10000) or
+			(self.damage_log[DamageType.DARKNESS] and self.damage_log[DamageType.DARKNESS] >= 10000) or
+			(self.damage_log[DamageType.MIND] and self.damage_log[DamageType.MIND] >= 10000) or
+			(self.damage_log[DamageType.TEMPORAL] and self.damage_log[DamageType.TEMPORAL] >= 10000)
+		)
+	end} },
+	getBlight = function(self, t)
+		return self:combatStatScale("cun", 1, 20, 0.75), self:combatStatScale("cun", 5, 30, 0.75)
+	end,
+	getDarkness = function(self, t) return self:combatStatScale("cun", 1, 30, 0.75) end,
+	getAcid = function(self, t) return self:combatStatScale("cun", 10, 100, 0.75) end,
+	getTemporal = function(self, t) return self:combatStatScale("cun", 1, 40, 0.75) end,
+	getMind = function(self, t) return util.bound(self:combatStatScale("cun", 1, 40, 0.75), 0, 50) end,
+	range = 10,
+	radius = 3,
+	dts = {TEMPORAL=true, BLIGHT=true, ACID=true, DARKNESS=true, MIND=true, PHYSICAL=true},
+	getThreshold = function(self, t) return 16*self.level end,
+	getDamage = function(self, t) return self:combatStatScale("cun", 10, 350) end,
+	doProject = function(self, t, damtype, effect, part)
+		local tgts = {}
+		-- Find everything nearby and pick one at random
+		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), true)
+		for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do
+			local a = game.level.map(x, y, Map.ACTOR)
+			if a and self:reactionToward(a) < 0 then
+				tgts[#tgts+1] = a
+			end
+		end end
+		local target = rng.table(tgts)
+		if not target then return end
+		
+		local eff = effect
+		local tg = {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, friendlyfire=false, talent=t}
+		game.level.map:particleEmitter(target.x, target.y, tg.radius, part, {radius=tg.radius})
+		self:project(tg, target.x, target.y, function(tx, ty)
+			local target = game.level.map(tx, ty, Map.ACTOR)
+			if not target or target == self then return end
+			if not effect.params then return end
+			local eff = table.clone(effect, true)
+
+			if not effect.canbe or (effect.canbe and target:canBe(effect.canbe)) then
+				eff.params.apply_power = math.max(self:combatSpellpower(), self:combatMindpower())
+				target:setEffect(target[effect.id], 5, eff.params)
+			end
+
+			self:projectSource({}, target.x, target.y, damtype, t.getDamage(self, t), nil, t)
+		end)
+	end,
+	callbackOnRest = function(self, t) self.endless_woes = {} end, -- No storing damage out of combat
+	callbackOnRun = function(self, t) self.endless_woes = {} end,
+	callbackOnDealDamage = function(self, t, value, target, dead, death_note)
+		if not death_note then return end
+		if not death_note.damtype then return end
+		local damtype = death_note.damtype
+		if not t.dts[damtype] then return end
+		self.endless_woes = self.endless_woes or {}
+		self.endless_woes[damtype] = (self.endless_woes[damtype] or 0) + value
+
+		if self.endless_woes[damtype] > t.getThreshold(self, t) then
+			self.endless_woes[damtype] = 0
+			if damtype == DamageType.TEMPORAL and not self:hasProc("endless_woes_temporal") then
+				self:setProc("endless_woes_temporal", true, 10)
+				game.logSeen(self, "You unleash a blast of #LIGHT_STEEL_BLUE#temporal#LAST# energy!", self:getName():capitalize())
+				t.doProject(self, t, damtype, {id="EFF_SLOW", dur=5, params={power=t.getTemporal(self, t)/100}, canbe="slow"}, "ball_temporal")
+			elseif damtype == DamageType.BLIGHT and not self:hasProc("endless_woes_blight") then
+				self:setProc("endless_woes_blight", true, 10)				
+				game.logSeen(self, "You unleash a blast of #DARK_GREEN#virulent blight!#LAST#!", self:getName():capitalize())
+				local dam, stat = t.getBlight(self, t)
+				t.doProject(self, t, damtype, {id="EFF_WOEFUL_DISEASE", dur=5, params = {src=self, dam=dam, str=stat, con=stat, dex=stat}, canbe="disease"}, "ball_blight")
+			elseif damtype == DamageType.ACID and not self:hasProc("endless_woes_acid") then
+				self:setProc("endless_woes_acid", true, 10)
+				local dam = t.getAcid(self, t)
+				game.logSeen(self, "You unleash a blast of #GREEN#acid#LAST#!", self:getName():capitalize())
+				t.doProject(self, t, damtype, {id="EFF_WOEFUL_CORROSION", dur=5, params={src=self, dam=dam}}, "ball_acid")
+			elseif damtype == DamageType.DARKNESS and not self:hasProc("endless_woes_darkness") then
+				self:setProc("endless_woes_darkness", true, 10)
+				game.logSeen(self, "You unleash a blast of numbing #GREY#darkness#LAST#!", self:getName():capitalize())
+				t.doProject(self, t, damtype, {id="EFF_WOEFUL_DARKNESS", dur=5, params={reduce=t.getDarkness(self, t)}}, "shadow_flash")
+			elseif damtype == DamageType.MIND and not self:hasProc("endless_woes_mind") then
+				self:setProc("endless_woes_mind", true, 10)
+				game.logSeen(self, "You unleash a confusing blast of #YELLOW#mental#LAST# energy!", self:getName():capitalize())
+				t.doProject(self, t, damtype, {id="EFF_CONFUSED", dur=5, params={power=t.getMind(self, t)}, canbe="confusion"}, "starfall")
+			elseif damtype == DamageType.PHYSICAL and not self:hasProc("endless_woes_physical") then
+				self:setProc("endless_woes_physical", true, 10)
+				game.logSeen(self, "You unleash a crippling blast of earthen energy!", self:getName():capitalize())
+				t.doProject(self, t, damtype, {id="EFF_WOEFUL_CRIPPLE", dur=5, params={power=0.2}}, "ball_earth")
+			end
+		end
+	end,
+	info = function(self, t)
+		local blight_dam, blight_disease = t.getBlight(self, t)
+		local cooldowns = {}
+		local str = ""
+		-- Display the remaining cooldowns in the talent tooltip only if its learned
+		if self:knowTalent(self.T_ENDLESS_WOES) then
+			for dt, _ in pairs(t.dts) do
+				local proc = self:hasProc("endless_woes_"..dt:lower())
+				if proc then cooldowns[#cooldowns+1] = DamageType:get(DamageType[dt]).name:capitalize()..": "..proc.turns end
+			end
+			str = _t"(Cooldowns)".."\n"..table.concat(cooldowns, "\n")
+		end
+		return ([[Surround yourself with a malevolent aura that stores damage you deal.
+		Whenever you have stored %d damage of one type you unleash a powerful blast at a random enemy dealing %d damage of that type in radius %d and applying one of the following effects:
+
+		Physical:		Slows combat, mind, and spell speed by 20%%.
+		#GREEN#Acid:#LAST#  Deals %d acid damage each turn for 5 turns (%d total).
+		#DARK_GREEN#Blight:#LAST#  Deals %d blight damage each turn for 5 turns and reduces strength, constitution, and dexterity by %d.
+		#GREY#Darkness:#LAST#  Reduces damage dealt by %d%% for 5 turns.
+		#LIGHT_STEEL_BLUE#Temporal:#LAST#  Slows global action speed by %d%% for 5 turns.
+		#ORANGE#Mind:#LAST#  Confuses (power %d%%) for 5 turns.
+
+		Each effect can only happen once per 10 player turns.  This does not count as a typical cooldown.
+		The damage and effect power increase with your Cunning, the threshold with your level, and the apply power is the highest of your mind or spell power.
+		%s]])
+		:tformat(t.getThreshold(self, t), t.getDamage(self, t), self:getTalentRadius(t), damDesc(self, DamageType.ACID, t.getAcid(self, t)), damDesc(self, DamageType.ACID, t.getAcid(self, t)*5), blight_dam, blight_disease, t.getDarkness(self, t), t.getTemporal(self, t), t.getMind(self, t), str)
+	end,
+}
+
+-- Item rarities
+uberTalent{
+	name = "Secrets of Telos",
+	mode = "passive",
+	require = { special={desc=_t"Possess Telos Top Half, Telos Bottom Half, and Telos Staff Crystal", fct=function(self)
+		local o1 = self:findInAllInventoriesBy("define_as", "GEM_TELOS")
+		local o2 = self:findInAllInventoriesBy("define_as", "TELOS_TOP_HALF")
+		local o3 = self:findInAllInventoriesBy("define_as", "TELOS_BOTTOM_HALF")
+		return o1 and o2 and o3
+	end} },
+	cant_steal = true,
+	no_npc_use = true,
+	on_learn = function(self, t)
+		if not game.party:hasMember(self) then return end
+		local list = mod.class.Object:loadList("/data/general/objects/special-artifacts.lua")
+		local o = game.zone:makeEntityByName(game.level, list, "TELOS_SPIRE", true)
+		if o then
+			o:identify(true)
+			self:addObject(self.INVEN_INVEN, o)
+
+			local o1, item1, inven1 = self:findInAllInventoriesBy("define_as", "GEM_TELOS")
+			if item1 and inven1 then self:removeObject(inven1, item1, true) end
+			local o2, item2, inven2 = self:findInAllInventoriesBy("define_as", "TELOS_TOP_HALF")
+			if item2 and inven2 then self:removeObject(inven2, item2, true) end
+			local o3, item3, inven3 = self:findInAllInventoriesBy("define_as", "TELOS_BOTTOM_HALF")
+			if item3 and inven3 then self:removeObject(inven3, item3, true) end
+
+			self:sortInven()
+
+			game.logSeen(self, "#VIOLET#%s assembles %s!", self:getName():capitalize(), o:getName{do_colour=true, no_count=true})
+		end
+	end,
+	info = function(self, t)
+		return ([[You have obtained the three parts of the Staff of Telos and studied them carefully. You believe that you can merge them back into a single highly potent staff.]])
+		:tformat()
+	end,
+}
+
+uberTalent{
+	name = "Elemental Surge",
+	mode = "passive",
+	require = { special={desc=_t"Have dealt over 10000 arcane, fire, cold, lightning, light or nature damage", fct=function(self) return 
+		self.damage_log and (
+			(self.damage_log[DamageType.ARCANE] and self.damage_log[DamageType.ARCANE] >= 10000) or
+			(self.damage_log[DamageType.FIRE] and self.damage_log[DamageType.FIRE] >= 10000) or
+			(self.damage_log[DamageType.COLD] and self.damage_log[DamageType.COLD] >= 10000) or
+			(self.damage_log[DamageType.LIGHTNING] and self.damage_log[DamageType.LIGHTNING] >= 10000) or
+			(self.damage_log[DamageType.LIGHT] and self.damage_log[DamageType.LIGHT] >= 10000) or
+			(self.damage_log[DamageType.NATURE] and self.damage_log[DamageType.NATURE] >= 10000)
+		)
+	end} },
+	dts = {PHYSICAL=true, ARCANE=true, LIGHT=true, COLD=true, LIGHTNING=true, FIRE=true, NATURE=true,},	
+	getCold = function(self, t)
+		return {
+			armor = self:combatStatScale("cun", 10, 30, 0.75),
+			dam = math.max(100, self:getCun()),
+		}
+	end,
+	getLight = function(self, t) return 20 end,
+	getLightning = function(self, t) return self:combatStatScale("cun", 200, 500, 0.75) end,
+	getFire = function(self, t) return 30 end,
+	range = 10,
+	radius = 3,
+	getThreshold = function(self, t) return 16*self.level end,
+	getDamage = function(self, t) return self:combatStatScale("cun", 10, 350) end,
+	doProject = function(self, t, damtype, part)
+		local tgts = {}
+		-- Find everything nearby and pick one at random
+		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), true)
+		for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do
+			local a = game.level.map(x, y, Map.ACTOR)
+			if a and self:reactionToward(a) < 0 then
+				tgts[#tgts+1] = a
+			end
+		end end
+		local target = rng.table(tgts)
+		if not target then return end
+		
+		local tg = {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, friendlyfire=false, talent=t}
+		game.level.map:particleEmitter(target.x, target.y, tg.radius, part, {radius=tg.radius})
+		self:projectSource(tg, target.x, target.y, damtype, t.getDamage(self, t), nil, t)
+	end,
+	callbackOnRest = function(self, t) self.elemental_surge = nil end, -- No storing damage out of combat
+	callbackOnRun = function(self, t) self.elemental_surge = nil end,
+	callbackOnDealDamage = function(self, t, value, target, dead, death_note)
+		local damtype = death_note and death_note.damtype
+		if not damtype then return end
+		if not t.dts[damtype] then return end
+		self.elemental_surge = self.elemental_surge or {}
+		self.elemental_surge[damtype] = (self.elemental_surge[damtype] or 0) + value
+
+		if self.elemental_surge[damtype] > t.getThreshold(self, t) then
+			self.elemental_surge[damtype] = 0
+			if damtype == DamageType.PHYSICAL and not self:hasProc("elemental_surge_physical") then
+				self:setProc("elemental_surge_physical", true, 10)
+				game.logSeen(self, "%s surges with earthen power!", self:getName():capitalize())
+				self:removeEffectsFilter(self, {status="detrimental", type="physical", ignore_crosstier=true}, 1)
+				self:setEffect(self.EFF_ELEMENTAL_SURGE_PHYSICAL, 2, {})
+				t.doProject(self, t, damtype, "ball_earth")
+			elseif damtype == DamageType.ARCANE and not self:hasProc("elemental_surge_arcane") then
+				self:setProc("elemental_surge_arcane", true, 10)
+				game.logSeen(self, "%s surges with #PURPLE#arcane#LAST# power!", self:getName():capitalize())
+				self:setEffect(self.EFF_ELEMENTAL_SURGE_ARCANE, 3, {})
+				t.doProject(self, t, damtype, "ball_arcane")
+			elseif damtype == DamageType.FIRE and not self:hasProc("elemental_surge_fire") then
+				self:setProc("elemental_surge_fire", true, 10)
+				game.logSeen(self, "%s surges with #LIGHT_RED#fiery#LAST# power!", self:getName():capitalize())
+				self:setEffect(self.EFF_ELEMENTAL_SURGE_FIRE, 3, {damage = t.getFire(self, t)})
+				t.doProject(self, t, damtype, "ball_fire")
+			elseif damtype == DamageType.COLD and not self:hasProc("elemental_surge_cold") then
+				self:setProc("elemental_surge_cold", true, 10)
+				game.logSeen(self, "%s surges with #1133F3#icy#LAST# power!", self:getName():capitalize()) 
+				self:setEffect(self.EFF_ELEMENTAL_SURGE_COLD, 3, t.getCold(self, t) )
+				t.doProject(self, t, damtype, "ball_ice")
+			elseif damtype == DamageType.LIGHTNING and not self:hasProc("elemental_surge_lightning") then
+				self:setProc("elemental_surge_lightning", true, 10)
+				game.logSeen(self, "%s surges with #ROYAL_BLUE#lightning#LAST# power!", self:getName():capitalize())
+				self:setEffect(self.EFF_ELEMENTAL_SURGE_LIGHTNING, 2, {move = t.getLightning(self, t)})
+				t.doProject(self, t, damtype, "ball_lightning")
+			elseif damtype == DamageType.LIGHT and not self:hasProc("elemental_surge_light") then
+				self:setProc("elemental_surge_light", true, 10)
+				game.logSeen(self, "%s surges with #YELLOW#light#LAST# power!", self:getName():capitalize())
+				self:setEffect(self.EFF_ELEMENTAL_SURGE_LIGHT, 3, {cooldown = t.getLight(self, t)})
+				t.doProject(self, t, damtype, "ball_light")				
+			elseif damtype == DamageType.NATURE and not self:hasProc("elemental_surge_nature") then
+				self:setProc("elemental_surge_nature", true, 10)
+				game.logSeen(self, "%s surges with #LIGHT_GREEN#natural#LAST# power!", self:getName():capitalize())
+				self:removeEffectsFilter(self, {status="detrimental", type="magical", ignore_crosstier=true}, 1)
+				self:setEffect(self.EFF_ELEMENTAL_SURGE_NATURE, 2, {})
+				t.doProject(self, t, damtype, "slime")
+			end
+		end
+	end,
+	info = function(self, t)
+		local cooldowns = {}
+		local str = ""
+		local cold = t.getCold(self, t)
+		-- Display the remaining cooldowns in the talent tooltip only if its learned
+		if self:knowTalent(self.T_ELEMENTAL_SURGE) then
+			for dt, _ in pairs(t.dts) do
+				local proc = self:hasProc("elemental_surge_"..dt:lower())
+				if proc then cooldowns[#cooldowns+1] = DamageType:get(DamageType[dt]).name:capitalize()..": "..proc.turns end
+			end
+		str = _t"(Cooldowns)".."\n"..table.concat(cooldowns, "\n")
+		end
+		return ([[Surround yourself with an elemental aura that stores damage you deal.
+		Whenever you have stored %d damage of one type you unleash a powerful blast at a random enemy dealing %d damage of that type in radius %d and granting you one of the following effects:
+
+		Physical:		Cleanses 1 physical debuff and grant immunity to physical debuffs for 2 turns.
+		#PURPLE#Arcane:#LAST#		Increases your mind and spell action speeds by 30%% for 3 turns.
+		#LIGHT_RED#Fire:#LAST#		Increases all damage dealt by %d%% for 3 turns.
+		#1133F3#Cold:#LAST#		Turns your skin into ice for 3 turns increasing armor by %d and dealing %d ice damage to attackers.
+		#ROYAL_BLUE#Lightning:#LAST#	Increases your movement speed by %d%% for 2 turns.
+		#YELLOW#Light:#LAST#		Reduces all cooldowns by 20%% for 3 turns.
+		#LIGHT_GREEN#Nature:#LAST#		Cleanses 1 magical debuff and grant immunity to magical debuffs for 2 turns.
+
+		Each effect can only happen once per 10 player turns.  This does not count as a typical cooldown.
+		The damage and some effect powers increase with your Cunning and the threshold with your level.
+		%s]])
+		:tformat(t.getThreshold(self, t), t.getDamage(self, t), self:getTalentRadius(t), t.getFire(self, t), cold.armor, cold.dam, t.getLightning(self, t), str)
+	end,
+}
+
+eye_of_the_tiger_data = {
+	physical = {
+		desc = _t"All physical criticals reduce the remaining cooldown of a random technique or cunning talent by 2.",
+		types = { "^technique/", "^cunning/" },
+		reduce = 2,
+	},
+	spell = {
+		desc = _t"All spell criticals reduce the remaining cooldown of a random spell/corruption/celestial/chronomancy talent by 2.",
+		types = { "^spell/", "^corruption/", "^celestial/", "^chronomancy/" },
+		reduce = 2,
+	},
+	mind = {
+		desc = _t"All mind criticals reduce the remaining cooldown of a random wild gift/psionic/afflicted talent by 2.",
+		types = { "^wild%-gift/", "^cursed/", "^psionic/" },
+		reduce = 2,
+	},
+}
+
+uberTalent{
+	name = "Eye of the Tiger",
+	mode = "passive",
+	trigger = function(self, t, kind)
+		local kind_str = "eye_tiger_"..kind
+		if self:hasProc(kind_str) then return end
+
+		local tids = {}
+
+		for tid, _ in pairs(self.talents_cd) do
+			local t = self:getTalentFromId(tid)
+			if not t.fixed_cooldown then
+				local ok = false
+				local d = eye_of_the_tiger_data[kind]
+				if d then for _, check in ipairs(d.types) do
+						if t.type[1]:find(check) then ok = true break end
+				end end
+				if ok then
+					tids[#tids+1] = tid
+				end
+			end
+		end
+		if #tids == 0 then return end
+		local tid = rng.table(tids)
+		local d = eye_of_the_tiger_data[kind]
+		self.talents_cd[tid] = self.talents_cd[tid] - (d and d.reduce or 1)
+		if self.talents_cd[tid] <= 0 then self.talents_cd[tid] = nil end
+		self.changed = true
+		self:setProc(kind_str)
+	end,
+	info = function(self, t)
+		local list = {}
+		for _, d in pairs(eye_of_the_tiger_data) do list[#list+1] = d.desc end
+		return ([[%s		
+		This can only happen once per turn per type, and cannot affect the talent that triggers it.]])
+		:tformat(table.concat(list, "\n"))
+	end,
+}
+
+uberTalent{
+	name = "Worldly Knowledge",
+	mode = "passive",
+	cant_steal = true,
+	no_npc_use = true,
+	on_learn = function(self, t, kind)
+		if not game.party:hasMember(self) then return end
+		local Chat = require "engine.Chat"
+		local chat = Chat.new("worldly-knowledge", {name=_t"Worldly Knowledge"}, self)
+		chat:invoke()
+	end,
+	passives = function(self, t, tmptable)
+		self:talentTemporaryValue(tmptable, "unused_generics", 5)
+	end,
+	getRewards = function(self, t)
+		local EscortRewards = require("mod.class.EscortRewards")
+		local ERewardsList = EscortRewards:listRewards()
+		local r_all, r_normal, r_antimagic = {}, {}, {}
+		for _, rewards in pairs(ERewardsList) do
+			if rewards.types then
+				if rewards.antimagic and rewards.antimagic.types then
+					r_normal = table.merge(r_normal, rewards.types)
+					r_antimagic = table.merge(r_antimagic, rewards.antimagic.types)
+				else
+					r_all = table.merge(r_all, rewards.types)
+				end
+			end
+		end
+		return {
+			all=r_all,
+			normal=r_normal,
+			antimagic=r_antimagic
+		}
+	end,
+	getRewardTexts = function(self, t)
+		local rewards = t.getRewards(self, t)
+		local t_all, t_normal, t_antimagic = "", "", ""
+		local tt_to_string = function(tt)
+			local tt_def = self:getTalentTypeFrom(tt)
+			local cat = tt_def.type:gsub("/.*", "")
+			return _t(cat, "talent category"):capitalize() .. " / " .. tt_def.name:capitalize()
+		end
+		local all_list = table.keys(rewards.all) table.sort(all_list)
+		for _, tt in ipairs(all_list) do
+			t_all = t_all .. ("- %s\n"):tformat(tt_to_string(tt))
+		end
+		local normal_list = table.keys(rewards.normal) table.sort(normal_list)
+		for _, tt in ipairs(normal_list) do
+			t_normal = t_normal .. ("- %s\n"):tformat(tt_to_string(tt))
+		end
+		local antimagic_list = table.keys(rewards.antimagic) table.sort(antimagic_list)
+		for _, tt in ipairs(antimagic_list) do
+			t_antimagic = t_antimagic .. ("- %s\n"):tformat(tt_to_string(tt))
+		end
+		return t_all, t_normal, t_antimagic
+	end,
+	info = function(self, t)
+		local t_all, t_normal, t_antimagic = t.getRewardTexts(self, t)
+		return ([[Gain 5 generic talent points and learn a new talent category from one of the below at 1.0 mastery, unlocked. Group 1 categories are available to anyone; Group 2 are not available to magic users, and Group 3 are not available to antimagic characters.
+		GROUP 1:
+%s
+		GROUP 2:
+%s
+		GROUP 3:
+%s]])
+		:tformat(t_all, t_antimagic, t_normal)
+	end,
+}
+
+-- Re-used icon
+uberTalent{
+	name = "Adept",
+	mode = "passive",
+	cant_steal = true,
+	info = function(self, t)
+		return ([[You are adept at many different skills, granting you +1.5 to all known talent levels.
+		This works on already known talents and those that you will learn afterwards.]]):tformat()
+	end,
+	passives = function(self, t, p)
+		self:talentTemporaryValue(p, "all_talents_bonus_level", 1.5)
+
+		if not self._updating_adept then
+			self._updating_adept = true
+			self:updateAllTalentsPassives()
+			self._updating_adept = nil
+		end
+	end,
+}
+
+
+uberTalent{
+	name = "Tricks of the Trade",
+	mode = "passive",
+	cant_steal = true,
+	require = { special={desc=_t"Have sided with the Assassin Lord", fct=function(self) return game.state.birth.ignore_prodigies_special_reqs or (self:isQuestStatus("lost-merchant", engine.Quest.COMPLETED, "evil")) end} },
+	on_learn = function(self, t) 
+		if self:knowTalentType("cunning/stealth") then
+			self:setTalentTypeMastery("cunning/stealth", self:getTalentTypeMastery("cunning/stealth", true) + 0.2)
+		elseif self:knowTalentType("cunning/stealth") == false then
+			self:learnTalentType("cunning/stealth", true)
+		end
+		if self:knowTalentType("cunning/scoundrel") then
+			self:setTalentTypeMastery("cunning/scoundrel", self:getTalentTypeMastery("cunning/scoundrel", true) + 0.1)
+		else
+			self:learnTalentType("cunning/scoundrel", true)
+			self:setTalentTypeMastery("cunning/scoundrel", 0.9)
+		end
+		self.invisible_damage_penalty_divisor = (self.invisible_damage_penalty_divisor or 0) + 2
+	end,
+	info = function(self, t)
+		return ([[You have friends in low places and have learned some underhanded tricks.
+		Gain 0.2 Category Mastery to the Cunning/Stealth Category (or unlock it, if you have the tree and it is locked), and either gain +0.1 to the Cunning/Scoundrel category or learn and unlock the category at 0.9 if you lack it.
+		Additionally, all of your damage penalties from invisibility are permanently halved.]]):
+		tformat()
+	end,
+}
diff --git a/rabbit-bugfix/superload/.keep b/rabbit-bugfix/superload/.keep
deleted file mode 100644
index e69de29..0000000
--- a/rabbit-bugfix/superload/.keep
+++ /dev/null
diff --git a/rabbit-bugfix/superload/mod/class/Actor.lua b/rabbit-bugfix/superload/mod/class/Actor.lua
new file mode 100644
index 0000000..6084cd6
--- /dev/null
+++ b/rabbit-bugfix/superload/mod/class/Actor.lua
@@ -0,0 +1,11 @@
+local _M = loadPrevious(...)
+
+local base_onTemporaryValueChange = _M.onTemporaryValueChange
+function _M:onTemporaryValueChange(prop, v, base)
+	if base == self.talents_mastery_bonus then
+		self:updateAllTalentsPassives()
+	end
+	base_onTemporaryValueChange(self, prop, v, base)
+end
+
+return _M