ToME "Infinite" Scaling Design

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

ToME "Infinite" Scaling Design

#1 Post by Hachem_Muche »

Thanks to the efforts of the development team, a dedicated and engaged group of players, and especially DarkGod, ToME is an amazingly well balanced game across a wide range of race/class combinations and a range of scenarios. Starting with ToME development version SVN 6783 (for release version 1.0.5) improved "infinite" scaling methods have been applied to most talents and many game mechanics. The objective of the new changes is to project current game balance ahead into higher character/difficulty levels, the upcoming Orc Campaign, and beyond.

This guide is intended to be an introduction to the scaling methods for developers of ToME (including both official content and addons). It describes the math and functional support recently added to ToME (previously used in the Infinite 500 add-on) which enables the character level limit to be increased from 50 to more than 500. Most of this material can be applied directly to other modules as well.

The balance considerations needed to accomplish this are pretty simple. From most to least important they are:
  • I. Don't break anything. (Keep the game running.)
    It's no good if it doesn't work. For any conceivable range of talent levels, stats, and other character attributes, you don't want the game to crash. The most common problems of this sort are things like negative talent cool-downs, speeds, or other properties that have to be positive, divide by zero errors (in lua, you usually get inf or nan, which just pushes the error away from where the problem originates), or bad scaling that can overpower or obviate core game mechanics and result in strange behavior. The t-engine and the ToME module are pretty robust and tolerant of many coding errors, but nonsense results must still be avoided.

    II. Maintain fairness. (Provide a consistent challenge.)
    Make sure that individual talents or combinations of abilities don't become over- or under-powered at various points in the game. ToME accommodates a wide range of play styles because of the diverse range of abilities that are balanced with respect to each other. While there is considerable variation in effectiveness of various builds (race/class combinations) and the talent selections that go with them (which also supports more play styles), it is possible, if not easy, to clear the entire Maj'Eyal campaign with virtually any of them.

    To make this happen, the effectiveness of core game mechanics must scale consistently. That is, things like damage output, resistances, health, resources (and recovery), etc. must be of similar effectiveness across a variety of builds from level 1 onwards. This means, among other things, that an archmage and a berserker of similar level, using similar quality equipment, and played with equivalent skill can produce fairly similar amounts of damage (even if the mage is better at AOE and the berzerker is better at focused damage) and have similar survivability (the berserker is tougher and can recover more quickly, but the mage is better at avoiding damage).

    III. Provide continuous improvement. (Reward character progress.)
    Character development is a tenet of RPG game design. It's what makes games like ToME so engaging (addictive) and have great replayability. ToME excels at this because of all of the choice available to the player on how to develop a character including 26 classes, over 1,000 individually programmed unique talents, and strongly player-directed character development.

    To maintain this choice going forward, the game must support character improvment with investment in as wide a range character attributes as possible. For example, the Weapon Mastery talent steadily improves damage at all talent levels. On the other hand, the Hide In Plain Sight and Unseen Actions talents previously (ToME 1.0.4) had a flat % chance of success that increased in equal increments with talent level. 100% success chance was reached at about talent level 9 or 10, and then there there was no room for further improvement. The scaling changes replace this mechanic with an opposed skill system that rewards talent investment with a skill multiplier that is countered by the various detection and sensing abilities of enemys. As a result, investment in these skills is useful far past the previous limits.
Scaling Factors:
To provide consistent scaling, scaling factors (formula exponents) were chosen with which to guide improvement of various characteristics with character level and to stay consistent with existing game code and values. This applies equally to both players and NPC's, since all actors in ToME use the same game mechanics. Consistent with a diminishing returns philosophy, none of these factors are greater than 1.0:

Code: Select all

		Scaling factors (with character level)

		Attribute		  		Power
		Health/Damage*	 		1.0 (consistent scaling here is the objective of the other scaling factors)
		Duration		 	   	0.5
		Range/Radius	 	  	0.5
		Damage Multipliers		0.5 (some exceptions)
		Crit multiplier			0.75
		Armour/APR			  	0.5-0.75 (except armor training @ 1.0)
		Attack/Defense**	  	0.75-1.0  (some exceptions)
		Raw Saves/Power**	 	0.75-1.0
		Speed			    		0.75
		Stat bonus/penalty		0.5-1.0 (0.75)
		Resistances/reduction	0.5 (except thick skin, note caps and penetration)
		Stealth/Invis		  	1.0 (includes detection)
		Effect removal	 		log (includes talents limited by the number of targets)
		Crit rate/penalty		 0.5-0.75
		Resource recovery	 	0.75
