Noita Wiki
Noita Wiki
Modding Pages
Getting startedBasicsLua ScriptingData.wakUseful Tools
AudioEnemiesEnvironmentsPerksSpellsSpritesheetsMaterialsImage Emitters
Lua APIEnumsSpecial TagsUtility ScriptsSound EventsEnemy Information TableSpell and Perk IDs

A collection of scripts created by other modders, which you can copy directly to your own mod, for example into your own utilities file: mods/<MY_MOD>/files/scripts/utilities.lua


Obtain the id of the player entity[]

This function obtains you the id of the player entity. Using it, you can extract data from components and other behaviors of the player.

-- simple utility function to return the player entity
function getPlayerEntity()
	local players = EntityGetWithTag("player_unit")
	if #players == 0 then return end

	return players[1]
end

Easier entity debugging[]

function str(var)
  if type(var) == 'table' then
    local s = '{ '
    for k,v in pairs(var) do
      if type(k) ~= 'number' then k = '"'..k..'"' end
      s = s .. '['..k..'] = ' .. str(v) .. ','
    end
    return s .. '} '
  end
  return tostring(var)
end


function debug_entity(e)
    local parent = EntityGetParent(e)
    local children = EntityGetAllChildren(e)
    local comps = EntityGetAllComponents(e)

    print("--- ENTITY DATA ---")
    print("Parent: ["..parent.."] " .. (EntityGetName(parent) or "nil"))

    print(" Entity: ["..str(e).."] " .. (EntityGetName(e) or "nil"))
    print("  Tags: " .. (EntityGetTags(e) or "nil"))
    if (comps ~= nil) then
      for _, comp in ipairs(comps) do
          print("  Comp: ["..comp.."] " .. (ComponentGetTypeName(comp) or "nil"))
      end
    end

    if children == nil then return end

    for _, child in ipairs(children) do
        local comps = EntityGetAllComponents(child)
        print("  Child: ["..child.."] " .. EntityGetName(child))
        for _, comp in ipairs(comps) do
            print("   Comp: ["..comp.."] " .. (ComponentGetTypeName(comp) or "nil"))
        end
    end
    print("--- END ENTITY DATA ---")
end

Use simply with debug_entity(entity)

Add custom names in translations[]

This piece of code is placed in the mod's init and is used to add custom translations:

local content = ModTextFileGetContent("data/translations/common.csv")
ModTextFileSetContent("data/translations/common.csv", content .. [[
item_custom,Custom Name,,,,,,,,,,,,,
]])

So if we were to name the entity "$item_custom" it's name in game would show up as "Custom Name"

