b34 Damage question (duelist ring vs massacre)

Any discussions regarding the spoilers present in ToME 4.x.x should be restricted to this forum

Moderator: Moderator

Post Reply
Message
Author
Tom
Spiderkin
Posts: 555
Joined: Wed Apr 27, 2011 11:04 pm

b34 Damage question (duelist ring vs massacre)

#1 Post by Tom »

Hello.

I have two ring in the beginning of a game.

One is a wizards ring of massacre (5%).
One is a duelist ring of see invisible (+3 dex)

When I test them the second ring gives me 1 point extra damage on both daggers.
Will this always be the case, I mean that the +3dex is worth more than +5% physical?

Or can the percentage to physical be worth more later on with other daggers and more stats?
Yes, I am aware that I can test it out, but what does the code say about this?

Tom

Grey
Loremaster
Posts: 3517
Joined: Thu Sep 23, 2010 10:18 pm
Location: London, England
Contact:

Re: b34 Damage question (duelist ring vs massacre)

#2 Post by Grey »

The 5% will be worth more later in the game, whilst the +3 Dex will always give +1.5 damage (though it also helps other things like talents and accuracy).
http://www.gamesofgrey.com - My own T-Engine games!
Roguelike Radio - A podcast about roguelikes

marvalis
Uruivellas
Posts: 683
Joined: Sun Sep 05, 2010 5:11 am

Re: b34 Damage question (duelist ring vs massacre)

#3 Post by marvalis »

I can't really figure out how dex influences the final damage versus modifiers.
This is what I am looking at right now:
combat.lua

Code: Select all