*Health actually has a small tl^2 component, and damage can be greatly affected by equipment.
** These directly opposed values have an extra scaling step (rescaleCombatStats) designed to keep values from diverging too much.

The core of this system requires that both character health (and most resources in general) and offensive ability (damage) scale more or less in proportion to character level. That is, advancing a character to twice its level will result in it having around twice as many hit points and it will deal approximately twice as much damage (everything else being equal).

The duration of most talent effects, on the other hand, is proportional to the square root (0.5 power) of character level. So, if a character keeps investing in a typical talent while going from level 10 to level 40 the talent duration will increase by a factor of 2x. There are exceptions to this, of course.

Tiered scaling:
Some of the scaling factors have a range of powers. These characteristics are scaled in tiers in order to maintain competitive play balance between opposed abilities. For example, armor from the Armor Training skill, which almost every class has access to, scales in proportion to character level (1.0 power with talent level) in order to keep pace with melee damage, which also scales in proportion to character level. Other abilities that affect this balance (addional armor talents like Stoneskin (0.5 power with talent level) or armor piercing talents like Deadly Strikes (0.5 power with Cunning)) scale more slowly than the base because they can provide a strong differential advantage for or against this balance and are not available to all characters.

Reference Characters:
The new scaling changes are designed to maintain the previous game balance as much as possible. It is assumed that a character invests as heavily as possible in the appropriate talent and stats and takes into account a few properties of ToME's leveling system. Once higher character levels are reached, the maximum (raw) talent level that can be learned is level/10 and the maximum (base) stat that can be trained is level + 10. So talent levels and stats are both proportional to character level also.

When matching the new formulas to the old, two standard reference characters were chosen:

Code: Select all

	STARTING CHARACTER						HIGH LEVEL CHARACTER
	Level 			  1						Level 			  50
	Primary stats 	10					  Primary stats 	100
	Talent Mastery	1.0					 Talent Mastery	1.0
	Talent level	  1.0 (effective)	 Talent level	  5.0 (effective)
The stats for the level 50 reference are typical for an end game character on normal difficulty in the Maj'Eyal campaign and were given priority when matching existing talent values. When considering the Stealth talent, for example, an end game character was assumed to be level 50, with 100 Cunning (including the base stat and 40 bonus points from equipment and other sources) and to have an effective talent level of 5.0. While every character is different (Many classes have 1.3 talent mastery for their core talents.), deviations from the old formulas for characters similar to the reference characters, particularly the end game character, are small.

As an example, using the original formula for stealth (power = 4 + 0.1 * Cunning * Talent Level), the hypothetical starting reference character would have a stealth power of 5, and the high level character would have 54. The old formula calculated stealth power in proportion to the square of character level, since both maximum Cunning and maximum Talent Level are proportional to character level and they are multiplied together. The new formula (computed in a lua call like power = combatScale(Cunning * Talent Level, 5, 1, 54, 50), see the expanation of the scaling functions below) takes the square root of Cunning * Talent Level (so 1.0 power with character level) and scales this value so that when Cunning * Talent Level = 1, stealth power is 5 and when it's 50, stealth power is 54.

Limits:
In a number of places, characteristics are subject to limits. Limits are needed primarily to keep certain characteristics within bounds, like preventing total invulnerability to status effects, or to limit talent effects that could break game scaling from other sources. Some of the more important issues include (with some examples):
  • Speed reduction cannot create negative speeds. (Cripple, Thorn Grab. There are also some hard limits that prevent negative speeds as well to prevent game breaking bugs.)

    No negative cooldowns. (Supercharge Golem, Rush - can get to zero with other talents and equipment, but has a substantial stamina cost.)

    Most absolute status immunities, like stun, confusion, pinning, knockback, etc., are limited to less than 100%, at least for a single talent. (Shield Wall, Relentless)

    Some activated abilities with a lasting effect cannot have a duration longer than the cooldown. These include most instant use talents (many racial abilities) since otherwise they could be kept up continuously like a sustained skill, and certain very powerful effects that can unbalance the game if they are kept up continuously (Unstoppable, Paradox Clone, Adrenalin Surge).

    Resistance penetration (from a single talent) is limited to less than 100%. (Wildfire, Vulnerability Poison)

    Core stat multipliers. (Battle Shout <50% extra life, Greater Weapon Focus <100% chance, effectively a damage multiplier)

    Healing and damge penalties are limited to less than < 100% to prevent overpowering too many NPC's, which may not have an offsetting bonus. (Curse of Impotence, Insidious Poison)