Add a new genome (This currently Doesn't work)[]

The function add_new_genome let's you append a new genome to the data/genome_relations.csv file. Use it in your init.lua.


(THIS CURRENTLY DOESN'T WORK: Because genomes are currently loaded before mods even load)

function split_string(inputstr, sep)
  sep = sep or "%s"
  local t= {}
  for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
    table.insert(t, str)
  end
  return t
end

function add_new_genome(genome_name, relations)
  local content = ModTextFileGetContent("data/genome_relations.csv")
  local lines = split_string(content, "\r\n")
  local output = ""
  local genome_order = {}
  for i, line in ipairs(lines) do
    if i == 1 then
      output = output .. line .. "," .. genome_name .. "\r\n"
    else
      local herd = line:match("([%w_-]+),")
      output = output .. line .. ","..(relations[herd] or 0).."\r\n"
      table.insert(genome_order, herd)
    end
  end
  
  local line = genome_name
  for i, v in ipairs(genome_order) do
    line = line .. "," .. relations[v]
  end
  output = output .. line
  
  ModTextFileSetContent("data/genome_relations.csv", output)
end
-- Example usage:
add_new_genome("friendly", {
  ["-1"] = 0,
  player = 0,
  slimes = 100,
  ant = 100,
  robot = 100,
  fly = 100,
  boss_dragon = 0,
  crawler = 100,
  helpless = 100,
  eel = 100,
  fire = 100,
  fungus = 100,
  ghoul = 100,
  giant = 100,
  ice = 100,
  spider = 100,
  orcs = 100,
  rat = 0,
  electricity = 100,
  wolf = 100,
  worm = 100,
  zombie = 100,
  nest = 100,
  mage = 100,
  flower = 100,
  ghost = 100,
  boss_limbs = 0,
  healer = 100,
  apparition = 100, 
  bat = 100,
  mage_swapper = 100,
  friendly = 100,
})

Add a new tag to an existing entity[]

This function adds a tag to an existing entity without you having to overwrite existing entities:

function add_tags()

	local xml2lua = dofile("mods/new_enemies/files/xml2lua_library/xml2lua.lua")
	local xml_content = ModTextFileGetContent("data/entities/props/existing_entity.xml")
	local handler = xml2lua.parse(xml_content)
	if handler.root.Entity._attr.tags then
	  handler.root.Entity._attr.tags = handler.root.Entity._attr.tags .. ",your_tag_name"
	else
	  handler.root.Entity._attr.tags = "your_tag_name"
	end
	local xml_output = xml2lua.toXml(handler.root, "Entity", 0)
	ModTextFileSetContent("data/entities/props/existing_entity.xml", xml_output)

end

add_tags_seamine()

Rotate entity depending on topology of terrain[]

This script makes the entity rotate depending on terrain topology:

function approx_rolling_average(avg, new_sample, n)
  avg = avg - avg / n;
  avg = avg + new_sample / n;
  return avg
end

local old_average = GetValueNumber("average_rotation", 0)

function get_direction( x1, y1, x2, y2 )
  return math.atan2( ( y2 - y1 ), ( x2 - x1 ) )
end

local entity_id = GetUpdatedEntityID()
local x, y = EntityGetTransform(entity_id)
local found_normal, normal_x, normal_y, approximate_distance_from_surface = GetSurfaceNormal(x, y, 60, 16)
local dir = get_direction(0, 0, normal_x, normal_y)
local degree_shift = math.rad(-90) + dir -- 90 degree rotation to properly orient the entity
local new_average = approx_rolling_average(old_average, degree_shift, 5) -- if the number is higher than 5 the turning speed would be smoother and slower, if less it would be more instantaneous
SetValueNumber("average_rotation", new_average)


if found_normal then
 EntitySetTransform(entity_id, x, y, new_average)
end

This script is then attached to the xml entity with a lua component, example:

    <LuaComponent 
		_enabled="1" 
		script_source_file="mods/yourmod/files/the_script_above.lua" 
		execute_every_n_frame="1">
   </LuaComponent>

Shoot Projectile from Entity Towards Player[]

This script, made by Evaisa, lets you shoot a projectile from an entity towards an entity.

dofile( "data/scripts/lib/utilities.lua" )

function rotate_degrees(x, y, degrees)
	return rotate_radians(x, y, math.rad(degrees))
end

function rotate_radians(x, y, radians)
	local ca = math.cos(radians)
	local sa = math.sin(radians)
	local out_x = ca * x - sa * y
	local out_y = sa * x + ca * y
	return out_x, out_y
end

function shoot_at_entity(self_entity, target_entity, general_offset, x_offset, y_offset, speed, projectile, maxdistance, max_player_distance, rotation_offset)
	local rad_offset = 0
	if(rotation_offset ~= nil)then
		rad_offset = rotation_offset
	end
	local x, y = EntityGetTransform(self_entity)
	
	if(target_entity ~= nil)then
		local entityX, entityY = EntityGetTransform( target_entity )

		local headingX = entityX - (x + x_offset)
		local headingY = (entityY-5) - (y + y_offset)
		
		local len = math.sqrt((headingX*headingX) + (headingY*headingY))

		local directionX = headingX / len
		local directionY = headingY / len

		local didHit, hitX, hitY = RaytraceSurfaces( x, y, entityX, entityY )

		local headingHitX = entityX - hitX
		local headingHitY = entityY - hitY

		local hitDistance = math.sqrt((headingHitX*headingHitX) + (headingHitY*headingHitY))

		local pos_x, pos_y, currentRotation = EntityGetTransform( self_entity )	

		if(directionX < 0)then
			EntitySetTransform(self_entity,pos_x,pos_y,currentRotation,-1,1)
		else
			EntitySetTransform(self_entity,pos_x,pos_y,currentRotation,1,1)
		end
	
		if(len < max_player_distance)then
			if(hitDistance < maxdistance)then
			
				directionX, directionY = rotate_degrees(directionX, directionY, rad_offset)
				
				
				local ourProjectile = shoot_projectile( self_entity, projectile, x + (directionX * general_offset) + x_offset, y + (directionY * general_offset) + y_offset, directionX * speed, directionY * speed )
				edit_component( ourProjectile, "ProjectileComponent", function(comp,vars)
					vars.mWhoShot       = self_entity
					vars.mShooterHerdId = "player"
				end)
			end
		end
	end
end

To use this function, just do

shoot_at_entity([Attacker Entity], [Target Entity], [Projectile offset towards target], [Projectile Start X offset], [Projectile Start Y offset], [Projectile speed], [Projectile xml], [Distance to sense through walls], [Distance to see target])

Then attach a lua component to the entity xml which utilizes this script and runs every x frames, example:

<LuaComponent
  script_source_file="mods/yourmod/files/previous_script.lua"
  execute_every_n_frame="100"
  >
</LuaComponent>

Attach entities to verlet chains[]

dofile_once("data/scripts/lib/utilities.lua")

local entity = GetUpdatedEntityID()

local _, _, day, hour, minute, second = GameGetDateAndTimeLocal()
SetRandomSeed(day + hour + second, minute + second + entity)

local verlet_data = {point_count = 0, positions = {}}

local x, y = EntityGetTransform(entity)

local verlet_entities = {}
local verlet_files = {}
local empty_percentage = 0

component_read( EntityGetFirstComponent( entity, "VerletPhysicsComponent" ), { positions = {} }, function(comp)

    verlet_data.point_count = (#comp.positions / 2) 

    for i = 1, verlet_data.point_count, 4 do
        local point = {x = 0, y = 0}
        point.x, point.y = comp.positions[i], comp.positions[i + 1]
        table.insert(verlet_data.positions, point) 
    end
end)

function set_verlet_position(verlet, x, y)
    component_readwrite( EntityGetFirstComponent( verlet, "VerletPhysicsComponent" ), { positions = {} }, function(comp)
        comp.positions[1] = x
        comp.positions[2] = y
    end)
end

function delimiter_split(input, delimiter)
    local word_table = {}
    for word in string.gmatch(input, '([^'..delimiter..']+)') do
        table.insert(word_table, word)
    end
    return word_table
end

function tablelength(T)
    local count = 0
    for _ in pairs(T) do count = count + 1 end
    return count
end

local holder = nil

local variable_storage = EntityGetComponent(entity, "VariableStorageComponent")

for k, v in pairs(variable_storage)do
    local name = ComponentGetValue2(v, "name")

    if(name == "verlet_entity")then
        table.insert(verlet_entities, tonumber(ComponentGetValue2(v, "value_string")))     
    elseif(name == "verlet_files")then
        verlet_files = delimiter_split(ComponentGetValue2(v, "value_string"), ',')
    elseif(name == "holder")then
        holder = tonumber(ComponentGetValue2(v, "value_string"))
    end
end

for k, position in pairs(verlet_data.positions)do
    if(verlet_entities[1] ~= nil)then
        if(verlet_entities[k] ~= 0)then
            if(EntityGetIsAlive(verlet_entities[k]))then
                EntitySetTransform(verlet_entities[k], position.x, position.y)
                set_verlet_position(verlet_entities[k], position.x, position.y)
            end
        end
    else
        if(0 == empty_percentage)then
          
			-- for k, position in pairs(verlet_data.positions)do
			    -- if k % 2 == 0 then
					local verlet_file = verlet_files[Random(1, #verlet_files)]
					local verlet = EntityLoad(verlet_file, x, y)
					EntityAddChild(entity, verlet)
					if(verlet ~= nil)then
						
						EntityAddComponent(entity, "VariableStorageComponent", {
							name="verlet_entity",
							value_string=tostring(verlet),
						})
					   -- GamePrint("holder = "..holder)
						if(holder ~= 0)then
							EntityAddComponent(holder, "VariableStorageComponent", {
								name="verlet_entity",
								value_string=tostring(verlet),
							})
						end

					end
				-- end
			-- end	
        else
            EntityAddComponent(entity, "VariableStorageComponent", {
                name="verlet_entity",
                value_string="0",
            })           
        end
    end
end

Add this to the verlet chain entity like this:

    <VariableStorageComponent
        name="verlet_files"
        value_string="mods/yourmod/files/entity_you_want_to_attach_to_verlet.xml"
    ></VariableStorageComponent>

    <LuaComponent 
	_enabled="1" 
	script_source_file="mods/yourmod/files/the_lua_script_above.lua" 
	execute_on_added="1"
	execute_every_n_frame="0">
    </LuaComponent>

This will add one entity to each verlet chain segment! Keep in mind that it is heavy on performance.

Rotate physics object towards player[]

dofile_once("data/scripts/lib/utilities.lua")

local this_entity = GetUpdatedEntityID()

local x, y, r = EntityGetTransform(this_entity)

local variable_storage = EntityGetComponent(this_entity, "VariableStorageComponent")

local aim_direction = {x = 0, y = 0}

local players = EntityGetWithTag("player_unit")

if #players > 0 then

	local player_x, player_y = EntityGetTransform(players[1])

	local direction_x, direction_y = player_x - x, player_y - y

	local len = math.sqrt((direction_x*direction_x) + (direction_y*direction_y))

	aim_direction.x, aim_direction.y = direction_x / len, direction_y / len

	local phys_body = EntityGetFirstComponent(this_entity, "PhysicsBodyComponent")

	local ang_vel = PhysicsGetComponentAngularVelocity( this_entity, phys_body )

	function angle_rad(direction)
		return math.atan2(direction.y,direction.x)
	end

	function get_torque(current_angle, target_angle, speed)
		-- Make angle go from 0 to 2pi
		if target_angle < 0 then
		  target_angle = target_angle + math.pi * 2
		end
		if current_angle < 0 then
		  current_angle = current_angle + math.pi * 2
		end
	  
		local dist_up = math.abs(target_angle - current_angle)
		local dist_down = math.abs((current_angle + math.pi * 2) - target_angle)
		
		if current_angle > target_angle then
		  dist_up = math.abs((target_angle + math.pi * 2) - current_angle)
		  dist_down = math.abs(current_angle - target_angle)
		end
		
		local dir
		if math.abs(dist_up) < math.abs(dist_down) then
		  dir = 1
		else
		  dir = -1
		end
		local dist_smallest = dist_up
		if dist_down < dist_up then
		  dist_smallest = dist_down
		end
	  
		local new_angle = speed * dir

		return new_angle
	end

	PhysicsApplyTorque( this_entity, -ang_vel )

	PhysicsApplyTorque( this_entity, get_torque(r, angle_rad(aim_direction), 5) )

end

This script is then attached to the xml entity with a lua component, example:

    <LuaComponent 
		_enabled="1" 
		script_source_file="mods/yourmod/files/the_script_above.lua" 
		execute_every_n_frame="1">
   </LuaComponent>

For this to work it is important to remove any torque from the physics AI of the entity bearing the lua component by setting torque to 0 in the physics AI component.

<PhysicsAIComponent 
	torque_balancing_coeff="0" 
	torque_coeff="0" 
	torque_max="0" >
</PhysicsAIComponent>

Check if inside wall script[]

This function checks if you're spawning things in walls.

function is_inside_wall(x, y, radius)
  for i=1,8 do
    local slice = math.pi * 2 / 8 * i
    local did_hit, hit_x, hit_y = RaytraceSurfacesAndLiquiform(x, y, x + math.cos(slice) * radius, y + math.sin(slice) * radius)
    if did_hit then
      return true
    end
  end
  return false
end

if not is_inside_wall(x, y, 32) then
  -- Whatever you want to do!
end

Player movement checking[]

Use these functions to obtain flags regarding the player's movement.

-- returns "right" if the player is moving right, "left" if the player is  moving left, or an empty string if neither
function getPlayerHorizontalMovement()
	local vel_x, vel_y = getPlayerVelocities()
	local horizontal_movement = ""
	if(vel_x > 10) then
		horizontal_movement = "right"
	end
	if(vel_x < -10) then
		horizontal_movement = "left"
	end
	return horizontal_movement
end

-- returns "down" if the player is falling, "up" if the player is going up, or an empty string if neither
function getPlayerVerticalMovement()
	local vel_x, vel_y = getPlayerVelocities()
	local vertical_movement = ""
	if(vel_y > 0 and vel_y ~= 60) then
		vertical_movement = "down"
	end
	if(vel_y < 0) then
		vertical_movement = "up"
	end
	return vertical_movement
end

-- returns true if the player is moving, or false otherwise
function isPlayerMoving()
    local flag = false
    local x, y = EntityGetTransform( getPlayerEntity() )
    
    local movement_threshold = 0.1
    if old_position and (math.abs(x - old_position.x) > movement_threshold or math.abs(y - old_position.y) > movement_threshold) then
        flag = true
    end
    old_position = {x=x, y=y};
    
    return flag
end

-- returns true if the player is standing on solid ground, or false if in the air
function isPlayerStanding()
    local vel_x, vel_y = getPlayerVelocities()
    if(vel_y == 60) then -- default gravity value when the player is standing on something solid
        return true
    else
        return false
    end
end

-- obtains x and y velocities from the player
function getPlayerVelocities()
    local cdc_id = EntityGetFirstComponentIncludingDisabled(getPlayerEntity(), "CharacterDataComponent")
    local velocity_x, velocity_y = ComponentGetValue2(cdc_id, "mVelocity")
    return velocity_x, velocity_y
end

Obtaining and setting gold amount[]

These functions can be used to manipulate the amount of gold owned by the player.

-- function to add or subtract gold from player
-- amount can be either negative or positive
function addPlayerGold(amount)

	local player = getPlayerEntity()

	local wallet = EntityGetFirstComponent(player, "WalletComponent")
	local money = ComponentGetValueInt(wallet, "money")
	local player_money = money + amount

	edit_component(player, "WalletComponent", function(comp,vars)
		vars.money = player_money
	end)
end

-- function to get player gold
function getPlayerGold()
	
	local player = getPlayerEntity()
	
	local wallet = EntityGetFirstComponent(player, "WalletComponent")
	local money = ComponentGetValueInt(wallet, "money")

	return money
end

Internal variable manipulation[]

The following functions can be used to obtain or update internal variables present o variable storage component of an entity. Both get and set functions assume the variable name you are trying to get or set already exists on the entity you are providing.

-- function to obtain a value from an internal variable contained on a variable storage component
-- entity_id is the id of the entity whose internal variables will be accessed
-- variable_name is the name of the internal variable to be accesses
-- variable_type is type indicator of the internal variable, which can be value_string, value_int or value_float
function getInternalVariableValue(entity_id, variable_name, variable_type)
	local value = nil
	local components = EntityGetComponent( entity_id, "VariableStorageComponent" )
	if ( components ~= nil ) then
		for key,comp_id in pairs(components) do 
			local var_name = ComponentGetValue2( comp_id, "name" )
			if(var_name == variable_name) then
				value = ComponentGetValue2(comp_id, variable_type)
			end
		end
	end
	return value
end

-- function to set a value of an internal variabled contained on a variable storage component of an entity with entity_id with name variable_name and type variable_type
-- entity_id is the id of the entity whose internal variables will be accessed
-- variable_name is the name of the internal variable to be accesses
-- variable_type is type indicator of the internal variable, which can be value_string, value_int or value_float
function setInternalVariableValue(entity_id, variable_name, variable_type, new_value)

	local components = EntityGetComponent( entity_id, "VariableStorageComponent" )	
	if ( components ~= nil ) then
		for key,comp_id in pairs(components) do 
			local var_name = ComponentGetValue2( comp_id, "name" )
			if( var_name == variable_name) then
				ComponentSetValue2( comp_id, variable_type, new_value )
			end
		end
	end
end

Player Health manipulation[]

The following functions can be used to manipulate and obtain hp from the player.

-- function to return player health
-- health values are treated internally as floats, so if the player has 5 health, the internal value will be 0.008
function getPlayerHealth()
	local damagemodels = EntityGetComponent( getPlayerEntity(), "DamageModelComponent" )
	local health = 0
	if( damagemodels ~= nil ) then
		for i,v in ipairs(damagemodels) do
		
			health = tonumber( ComponentGetValue( v, "hp" ) )
			--GamePrint(health)
			break
		end
	end
	return health
end

-- function to return player max health
function getPlayerMaxHealth()
	local damagemodels = EntityGetComponent( getPlayerEntity(), "DamageModelComponent" )
	local maxHealth = 0
	if( damagemodels ~= nil ) then
		for i,v in ipairs(damagemodels) do
		
			maxHealth = tonumber( ComponentGetValue( v, "max_hp" ) )
			
			break
		end
	end
	return maxHealth
end

-- function to add to player health
-- amount is how much is player health to be added to, may be positive or negative
-- amount must be provided as an integer
-- overheal_flag tells the function if amount can update maximum health when amount added goes beyond it
function addPlayerHealth(amount, overheal_flag)

	local damagemodels = EntityGetComponent( getPlayerEntity(), "DamageModelComponent" )
	
	if( damagemodels ~= nil ) then
		for i,v in ipairs(damagemodels) do
		
			currentHealth = tonumber( ComponentGetValue( v, "hp" ) )
			currentMaxHealth = tonumber( ComponentGetValue( v, "max_hp") )
			
			local trueAmount = amount * 0.04 -- how health scaling works when shown by UI
			local updatedHealth = currentHealth + trueAmount
			local updatedMaxHealth = 0
			
			local updatedHealth = currentHealth + trueAmount
			if(updatedHealth > currentMaxHealth and overheal_flag) then -- overhealing, if new health goes beyond max health, max health is updated
				updatedMaxHealth = updatedHealth
				ComponentSetValue( v, "max_hp", updatedMaxHealth)
				ComponentSetValue( v, "hp", updatedHealth)
			else
				if(updatedHealth <= 0) then
					updatedHealth = 0.04
				end
				ComponentSetValue( v, "hp", updatedHealth) -- sets health to 1 when updatedHealth is zero or below
			end
			
			break
		end
	end
end

-- function to directly set a value to player health
-- newHealth is the new health the player is going to have
-- newHealth must be provided as a float value
function setPlayerHealth(newHealth)
	local damagemodels = EntityGetComponent( getPlayerEntity(), "DamageModelComponent" )
	
	if( damagemodels ~= nil ) then
		for i,v in ipairs(damagemodels) do
			ComponentSetValue( v, "hp", newHealth)
		end
	end
end

Get and set basic damage multipliers[]

The functions below are useful to obtain and modify damage multipliers of any creature with a valid id.

-- get all basic damage multipliers from entity with entity_id
function getBasicDamageMultipliers(entity_id)
	local dmc_id = EntityGetFirstComponentIncludingDisabled(entity_id, "DamageModelComponent")
	
	local ice = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "ice" )
	local fire = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "fire" )
	local drill = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "drill" )
	local slice = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "slice" )
	local melee = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "melee" )
	local projectile = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "projectile" )
	local radioactive = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "radioactive" )
	local explosion = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "explosion" )
	local electricity = ComponentObjectGetValue2( dmc_id, "damage_multipliers", "electricity" )
	
	return ice, fire, drill, slice, melee, projectile, radioactive, explosion, electricity