function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
	damtype = damtype or (weapon and weapon.damtype) or DamageType.PHYSICAL
	mult = mult or 1

	-- Does the blow connect? yes .. complex :/
	local atk, def = self:combatAttack(weapon), target:combatDefense()
	if not self:canSee(target) then atk = atk / 3 end
	local dam, apr, armor = force_dam or self:combatDamage(weapon), self:combatAPR(weapon), target:combatArmor()
	print("[ATTACK] to ", target.name, " :: ", dam, apr, armor, def, "::", mult)

	if target:knowTalent(target.T_DUCK_AND_DODGE) then
		local diff = util.bound((self.size_category or 3) - (target.size_category or 2), 0, 5)
		def = def + diff * target:getTalentLevelRaw(target.T_DUCK_AND_DODGE) * 1.2
	end

	-- If hit is over 0 it connects, if it is 0 we still have 50% chance
	local hitted = false
	local crit = false
	local evaded = false
	if self:checkEvasion(target) then
		evaded = true
		game.logSeen(target, "%s evades %s.", target.name:capitalize(), self.name)
	elseif self:checkHit(atk, def) then
		local pres = util.bound(target:combatArmorHardiness() / 100, 0, 1)
		print("[ATTACK] raw dam", dam, "versus", armor, pres, "with APR", apr)
		armor = math.max(0, armor - apr)
		dam = math.max(dam * pres - armor, 0) + (dam * (1 - pres))
		print("[ATTACK] after armor", dam)
		local damrange = self:combatDamageRange(weapon)
		dam = rng.range(dam, dam * damrange)
		print("[ATTACK] after range", dam)
		dam, crit = self:physicalCrit(dam, weapon, target)
		print("[ATTACK] after crit", dam)
		dam = dam * mult
		print("[ATTACK] after mult", dam)

		if weapon and weapon.inc_damage_type then
			for t, idt in pairs(weapon.inc_damage_type) do
				if target.type.."/"..target.subtype == t or target.type == t then dam = dam + dam * idt / 100 break end
			end
			print("[ATTACK] after inc by type", dam)
		end

		if crit then game.logSeen(self, "#{bold}#%s performs a critical strike!#{normal}#", self.name:capitalize()) end
		DamageType:get(damtype).projector(self, target.x, target.y, damtype, math.max(0, dam))
		hitted = true
	else
		local srcname = game.level.map.seens(self.x, self.y) and self.name:capitalize() or "Something"
		game.logSeen(target, "%s misses %s.", srcname, target.name)
	end

	-- Spread diseases
	if hitted and self:knowTalent(self.T_CARRIER) and rng.percent(4 * self:getTalentLevelRaw(self.T_CARRIER)) then
		-- Use epidemic talent spreading
		local t = self:getTalentFromId(self.T_EPIDEMIC)
		t.do_spread(self, t, target)
	end

	-- Melee project
	if hitted and not target.dead and weapon and weapon.melee_project then for typ, dam in pairs(weapon.melee_project) do
		if dam > 0 then
			DamageType:get(typ).projector(self, target.x, target.y, typ, dam)
		end
	end end
	if hitted and not target.dead then for typ, dam in pairs(self.melee_project) do
		if dam > 0 then
			DamageType:get(typ).projector(self, target.x, target.y, typ, dam)
		end
	end end

	-- Weapon of light cast
	if hitted and not target.dead and self:knowTalent(self.T_WEAPON_OF_LIGHT) and self:isTalentActive(self.T_WEAPON_OF_LIGHT) then
		local dam = 7 + self:getTalentLevel(self.T_WEAPON_OF_LIGHT) * self:combatSpellpower(0.092)
		DamageType:get(DamageType.LIGHT).projector(self, target.x, target.y, DamageType.LIGHT, dam)
		self:incPositive(-3)
		if self:getPositive() <= 0 then
			self:forceUseTalent(self.T_WEAPON_OF_LIGHT, {ignore_energy=true})
		end
	end

	-- Shadow cast
	if hitted and not target.dead and self:knowTalent(self.T_SHADOW_COMBAT) and self:isTalentActive(self.T_SHADOW_COMBAT) and self:getMana() > 0 then
		local dam = 2 + self:combatTalentSpellDamage(self.T_SHADOW_COMBAT, 2, 50)
		local mana = 2
		if self:getMana() > mana then
			DamageType:get(DamageType.DARKNESS).projector(self, target.x, target.y, DamageType.DARKNESS, dam)
			self:incMana(-mana)
		end
	end

	-- Temporal cast
	if hitted and not target.dead and self:knowTalent(self.T_WEAPON_FOLDING) and self:isTalentActive(self.T_WEAPON_FOLDING) then
		local t = self:getTalentFromId(self.T_WEAPON_FOLDING)
		local dam = t.getDamage(self, t)
		DamageType:get(DamageType.TEMPORAL).projector(self, target.x, target.y, DamageType.TEMPORAL, dam)
	end

	-- Autospell cast
	if hitted and not target.dead and self:knowTalent(self.T_ARCANE_COMBAT) and self:isTalentActive(self.T_ARCANE_COMBAT) then
		local t = self:getTalentFromId(self.T_ARCANE_COMBAT)
		t.do_trigger(self, t, target)
	end

	-- On hit talent
	if hitted and not target.dead and weapon and weapon.talent_on_hit and next(weapon.talent_on_hit) then
		for tid, data in pairs(weapon.talent_on_hit) do
			if rng.percent(data.chance) then
				self:forceUseTalent(tid, {ignore_cd=true, ignore_energy=true, force_target=target, force_level=data.level, ignore_ressources=true})
			end
		end
	end

	-- Shattering Impact
	if hitted and self:attr("shattering_impact") and (not self.shattering_impact_last_turn or self.shattering_impact_last_turn < game.turn) then
		local dam = dam * self.shattering_impact
		self:project({type="ball", radius=1, selffire=false}, target.x, target.y, DamageType.PHYSICAL, dam)
		self:incStamina(-15)
		self.shattering_impact_last_turn = game.turn
	end

	-- Onslaught
	if hitted and self:attr("onslaught") then
		local dir = util.getDir(target.x, target.y, self.x, self.y)
		local lx, ly = util.coordAddDir(self.x, self.y, dir_sides[dir or 6].left)
		local rx, ry = util.coordAddDir(self.x, self.y, dir_sides[dir or 6].right)
		local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR)

		if target:checkHit(self:combatAttack(weapon), target:combatPhysicalResist(), 0, 95, 10) and target:canBe("knockback") then
			target:knockback(self.x, self.y, self:attr("onslaught"))
		end
		if lt and lt:checkHit(self:combatAttack(weapon), lt:combatPhysicalResist(), 0, 95, 10) and lt:canBe("knockback") then
			lt:knockback(self.x, self.y, self:attr("onslaught"))
		end
		if rt and rt:checkHit(self:combatAttack(weapon), rt:combatPhysicalResist(), 0, 95, 10) and rt:canBe("knockback") then
			rt:knockback(self.x, self.y, self:attr("onslaught"))
		end
	end

	-- Reactive target on hit damage
	if hitted then for typ, dam in pairs(target.on_melee_hit) do
		if dam > 0 then
			DamageType:get(typ).projector(target, self.x, self.y, typ, dam)
		end
	end end

	-- Acid splash
	if hitted and target:knowTalent(target.T_ACID_BLOOD) then
		local t = target:getTalentFromId(target.T_ACID_BLOOD)
		t.do_splash(target, t, self)
	end

	-- Bloodbath
	if hitted and crit and self:knowTalent(self.T_BLOODBATH) then
		local t = self:getTalentFromId(self.T_BLOODBATH)
		t.do_bloodbath(self, t)
	end

	-- Mortal Terror
	if hitted and not target.dead and self:knowTalent(self.T_MORTAL_TERROR) then
		local t = self:getTalentFromId(self.T_MORTAL_TERROR)
		t.do_terror(self, t, target, dam)
	end

	-- Dwarves stoneskin
	if hitted and not target.dead and target:attr("auto_stoneskin") and rng.percent(15) then
		target:setEffect(target.EFF_STONE_SKIN, 5, {power=target:attr("auto_stoneskin")})
	end

	-- Conduit (Psi)
	if hitted and not target.dead and self:knowTalent(self.T_CONDUIT) and self:isTalentActive(self.T_CONDUIT) and self.use_psi_combat then
		local t =  self:getTalentFromId(self.T_CONDUIT)
		t.do_combat(self, t, target)
	end

	-- Exploit Weakness
	if hitted and not target.dead and self:knowTalent(self.T_EXPLOIT_WEAKNESS) and self:isTalentActive(self.T_EXPLOIT_WEAKNESS) then
		local t = self:getTalentFromId(self.T_EXPLOIT_WEAKNESS)
		t.do_weakness(self, t, target)
	end

	-- Special effect
	if hitted and not target.dead and weapon and weapon.special_on_hit and weapon.special_on_hit.fct then
		weapon.special_on_hit.fct(weapon, self, target)
	end

	-- Poison coating
	if hitted and not target.dead and self.vile_poisons and next(self.vile_poisons) and target:canBe("poison") then
		local tid = rng.table(table.keys(self.vile_poisons))
		if tid then
			local t = self:getTalentFromId(tid)
			t.proc(self, t, target, weapon)
		end
	end

	-- Regen on being hit
	if hitted and not target.dead and target:attr("stamina_regen_on_hit") then target:incStamina(target.stamina_regen_on_hit) end
	if hitted and not target.dead and target:attr("mana_regen_on_hit") then target:incMana(target.mana_regen_on_hit) end
	if hitted and not target.dead and target:attr("equilibrium_regen_on_hit") then target:incEquilibrium(-target.equilibrium_regen_on_hit) end

	if hitted and self:attr("stamina_use_on_hit") then
		self:incStamina(-self.stamina_use_on_hit)
	end

	if hitted and not target.dead and target:knowTalent(target.T_STONESHIELD) then
		local t = target:getTalentFromId(target.T_STONESHIELD)
		local m, mm, e, em = t.getValues(self, t)
		target:incMana(math.min(dam * m, mm))
		target:incEquilibrium(-math.min(dam * e, em))
	end

	-- Ablative Armor
	if hitted and not target.dead and target:attr("carbon_spikes") then
		local t = target:getTalentFromId(target.T_CARBON_SPIKES)
		t.do_carbonLoss(target, t)
	end

	-- Riposte!
	if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:knowTalent(target.T_RIPOSTE) and rng.percent(target:getTalentLevel(target.T_RIPOSTE) * (5 + target:getDex(5, true))) then
		game.logSeen(self, "%s ripostes!", target.name:capitalize())
		target:attackTarget(self, nil, nil, true)
	end

	-- Set Up
	if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:hasEffect(target.EFF_DEFENSIVE_MANEUVER) then
		local t = target:getTalentFromId(target.T_SET_UP)
		local power = t.getPower(target, t)
		self:setEffect(self.EFF_SET_UP, 2, {src = target, power=power})
	end

	-- Counter Attack!
	if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:knowTalent(target.T_COUNTER_ATTACK) and rng.percent(target:getTalentLevel(target.T_COUNTER_ATTACK) * (5 + target:getCun(5, true))) then
		game.logSeen(self, "%s counters the attack!", target.name:capitalize())
		local t = target:getTalentFromId(target.T_COUNTER_ATTACK)
		target:attackTarget(self, nil, t.getDamage(target, t), true)
	end

	-- Defensive Throw!
	if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:knowTalent(target.T_DEFENSIVE_THROW) and rng.percent(target:getTalentLevel(target.T_DEFENSIVE_THROW) * (5 + target:getCun(5, true))) then
		local t = target:getTalentFromId(target.T_DEFENSIVE_THROW)
		t.do_throw(target, self, t)
	end

	-- Greater Weapon Focus
	local gwf = self:hasEffect(self.EFF_GREATER_WEAPON_FOCUS)
	if hitted and not target.dead and weapon and gwf and not gwf.inside and rng.percent(gwf.chance) then
		gwf.inside = true
		game.logSeen(self, "%s focuses and gains an extra blow!", self.name:capitalize())
		self:attackTargetWith(target, weapon, damtype, mult)
		gwf.inside = nil
	end

	-- Zero gravity
	if hitted and game.level.data.zero_gravity and rng.percent(util.bound(dam, 0, 100)) then
		target:knockback(self.x, self.y, math.ceil(math.log(dam)))
	end

	-- Visual feedback
	if hitted then game.level.map:particleEmitter(target.x, target.y, 1, "melee_attack", {color=target.blood_color}) end

	return self:combatSpeed(weapon), hitted
