User:Clairebun/Sandbox/Common Lua Functions

From SRB2 Wiki
Jump to navigation Jump to search

This page contains lua functions that could be generally useful to modders.

These are all formatted as global functions using the rawset() function. You might consider reformatting some of these as local functions or functions under global tables. Please note that a few of these functions may call on other functions (listed as "Dependencies") -- dependencies need to be included in tandem with any functions you use from this list.

Math

Handles various math related functions.

For physics-related functions that affect objects, see #Objects.


L_DecimalFixed

Converts a decimal/float number within a string into a fixed value. If no number is found, returns 0.

arg (string) Should contain a number (e.g. '-2' or '1.506').

rawset(_G, 'L_DecimalFixed', function(str)
	if str == nil return nil end
	local dec_offset = string.find(str,'%.')
	if dec_offset == nil
		return (tonumber(str) or 0)*FRACUNIT
	end
	local whole = tonumber(string.sub(str,0,dec_offset-1)) or 0
	local decimal = tonumber(string.sub(str,dec_offset+1)) or 0
	whole = $ * FRACUNIT
	local dec_len = string.len(decimal)
	decimal = $ * FRACUNIT / (10^dec_len)
	return whole + decimal
end)

L_FixedDecimal

Converts a fixed integer into a decimal/float within a string.

arg1 (string or fixed) Fixed value to convert. Returns an error string if invalid.

[arg2] (integer) Number of decimal places to output. 3 by default.

rawset(_G, 'L_FixedDecimal', function(str,maxdecimal)
	if str == nil or tostring(str) == nil
		return '<invalid FixedDecimal>'
	end
	local number = tonumber(str)
	maxdecimal = ($ != nil) and $ or 3
	if tonumber(str) == 0 return '0' end
	local polarity = abs(number)/number
	local str_polarity = (polarity < 0) and '-' or ''
	local str_whole = tostring(abs(number/FRACUNIT))
	if maxdecimal == 0
		return str_polarity..str_whole
	end
	local decimal = number%FRACUNIT
	decimal = FRACUNIT + $
	decimal = FixedMul($,FRACUNIT*10^maxdecimal)
	decimal = $>>FRACBITS
	local str_decimal = string.sub(decimal,2)
	return str_polarity..str_whole..'.'..str_decimal
end)

L_FixedLerp

Returns a fixed number using linear interpolation.

arg1 (fixed) value 1 to interpolate.

arg2 (fixed) value 2 to interpolate.

arg3 fixed) interpolation amount. e.g. 0 = 0%, FRACUNIT = 100%

rawset(_G, 'L_FixedLerp', function(val1,val2,amt)
	local p = FixedMul(FRACUNIT-amt,val1) + FixedMul(amt,val2)
	return p
end)

L_FixedPower

Performs a power operation on a fixed value.

arg1 (fixed) base number

arg2 (fixed) exponent. Supports negatives, and fractions (sort of)

[arg3] (integer) number of times the function is allowed to bitshift. 1 by default -- higher values may overflow!

Dependencies: #L_FixedDecimal

local warning
rawset(_G, 'L_FixedPower', function(base, exponent, shiftlimit)
	if base == 0 return 0 end --Prevent div/0
	if exponent == 0 return FRACUNIT end --Prevent div/0
	local base_copy = base
	local exp_copy = exponent
	local inversion = false
	local shift = 0
	shiftlimit = $ or 1

	--If our exponent is negative, turn positive and remember the negative for later.
	if exponent < 0
		exponent = abs(exponent)
		inversion = true
	end
	
	--Deal with fractional exponents.
	while exponent%FRACUNIT
		exponent = $ << 1
		shift = $ + 1
		--Shift the exponent. We will correct for this later with square roots.
		if shift >= shiftlimit
			FixedRound(exponent)
			break
		end
		if shift >= FRACBITS
			if not warning
				print('\x82'..'WARNING: Overflow detected in FixedPower function (base: '..Lib.FixedDecimal(base_copy)..', exponent: '..Lib.FixedDecimal(exp_copy)..')')
				warning = true
			end
			return FRACUNIT
		end
	end
	exponent = $>>FRACBITS
	local result = FRACUNIT

	--Perform multiplication according to exponent amount
	for n = 1, exponent
		result = FixedMul($, base)
	end
	if result == 0 --Prevent div/0
		return 0
	end

	--Compensate for bitshifting using square roots
	for n = 1, shift
		result = FixedSqrt(result)
	end
	
	--Correct for negative exponents by inverting the result.
	if inversion
		result = FixedDiv(FRACUNIT, $)
	end

	return result
end)

