Alchemist equipdoll location? and affinity question

All development conversation and discussion takes place here

Moderator: Moderator

Post Reply
Message
Author
Nevuk
Thalore
Posts: 189
Joined: Thu Jul 27, 2006 2:50 am

Alchemist equipdoll location? and affinity question

#1 Post by Nevuk »

I'm trying to add a head slot onto the alchemist golem. That's actually really easy :

Code: Select all

		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: Select all

	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: Select all

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: Select all

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.

HousePet
Perspiring Physicist
Posts: 6215
Joined: Sun Sep 09, 2012 7:43 am

Re: Alchemist equipdoll location? and affinity question

#2 Post by HousePet »

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.

Nevuk
Thalore
Posts: 189
Joined: Thu Jul 27, 2006 2:50 am

Re: Alchemist equipdoll location? and affinity question

#3 Post by Nevuk »

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: Select all

	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 :
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: Select all

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: Select all

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: Select all

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)

HousePet
Perspiring Physicist
Posts: 6215
Joined: Sun Sep 09, 2012 7:43 am

Re: Alchemist equipdoll location? and affinity question

#4 Post by HousePet »

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.

Nevuk
Thalore
Posts: 189
Joined: Thu Jul 27, 2006 2:50 am

Re: Alchemist equipdoll location? and affinity question

#5 Post by Nevuk »

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: Select all

		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.

minmay
Wyrmic
Posts: 286
Joined: Fri Sep 07, 2012 1:34 am
Contact:

Re: Alchemist equipdoll location? and affinity question

#6 Post by minmay »

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.

Nevuk
Thalore
Posts: 189
Joined: Thu Jul 27, 2006 2:50 am

Re: Alchemist equipdoll location? and affinity question

#7 Post by Nevuk »

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: Select all

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: Select all

		
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

Post Reply