Function Support for Scaling:
As of SVN 6783 for release version 1.0.5, new functions have been added to Combat.lua in order to make scaling much easier to manage. These include scaling functions and limit functions. To use these functions in addons prior to ToME 1.0.5's release you can superload them:
Combat.txt
(7.66 KiB) Downloaded 633 times
(change the extension to .lua)

Scaling functions:
These functions are designed to manage diminishing returns effects. One is for talents and the other is for raw stats, but they both employ a variation of the same formula:

f(x) = m*(x + shift)^power + b + add)

where shift, power, and add are inputs, and m and b are internally computed to match desired values.

Code: Select all

-- Compute a diminishing returns value based on talent level that scales with a power
-- t = talent def table or a numeric value
-- low = value to match at talent level 1
-- high = value to match at talent level 5
-- power = scaling factor (default 0.5) or "log" for log10
-- add = amount to add the result (default 0)
-- shift = amount to add to the talent level before computation (default 0)
-- raw if true specifies use of raw talent level
function _M:combatTalentScale(t, low, high, power, add, shift, raw)

-- Compute a diminishing returns value based on a stat value that scales with a power
-- stat = "str", "con",.... or a numeric value
-- low = value to match when stat = 10
-- high = value to match when stat = 100
-- power = scaling factor (default 0.5) or "log" for log10
-- add = amount to add the result (default 0)
-- shift = amount to add to the stat value before computation (default 0)
function _M:combatStatScale(stat, low, high, power, add, shift)
To use these, you specify two values you want to match, and the function computes a curve passing through those points using the power you specified. The power is either a number, usually less than 1 (default 0.5), or "log," which sets the kind of curve to use. Then, the point on the curve corresponding to the first argument is found and returned as the result.

For combatTalentScale the first argument is either the talent definition table (from which the talent level is determined) or a number. The high and low arguments correspond to the values you want to match for effective talent levels 1.0 and 5.0. The add argument is added after the computation. The shift argument adjusts talent level up or down before computing the curve. This allows for better matching of desired values in between the points specified, and generally has the effect of straightening out the curve, like fitting the part of the curve to the right by the specified amount.

This function never returns negative values, but otherwise always returns low + add for talent level 1.0 and high + add for talent level 5.0.

The combatStatScale function is very similar, except that the first value is a stat abbreviation ("str", "dex", "mag", "wil", "cun", "con") or the stat value to use, and the high and low arguments correspond to stat values of 10 and 100 respectively.

Examples:
  • self:combatTalentScale(t, 1, 7)

    This function computes a power curve (with default power 0.5) passing through the points (1, 1) and (5, 7). Using self as the actor with the talent, this takes the talent definition table, t, and returns 1 if the talent level is 1 and 7 if the talent level is 5. For (effective) talent levels {1, 2, 3, 4, 5, 10} it returns:

    {1.00, 3.01, 4.55, 5.85, 7.00, 11.50}

    self:combatTalentScale(t, 1, 7, 0.75)

    As before, but uses power = 0.75 and returns:

    {1.00, 2.75, 4.28, 5.68, 7.00, 12.84}

    self:combatTalentScale(t, 1, 7, "log")

    Uses a logarithmic scale, returning:

    {1.00, 3.58, 5.10, 6.17, 7.00, 9.58}

    self:combatTalentScale(t, 1, 7, 0.5, 2)

    Adds 2 to the first result, returning:

    {3.00, 5.01, 6.55, 7.85, 9.00, 13.50}

    self:combatTalentScale(t, 1, 7, 0.5, 0, 10)

    Uses the straighter part of the first curve, shifting the references "to the right" by 10, returning:

    {1.00, 2.59, 4.12, 5.58, 7.00, 13.46}
The differences in these curves are obvious in the following plot:
ToMEScalingPlot1.jpg
ToMEScalingPlot1.jpg (80.02 KiB) Viewed 14546 times