end

-- gets the value for a damage multiplier of an entity with entity_id
-- multiplier_name must be either ice, fire, drill, slice, melee, projectile, radioactive, explosion or electricity
function getBasicDamageMultiplier(entity_id, multiplier_name)
	local dmc_id = EntityGetFirstComponentIncludingDisabled(entity_id, "DamageModelComponent")
	local multiplier = ComponentObjectGetValue2( dmc_id, "damage_multipliers", multiplier_name )
	return multiplier
end

-- sets a new value for a damage multiplier of an entity with entity_id
-- multiplier_name must be either ice, fire, drill, slice, melee, projectile, radioactive, explosion or electricity
function setBasicDamageMultiplier(entity_id, multiplier_name, new_multiplier)
	local dmc_id = EntityGetFirstComponentIncludingDisabled(entity_id, "DamageModelComponent")
	
	ComponentObjectSetValue2( dmc_id, "damage_multipliers", multiplier_name, new_multiplier )
end

Verlet Carrion Legs Script, Made By Horscht[]

This script gives your entity verlet tentacles which attach to walls like in the game Carrion.

dofile_once("data/scripts/lib/utilities.lua")

------- CONFIG------
local num_limbs = 5
local angle_spread = 220
local cast_length = 200
local move_speed = 5
local resting_length = 20
--------------------