L_GetZAngle

Returns the vertical angle trajectory between two fixed sets of coordinates.

arg1 (fixed) x1

arg2 (fixed) y1

arg3 (fixed) z1

arg4 (fixed) x2

arg5 (fixed) y2

arg6 (fixed) z2

rawset(_G,'L_GetZAngle', function(x1,y1,z1,x2,y2,z2)
	local xydist = R_PointToDist2(x1,y1,x2,y2)
	local zdist = z2-z1
	return R_PointToAngle2(0,0,xydist,zdist)
end)

L_PredictLinearCollision

Determines whether two objects will collide based on their current positions, collision boxes, and movement speeds. Returns true if the two objects are already colliding. Otherwise, returns the number of tics that a collision is expected. If no collision is found or expected, returns false.

Note that this script assumes object momentum remains constant, and will be inaccurate if an object's speed is accelerating or changing direction.

arg1 (mobj) First object

arg2 (mobj) Second object

arg3 (integer) Number of dimensions to check (should be a number between 1-3)

rawset(_G,'L_PredictCollision', function(mo1, mo2, axes)
	local c = {}
	local r = {}
	local m = {}
	local near = {{}, {}}
	axes = $ or 3
	for n = 1, 2 do
		local mo = n == 1 and mo1 or mo2
		c[n] = {mo.x, mo.y, mo.z+mo.height/2}
		r[n] = {mo.radius, mo.radius, mo.height/2}
		m[n] = {mo.momx, mo.momy, mo.momz}
	end	
	-- Get dimensions to check. Choose closest corner on bounding box to use for checks
	local vacant = {}
	local failed = 0
	for n = 1,axes do
		local c1, c2, r1, r2 = c[1][n], c[2][n], r[1][n], r[2][n]
		if c2-r2 > c1+r1
			vacant[n] = true
			near[1][n] = c1 + r1
			near[2][n] = c2 - r2
			failed = $+1
		elseif c1-r1 > c2+r2
			vacant[n] = true
			near[1][n] = c1 - r1
			near[2][n] = c2 + r2
			failed = $+1
		else -- Overlapping coordinates
			-- Get mutual center coordinate for both objects
			if c1 > c2
				near[1][n] = (c1+c2-r1+r2)/2	
			elseif c2 > c1
				near[1][n] = (c1+c2+r1-r2)/2	
			else -- Resting on same position
				near[1][n] = c1
			end
			near[2][n] = near[1][n]
			-- Keep "vacant" uninitialized if bounding boxes are intersecting.
		end
	end
	
	if not(failed) 
		return true -- If there are intersections across all axes, then we have already collided
	end
	
	local diff = {}
	local rate = {}
	local time = {}
	-- Get rate of change and time needed for coordinates to intersect
	for n = 1,axes do
		if not(vacant[n]) continue end -- Already intersecting; time would simply return 0.
		-- Get coordinate distance
		diff[n] = near[2][n] - near[1][n]
		-- Get rate of change in distance
		rate[n] = m[1][n] - m[2][n]
		-- Get time it will take to reach mutual coordinate
		if rate[n] != 0
			time[n] = FixedCeil(FixedDiv(diff[n], rate[n]))
		end
	end
	local nearest_time
	local new = {{}, {}}
	for n = 1,axes do
		-- Get coordinates at time
		if not(rate[n]) continue end -- Not traveling on this axis (Prevent div/0)
		for o = 1,axes do
			-- Don't skip this axis even if we are already colliding; momentum might make us not collide anymore!
			new[1][o] = c[1][o] + FixedMul(m[1][o], time[n]) -- Object 1's new coordinates (current coord + momentum * tics)
			new[2][o] = c[2][o] + FixedMul(m[2][o], time[n])	-- Object 2's new coordinates
		end
		-- Check that there would be a collision at point
		local failed = false
		for o = 1,axes do
			-- Again, all axes must be checked for this
			if new[1][o] - r[1][o] > new[2][o] + r[2][o]
			or new[2][o] - r[2][o] > new[1][o] + r[1][o]
				failed = true
				break
			end
		end
		-- Get nearest time
		if not(failed)
			if nearest_time != nil
				nearest_time = min($, time[n])
			else
				nearest_time = time[n]
			end
		end
	end
	if nearest_time == nil
		return false -- Could not find a collision at any point along trajectories
	end
	return nearest_time/FRACUNIT -- Return the time it will take for objects to collide
end)