Limit functions:
These functions are designed to allow for a gradual (smooth) progression from values you specify towards a limit that cannot be passed. There is one for talents and another for stats that use the same default base values as the scaling functions. The formula used is a variation of:

f(x) = (limit-b)*x/(x + h) + b

where b and h ("add" and "halfpoint" in the lua code) are computed to match the high and low values specified. Note that the function reaches the midpoint of its range when x = h.

Code: Select all

-- Compute a diminishing returns value based on talent level that cannot go beyond a limit
-- t = talent def table or a numeric value
-- limit = value approached as talent levels increase
-- high = value at talent level 5
-- low = value at talent level 1 (optional)
-- raw if true specifies use of raw talent level
--	returns (limit - add)*TL/(TL + halfpoint) + add == add when TL = 0 and limit when TL = infinity
-- TL = talent level, halfpoint and add are internally computed to match the desired high/low values
-- note that the progression low->high->limit must be monotone, consistently increasing or decreasing
function _M:combatTalentLimit(t, limit, low, high, raw)

-- Compute a diminishing returns value based on a stat value that cannot go beyond a limit
-- stat == "str", "con",.... or a numeric value
-- limit = value approached as talent levels increase
-- high = value to match when stat = 100
-- low = value to match when stat = 10 (optional)
--	returns (limit - add)*stat/(stat + halfpoint) + add == add when STAT = 0 and limit when stat = infinity
-- halfpoint and add are internally computed to match the desired high/low values
-- note that the progression low->high->limit must be monotone, consistently increasing or decreasing
function _M:combatStatLimit(stat, limit, low, high)
For combatTalentLimit, the first argument is either the talent definition table (from which the talent level is determined) or a number. The limit argument is the value that is approached as talent levels increase, but that cannot be reached. The low and high arguments are the desired values corresponding to (effective) talent levels 1.0 and 5.0 respectively. To get meaningful results, if high > low, make sure limit > high and visa versa.

combatStatLimit is similar, except that the first argument is a stat abbreviation or a number, and the high and low values correspond to stat values of 100 and 10 respectively.

Examples:
  • Calling

    self:combatStatLimit("dex", 100, 30, 85)

    causes the function to compute an increasing curve passing through the points (10, 30) and (100, 85) that is always less than 100, look up the value of the Dexterity stat and then find the corresponding point on the curve (the return value). For dex values of {10, 25, 50, 75, 100, 250}, the following values are returned:

    {30.00, 56.55, 73.38, 80.81, 85.00, 93.51}

    Conversely, calling

    self:combatStatLimit("wil", 0, 50, 15)

    on the same values for Willpower computes a curve that is always more than 0, and returns the following values:

    {50.00, 36.00, 24.55, 18.62, 15.00, 6.92}
The functions used by these calls are illustrated in the following graph
ToMEScalingPlot2.jpg
ToMEScalingPlot2.jpg (106.52 KiB) Viewed 14546 times
Generic functions:

Code: Select all

-- Scale a value up or down by a power
-- x = a numeric value
-- y_low = value to match at x_low
-- y_high = value to match at x_high
-- power = scaling factor (default 0.5)
-- add = amount to add the result (default 0)
-- shift = amount to add to the input value before computation (default 0)
function _M:combatScale(x, y_low, x_low, y_high, x_high, power, add, shift)

-- Scale a value up or down subject to a limit
-- x = a numeric value
-- limit = value approached as x increases
-- y_high = value to match at when x = x_high
-- y_low (optional) = value to match when x = x_low
--	returns (limit - add)*x/(x + halfpoint) + add (= add when x = 0 and limit when x = infinity), halfpoint, add
-- halfpoint and add are internally computed to match the desired high/low values
-- note that the progression low->high->limit must be monotone, consistently increasing or decreasing
function _M:combatLimit(x, limit, y_low, x_low, y_high, x_high)
These functions work in the same way as the stat and talent scale and limit functions above, but make no assumptions about the points to match. Both x and y values at two points are required arguments to specify the curve to be used, which passes through (x_low, y_low) and (x_high, y_high).

Negative values are valid results for combatScale and the power, if specified, must be a numeric argument ("log" cannot be specified).

Integrated Example: Converting a talent to use infinite scaling

To illustrate the use of the new scaling methods, here is a step-by-step example of how the Scoundrel Strategies talent was modified. This talent has a number of effects when being attacked by or when attacking bleeding enemies. The starting talent definition is (for ToME 1.0.4):

