Simplied and Flexible Talent Updates for Addons

All development conversation and discussion takes place here

Moderator: Moderator

Post Reply
Message
Author
Hachem_Muche
Uruivellas
Posts: 744
Joined: Thu Nov 18, 2010 6:42 pm

Simplied and Flexible Talent Updates for Addons

#1 Post by Hachem_Muche »

As part of an addon project I have been working on to extend high level play, I rewrote the talent definition processor in the engine. This has saved me a lot of work, so I thought I'd share it in case it's useful for other addon developers.

Once it's installed, you can reload talent definitions multiple times (it's idempotent) and only need to include updated data in the new definition for existing talents. For example, to update the Fireflash talent radius and tooltip, and move it from spell/fire to spell/wildfire, the new definition is simplified to:

Code: Select all

newTalent{
	name = "Fireflash",
	type = {"spell/wildfire",3}, -- Moving to the "spell/wildfire" talent group
--	type = {"spell/fire",3},
	radius = function(self, t) 	return math.floor(0.45 + 2.5*self:getTalentLevelRaw(t)^.5) end, -- New function definition overwrites the old one
	info = function(self, t)
		local damage = t.getDamage(self, t)  -- This refers to the original function since it's not redefined here
		local radius = t.radius(self, t) -- This refers to the new definition in this data block (above)
		return ([[Conjures up a bolt of fire moving toward the target that bursts into a TOTALLY MASSIVE EXPLOSION of WILDFIRE doing %0.2f fire damage in a radius of %d.
		The damage will increase with your Spellpower.]]):
		format(damage, radius)
	end,
}
For reference, here is the original definition:

Code: Select all

-- Old definition for reference:
--newTalent{
--	name = "Fireflash",
--	type = {"spell/fire",3},
--	require = spells_req3,
--	points = 5,
--	random_ego = "attack",
--	mana = 40,
--	cooldown = 8,
--	tactical = { ATTACKAREA = { FIRE = 2 } },
--	range = 7,
--	radius = function(self, t)
--		return 1 + self:getTalentLevelRaw(t)
--	end,
--	proj_speed = 4,
--	direct_hit = true,
--	requires_target = true,
--	target = function(self, t)
--		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=self:spellFriendlyFire(), talent=t, display={particle="bolt_fire", trail="firetrail"}, sound_stop="talents/fireflash"}
--	end,
--	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 28, 280) end,
--	action = function(self, t)
--		local tg = self:getTalentTarget(t)
--		local x, y = self:getTarget(tg)
--		if not x or not y then return nil end
--		self:projectile(tg, x, y, DamageType.FIRE, self:spellCrit(t.getDamage(self, t)), function(self, tg, x, y, grids)
--			game.level.map:particleEmitter(x, y, tg.radius, "fireflash", {radius=tg.radius, tx=x, ty=y})
--			if self:attr("burning_wake") then
--				game.level.map:addEffect(self,
--					x, y, 4,
--					engine.DamageType.INFERNO, self:attr("burning_wake"),
--					tg.radius,
--					5, nil,
--					{type="inferno"},
--					nil, tg.selffire
--				)
--			end
--		end)
--		game:playSoundNear(self, "talents/fire")
--		return true
--	end,
--	info = function(self, t)
--		local damage = t.getDamage(self, t)
--		local radius = self:getTalentRadius(t)
--		return ([[Conjures up a bolt of fire moving toward the target that explodes into a flash of fire doing %0.2f fire damage in a radius of %d.
--		The damage will increase with your Spellpower.]]):
--		format(damDesc(self, DamageType.FIRE, damage), radius)
--	end,
--}
Here is the updated function in engine.interface.ActorTalents.lua:

Code: Select all

-- Updated to allow for partial talent redefinitions (merge)
-- Can be used to define new talents or to change existing ones.
-- t.name (Talent name) and t.type (Talent type) are the only required fields in a redefinition
-- An existing talent can be moved to a new talent group by changing it's type
--- Defines one talent
-- Static!
function _M:newTalent(t)
	assert(t.name, "no talent name")
	assert(t.type, "no or unknown talent type")
	if type(t.type) == "string" then t.type = {t.type, 1} end
	if not t.type[2] then t.type[2] = 1 end
	t.short_name = t.short_name or t.name
	t.short_name = t.short_name:upper():gsub("[ ']", "_")

	-- Can pass a string, make it into a function
	if type(t.info) == "string" then
		local infostr = t.info
		t.info = function() return infostr end
	end
	-- Remove line stat with tabs to be cleaner ..
	if t.info then
		local info = t.info
		t.info = function(self, t) return info(self, t):gsub("\n\t+", "\n") end
	end
	
	t.id = "T_"..t.short_name
	if self[t.id] then -- If the talent already exists, merge new data with old by overwriting previous values with new ones
		print("[TALENT] Talent "..t.short_name.." already exists.  Updating with new definition information.")
		
		-- Update talent type tables if type has changed
		if self.talents_def[t.id].type[1] ~= t.type[1] then
			print("[TALENT]   Updating type tables for "..t.id)
			table.removeFromList(self.talents_types_def[self:getTalentFromId(t.id).type[1]].talents,self.talents_def[t.id])  --Unregister from old type table
			table.insert(self.talents_types_def[t.type[1]].talents, t) --Register with new type table
		end
		-- Merge the talent data
		table.merge(self.talents_def[t.id],t)
		
	else -- load as a new talent
		self.talents_def[t.id] = t
		self[t.id] = t.id
		-- Register in the type table
		table.insert(self.talents_types_def[t.type[1]].talents, t)
	end

	print("[TALENT]", t.name, t.short_name, t.id)
	t.mode = t.mode or "activated" -- default to activated mode
	-- Reject if mode or info are not properly specified
	assert(t.mode == "activated" or t.mode == "sustained" or t.mode == "passive", "wrong talent mode, requires either 'activated' or 'sustained'") 
	t.points = t.points or 1
	assert(self.talents_def[t.id].info, "no talent info") 
	
end
I have attached this example in the form of an addon using the new function by superloading. (Change the extension to .TEAA to use.)
Attachments
tome-TalentMerge.zip
(5.17 KiB) Downloaded 124 times
Author of the Infinite 500 and PlenumTooltip addons, and the joys of Scaling in ToME.

Post Reply