ToME: the Tales of Maj'Eyal

Everything about ToME
It is currently Tue Aug 21, 2018 10:40 am

All times are UTC




Post new topic Reply to topic  [ 7 posts ] 
Author Message
PostPosted: Thu Feb 08, 2018 7:02 pm 
Offline
Thalore

Joined: Thu Jul 27, 2006 2:50 am
Posts: 157
I'm trying to add a head slot onto the alchemist golem. That's actually really easy :
Code:
      body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, BODY=1, HEAD=1, GEM={max = 2, stack_limit = 1} },
      canWearObjectCustom = function(self, o)
         if o.type ~= "gem" then return end
         if not self.summoner then return "Golem has no master" end
         if not self.summoner:knowTalent(self.summoner.T_GEM_GOLEM) then return "Master must know the Gem Golem talent" end
         if not o.material_level then return "impossible to use this gem" end
         if o.material_level > self.summoner:getTalentLevelRaw(self.summoner.T_GEM_GOLEM) then return "Master's Gem Golem talent too low for this gem" end
      end,
      equipdoll = "alchemist_golem",


The golem can now equip a helm. The issue is that there's no space in the character slot for it (makes it hard to unequip), which I assume means I'll need to change "equipdoll="alchemist_golem" section. However, I can't find where this alchemist_golem equip doll is located in the game files. I'm assuming it'll be quite easy to change once I find it, but I've been looking for 30 minutes to no avail.

Second question:

I want to change body of fire to give fire affinity, so I looked at ice core for inspiration but the structure of the relevant parts of the two spells is totally different. Will I need to totally change body of fire?


Ice core :
Code:
   critResist = function(self, t) return self:combatTalentScale(t, 10, 50) end,
   getResistance = function(self, t) return self:combatTalentSpellDamage(t, 5, 45) end,
   getAffinity = function(self, t) return self:combatTalentLimit(t, 50, 5, 20) end, -- Limit <50%
   activate = function(self, t)
      game:playSoundNear(self, "talents/ice")
      local ret = {}
      self:addShaderAura("body_of_ice", "crystalineaura", {}, "particles_images/spikes.png")
      ret.particle = self:addParticles(Particles.new("snowfall", 1))
      self:talentTemporaryValue(ret, "resists", {[DamageType.PHYSICAL] = t.getResistance(self, t) * 0.6})
      self:talentTemporaryValue(ret, "damage_affinity", {[DamageType.COLD] = t.getAffinity(self, t)})
      self:talentTemporaryValue(ret, "ignore_direct_crits", t.critResist(self, t))
      return ret
   end,
   deactivate = function(self, t, p)
      self:removeParticles(p.particle)
      self:removeShaderAura("body_of_ice")
      return true
   end,

Body of fire:
Code:
activate = function(self, t)
      game:playSoundNear(self, "talents/fireflash")
      game.logSeen(self, "#FF8000#%s turns into pure flame!", self.name:capitalize())
      self:addShaderAura("body_of_fire", "awesomeaura", {time_factor=3500, alpha=1, flame_scale=1.1}, "particles_images/wings.png")
      return {
         onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.FIRE]=t.getFireDamageOnHit(self, t)}),
         res = self:addTemporaryValue("resists", {[DamageType.FIRE] = t.getResistance(self, t)}),
      }
   end,
   deactivate = function(self, t, p)
      self:removeShaderAura("body_of_fire")
      game.logSeen(self, "#FF8000#The raging fire around %s calms down and disappears.", self.name)
      self:removeTemporaryValue("on_melee_hit", p.onhit)
      self:removeTemporaryValue("resists", p.res)
      return true
   end,



Bonus question
Does anyone know what this is about?
Code:
function getGolem(self)
   if game.level:hasEntity(self.alchemy_golem) then
      return self.alchemy_golem, self.alchemy_golem
   elseif self:hasEffect(self.EFF_GOLEM_MOUNT) then
      return self, self.alchemy_golem
   end



Edit:

I've found where it's located : load.lua
Unfortunately, that feels really bad to mess around with. I tried overloading it and that works, equips it on the doll. How do I get the inventory slot to appear in the picture above that? (Overloading load.lua is definitely a bad idea though, so I'll be looking at other ways).

Is what I'm looking for attachement spot? The terminology is a little confusing.


Top
 Profile  
 
PostPosted: Thu Feb 08, 2018 11:23 pm 
Offline
Perspiring Physicist

Joined: Sun Sep 09, 2012 7:43 am
Posts: 5747
For number 2:
Ice Core is using the updated functionality that was added later to avoid all the messy and easy to break with typos system used by Body of Fire.
You can either convert Body of Fire to the new system by copying Ice Core, or just add in the new lines to the activate and deactivate to Body of Fire.

_________________
My feedback meter decays into coding. Give me feedback and I make mods.


Top
 Profile  
 
PostPosted: Fri Feb 09, 2018 3:45 am 
Offline
Thalore

Joined: Thu Jul 27, 2006 2:50 am
Posts: 157
HousePet wrote:
For number 2:
Ice Core is using the updated functionality that was added later to avoid all the messy and easy to break with typos system used by Body of Fire.
You can either convert Body of Fire to the new system by copying Ice Core, or just add in the new lines to the activate and deactivate to Body of Fire.

Thanks, that was very helpful. Copying over wasn't working for me so I eventually just added the new lines.

Does anyone understand explosion expert? It's very convoluted.

Full code :
Code:
   getRadius = function(self, t) return math.max(1, math.floor(self:combatTalentScale(t, 2, 6, 0.5, 0, 0, true))) end,
   minmax = function(self, t, grids)
      local theoretical_nb = (2 * t.getRadius(self, t) + 1)^1.94 -- Maximum grids hit vs. talent level
      if grids then
         local lostgrids = math.max(theoretical_nb - grids, 0)
         local mult = math.max(0,math.log10(lostgrids)) / (6 - math.min(self:getTalentLevel(self.T_EXPLOSION_EXPERT), 5))
         print("Adjusting explosion damage to account for ", lostgrids, " lost tiles => ", mult * 100)
         return mult
      else
         local min = 1
         local min = (math.log10(min) / (6 - math.min(self:getTalentLevel(t), 5)))
         local max = theoretical_nb
         local max = (math.log10(max) / (6 - math.min(self:getTalentLevel(t), 5)))
         return min, max
      end
   end,


Parts I'm curious about :

Quote:
getRadius = function(self, t) return math.max(1, math.floor(self:combatTalentScale(t, 2, 6, 0.5, 0, 0, true))) end,


What is the point of the initial 1 in math.max(1, math.floor), and similarly, what do the last few numbers represent in combatTalentScale(t, 2, 6, 0.5, 0, 0, true))) ? I can tell that t is talent level, 2 is ithe initial bonus, 6 is the max bonus, and I'm guessing 0.5 is the increment amount per talent level. So what are the 0, 0, true for? (This is commented on other similar talents, but I didn't see any with this many numbers that were documented). I know that at the end of the day, this is going to return 6 at most, but I'm still puzzled by the rest of it.


The other part of the code I'm unsure of :
Code:
local theoretical_nb = (2 * t.getRadius(self, t) + 1)^1.94 -- Maximum grids hit vs. talent level
      if grids then

What does if grids refer to? Does it mean if grids exist? Normally in a if statement there's a condition like if grids>0, instead it's just "if grids".

Possibly relevant sections from Throw bomb:

Code:
if self:knowTalent(self.T_EXPLOSION_EXPERT) then
      local nb = 0
      local grids = self:project(tg, x, y, function(tx, ty) end)
      if grids then for px, ys in pairs(grids or {}) do for py, _ in pairs(ys) do nb = nb + 1 end end end
      if nb > 0 then
      dam = dam + dam * self:callTalent(self.T_EXPLOSION_EXPERT, "minmax", nb)
      end
      end


