New Addon in Construction: Avatar of Horror
Moderator: Moderator
-
- Wayist
- Posts: 24
- Joined: Mon Mar 20, 2017 12:03 am
New Addon in Construction: Avatar of Horror
Hiya, I'm making a new addon for a Demented class I want to add, but I was thinking of adding some new talents and can't find a good tutorial for doing so. I did use grayswandir's tutorial on making a class, but there's no tutorial for talent making on there. If someone could link a nice easy-to-understand tutorial for that purpose, I'd appreciate a link.
EDIT: changed title of thread to add my new class name and some clarifications to first post.
EDIT 2: My class that I'm working on is Avatar of Horror, a demented sub-class. Focuses on manipulating and using horrors, including actually turning into a horror, as well as manipulating sanity. Higher level talents include possession.
EDIT: changed title of thread to add my new class name and some clarifications to first post.
EDIT 2: My class that I'm working on is Avatar of Horror, a demented sub-class. Focuses on manipulating and using horrors, including actually turning into a horror, as well as manipulating sanity. Higher level talents include possession.
Last edited by Lilith Dragmire on Wed Apr 10, 2019 6:04 pm, edited 1 time in total.
Re: New Addon in Construction
I usually just recommend deconstructing one of my addons to see how to do the general stuff.
Witherer is almost purely a new class mod, so you don't have to wok out which bit are for adding a class, and what is for something else.
Witherer is almost purely a new class mod, so you don't have to wok out which bit are for adding a class, and what is for something else.
My feedback meter decays into coding. Give me feedback and I make mods.
-
- Wayist
- Posts: 24
- Joined: Mon Mar 20, 2017 12:03 am
Re: New Addon in Construction
Well, I was trying to make a 'transformation' talent tree for my addon, and looked at the Ashes DLC to try and find some similar talent to modify, and decided to use the Corruption/Wrath tree talent 'Destroyer' but all the different values there just confused me. Which is why I made this thread in the first place.
Re: New Addon in Construction: Avatar of Horror
Feel free to ask specific questions here. You can also try the IRC channel, but that will depend on what time you are there.
My feedback meter decays into coding. Give me feedback and I make mods.
-
- Sher'Tul Godslayer
- Posts: 2402
- Joined: Tue Jun 18, 2013 10:46 pm
- Location: Ambush!
Re: New Addon in Construction: Avatar of Horror
There's some avatar replacement code in the New Gems addon: https://te4.org/games/addons/tome/nullnewgems
It's in the Crystal racial talents, and it changes the icon to a different color -- plus one which applies a shader if you're installing a multi-hued gemstone in yourself.
I think that's what you mean by "transformation", is that right?
It's in the Crystal racial talents, and it changes the icon to a different color -- plus one which applies a shader if you're installing a multi-hued gemstone in yourself.
I think that's what you mean by "transformation", is that right?
-
- Wayist
- Posts: 24
- Joined: Mon Mar 20, 2017 12:03 am
Re: New Addon in Construction: Avatar of Horror
Actually, no. I meant more like transformation like... like the Destroyer status buff you can get from demon statues or the Doombringer class in the Corruption/Wrath tree. You know, the one that turns you into a demon and affects your player sprite? That's the closest I can actually get to my idea in game mechanics terms.
EDIT: This, specifically: https://te4.org/wiki/Destroyer_(talent)
EDIT: This, specifically: https://te4.org/wiki/Destroyer_(talent)
Re: New Addon in Construction: Avatar of Horror
What Doctornull is suggesting probably does what you are after, but in a different way. You could use that to compare with the Destroyer talent code to nail down exactly what does what.
I'm a bit puzzled that you were confused by all the number though. Numbers are obviously not what you want if you are changing the sprite, so just ignore them and look at what is left...
In activate:
And in deactivate:
That code is all that is needed in a timed effect to change the sprite.
I'm a bit puzzled that you were confused by all the number though. Numbers are obviously not what you want if you are changing the sprite, so just ignore them and look at what is left...
In activate:
Code: Select all
self.replace_display = mod.class.Actor.new{
image="invis.png", add_mos = {{image = "npc/demon_major_champion_of_urh_rok.png", display_y = -1, display_h = 2}},
}
self:removeAllMOs()
game.level.map:updateMap(self.x, self.y)
Code: Select all
self.replace_display = nil
self:removeAllMOs()
game.level.map:updateMap(self.x, self.
My feedback meter decays into coding. Give me feedback and I make mods.
-
- Wayist
- Posts: 24
- Joined: Mon Mar 20, 2017 12:03 am
Re: New Addon in Construction: Avatar of Horror
Well, keep in mind, I'm relatively new to coding in Lua, so I'm mostly cannibalizing stuff from other addons.
-
- Sher'Tul Godslayer
- Posts: 2402
- Joined: Tue Jun 18, 2013 10:46 pm
- Location: Ambush!
Re: New Addon in Construction: Avatar of Horror
Ah, I didn't explain sufficiently.Lilith Dragmire wrote:You know, the one that turns you into a demon and affects your player sprite? That's the closest I can actually get to my idea in game mechanics terms.
The Crystal talents change your player sprite in three ways:
- When you socket a gem, your sprite changes to one of the pre-rendered per-color sprites.
- Dynamically, when you sustain Crystal Crawl, you change icon to the corresponding "crawling" icon (which is also pre-rendered, one set per color).
- Multi-Hued gems also apply a shader, which works on both of the underlying sprites: the upright and crawling ones.
Re: New Addon in Construction: Avatar of Horror
I'm still not totally sure if the part you were stuck on was the image replacement, or if all of the values in the description for the talent are causing some confusing, or what exactly. I'll help if I can and if you have specific questions about specific values, feel free to reference the code and ask. I'll attempt to give a bit of a rundown of everything that is happening with Destroyer Form.
You mentioned you were confused by some values in Destroyer. Most of it is actually handled by an effect, which I'll go over as well, and the other talents themselves. Here's the talent with some notes I've added for each field. You probably know what most these are already, but there's a lot going on in this talent due to its interaction with quite a few other talents.
This is the code for the effect, with comments added.
So the effect basically handles the stamina regen and physical power, as well as swapping the image and indicating you are a demon. All of the talent-specific effets are actually coded into those talents. I won't go through every one, but here's Abduction for an example. I'll just point out where it's referencing Destroyer Form, inside the action field.
To create a similar talent, you would likely want to set it up in similar manner, with the talent calculating the values and passing them to an effect you will set on the player to handle the sprite replacement and any temporary values you also want to impart.
Hopefully this clears some things up, or at least gives you a jumping off point for more questions. Feel free to ask.
Another example from vanilla ToME would be Shivgoroth Form in the Spells/Ice tree, which functions in a very similar manner.
You mentioned you were confused by some values in Destroyer. Most of it is actually handled by an effect, which I'll go over as well, and the other talents themselves. Here's the talent with some notes I've added for each field. You probably know what most these are already, but there's a lot going on in this talent due to its interaction with quite a few other talents.
Code: Select all
newTalent{
name = "Destroyer", --Simply the display name of the talent. As this talent has no short_name field defined, it will automatically generate a short name based on this field, in this case T_DESTROYER. the short effectively acts as the id for the talent.
type = {"corruption/wrath", 4}, -- the talent tree this talent is part of, and it's position in the tree
require = str_corrs_req_high4, -- the requirements to learn the talent. this references a table defined in corruptions.lua, but essentially passes a table with a level and a stat requirement
points = 5, -- the maximum number you points you may invest in the talent
range=10, -- the range to which the target may talent. this is a buff you cast on yourself, so I have no idea why it is 10 in this case. peculiar.
cooldown = 30, -- the number of turns that must pass between uses of the talent
vim = 38, -- the vim cost of the talent
--no_npc_use = true, -- when set to true, NPCs will not use this talent. this line is commented out, so NPCs may use the talent.
no_energy=true, -- when set to true, the talent will use no turn energy, i.e. it is instant to use
tactical = { BUFF = 3 }, -- tells the AI this talent is a buff with a priority of 3. I'm really not clear on how these are actually used, but I've never really dug into it too far
getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 7)) end, -- this is a function defined specifically for this talent that will be called by the talent action to determine the duration of the buff. at effective talent level 1 the duration will be 4, at effective talent level 5 it will be 7
getDestroy = function(self, t) return util.bound(self:getTalentLevelRaw(t), 1, 5) end, -- another function defined for this talent, it will return the raw talent level limited between 1 and 5. this number is used by the buff set in the action.
getPower = function(self, t) return 5 + math.ceil(self:combatTalentSpellDamage(t, 6, 30)) end, -- same as the above, except this returns a value which scales with talent level and spellpower. at effective talent level 1 and 10 spellpower this will be 6, at effective talent level 5 and 100 spellpower it will be 30
action = function(self, t) -- defines what will happen when the talent is used
self:setEffect(self.EFF_DESTROYER_FORM, t.getDuration(self, t), {power=t.getPower(self, t), destroyer_level = t.getDestroy(self, t)}) -- sets the destroyer form effect on the payer, passing the duration from getDuration function above, and passes the parameters 'power' and 'destroyer_level' from the respective functions above
game:playSoundNear(self, "talents/fire") -- plays a sound clip
return true -- active talents have to return true in order for the talent cooldown to start, resources consumed, etc
end,
info = function(self, t) -- defines the description for the talent. all of the %d and %0.1f are variables provided via the format at the end of the description
local dest = t.getDestroy(self, t) -- just creates a variable called 'dest' that takes the value returned by getDestroy, which will be the raw talent level (limited from 1 - 5)
return ([[Your body overflows with the power of the Fearscape, turning you into a powerful demon for %d turns. This increases your stamina regen and physical power by %d, and your disarm and stun immunity by %d%%.
The physical power, stamina regen, and status resistances increase with your spellpower.
Your other talents also gain a variety of bonuses:
-Draining Assault: Reduces cooldown by %d.
-Reckless Strike: Gain %d%% resistance penetration for all elements for %d turns.
-Obliterating Smash: Increases range by %d.
-Abduction: If it hits, get an additional %d attacks at 35%% weapon damage.
-Incinerating Blows: Increases chance of bonus damage to %d%%.
-Fearfeast: Gain %0.1f vim per stack.
-Maw of Urh'rok: Increases cone width by %d degrees.]]):
format( -- this tells the description what numbers to plug into the variables in the text of the description
t.getDuration(self, t), -- the duration of the destroyer form effect. the value from getDuration.
t.getPower(self, t), -- the physical power and stamina regen bonus. the value from getPower.
math.min(math.ceil(t.getPower(self, t)/40 *100),100), -- the status immunity bonus -- the lower of 100 and getPower divided by 40 times 100
math.ceil(dest/3), -- the cooldown reduction for Draining Assault. raw talent level (limited from 1-5) divided 3 and 'rounded' up
10 * dest, 3 + math.ceil(dest/2), -- the power of the resistance penetration bonus from reckless strike, and the duration. raw talent level (limited from 1-5) times 10, 3 + raw talent level (limited from 1-5) divided by 3 and 'rounded' up
math.ceil(dest/4), -- increased range for Obliteration Smash raw talent level (limited from 1-5) divided by 4 and 'rounded' up
math.ceil(dest/2), -- number of extra strikes from Abduction. raw talent level (limited from 1-5) divided by 2 and 'rounded' up
25 + 10 * dest, -- increased chance of bonus damage for Incinerating Blows. 25 plus 10 times raw talent level (limited from 1-5)
dest * 0.4, -- extra vim gained per stack for Fearfeast. raw talent level (limited from 1-5) times 0.4
dest * 10 -- increased cone width in degrees for Maw of Urh'rok. raw talent level (limited from 1-5) times 10
)
end,
}
Code: Select all
newEffect{
name = "DESTROYER_FORM", image = "talents/destroyer.png", -- the id for the effect, and the image to use for the effect
desc = "Destroyer", -- the displayed name of the effect
long_desc = function(self, eff) return ("The target assumes the form of a powerful demon."):format() end, -- the tooltip displayed when you mouse over the effect icon
type = "magical", -- the type of effect (e.g. physical, mental, magical, other)
subtype = { fire=true }, -- a field which contains the subtype(s) for the effect. most effects have one or two at most, these are mostly used with cleanse that target a specific subtype of effect, like wound or disease. probably is not actually used at all in this case.
status = "beneficial", -- the nature of the status, e.g. a 'beneficial' buff or a 'detrimental' debuff
parameters = {power = 5, destroyer_level = 1}, -- parameters the talent will use if it is not passed other parameters when set. essentially a failsafe
on_gain = function(self, err) return "#Target# turns into a demon!", "+Destroyer" end, -- the text that will display when an actor gains the effect
on_lose = function(self, err) return "#Target# is no longer transformed.", "-Destroyer" end, -- the text that will display when an actor loses the effect
activate = function(self, eff) -- this defines what the effect will actually do when set. in this case, grant some temporary values and change the player sprite
self:effectTemporaryValue(eff, "stamina_regen", eff.power) -- increases stamina regen by the 'power' parameter of the effect, which was called from getPower in the Destoyer talent
self:effectTemporaryValue(eff, "combat_dam", eff.power) -- combat_dam is Physical Power, which is a little confusing. increases your Physical Power by the power parameter
self:effectTemporaryValue(eff, "stun_immune", eff.power/40) -- increases stun resistance by power divided by 40
self:effectTemporaryValue(eff, "disarm_immune", eff.power/40) -- increases disarm resistance by power divided by 40
self:effectTemporaryValue(eff, "is_destroyer", eff.destroyer_level) -- sets a value called 'is_destroyer' on the actor equal to the destroyer_level parameter, which will be equal to the raw talent level of Destroyer Form (limited from 1-5)
self:effectTemporaryValue(eff, "demon", 1) -- sets the 'demon' value on the actor, indicating it should be treated as a demon (important for demonfire damage and fearscape in particular)
self:effectTemporaryValue(eff, "size_category", 2) -- increases size category by 2
self.replace_display = mod.class.Actor.new{
image="invis.png", add_mos = {{image = "npc/demon_major_champion_of_urh_rok.png", display_y = -1, display_h = 2}}, -- this makes the actor sprite invisible and sets an image file to be displayed on top of the actor, with adjusted x and y value. i believe this is where the image will center relative to the center of the tile
}
self:removeAllMOs() -- removes shaders? honestly, not totally sure, but it has do with making the game actually update the displayed image
game.level.map:updateMap(self.x, self.y) -- again, updates the display of the image and shaders
end,
deactivate = function(self, eff) -- what will happen when the effect is deactivated. automatically removes values that are added with the effectTemporaryValue syntax.
self.replace_display = nil -- sets the replace_display field to nil
self:removeAllMOs() -- updates display
game.level.map:updateMap(self.x, self.y) -- updates display
end,
}
Code: Select all
newTalent{
name = "Abduction",
type = {"corruption/torture", 2},
require = str_corrs_req2,
points = 5,
cooldown = 10,
random_ego = "attack",
stamina = 25,
vim = 14,
range = function(self, t) return math.ceil(self:combatTalentScale(t, 3.75, 8)) end,
tactical = { CLOSEIN = 3, DISABLE = 1, ATTACK = {weapon = 1}, },
requires_target = true,
getSmallhit = function (self, t) return self:combatTalentWeaponDamage(t, 0.7, 1.05) end,
getBighit = function (self, t) return self:combatTalentWeaponDamage(t, 1, 1.35) end,
on_pre_use = function(self, t, silent) if not self:hasTwoHandedWeapon() then if not silent then game.logPlayer(self, "You require a two handed weapon to use this talent.") end return false end return true end,
action = function(self, t)
local tg = {type="bolt", range=self:getTalentRange(t)}
local x, y = self:getTarget(tg)
if not x or not y then return nil end
if core.fov.distance(self.x, self.y, x, y) > self:getTalentRange(t) then return nil end
if not game.level.map.seens(x, y) or not self:hasLOS(x, y) then return nil end
local had_effect = false
self:project(tg, x, y, function(px, py)
local target = game.level.map(px, py, engine.Map.ACTOR)
if not target then return end
had_effect = true
if self:attackTarget(target, nil, t.getSmallhit(self,t), true) and not target.dead then
target:pull(self.x, self.y, tg.range)
self:attackTarget(target, nil, t.getBighit(self,t), true)
if self:attr("is_destroyer") then -- this checks to see if the actor has the 'is_destroyer' value, which is set by the Destroyer Form effect
for i = 1, math.ceil(self.is_destroyer/2) do -- this will repeat the loop from i = 1 to i = 'is_destroyer' divided by 2 and 'rounded' up. tracing back, this value is originally called by getDestroy in the Destroyer talent and passed to the Destroyer Form effect and will be equal to the raw talent level, limited from 1 to 5.
if not target.dead then self:attackTarget(target, nil, 0.35, true) end -- attacks the target with a 35% power weapon attack, if it is not dead
end
end
end
end)
return had_effect
end,
info = function(self, t)
return ([[Hits the target doing %d%% weapon damage. If the attack hits, you pull the target in and strike them again, dealing another %d%% weapon damage.]]):format(100 * t.getSmallhit(self, t), 100 * t.getBighit(self, t))
end,
}
Hopefully this clears some things up, or at least gives you a jumping off point for more questions. Feel free to ask.
Another example from vanilla ToME would be Shivgoroth Form in the Spells/Ice tree, which functions in a very similar manner.