L_TurnAngle

Interpolates between two angles by a specified amount.

arg1 (angle) First angle to interpolate

arg2 (angle) Second angle to interpolate

[arg3] (fixed) Interpolation amount. Defaults to FRACUNIT/8

rawset(_G,'L_TurnAngle', function(oldangle,newangle,factor)
	factor = $ or FRACUNIT/8
	return oldangle + FixedMul(newangle-oldangle,factor)
end)


Objects

Functions that perform operations on objects, including movement and other object properties.

L_AngleTeleport

Teleports an object a specified distance away from a set of coordinates at a specified angle.

arg1 (mobj) Object to modify arg2 ({fixed, fixed, fixed}) Table containing the set of XYZ coordinates to use as a center of origin. arg3 (angle) XY angle to teleport away from the XYZ center. arg4 (angle) Z angle to teleport away from the XYZ center. arg5 (fixed) Distance to teleport away from the XYZ center.

rawset(_G,'L_AngleTeleport', function(mo,coords,xyangle,zangle,distance)
	local x = coords[1]
	local y = coords[2]
	local z = coords[3]
	local xythrust = P_ReturnThrustX(nil,zangle,distance)
	local xthrust = P_ReturnThrustX(nil,xyangle,xythrust)
	local ythrust = P_ReturnThrustY(nil,xyangle,xythrust)
	local zthrust = P_ReturnThrustY(nil,zangle,distance)
	P_TeleportMove(mo,x+xthrust,y+ythrust,z+zthrust)
	return {xythrust/FRACUNIT,zthrust/FRACUNIT}
end)

L_DoBrakes

Reduces XYZ momentum of an object.

arg1 (mobj) object to modify.

arg2 (fixed) amount of speed to keep, e.g. mo.friction

rawset(_G,'L_DoBrakes', function(mo,factor)
	mo.momx = FixedMul($,factor)
	mo.momy = FixedMul($,factor)
	mo.momz = FixedMul($,factor)
end)

L_DoBrakesXY

Reduces XY momentum of an object.

arg1 (mobj) object to modify. arg2 (fixed) amount of speed to keep, e.g. mo.friction

rawset(_G,'L_DoBrakesXY', function(mo,factor)
	mo.momx = FixedMul($,factor)
	mo.momy = FixedMul($,factor)
end)

L_DoBrakesZ

Reduces Z momentum of an object.

arg1 (mobj) object to modify.

arg2 (fixed) amount of speed to keep, e.g. mo.friction

rawset(_G,'L_DoBrakesZ', function(mo,factor)
	mo.momz = FixedMul($,factor)
end)

L_SpeedCap

Restrains an object to a specified speed limit.

arg1 (mobj) object to modify

arg2 (fixed) maximum allowed speed

[arg3] (fixed) % speed reduction if over the limit. If nil, speed reduction is instant.

Dependencies: #L_DoBrakes

rawset(_G,'L_SpeedCap', function(mo,limit,factor)
	local spd_xy = R_PointToDist2(0,0,mo.momx,mo.momy)
	local spd, ang =
		R_PointToDist2(0,0,spd_xy,mo.momz),
		R_PointToAngle2(0,0,mo.momx,mo.momy)
	if spd > limit
		if factor == nil
			factor = FixedDiv(limit,spd)
		end
		L_DoBrakes(mo,factor)
		return factor
	end
end)