Code: Select all

newTalent{
	name = "Scoundrel's Strategies", short_name = "SCOUNDREL",
	type = {"cunning/scoundrel", 2},
	require = cuns_req2,
	mode = "passive",
	points = 5,
	getDuration = function(self, t) return 3 + math.ceil(self:getTalentLevel(t)/2) end,
	getMovePenalty = function(self, t) return (5 + self:combatTalentStatDamage(t, "cun", 10, 30)) / 100 end,
	getAttackPenalty = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 5, 20) end,
	getWillPenalty = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 5, 20) end,
	getCunPenalty = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 5, 20) end,
	do_scoundrel = function(self, t, target)
		if not rng.percent(5+(self:getTalentLevel(t)*3)) then return end
		if rng.percent(50) then
			if target:hasEffect(target.EFF_DISABLE) then return end
			target:setEffect(target.EFF_DISABLE, t.getDuration(self, t), {speed=t.getMovePenalty(self, t), atk=t.getAttackPenalty(self, t), apply_power=self:combatAttack()})
		else
			if target:hasEffect(target.EFF_ANGUISH) then return end
			target:setEffect(target.EFF_ANGUISH, t.getDuration(self, t), {will=t.getWillPenalty(self, t), cun=t.getCunPenalty(self, t), apply_power=self:combatAttack()})
		end
	end,
	info = function(self, t)
		local duration = t.getDuration(self, t)
		local move = t.getMovePenalty(self, t)
		local attack = t.getAttackPenalty(self, t)
		local will = t.getWillPenalty(self, t)
		local cun = t.getCunPenalty(self, t)
		return ([[Learn to take advantage of your enemy's pain.
		If your enemy is bleeding and attempts to attack you, their critical hit rate is reduced by %d%%, as their wounds make them more predictable.
		If you attack a bleeding enemy, there is a %d%% chance that, for %d turns, they are disabled as you take advantage of openings (reducing their movement speed by %d%% and Accuracy by %d) or anguished as you strike their painful wounds (reducing their Willpower by %d and their Cunning by %d).
		The statistical reductions will increase with your Cunning.
		]]):format(5+(self:getTalentLevel(t)*5),5+(self:getTalentLevel(t)*3),duration,move * 100,attack,will,cun)
	end,
}
and one of the talent's effects is computed in mod.class.interface.Combat.lua (part of the _M:physicalCrit function):

Code: Select all

...
	-- Scoundrel's Strategies
	if self:attr("cut") and target:knowTalent(self.T_SCOUNDREL) then
		chance = chance - (5 + (target:getTalentLevel(self.T_SCOUNDREL)*5))
	end
...
(The do_scoundrel function is also called by the _M:attackTargetWith function in mod.class.interface.Combat.lua, but the calling code does not need to be changed in this example.)

Looking at this code, consider what happens when a character of arbitrarily high level and corresponding stats invests fully in this talent. What will be the effects for a level 100 character? 200? 500+? (These levels can occur for NPCs in the High Peak or deep in the Infinite Dungeon.)

The duration (of the disabling effect) increases linearly with talent level (and thus character level) and could get excessively long - 10's of turns, quite possibly much longer than the fight itself. The duration can be brought in line with more typical game time scales by replacing the getDuration function with:

Code: Select all

getDuration = function(self, t) return math.ceil(self:combatTalentScale(t, 3.3, 5.3)) end, 
which scales as the 0.5 power. Here, the rounding and parameters are chosen to match the same duration for the first 5 talent investments (assumed to correspond to talent levels 1.0, 2.0, 3.0, 4.0, 5.0 for the reference characters).

The movement penalty, though it's based on the combatTalentStatDamage function, which has diminishing returns built in, can increase beyond 100%, which would correspond to a negative movement speed. The call to self:combatTalentStatDamage(t, "cun", 10, 30) has a minimum value of 0 and returns 22.4 for the high level reference character. To limit this to less than a 1.0 move factor (100% move speed reduction), replace the getMovePenalty function with:

Code: Select all

getMovePenalty = function(self, t) return self:combatLimit(self:combatTalentStatDamage(t, "cun", 10, 30), 1, 0.05, 0, 0.274, 22.4) end, -- Limit <100%
which returns 0.274 and 0.05 when self:combatTalentStatDamage(t, "cun", 10, 30) returns 22.4 and 0 respectively, matching 2 points for the existing function.