Only place in throw bomb/explosion expert I could find grids defined :
Code:
local grids = self:project(tg, x, y, function(tx, ty)
         local d = dam
         local target = game.level.map(tx, ty, Map.ACTOR)
         -- Protect yourself
         if tx == self.x and ty == self.y then
            d = dam * (1 - prot)
         -- Protect the golem
         elseif golem and tx == golem.x and ty == golem.y then
            d = dam * (1 - prot)
            if self:isTalentActive(self.T_FROST_INFUSION) and self:knowTalent(self.T_ICE_ARMOUR) then
               self:callTalent(self.T_ICE_ARMOUR, "applyEffect", golem)
            elseif self:isTalentActive(self.T_ACID_INFUSION) and self:knowTalent(self.T_CAUSTIC_GOLEM) then
               self:callTalent(self.T_CAUSTIC_GOLEM, "applyEffect", golem)
            elseif self:isTalentActive(self.T_LIGHTNING_INFUSION) and self:knowTalent(self.T_DYNAMIC_RECHARGE) then
               self:callTalent(self.T_DYNAMIC_RECHARGE, "applyEffect", golem)
            end
         else -- reduced damage to friendly npcs (could make random chance like friendlyfire instead)
            if target and self:reactionToward(target) >= 0 then d = dam * (1 - prot) end
         end
         if d <= 0 then return end

--         local target = game.level.map(tx, ty, Map.ACTOR)
         dam_done = dam_done + DamageType:get(damtype).projector(self, tx, ty, damtype, d, tmp)
         if ammo.alchemist_bomb and ammo.alchemist_bomb.splash then
            DamageType:get(DamageType[ammo.alchemist_bomb.splash.type]).projector(self, tx, ty, DamageType[ammo.alchemist_bomb.splash.type], ammo.alchemist_bomb.splash.dam)
         end
         if not target then return end
         if ammo.alchemist_bomb and ammo.alchemist_bomb.stun and rng.percent(ammo.alchemist_bomb.stun.chance) and target:canBe("stun") then
            target:setEffect(target.EFF_STUNNED, ammo.alchemist_bomb.stun.dur, {apply_power=self:combatSpellpower()})
         end
         if ammo.alchemist_bomb and ammo.alchemist_bomb.daze and rng.percent(ammo.alchemist_bomb.daze.chance) and target:canBe("stun") then
            target:setEffect(target.EFF_DAZED, ammo.alchemist_bomb.daze.dur, {apply_power=self:combatSpellpower()})
         end
      end)


Top
 Profile  
 
PostPosted: Fri Feb 09, 2018 4:49 am 
Offline
Perspiring Physicist

Joined: Sun Sep 09, 2012 7:43 am
Posts: 5747
To be honest, I think that the power boost for blocked tiles is so unbalancing it is stopping Throw Bomb from being properly balanced and should be totally removed.

_________________
My feedback meter decays into coding. Give me feedback and I make mods.


Top
 Profile  
 
PostPosted: Fri Feb 09, 2018 5:06 am 
Offline
Thalore

Joined: Thu Jul 27, 2006 2:50 am
Posts: 157
According to the comments in minmay's revamped alchemist mod it currently incorrectly calculates it and is always off by at least 1 (i believe it always gives some bonus), so he redid it with a different function (core.fov.calc_circle), so I will probably may use that approach instead on my mod, rendering the explosion expert stuff kind of moot. When trying his it felt noticeably weaker, far more than you'd think from the numbers only going to 125 instead of 216, so I'm thinking there's some sort of calculation error in the original one making it stronger than intended (but it's also the only thing making the class playable so no one is going to beg for it to be fixed).

Got that working, stumbled across either a bug or old code in the process I think:
Under throw bomb :
Code:
      inc_dam = inc_dam + (ammo.alchemist_bomb and ammo.alchemist_bomb.power or 0) / 100
      local dam = self:combatTalentSpellDamage(t, 1, 320, ((ammo.alchemist_power or 0) + self:combatSpellpower()) )


There's no such thing as ammo.alchemist_bomb.power , at least it's never mentioned in the code aside from this line. I think it's just ammo.alchemist_power. I could be wrong, though.


Top
 Profile  
 
PostPosted: Fri Feb 09, 2018 7:15 pm 
Offline
Wyrmic

Joined: Fri Sep 07, 2012 1:34 am
Posts: 257
Nevuk wrote:
When trying his it felt noticeably weaker, far more than you'd think from the numbers only going to 125 instead of 216, so I'm thinking there's some sort of calculation error in the original one making it stronger than intended (but it's also the only thing making the class playable so no one is going to beg for it to be fixed).
Remember that I also completely changed the damage formula for Throw Bomb itself. The vanilla one is combatTalentSpellDamage(15, 150, (ammo.alchemist_power + spellpower)/2), whereas the one in Revamped Alchemist is combatTalentSpellDamage(0, 260). ammo.alchemist_power is 20 for tier 1 gems, 35 for tier 2, 50 for tier 3, 65 for tier 4, and 70 for tier 5.
The alchemist_bomb fields exist and work, but the way they're created is rather weird (look at newGem() in gems.lua).