L_SpeedCapXY

Restrains an object to a specified speed limit, but only over the X and Y axes.

arg1 (mobj) object to modify

arg2 (fixed) maximum allowed speed

[arg3] (fixed) % speed reduction if over the limit. If nil, speed reduction is instant.

Dependencies: #L_DoBrakesXY

rawset(_G,'L_SpeedCapXY', function(mo,limit,factor)
	local spd, ang =
		R_PointToDist2(0,0,mo.momx,mo.momy),
		R_PointToAngle2(0,0,mo.momx,mo.momy)
	if spd > limit
		if factor == nil
			factor = FixedDiv(limit,spd)
		end
		L_DoBrakesXY(mo,factor)
		return factor
	end
end)

L_ThrustXYZ

Thrusts an object using the specified horizontal and vertical angles.

arg1 (mobj) object to modify

arg2 (angle) XY angle

arg3 (angle) Z angle

arg4 (fixed) thrust speed amount

arg5 (boolean) relative

rawset(_G,'L_ThrustXYZ', function(mo,xyangle,zangle,speed,relative)
	local xythrust = P_ReturnThrustX(nil,zangle,speed)
	local zthrust = P_ReturnThrustY(nil,zangle,speed)
	if relative then
		P_Thrust(mo,xyangle,xythrust)		
		mo.momz = $+zthrust	
	else
		P_InstaThrust(mo,xyangle,xythrust)		
		mo.momz = zthrust	
	end
	return xythrust/FRACUNIT, zthrust/FRACUNIT
end)

L_ZCollide

Performs a height check between two objects. For use in MobjCollide hooks.

arg1 (mobj) first object arg2 (mobj) second object

rawset(_G, 'L_ZCollide', function(mo1,mo2)
	if mo1.z > mo2.height+mo2.z then return false end
	if mo2.z > mo1.height+mo1.z then return false end
	return true
end)

L_ZLaunch

Vertically thrusts an object with respect to water physics, scale and gravity polarity.

arg1 (mobj) object to modify

arg2 (fixed) thrust amount

[arg3] (boolean) relative

rawset(_G, 'L_ZLaunch', function(mo,thrust,relative)
	if mo.eflags&MFE_UNDERWATER
		thrust = $*3/5
	end
	P_SetObjectMomZ(mo,thrust,relative)
end)

Random

Functions for generating random ouputs.

L_Choose

Randomly returns one of the arguments put into the function.

Accepts any number/type of arguments. e.g. L_Choose(FRACUNIT, 0, -FRACUNIT), L_Choose(table1, table2, table3) etc.

rawset(_G, 'L_Choose', function(...)
	local args = {...}
	local choice = P_RandomRange(1,#args)
	return args[choice]
end)

L_Shuffle

Randomly shuffles the elements of a table. Returns the reshuffled table.

arg1 (table) Table to shuffle.

rawset(_G, 'L_Shuffle', function(t)
    for n = #t, 1, -1 do
        local k = P_RandomRange(1, n)
        t[n], t[k] = t[k], t[n]
    end
    return t
end)

Statistics

prtable

Lists table contents, including subtable contents.

argument1 is text label to display, argument2 is table to output. argument3 and argument4 are reserved for recursion purposes.

rawset(_G, "prtable", function(text, t, prefix, cycles)
    prefix = $ or ""
    cycles = $ or {}

    print(prefix..text.." = {")

    for k, v in pairs(t)
        if type(v) == "table"
            if cycles[v]
                print(prefix.."    "..tostring(k).." = "..tostring(v))
            else
                cycles[v] = true
                prtable(k, v, prefix.."    ", cycles)
            end
        elseif type(v) == "string"
            print(prefix.."    "..tostring(k)..' = "'..v..'"')
        else
			if type(v) == "userdata" and v.valid and v.name
				v = v.name
			end
            print(prefix.."    "..tostring(k).." = "..tostring(v))
        end
    end

    print(prefix.."}")
end)

See Also

Varren's lua repo