The chance to apply the Disable or Anguish effects, computed with rng.percent(5+(self:getTalentLevel(t)*3)), can also increase to more than 100%. To limit these to 100%, introduce a new function to the talent definition:

Code: Select all

disableChance = function(self,t) return self:combatTalentLimit(t, 100, 8, 20) end, -- Limit <100%
This limits the chance to < 100%, while matching the 8% chance at talent level 1.0 and the 20% chance at talent level 5.0, for the low and high level reference characters respectively, with the existing formula. The previous computuations in the do_scoundrel and info functions are then replaced with a call to the new function.

The crit rate reduction for a bleeding enemy attacking a character with this talent, computed as 5 + (target:getTalentLevel(self.T_SCOUNDREL)*5) in the _M:physicalCrit and the talent info functions, scales linearly with talent level (1.0 power). This is faster than the normal crit rate (0.5-0.75 power, see the scaling factors above). This means that a high level character with this talent can become completely immune to critical strikes, even against opponents with a bonus crit chance, since normal crit rates cannot keep up. As this is not a widely available talent, the lower tier scaling (0.5 power) is appropriate. The scaling can be made more consistent by adding a another new function:

Code: Select all

getCritPenalty = function(self,t) return self:combatTalentScale(t, 10, 30) end,
which matches the existing effects (10% at talent level 1.0, 30% at talent level 5.0), but scales as the default 0.5 power with talent level. The computations in the talent info function and in the _M:physicalCrit function in mod.class.interface.Combat.lua are then updated to call this new function.

Here is the revised talent definition (for ToME 1.0.5):

Code: Select all

newTalent{
	name = "Scoundrel's Strategies", short_name = "SCOUNDREL",
	type = {"cunning/scoundrel", 2},
	require = cuns_req2,
	mode = "passive",
	points = 5,
	getDuration = function(self, t) return math.ceil(self:combatTalentScale(t, 3.3, 5.3)) end,
	-- _M:physicalCrit function in mod\class\interface\Combat.lua handles crit penalty 
	getCritPenalty = function(self,t) return self:combatTalentScale(t, 10, 30) end,
	disableChance = function(self,t) return self:combatTalentLimit(t, 100, 8, 20) end, -- Limit <100%
	getMovePenalty = function(self, t) return self:combatLimit(self:combatTalentStatDamage(t, "cun", 10, 30), 1, 0.05, 0, 0.274, 22.4) end, -- Limit <100%
	getAttackPenalty = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 5, 20) end,
	getWillPenalty = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 5, 20) end,
	getCunPenalty = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 5, 20) end,
	do_scoundrel = function(self, t, target)
		if not rng.percent(t.disableChance(self, t)) then return end
		if rng.percent(50) then
			if target:hasEffect(target.EFF_DISABLE) then return end
			target:setEffect(target.EFF_DISABLE, t.getDuration(self, t), {speed=t.getMovePenalty(self, t), atk=t.getAttackPenalty(self, t), apply_power=self:combatAttack()})
		else
			if target:hasEffect(target.EFF_ANGUISH) then return end
			target:setEffect(target.EFF_ANGUISH, t.getDuration(self, t), {will=t.getWillPenalty(self, t), cun=t.getCunPenalty(self, t), apply_power=self:combatAttack()})
		end
	end,
	info = function(self, t)
		local duration = t.getDuration(self, t)
		local move = t.getMovePenalty(self, t)
		local attack = t.getAttackPenalty(self, t)
		local will = t.getWillPenalty(self, t)
		local cun = t.getCunPenalty(self, t)
		return ([[Learn to take advantage of your enemy's pain.
		If your enemy is bleeding and attempts to attack you, their critical hit rate is reduced by %d%%, as their wounds make them more predictable.
		If you attack a bleeding enemy, there is a %d%% chance that, for %d turns, they are disabled as you take advantage of openings (reducing their movement speed by %d%% and Accuracy by %d) or anguished as you strike their painful wounds (reducing their Willpower by %d and their Cunning by %d).
		The statistical reductions will increase with your Cunning.
		]]):format(t.getCritPenalty(self,t), t.disableChance(self, t), duration, move * 100, attack, will, cun)
	end,
}
and the call to the new function in the _M:physicalCrit in mod.class.interface.Combat.lua:

Code: Select all