Hers, btw.


Top
 Profile  
 
PostPosted: Fri Feb 09, 2018 11:52 pm 
Offline
Thalore

Joined: Thu Jul 27, 2006 2:50 am
Posts: 157
minmay wrote:
Nevuk wrote:
When trying his it felt noticeably weaker, far more than you'd think from the numbers only going to 125 instead of 216, so I'm thinking there's some sort of calculation error in the original one making it stronger than intended (but it's also the only thing making the class playable so no one is going to beg for it to be fixed).
Remember that I also completely changed the damage formula for Throw Bomb itself. The vanilla one is combatTalentSpellDamage(15, 150, (ammo.alchemist_power + spellpower)/2), whereas the one in Revamped Alchemist is combatTalentSpellDamage(0, 260). ammo.alchemist_power is 20 for tier 1 gems, 35 for tier 2, 50 for tier 3, 65 for tier 4, and 70 for tier 5.
The alchemist_bomb fields exist and work, but the way they're created is rather weird (look at newGem() in gems.lua).

Hers, btw.

Thanks, that explains some of the mystery. (I looked at the gem file but didn't check the newgem() field).

Currently I'm just changing numbers, creating a new character and throwing bombs at test dummies to try out what changing the formula does. Your version of explosion expert feels much more predictable than the old one, at the least, but that could just be the superloaded part that tells you the % boost before the bomb is thrown.

My current code is sloppy here, as I'm still not fully understanding the significance of some of the variables.

Code:
inc_dam = inc_dam + (ammo.alchemist_bomb and ammo.alchemist_bomb.power or 0) / 100
local dam = self:combatTalentSpellDamage(t, 0, 175, (((ammo.alchemist_power or 0) + self:combatSpellpower()*3)-50) )
dam = dam * (1 + inc_dam)


(I'm just using your default values for the % bonus from explosion expert. The damage range was too high when I tried upping it - I don't think it makes sense for an ability to do up to 300% more damage before critting).

My goal is to get it to be about 400 damage "raw" at level 50 with just points in magic (so at about 60 magic), with a well aimed bomb being able to get roughly double damage, meaning 800 damage pre-crit, while retaining a value of throw bomb that's similar to vanilla ToME at low levels, but also allow it to scale a bit better/more smoothly than vanilla throw bomb does. Does my method make sense for this? (I'm also not sure if these damage numbers would make sense compared to other class' damage output numbers)


I have a somewhat nasty cold today, so let me know if I'm not making much sense.

Also:
Sorry about the assumption, updated the relevant thanking text on my addon.


edit:
So here's what I'm understanding :
ammo.alchemist_bomb refers to the bomb effects
alchemist_bomb.power refers to the bombs with a special multiplier (such as diamonds giving 25% extra damage)
ammo.alchemist_power refers to the inherent power of each gem tier (20, 35, 50, 65, 70)

This actually solves my problem most of the way. I'm going to deprecate alchemist_bomb.power and use alchemist_power as a similar modifier.

So
Code:
      
inc_dam = inc_dam + (ammo.alchemist_bomb and ammo.alchemist_power or 0) / 100
local dam = self:combatTalentSpellDamage(t, 15, 175, ((10) + self:combatSpellpower()))
dam = dam * (1 + inc_dam)

I think will make it much easier to balance


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group