end

edge2054
Retired Ninja
Posts: 3756
Joined: Fri May 28, 2010 4:38 pm

Re: b34 Damage question (duelist ring vs massacre)

#4 Post by edge2054 »

Code: Select all

function _M:combatDamage(weapon)
You need that function, which is called on this line in your paste..

Code: Select all

 local dam, apr, armor = force_dam or self:combatDamage(weapon), self:combatAPR(weapon), target:combatArmor()

greycat
Sher'Tul
Posts: 1396
Joined: Tue May 11, 2010 11:51 pm

Re: b34 Damage question (duelist ring vs massacre)

#5 Post by greycat »

Grey wrote:the +3 Dex will always give +1.5 damage (though it also helps other things like talents and accuracy).
I'm fairly sure I can say that statement is incorrect.

+3 Dex on a (45% Dex) weapon gives 1.35 points toward the totstat variable in combatDamage. And totstat is one of the inputs used here:

Code: Select all

rescaleDamage(totstat / 1.5 * power * talented_mod)
And rescaleDamage is:

Code: Select all

function _M:rescaleDamage(dam)
        if dam <= 0 then return dam end
        return dam * (1 - math.log10(dam * 2) / 7)
end
And, um, well... I don't think output(Dex+3) minus output(Dex) is always going to be 1.5. Maybe sometimes it will.

Let's take an example, with totstat = 10, and all the other gibberish (power * talented_mod / 1.5) = 2. We'll call rescaleDamage(2*10) =~ 15.4. Now if we add 3 Dex, totstat becomes 11.35, so we call rescaleDamage(2*11.35) =~ 17.3. So we gained about 1.9 points of damage in that case. Assuming I didn't screw up the logarithm change of base calculation. Or anything else.

Post Reply