...
	-- Scoundrel's Strategies
	if self:attr("cut") and target:knowTalent(self.T_SCOUNDREL) then
		chance = chance - target:callTalent(target.T_SCOUNDREL,"getCritPenalty")
	end
...
Author of the Infinite 500 and PlenumTooltip addons, and the joys of Scaling in ToME.

MalReynolds
Halfling
Posts: 98
Joined: Thu Aug 08, 2013 3:02 am

Re: ToME "Infinite" Scaling Design

#2 Post by MalReynolds »

Why stop with extending levels upwards?

I think even better would be extending them *inwards*: fractional levels, like 14 + 3/4! And then irrational levels, like 4 + pi! And finally imaginary levels!

But seriously, this is a massive challenge, with more moving parts than a clock, and the work here is fascinating.

It would be more relevant if I could get a character in the infinite dungeon past about 12.

Hirumakai
Thalore
Posts: 192
Joined: Wed Aug 11, 2010 2:39 pm

Re: ToME "Infinite" Scaling Design

#3 Post by Hirumakai »

How is equipment and XP (and/or level) to normal/boss kills intended to scale in the 50 to 500 range? Also, you don't seem to touch on Physical Power, Spell Power, Mind Power, Accuracy, Defense, and saving throws. Are these expected to simply max out 100 and stay there in high level play?

Basically, I'd like to know how the stats on equipment (both raw magical bonuses like +Dex or Fire resist, as well as base armor/base damage values) are scaling with level.

I ask because my Spellsword module has a talent which converts non-artifact weapons/shields into level appropriate artifact weapons, based on number of overall kills as well as tier 4 boss kills (actual end dungeon bosses as opposed to chest spawned bosses, for example). Its taken me a while to find the right scaling parameters to produce weapons comparable to 4x Ego random artifact weapons at end game while scaling reasonably close to what one might expect to find along the way. Are bonuses like +Dex or Fire Resist going to scale without limit, or do they have similar functions planned for them?