local entity_id = GetUpdatedEntityID()
local x, y = EntityGetTransform(entity_id)
local last_x = GetValueNumber("last_x", x)
local last_y = GetValueNumber("last_y", y)
local vx, vy = x - last_x, y - last_y
local last_direction = GetValueNumber("last_direction", 0)
local direction = math.atan2(vy, vx)
if math.abs(vx) + math.abs(vy) > 0.1 then
  direction = rot_lerp(direction, last_direction, 0.05)
else
  direction = last_direction
end
SetValueNumber("last_direction", direction)
SetValueNumber("last_x", x)
SetValueNumber("last_y", y)
SetRandomSeed(x + entity_id, y)

if not GetValueBool("initialized", false) then
  SetValueBool("initialized", true)
  for i=1, num_limbs do
    local ent = EntityLoad("data/entities/verlet_chains/vines/verlet_vine_pixelscene.xml")
    EntityAddComponent2(ent, "VerletWorldJointComponent")
    EntitySetName(ent, "tentacle_limb")
    SetValueNumber("jiggle_speed_"..tostring(i), Randomf(1, 3))
    EntityAddChild(entity_id, ent)
  end
end

local i = 0
for _, child in ipairs(EntityGetAllChildren(entity_id)) do
  if EntityGetName(child) ~= "tentacle_limb" then goto continue end
  i = i + 1
  -- The limb's current position
  local x2, y2 = GetValueNumber("x_"..tostring(i), x), GetValueNumber("y_"..tostring(i), y)
  -- The position it's currently moving towards
  local target_x, target_y = GetValueNumber("target_x_"..tostring(i), x), GetValueNumber("target_y_"..tostring(i), y)
  local anchor_found = GetValueBool("anchor_found_"..tostring(i), false)
  local cast_direction_x, cast_direction_y = vec_rotate(math.cos(direction), math.sin(direction), math.rad(-angle_spread/2 + (i-1) * angle_spread / (num_limbs-1)))
  local move_x, move_y = vec_normalize(target_x - x2, target_y - y2)
  x2 = x2 + move_x * move_speed
  y2 = y2 + move_y * move_speed
  if math.abs(x2 - target_x) < 5 then
    x2 = target_x
  end
  if math.abs(y2 - target_y) < 5 then
    y2 = target_y
  end

  if not anchor_found then
    SetValueNumber("target_x_"..tostring(i), x + cast_direction_x * resting_length)
    SetValueNumber("target_y_"..tostring(i), y + cast_direction_y * resting_length)
  end

  -- Only do the raytracing stuff every x frames because it's probably expensive
  if GameGetFrameNum() % 20 == 0 then
    -- Check if there's still a pixel to hold on to
    local pixel_found = GetSurfaceNormal(target_x, target_y, 2, 16)
    if not pixel_found then
      anchor_found = false
    end
    -- target_hit checks if there's an obstacle between the main body and target
    local target_hit, target_hit_x, target_hit_y = RaytraceSurfaces(x, y, target_x - cast_direction_x * 2, target_y - cast_direction_y * 2)
    local distance_to_target = get_distance(x, y, target_x, target_y)
  
    -- Check if anchor is still in sight and range
    if not anchor_found or target_hit or (anchor_found and distance_to_target > cast_length) then
      -- Lost sight of anchor or anchor too far away, look for new anchor
      local hit, hit_x, hit_y = RaytraceSurfaces(x, y, x + cast_direction_x * cast_length, y + cast_direction_y * cast_length)
      if hit then
        -- Found new anchor, set target to it
        SetValueNumber("target_x_"..tostring(i), hit_x)
        SetValueNumber("target_y_"..tostring(i), hit_y)
        SetValueBool("anchor_found_"..tostring(i), true)
      else
        SetValueBool("anchor_found_"..tostring(i), false)
      end
    end
  end

  SetValueNumber("x_"..tostring(i), x2)
  SetValueNumber("y_"..tostring(i), y2)

  local verlet_component = EntityGetFirstComponentIncludingDisabled(child, "VerletPhysicsComponent")
  local verlet_world_joint_component = EntityGetFirstComponentIncludingDisabled(child, "VerletWorldJointComponent")
  local positions = ComponentGetValue2(verlet_component, "positions")
  local add_wave = anchor_found and 0 or 3
  local jiggle_speed = GetValueNumber("jiggle_speed_"..tostring(i), 1)
  positions[27] = x2 + math.cos(GameGetFrameNum() * 0.02 + jiggle_speed) * add_wave
  positions[28] = y2 + math.sin(GameGetFrameNum() * 0.05 + jiggle_speed) * add_wave
  ComponentSetValue2(verlet_component, "positions", positions)
  -- world_position is the origin of the verlet
  ComponentSetValue2(verlet_world_joint_component, "world_position", x, y)
  ::continue::
end

You just put this script in a lua component on the entity you want to have the tentacles like this:

   <LuaComponent
        script_source_file="mods/your_mod/files/scripts/this_script.lua"
        execute_on_added="1"
        execute_every_n_frame="1"
        >
    </LuaComponent>