Math Utilities
Version History
Build Version | Change Date | Change Type | Description |
---|---|---|---|
676042 | 2025-06-21 | stable | Current version |
Overview
The mathutil.lua
script provides essential mathematical utility functions used throughout Don't Starve Together. These functions handle common mathematical operations including sine wave generation, interpolation, rounding, clamping, distance calculations, and angle manipulations.
Usage Example
-- Interpolate between values
local result = Lerp(10, 20, 0.5) -- Returns 15
-- Generate sine wave for animations
local wave = GetSineVal(1, true) -- Absolute sine wave
-- Clamp values to range
local clamped = Clamp(150, 0, 100) -- Returns 100
-- Calculate distance between points
local dist_sq = DistXZSq(pos1, pos2)
Wave Generation Functions
GetSineVal(mod, abs, inst)
Status: stable
Description: Generates a sine wave value based on game time with optional period modification and absolute value.
Parameters:
mod
(number): Optional period modifier (default: 1)abs
(boolean): Whether to return absolute value of the sine waveinst
(EntityScript): Optional entity to use for time calculation (uses entity's alive time)
Returns:
- (number): Sine wave value between -1 and 1 (or 0 and 1 if abs is true)
Example:
-- Basic sine wave for gentle animation
local gentle_wave = GetSineVal()
-- Fast oscillation with absolute values (0 to 1)
local fast_pulse = GetSineVal(3, true)
-- Slow wave tied to entity's lifetime
local entity_wave = GetSineVal(0.5, false, inst)
-- Use for breathing effect
local scale = 1 + GetSineVal(2, true, inst) * 0.1
inst.Transform:SetScale(scale, scale, scale)
Interpolation Functions
Lerp(a, b, t)
Status: stable
Description: Linear interpolation between two values over a given parameter t.
Parameters:
a
(number): Start valueb
(number): End valuet
(number): Interpolation parameter (0 to 1)
Returns:
- (number): Interpolated value
Example:
-- Basic interpolation
local mid_point = Lerp(0, 100, 0.5) -- Returns 50
-- Animation over time
local progress = GetTime() % 2 / 2 -- 0 to 1 over 2 seconds
local animated_x = Lerp(start_x, end_x, progress)
-- Color blending
local red_component = Lerp(0.2, 0.8, health_percent)
Remap(i, a, b, x, y)
Status: stable
Description: Remaps a value from one range to another range.
Parameters:
i
(number): Input value to remapa
(number): Input range minimumb
(number): Input range maximumx
(number): Output range minimumy
(number): Output range maximum
Returns:
- (number): Remapped value
Example:
-- Remap health (0-100) to alpha (0.3-1.0)
local health = 75
local alpha = Remap(health, 0, 100, 0.3, 1.0) -- Returns 0.825
-- Remap temperature (-20 to 40) to color intensity (0 to 255)
local temp = 15
local color_intensity = Remap(temp, -20, 40, 0, 255)
-- Remap day progress (0-1) to sun angle (-90 to 90)
local day_progress = 0.25
local sun_angle = Remap(day_progress, 0, 1, -90, 90)
Rounding Functions
RoundBiasedUp(num, idp)
Status: stable
Description: Rounds a number to specified decimal places, with 0.5-values always rounded up.
Parameters:
num
(number): Number to roundidp
(number): Decimal places (default: 0)
Returns:
- (number): Rounded number
Example:
-- Round to integers (0.5 goes up)
local rounded = RoundBiasedUp(2.5) -- Returns 3
local rounded2 = RoundBiasedUp(2.4) -- Returns 2
-- Round to 2 decimal places
local precise = RoundBiasedUp(3.14159, 2) -- Returns 3.14
-- Round currency values
local price = RoundBiasedUp(19.95, 0) -- Returns 20
RoundBiasedDown(num, idp)
Status: stable
Description: Rounds a number to specified decimal places, with 0.5-values always rounded down.
Parameters:
num
(number): Number to roundidp
(number): Decimal places (default: 0)
Returns:
- (number): Rounded number
Example:
-- Round to integers (0.5 goes down)
local rounded = RoundBiasedDown(2.5) -- Returns 2
local rounded2 = RoundBiasedDown(2.6) -- Returns 3
-- Conservative rounding for damage calculations
local damage = RoundBiasedDown(player_damage * modifier, 1)
RoundToNearest(numToRound, multiple)
Status: stable
Description: Rounds a number to the nearest multiple of a specified value.
Parameters:
numToRound
(number): Number to roundmultiple
(number): Multiple to round to
Returns:
- (number): Number rounded to nearest multiple
Example:
-- Round to nearest 5
local rounded = RoundToNearest(23, 5) -- Returns 25
-- Round to nearest grid position
local grid_x = RoundToNearest(player_x, TILE_SCALE)
local grid_z = RoundToNearest(player_z, TILE_SCALE)
-- Round spawn time to nearest minute
local spawn_time = RoundToNearest(random_time, 60)
Clamping Functions
Clamp(num, min, max)
Status: stable
Description: Constrains a number between minimum and maximum values.
Parameters:
num
(number): Number to clampmin
(number): Minimum allowed valuemax
(number): Maximum allowed value
Returns:
- (number): Clamped number
Example:
-- Clamp health to valid range
local health = Clamp(new_health, 0, max_health)
-- Clamp UI element position
local x = Clamp(mouse_x, panel_left, panel_right)
local y = Clamp(mouse_y, panel_top, panel_bottom)
-- Clamp angle for limited rotation
local angle = Clamp(target_angle, -45, 45)
math.clamp(num, min, max)
Status: stable
Description: Math library extension for clamping values. Identical to Clamp function.
Parameters:
num
(number): Number to clampmin
(number): Minimum allowed valuemax
(number): Maximum allowed value
Returns:
- (number): Clamped number
Example:
-- Using math library extension
local clamped = math.clamp(value, 0, 1)
Utility Functions
IsNumberEven(num)
Status: stable
Description: Checks if a number is even.
Parameters:
num
(number): Number to test
Returns:
- (boolean): True if number is even
Example:
-- Alternate behavior for even/odd
if IsNumberEven(day_number) then
SpawnSpecialEvent()
end
-- Pattern generation
for i = 1, 10 do
local color = IsNumberEven(i) and "white" or "black"
CreateChessboardTile(i, color)
end
math.range(start, stop, step)
Status: stable
Description: Creates a table containing a range of numbers from start to stop with optional step.
Parameters:
start
(number): Starting valuestop
(number): Ending valuestep
(number): Step size (default: 1)
Returns:
- (table): Array of numbers in the range
Example:
-- Create range 1 to 10
local numbers = math.range(1, 10) -- {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
-- Create range with step
local evens = math.range(2, 10, 2) -- {2, 4, 6, 8, 10}
-- Create countdown
local countdown = math.range(10, 1, -1) -- {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
math.diff(a, b)
Status: stable
Description: Calculates the absolute difference between two numbers.
Parameters:
a
(number): First numberb
(number): Second number
Returns:
- (number): Absolute difference
Example:
-- Compare values
local difference = math.diff(player_level, required_level)
if difference <= 2 then
AllowAccess()
end
-- Find closest match
local closest_value = nil
local smallest_diff = math.huge
for _, candidate in ipairs(options) do
local diff = math.diff(target, candidate)
if diff < smallest_diff then
smallest_diff = diff
closest_value = candidate
end
end
Distance Functions
DistXYSq(p1, p2)
Status: stable
Description: Calculates the squared distance between two points in XY plane (2D).
Parameters:
p1
(table): First point with .x and .y propertiesp2
(table): Second point with .x and .y properties
Returns:
- (number): Squared distance
Example:
-- Check if entities are close (avoids expensive sqrt)
local pos1 = {x = 10, y = 20}
local pos2 = {x = 13, y = 24}
local dist_sq = DistXYSq(pos1, pos2) -- Returns 25 (5² = 25)
if dist_sq < 100 then -- Within 10 units
TriggerNearbyEffect()
end
DistXZSq(p1, p2)
Status: stable
Description: Calculates the squared distance between two points in XZ plane (3D world coordinates).
Parameters:
p1
(table): First point with .x and .z propertiesp2
(table): Second point with .x and .z properties
Returns:
- (number): Squared distance
Example:
-- 3D world distance calculation
local player_pos = {x = ThePlayer.Transform:GetWorldPosition()}
local target_pos = {x = target.Transform:GetWorldPosition()}
local dist_sq = DistXZSq(player_pos, target_pos)
local interaction_range_sq = INTERACTION_RANGE * INTERACTION_RANGE
if dist_sq <= interaction_range_sq then
ShowInteractionPrompt()
end
Angle Functions
ReduceAngle(rot)
Status: stable
Description: Normalizes an angle to the range [-180, 180] degrees.
Parameters:
rot
(number): Angle in degrees
Returns:
- (number): Normalized angle
Example:
-- Normalize rotation angles
local angle = 450 -- Equivalent to 90 degrees
local normalized = ReduceAngle(angle) -- Returns 90
-- Handle continuous rotation
local new_rotation = old_rotation + rotation_speed * dt
new_rotation = ReduceAngle(new_rotation)
DiffAngle(rot1, rot2)
Status: stable
Description: Calculates the absolute angular difference between two angles in degrees.
Parameters:
rot1
(number): First angle in degreesrot2
(number): Second angle in degrees
Returns:
- (number): Absolute angular difference
Example:
-- Check if facing target
local player_angle = ThePlayer.Transform:GetRotation()
local target_angle = math.atan2(target_z - player_z, target_x - player_x) * 180 / math.pi
local angle_diff = DiffAngle(player_angle, target_angle)
if angle_diff < 10 then -- Within 10 degrees
SetFacingTarget(true)
end
ReduceAngleRad(rot)
Status: stable
Description: Normalizes an angle to the range [-π, π] radians.
Parameters:
rot
(number): Angle in radians
Returns:
- (number): Normalized angle in radians
Example:
-- Normalize radian angles
local angle_rad = 7 -- Greater than 2π
local normalized = ReduceAngleRad(angle_rad)
-- Vector rotation calculations
local normalized_heading = ReduceAngleRad(current_heading + turn_amount)
DiffAngleRad(rot1, rot2)
Status: stable
Description: Calculates the absolute angular difference between two angles in radians.
Parameters:
rot1
(number): First angle in radiansrot2
(number): Second angle in radians
Returns:
- (number): Absolute angular difference in radians
Example:
-- Precise angular comparison using radians
local heading1 = math.atan2(vel1.z, vel1.x)
local heading2 = math.atan2(vel2.z, vel2.x)
local angular_diff = DiffAngleRad(heading1, heading2)
if angular_diff < math.pi / 18 then -- Within 10 degrees (π/18 radians)
SetSameDirection(true)
end
Common Usage Patterns
Animation and Visual Effects
-- Breathing/pulsing animation
local scale = 1 + GetSineVal(2, true) * 0.1
inst.Transform:SetScale(scale, scale, scale)
-- Floating animation
local bob_offset = GetSineVal(1.5, false) * 0.3
inst.Transform:SetPosition(x, y + bob_offset, z)
-- Color transitions
local health_percent = inst.components.health:GetPercent()
local red = Lerp(0, 1, 1 - health_percent)
local green = Lerp(0, 1, health_percent)
Game Mechanics
-- Damage calculation with rounding
local base_damage = weapon.damage
local modified_damage = base_damage * damage_multiplier
local final_damage = RoundBiasedDown(modified_damage)
-- Range checking (squared distance is faster)
local range_sq = ATTACK_RANGE * ATTACK_RANGE
local dist_sq = DistXZSq(attacker_pos, target_pos)
if dist_sq <= range_sq then
PerformAttack()
end
-- Progress mapping
local completion = Remap(current_resources, 0, required_resources, 0, 1)
UpdateProgressBar(completion)
Performance Notes
- Squared distance functions avoid expensive square root calculations
- GetSineVal uses game time efficiently for synchronized animations
- Angle normalization prevents floating point precision issues
- Clamping is faster than conditional statements for range limiting