At 5/5 talent level, what I'd call the bonus damage (i.e. damage above and beyond an equivalent weapon because you're paying a category point and 5 generic points - similar to the psionic Reshape Weapon talent) increases roughly as the log of the number of kills. You get +2 damage/+2 accuracy for killing 1, then another +2/+2 after killing 2 more (3 total for +4/+4), then 4 more (7 total for +6/+6), etc. The talent points control how the number of kills needed scales. The progression is (max(10/ raw talent points, 2)^tier, where tier is the number of +2/+2 bonuses that have been awarded. The max hard stops the number 2^tier. So it starts with needing a progression of 1, 10, 100, 1000, and maxes out at needing 1, 2, 4, 8. Because of the log nature, in a normal campaign game, the bonus would max out somewhere in the +20 to +24 range. This matches up reasonably well against Reshape Weapon, which boosts the same stats by about 20 at end game, but without the restriction of only applying to 1 (or a pair of dual wielded) weapon(s).

I could imagine changing the raw talent points to scaled with limit of 1 for the 10/raw talent points function, thus reducing number of kills to reachable levels again as talent points are placed in it. However, I'm not sure what scaling at level 100 or 500 I would be aiming for, and typical enemy kill counts to expect.

The talent also boosts the effective base damage of the weapon to an appropriate material tier based on character level (i.e. tier 2 in 10-19, tier 3 in 20-29, tier 4 in 30-39, and tier 5 in 40-50+). It stops at tier 5. Will there be material tiers past that?

Anyways, looks like a very interesting development.

Hachem_Muche
Uruivellas
Posts: 744
Joined: Thu Nov 18, 2010 6:42 pm

Re: ToME "Infinite" Scaling Design

#4 Post by Hachem_Muche »

Hirumakai wrote:How is equipment and XP (and/or level) to normal/boss kills intended to scale in the 50 to 500 range? Also, you don't seem to touch on Physical Power, Spell Power, Mind Power, Accuracy, Defense, and saving throws. Are these expected to simply max out 100 and stay there in high level play?
Of course you can use whatever scaling you feel is appropriate in your module. In ToME, Spellpower, Mindpower, Physicalpower, attack and defense and saves generally scale proportional to character level (the attack/defense and raw saves/power lines in the first table). These are then fed through the mod.class.interface.Combat.lua:rescaleCombatStats function to get the effective stats. To allow for effective stats above 100, you need to increase the local tiers variable in that function upwards from 5.
Hirumakai wrote: Basically, I'd like to know how the stats on equipment (both raw magical bonuses like +Dex or Fire resist, as well as base armor/base damage values) are scaling with level.
Most egos have a limited range of bonuses they can produce and it's mostly just a matter of luck how close any given ego gets to the maximum. The randart code, however, scales the points it has to spend for extra powers as level^0.75 and stacking the same power scales as level^0.5, meaning that a very focused high-level randart will scale powers as level^0.375 plus egos.
Hirumakai wrote: I ask because my Spellsword module has a talent which converts non-artifact weapons/shields into level appropriate artifact weapons, based on number of overall kills as well as tier 4 boss kills (actual end dungeon bosses as opposed to chest spawned bosses, for example). Its taken me a while to find the right scaling parameters to produce weapons comparable to 4x Ego random artifact weapons at end game while scaling reasonably close to what one might expect to find along the way. Are bonuses like +Dex or Fire Resist going to scale without limit, or do they have similar functions planned for them?
Assuming these bonuses are simply added to character stats the way ToME does it, these stats should scale just like the equivalent character stats.
Hirumakai wrote:At 5/5 talent level, what I'd call the bonus damage (i.e. damage above and beyond an equivalent weapon because you're paying a category point and 5 generic points - similar to the psionic Reshape Weapon talent) increases roughly as the log of the number of kills. You get +2 damage/+2 accuracy for killing 1, then another +2/+2 after killing 2 more (3 total for +4/+4), then 4 more (7 total for +6/+6), etc. The talent points control how the number of kills needed scales. The progression is (max(10/ raw talent points, 2)^tier, where tier is the number of +2/+2 bonuses that have been awarded. The max hard stops the number 2^tier. So it starts with needing a progression of 1, 10, 100, 1000, and maxes out at needing 1, 2, 4, 8. Because of the log nature, in a normal campaign game, the bonus would max out somewhere in the +20 to +24 range. This matches up reasonably well against Reshape Weapon, which boosts the same stats by about 20 at end game, but without the restriction of only applying to 1 (or a pair of dual wielded) weapon(s).
While I'm not familiar with how this talent works, if I'm understanding your formula it sounds like the bonus scales with the log of talent level (with a sufficient number of kills). This is pretty slow, but may be compounded by an damage multipliers you have from other talents. In ToME most single talent damage multipliers scale with level^0.5, so if you're going for ToME-like scaling, compute some best-case (maximum number of kills) bonuses at higher character levels and see how it compares with that. For reference, the bonuses for the Reshape Weapon talent scale with combatMindpower, which can scale proportionally to character level before rescaleCombatStats is applied.
Hirumakai wrote: I could imagine changing the raw talent points to scaled with limit of 1 for the 10/raw talent points function, thus reducing number of kills to reachable levels again as talent points are placed in it. However, I'm not sure what scaling at level 100 or 500 I would be aiming for, and typical enemy kill counts to expect.

The talent also boosts the effective base damage of the weapon to an appropriate material tier based on character level (i.e. tier 2 in 10-19, tier 3 in 20-29, tier 4 in 30-39, and tier 5 in 40-50+). It stops at tier 5. Will there be material tiers past that?
The number of enemy kills needed to reach a certain character depends a lot on the experience modifier, your exp versus level function (ActorLevel.exp_chart in mod.load.lua for ToME) , how much experience NPC kills are worth (mod.class.Actor.lua:worthExp for ToME), and what is killed, of course.

One of my current test characters is currently 257th level on level 470 of the Infinite Dungeon (played sequentially with a lot of detours for debugging along the way) where NPC levels are computed as dungeon level * 1.2 (about level 565). This character has an exp modifier of 1.2, and ~49,000 kills. At this point, progression is at about 1 character level per 2 dungeon levels, which each have at least 30-40 NPC's, plus the stair guard, vaults, pits, and escorts, etc, (typically 70-80 NPCs of various ranks per level).

I don't know what DarkGod has planned as far as additional material tiers, but I could certainly see things beyond voraton ,drakeskin, etc. In my Infinite500 addon, I scale the best material level with dungeon level ^0.5, which seems to work well.
Hirumakai wrote:Anyways, looks like a very interesting development.
Thanks, and I hope it is useful for you.
Author of the Infinite 500 and PlenumTooltip addons, and the joys of Scaling in ToME.

Post Reply