Modding: Utilities

From Noita Wiki
Jump to navigation Jump to search
Modding Navigation
Fundamentals
BasicsData.wakGetting startedLua ScriptingUseful Tools
Guides
AudioEnemiesEnvironments (Fog of War) • Image EmittersMaterialsPerksSpecial BehaviorsSpellsSpritesheetsSteam WorkshopUsing CMake
Components/Entities
Component DocumentationEnumsList of all tagsSpecial TagsTags SystemUpdate Order
Lua Scripting
Lua APIUtility Scripts
Other Information
Enemy Information TableMagic NumbersSound EventsSpell IDsPerk IDsMaterial 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.lua and is used to add custom translations:

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

So if we were to name the entity "$item_custom" its name in game would show up as "Custom Name". Alternatively, you can use an external file with the exact same headers as "data/translations/common.csv":

local TRANSLATIONS_FILE = "data/translations/common.csv"
local translations = ModTextFileGetContent(TRANSLATIONS_FILE) .. ModTextFileGetContent("data/translations/my_custom_translations.csv")
ModTextFileSetContent(TRANSLATIONS_FILE, translations:gsub("\r\n","\n"):gsub("\n\n","\n"))
  • Note: Custom translations applied in mods' init.lua will not be applied when looking at a mod's settings UI from the main menu.

Add new genomes (compatible with other mods)

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.


(Keith and Horscht fixed this so now it finally works!)

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

local content = ModTextFileGetContent("data/genome_relations.csv")

--The function works like this: genome_name is the name of your new genome/faction, 
--default_relation_ab is the relation with all the horizontal genomes which relations weren't specified in the table, 
--default_relation_ba is the relation with all the vertical genomes which relations weren't specified in the table, 
--self relation is the genome's relation with itself, 
--relations is a table which directly specifies the value of the genome relation with.

function add_new_genome(content, genome_name, default_relation_ab, default_relation_ba, self_relation, relations)
  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 default_relation_ba).."\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] or default_relation_ab)
  end
  output = output .. line .. "," .. self_relation

  return output
end

-- Example usage: (This sets all genome relations of this genome to 100, unless indicated so, in this case, I want 100 with everything, except the player and -1)
content = add_new_genome(content, "genome1", 100, 100, 100, {
  player = 0,
  ["-1"] = 0
})
--Here I want 0 genome relations with everything, except for 100 with itself
content = add_new_genome(content, "genome2", 0, 0, 100, {})

ModTextFileSetContent("data/genome_relations.csv", content)

Add a new tag to an existing entity file

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 LuaComponent, 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 in a VariableStorageComponent of an entity. Both get and set functions assume the variable name you are trying to get or set already exists in the entity you are providing.

-- function to obtain a value from an internal variable contained in 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 variable contained in 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

-- function to add new internal variables to an entity
-- entity_id is the id of the entity that will receive a new internal variable
-- variable_type is the type of variable being added, can be value_int, value_string or value_float
-- initial_value is the starting value of the variable being added, if none, must be provided as, e.g., 0, "", or 0.0
function addNewInternalVariable(entity_id, variable_name, variable_type, initial_value)
	if(variable_type == "value_int") then
		EntityAddComponent2(entity_id, "VariableStorageComponent", {
			name=variable_name,
			value_int=initial_value
		})
	elseif(variable_type == "value_string") then
		EntityAddComponent2(entity_id, "VariableStorageComponent", {
			name=variable_name,
			value_string=initial_value
		})
	elseif(variable_type == "value_float") then
		EntityAddComponent2(entity_id, "VariableStorageComponent", {
			name=variable_name,
			value_float=initial_value
		})
	elseif(variable_type == "value_bool") then
		EntityAddComponent2(entity_id, "VariableStorageComponent", {
			name=variable_name,
			value_bool=initial_value
		})
	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 - 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>

Find if the player is polymorphed - by Soler91

This returns both a bool and the player entity if the player is indeed polymorphed; otherwise it returns nil.

function IsPlayerPolymorphed() -- returns bool, entityId/nil
    local polymorphed_entities = EntityGetWithTag("polymorphed")
    if (polymorphed_entities ~= nil) then
        for _, entity_id in ipairs(polymorphed_entities) do
            local is_player = false
            local game_stats_comp = EntityGetFirstComponent(entity_id, "GameStatsComponent")
            if (game_stats_comp ~= nil) then 
                is_player = ComponentGetValue2(game_stats_comp, "is_player")
            end
            if (is_player) then
                return true, entity_id
            end
        end
        return false, nil
    end
end

Modify XML files

To modify XML files, it's best to use Zatherz' XML parser library: https://github.com/zatherz/luanxml

First, you get the contents of the XML you want to modify using ModTextFileGetContent, then you parse that using the XML parser, modify it (check the Github page for documentation on that) and write it back using ModTextFileSetContent, to convert the NXML object back to a string for saving, use tostring().

Keep in mind that the ModTextFileGet/SetContent functions only exist in init.lua and disappear after OnMagicNumbersAndWorldSeedInitialized has run, so you need to do this before that. For instance simply outside any function, at the global level in init.lua

Here is some example code for how to change the EDR's durability to 5 in the data/materials.xml file.

local nxml = dofile_once("nxml.lua")
local content = ModTextFileGetContent("data/materials.xml")
local xml = nxml.parse(content)
for element in xml:each_child() do
  if element.attr.name == "rock_hard_border" then
    element.attr.durability = 5
  end
end
ModTextFileSetContent("data/materials.xml", tostring(xml))

Or here's how to add a biome to data/biome/_biomes_all.xml:

local nxml = dofile_once("nxml.lua")
local content = ModTextFileGetContent("data/biome/_biomes_all.xml")
local xml = nxml.parse(content)
xml:add_child(nxml.parse([[
<Biome 
  biome_filename="mods/yourmod/files/biomes/your_biome.xml" 
  height_index="0"
  color="ff123456">
</Biome>
]]))
ModTextFileSetContent("data/biome/_biomes_all.xml", tostring(xml))

Constrain an entity to a set of coordinates - Thatrius

This pulls the effected entity back if it gets too far away. Works with physics objects.

function constrain(entity, x2, y2, max_dist)
    local chardatacomp = EntityGetFirstComponent(entity, "CharacterDataComponent")
    local projcomp = EntityGetFirstComponent(entity, "VelocityComponent")
    local physcomp = EntityGetFirstComponent(entity, "PhysicsBodyComponent") or EntityGetFirstComponent(entity1, "PhysicsBody2Component") or EntityGetFirstComponent(entity1, "SimplePhysicsComponent")
    local vel_x, vel_y = ComponentGetValue2(chardatacomp, "mVelocity")
    local proj_vel_x, proj_vel_y = ComponentGetValue2(projcomp, "mVelocity")
    local x1, y1 = EntityGetTransform(entity)
    if (not x1) or (not x2) then return nil end
        
    local dist = math.sqrt(((x2-x1)^2) + ((y2-y1)^2))
    local target_x = x2 + (((x1-x2)/dist)*max_dist)
    local target_y = y2 + (((y1-y2)/dist)*max_dist)
    local target_vel_x = target_x - x1
    local target_vel_y = target_y - y1

    if dist > max_dist then
        if physcomp or not proj_vel_x then
            PhysicsApplyForce(entity, target_vel_x*100, target_vel_y*100)
        end
        if vel_x then
            vel_y = vel_y - 60
            local target_mag = math.sqrt((target_vel_x^2) + (target_vel_y^2))
            local mag = math.sqrt((vel_x^2) + (vel_y^2)) / 40
            local force_x = ((target_vel_x/target_mag)*mag)
            local force_y = ((target_vel_y/target_mag)*mag)

            vel_x = ((vel_x + force_x)*1.025) + (target_vel_x*dist*0.8)
            vel_y = ((vel_y + force_y)*1.025) + (target_vel_y*dist*0.8)
            EntitySetTransform(entity, target_x, target_y)
            if ComponentGetValue2(chardatacomp, "is_on_ground") == false then vel_x = vel_x*1.16 end
            ComponentSetValue2(chardatacomp, "mVelocity", vel_x, vel_y+60)
        elseif proj_vel_x then
            ComponentSetValue2(projcomp, "mVelocity", proj_vel_x+(target_vel_x*100), proj_vel_y+(target_vel_y*100))
            EntitySetTransform(entity, target_x, target_y)
        end
    end
end


To constrain two entities to each other, just run it on each of them using the other's coordinates:

x1, y1 = EntityGetTransform(entity1)
x2, y2 = EntityGetTransform(entity2)
constrain(entity1, x2, y2, 50)
constrain(entity2, x1, y1, 50)


Inverse Kinematics script (works with more than 2 limb parts)

This glorious script was wrought into being by Thatrius:

---------------------------------------------------------------------------------
--Define Functions
---------------------------------------------------------------------------------
function distance(x1,y1,x2,y2)
    return math.sqrt((x1 - x2)^2 + (y1 - y2)^2)
end

function approx_rolling_average(avg, target, n)
    avg = avg - avg / n;
    avg = avg + target / n;
    return avg
end
local smoothness_factor = 6

---------------------------------------------------------------------------------
--General Setup
---------------------------------------------------------------------------------
local entity = GetUpdatedEntityID()
local ex, ey = EntityGetTransform(entity)

local parent = EntityGetParent(entity)
local vel_x, vel_y = GameGetVelocityCompVelocity(parent)
local vel_mag = math.sqrt((vel_x)^2 + (vel_y)^2)
local futurex, futurey = ex+(vel_x*60), ey+(vel_y*60)

local lengthcomp = EntityGetFirstComponent(entity, "VariableStorageComponent", "length")
local target = {GetValueNumber("targ_x", ex), GetValueNumber("targ_y", ey)}
local length = ComponentGetValue2(lengthcomp, "value_int")
local time = GetValueNumber("time", 0)

---------------------------------------------------------------------------------
--Inverse Kinematics
---------------------------------------------------------------------------------
--organize the segments and joints into neat little tables:
local legments = {} --"leg" + "segments"... haha funny word combo do you get
local joints = {}
local knees = {}
for i, child in ipairs(EntityGetAllChildren(entity)) do
    if not EntityHasTag(child, "knee") then
        local x, y = EntityGetTransform(child)
        if x == 0 then x = 1 end --safeguards against stupid "nan"s by making sure the code can never divide 0/0. Lost a piece of my soul in the frustration brought about by trying to track down a bug cause by this. Why does 0/0 have to equal "nan" in lua anyways and not just 0 or something, it serves no purpose except to create frustration, this is utterly retarted
        if y == 0 then y = 1 end
        table.insert(legments, child)
        if i == 1 then table.insert(joints, {ex, ey}) else table.insert(joints, {x, y}) end
    else
        table.insert(knees, child)
    end
end
table.insert(joints, target)


--First pass, starting at end of chain:
for i = #joints, 1, -1 do
    if i == #joints then
        --move joint directly to the target coords:
        joints[i] = target
    elseif i ~= 1 then
        --move joint within range of the previous one iterated:
        local jx, jy = joints[i][1], joints[i][2]
        local jx2, jy2 = joints[i+1][1], joints[i+1][2]
        if jx == jx2 or jy == jy2 then jx, jx = jx+1, jy+1 end --more nan-avoiding bullcrap
        local joint_dist = distance(jx, jy, jx2, jy2)
        local jx = jx2 + (((jx-jx2) / joint_dist) * length)
        local jy = jy2 + (((jy-jy2) / joint_dist) * length)
        joints[i] = {jx, jy}
    end
end

--Second pass, starting at beginning of chain:
for i, joint in ipairs(joints) do
    if i ~= 1 then
        --move joint within range of the previous one iterated:
        local jx, jy = joints[i][1], joints[i][2]
        local jx2, jy2 = joints[i-1][1], joints[i-1][2]
        if jx == jx2 or jy == jy2 then jx, jx = jx+1, jy+1 end
        local joint_dist = distance(jx, jy, jx2, jy2)
        local jx = jx2 + (((jx-jx2) / joint_dist) * length)
        local jy = jy2 + (((jy-jy2) / joint_dist) * length)
        joints[i] = {jx, jy}
    end
end

--Arrange all the legments to connect in between the joints:
for i, legment in ipairs(legments) do
    local jx, jy = math.floor(joints[i][1]), math.floor(joints[i][2])
    local jx2, jy2 = joints[i+1][1], joints[i+1][2]
    if jx == jx2 or jy == jy2 then jx, jx = jx+1, jy+1 end
    local vec_x, vec_y = jx2-jx, jy2-jy
    local targ_rot = math.atan2(vec_y, vec_x)
    EntitySetTransform(legment, jx, jy, targ_rot)
    if knees[i] then EntitySetTransform(knees[i], jx2, jy2) end
end

---------------------------------------------------------------------------------
--AI:
---------------------------------------------------------------------------------
local total_leglength = length*#legments --combined length of all the segments in the leg
local length_tolerance = total_leglength--how far to let the end of the leg get before re-positioning it

--what the leg needs to point at:
local tx = GetValueNumber("tx", target[1])
local ty = GetValueNumber("ty", target[2])
local distance_to_target = distance(tx, ty, target[1], target[2])


--mix target position between ex, ey and tx, ty, based on time (picks up feet slightly when stepping):
local mult = smoothness_factor * (1+(2/3))
local inv = mult-time
local mix_x = (ex/mult)*time + (tx/mult)*inv
local mix_y = (ey/mult)*time + (ty/mult)*inv

--move feet toward target, unless already within range of it, in which case snap to the coordinates:
if distance_to_target > 10 then
    target[1] = approx_rolling_average(target[1], mix_x, smoothness_factor)
    target[2] = approx_rolling_average(target[2], mix_y, smoothness_factor)
elseif target[1] ~= tx and target[2] ~= ty then
    target[1] = tx
    target[2] = ty
    --[a stepping noise could go here]
end

--if the leg stretches too far, find a new target:
if (distance(tx, ty, ex, ey) > total_leglength and distance(tx, ty, futurex, futurey) > total_leglength) or distance(tx, ty, ex, ey) == 0 then 
    local surfaces_found = false
    --try to find nearby surfaces up to 3 times before resorting to stepping on air:
    for i=1,3 do
        potential_x, potential_y = (vel_x*60)+math.random(-total_leglength, total_leglength), (vel_y*60)+math.random(-total_leglength, total_leglength)
        mag = math.sqrt(((ex+potential_x)-ex)^2 + ((ey+potential_y)-ey)^2)
        potential_x, potential_y = ex + ((potential_x/mag)*total_leglength), ey + ((potential_y/mag)*total_leglength)
        did_hit, hit_x, hit_y = RaytracePlatforms(ex, ey, potential_x, potential_y)
        if did_hit then
            tx = hit_x
            ty = hit_y
            surfaces_found = true
            break
        end
    end
    --step on air:
    if not surfaces_found then
        tx = potential_x
        ty = potential_y
    end
    time = mult
end

--apply changes:
SetValueNumber("tx", tx)
SetValueNumber("ty", ty)
SetValueNumber("targ_x", target[1])
SetValueNumber("targ_y", target[2])
SetValueNumber("time", math.max(0,time-1))


To use it, name it "IK.lua" and put it in mods/yourmod/files/.

Then, save this xml script below as "ik_leg.xml", and put it in mods/yourmod/files/ as well.

	<Entity name="ik_leg">

		<InheritTransformComponent>
		</InheritTransformComponent>

		<Entity tags="IK">
			<SpriteComponent 
				image_file="data/entities/animals/lukki/lukki_feet/lukki_limb_a_long.png" 
				z_index="1.1"
				offset_x="0"
				offset_y="5"
				>
			</SpriteComponent>
		</Entity>
		<Entity tags="knee">
			<SpriteComponent 
				image_file="data/entities/animals/lukki/lukki_feet/lukki_knee.png" 
				z_index="1.09"
				offset_x="5"
				offset_y="5"
				>
			</SpriteComponent>
		</Entity>
		<Entity tags="IK">
			<SpriteComponent 
				image_file="data/entities/animals/lukki/lukki_feet/lukki_limb_a_long.png" 
				z_index="1.1"
				offset_x="0"
				offset_y="5"
				>
			</SpriteComponent>
		</Entity>
		<Entity tags="knee">
			<SpriteComponent 
				image_file="data/entities/animals/lukki/lukki_feet/lukki_knee.png" 
				z_index="1.09"
				offset_x="5"
				offset_y="5"
				>
			</SpriteComponent>
		</Entity>
		<Entity tags="IK">
			<SpriteComponent 
				image_file="data/entities/animals/lukki/lukki_feet/lukki_limb_b_long.png" 
				z_index="1.1"
				offset_x="0"
				offset_y="5"
				>
			</SpriteComponent>
		</Entity>

		<LuaComponent
			script_source_file="yourmod/files/scripts/IK.lua"
			execute_every_n_frame="1"
			remove_after_executed="0"
			>
		</LuaComponent>
		<VariableStorageComponent
			_tags="length"
			value_int="38"
			>
		</VariableStorageComponent>
		<VariableStorageComponent
			_tags="IK_targ_x"
			value_int="30"
			>
		</VariableStorageComponent>
		<VariableStorageComponent
			_tags="IK_targ_y"
			value_int="-60"
			>
		</VariableStorageComponent>
	</Entity>


By default this adds 3 limb segments to the leg. To add or remove segments, add or remove however many of these </Entity> child-block-things as you want (the parts of the xml that look like this):

    <Entity>
    	<SpriteComponent 
		    image_file="data/entities/animals/lukki/lukki_feet/lukki_limb_a.png" 
		    z_index="1.1"
		    offset_x="0"
		    offset_y="5"
		    >
	    </SpriteComponent>
    </Entity>


After you've saved the file, use Lua to spawn the ik_leg.xml entity, and parent it to whichever other entity you want the IK leg to stick out of, like this:

local x, y = EntityGetTransform(entity)
local ik_leg = EntityLoad("mods/yourmod/files/ik_leg.xml", x, y)
EntityAddChild(entity, ik_leg)


To set the coordinates you want the IK limb to point at, or to change the length between the joints, use Lua again to set ik_leg.xml's VariableStorageComponent values, like this:

local targcomp_x = EntityGetFirstComponent(entity, "VariableStorageComponent", "IK_targ_x")
local targcomp_y = EntityGetFirstComponent(entity, "VariableStorageComponent", "IK_targ_y")
local length_between_joints_component = EntityGetFirstComponent(entity, "VariableStorageComponent", "length")

ComponentSetValue2(targcomp_x, "value_int", whatever_number)
ComponentSetValue2(targcomp_x, "value_int", whatever_number)
ComponentSetValue2(length_between_joints_component, "value_int", whatever_number)

Lua Wall: A Wall that works entirely through lua

This script gives you a wall through lua. It requires you to set the width and height of the wall, and then it does the rest.

As the script is built, the wall works only for the player character.

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

local entity_id = GetUpdatedEntityID()
local x, y = EntityGetTransform(entity_id)

local players = EntityGetWithTag("player_unit")

local character_data_component = EntityGetFirstComponentIncludingDisabled(players[1], "CharacterDataComponent")
local vel_x, vel_y = ComponentGetValue2( character_data_component, "mVelocity" )
if #players > 0 then
  
    local player_x, player_y = EntityGetTransform(players[1])
	
	local old_player_x = GetValueNumber("old_player_x", player_x)
	local old_player_y = GetValueNumber("old_player_y", player_y)
	SetValueNumber("old_player_x", player_x)
	SetValueNumber("old_player_y", player_y)

    new_x = player_x
    new_y = player_y
	
	local variable_storage = EntityGetComponent(entity_id, "VariableStorageComponent")
	
	for k, v in pairs(variable_storage)do
		local name = ComponentGetValue2(v, "name")

		if(name == "top_edge")then
			top_edge = ComponentGetValue2(v, "value_string")
		elseif(name == "bottom_edge")then
			bottom_edge = ComponentGetValue2(v, "value_string")      
		elseif(name == "left_edge")then
			left_edge = ComponentGetValue2(v, "value_string")      
		elseif(name == "right_edge")then
			right_edge = ComponentGetValue2(v, "value_string")  
		end
	end
	
	local old_top_edge = GetValueNumber("old_top_edge", y)
	local old_bottom_edge = GetValueNumber("old_bottom_edge", y)
	SetValueNumber("old_top_edge", y + top_edge)
	SetValueNumber("old_bottom_edge", y + bottom_edge)
	
	local old_left_edge = GetValueNumber("old_left_edge", x)
	local old_right_edge = GetValueNumber("old_right_edge", x)
	SetValueNumber("old_left_edge", x + left_edge)
	SetValueNumber("old_right_edge", x + right_edge)
		
	if ( old_player_y < y + top_edge or old_player_y > y + bottom_edge ) or ( old_top_edge > player_y ) or ( old_bottom_edge < player_y ) then

		if ( player_y > y + top_edge and player_y < y ) and ( player_x > x + left_edge and player_x < x + right_edge ) then

			new_y = y + top_edge - 1
			ComponentSetValue2(character_data_component, "is_on_ground", true)
			ComponentSetValue2(character_data_component, "mVelocity", vel_x, 0)
			
		elseif ( player_y < y + bottom_edge and player_y > y ) and ( player_x > x + left_edge and player_x < x + right_edge ) then

			new_y = y + bottom_edge + 2
			ComponentSetValue2(character_data_component, "mVelocity", vel_x, 0)
		end
		
	-- elseif ( old_player_x < x + left_edge or old_player_x > x + right_edge ) or ( old_left_edge > player_x ) or ( old_right_edge < player_x ) then
	else
		
		if ( player_x > x + left_edge and player_x < x ) and ( player_y > y + top_edge and player_y < y + bottom_edge ) then
		
			new_x = x + left_edge - 1
		
		elseif ( player_x < x + right_edge and player_x > x ) and ( player_y > y + top_edge and player_y < y + bottom_edge ) then
		
			new_x = x + right_edge + 1

		end
	
	end

    if ( new_x ~= player_x ) or ( new_y ~= player_y ) then
	
        EntitySetTransform(players[1], new_x, new_y)
		
		--Inflict damage if squashing player:
		function kill_player()
		
			EntityInflictDamage(players[1], 999, "DAMAGE_PHYSICS_HIT", "", "NORMAL", 0, 0)
			
			-- local damage_model_component = EntityGetFirstComponent(players[1], "DamageModelComponent")

			-- ComponentSetValue2(damage_model_component, "hp", 0)
			-- ComponentSetValue2(damage_model_component, "air_needed", true)
			-- ComponentSetValue2(damage_model_component, "air_in_lungs", 0)
			
		end
		
		local ingrown_factor = 1
		
		local player_left_width = ComponentGetValue2( character_data_component, "collision_aabb_min_x" )
		local player_right_width = ComponentGetValue2( character_data_component, "collision_aabb_max_x" )
		local player_upper_height = ComponentGetValue2( character_data_component, "collision_aabb_min_y" ) - 1
		local player_lower_height = ComponentGetValue2( character_data_component, "collision_aabb_max_y" )
		
		local check_left_edge, hit_x1, hit_y1 = RaytracePlatforms(player_x + player_left_width, player_y + player_upper_height + 1, player_x + player_left_width, player_y + player_lower_height - 1)
		local check_right_edge, hit_x2, hit_y2 = RaytracePlatforms(player_x + player_right_width, player_y + player_upper_height + 1, player_x + player_right_width, player_y + player_lower_height -1 )
		local check_upper_edge, hit_x3, hit_y3 = RaytracePlatforms(player_x + player_left_width + 1, player_y + player_upper_height, player_x + player_right_width - 1, player_y + player_upper_height )
		local check_bottom_edge, hit_x4, hit_y4 = RaytracePlatforms(player_x + player_left_width + 1, player_y + player_lower_height, player_x + player_right_width - 1, player_y + player_lower_height )

		--if ( ( old_player_x < x + left_edge or old_player_x > x + right_edge ) or ( old_left_edge > player_x ) or ( old_right_edge < player_x ) ) and ( check_left_edge or check_right_edge) then
		if ( ( old_player_x < x + left_edge or old_player_x > x + right_edge ) or ( old_left_edge > player_x ) or ( old_right_edge < player_x ) ) and ( check_left_edge or check_right_edge) then
			
			kill_player()
			
		elseif ( ( old_player_y < y + top_edge ) or ( old_top_edge > player_y ) ) and check_upper_edge then 
		-- elseif ( ( old_player_y < y + top_edge or old_player_y > y + bottom_edge ) or ( old_top_edge > player_y ) or ( old_bottom_edge < player_y ) ) and ( check_upper_edge or check_bottom_edge) then 
			GamePrint("Squished Top")
			kill_player()
			
		elseif ( ( old_player_y > y + bottom_edge ) or ( old_bottom_edge < player_y ) ) and check_bottom_edge then 
			
			GamePrint("Squished Bottom")
			kill_player()
			
		end
		
    end
    
end

Just run this every frame through a lua script on an entity and it will produce this malleable wall.

For this to work, the entity bearing the lua component needs to have a couple of variable storage components, defining the distance of edges from the center.

<Entity name="Lua Wall">

	<SpriteComponent 
		image_file="data/props_gfx/temple_door.png" 
		offset_x="7"
		offset_y="30" >
	</SpriteComponent>

    <VariableStorageComponent
        name="top_edge"
        value_string="-30"
    ></VariableStorageComponent>
	
    <VariableStorageComponent
        name="bottom_edge"
        value_string="30"
    ></VariableStorageComponent>
	
    <VariableStorageComponent
        name="left_edge"
        value_string="-7"
    ></VariableStorageComponent>
	
    <VariableStorageComponent
        name="right_edge"
        value_string="7"
    ></VariableStorageComponent>
	
	<LuaComponent
		script_source_file="data/scripts/props/lua_wall.lua"
		execute_every_n_frame="1"
		>
	</LuaComponent>

</Entity>

Storing values from a component and using them later

This tool will allow you to store values of a component in a variable storage component, make temporary changes to a component and then use insert the old values back later.

So first here's the lua script that stores the old values and sets the new values. In this case it's setting a bunch of values in the CharacterPlatformingComponent to 0.

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

local entity_id    = GetUpdatedEntityID()

local character_platforming_component = EntityGetFirstComponentIncludingDisabled(entity_id, "CharacterPlatformingComponent")

local properties_to_change = {
	run_velocity = 0,
	fly_speed_max_up = 0,
	jump_velocity_y = 0,
	jump_velocity_x = 0,
	fly_velocity_x = 0,
	fly_speed_max_up = 0
}

if character_platforming_component then
	local old_values = ""
	for k, v in pairs(properties_to_change) do
		local value = ComponentGetValue2(character_platforming_component, k)
		local string_value = tostring(value)
		if type(value) == "boolean" then
			string_value = value and "1" or "0"
		end
		old_values = old_values .. k .. ":" .. type(value) .. "=" .. string_value
		if next(properties_to_change, k) then
			old_values = old_values .. ","
		end
		ComponentSetValue2(character_platforming_component, k, v)
	end

	EntityAddComponent2(entity_id, "VariableStorageComponent", {
		name = "old_values",
		value_string = old_values,
	})
end

Here's the script which would read out the old values from the variable storage component and which would set them back to what they were before on the entity:

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

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 toboolean(str)
	return str == "1"
end

local entity_id = GetUpdatedEntityID()
local character_platforming_component = EntityGetFirstComponentIncludingDisabled(entity_id , "CharacterPlatformingComponent")
local var_store = get_variable_storage_component(entity_id, "old_values")
local stored_values = ComponentGetValue2(var_store, "value_string")
if stored_values then
	local properties = split_string(stored_values, ",")
	
	for i, v in ipairs(properties) do
		local name, prop_type, value = string.match(v, "(.+):(.+)=(.+)")
		value = _G["to"..prop_type](value)
		ComponentSetValue2(character_platforming_component, name, value)
	end
	
	EntityRemoveComponent(entity_id, var_store)
end

And there you go. This will allow you to make temporary changes to a component for a temporary time, and then apply the old values back once you're done. And that's about it, hopefully this all made sense, will update soon so it makes even more sense :D

Getting surface normals using RaytracePlatforms()

This function is really useful when you are trying to get surface normals but don't want to get any kind of surface, but rather using a specific raytrace.

function GetSurfaceNormal(pos_x, pos_y, ray_length, ray_count)
	local total_normal_x = 0
	local total_normal_y = 0
	local total_distance = 0
	local found_normal = false

	for i = 1, ray_count do
		local angle = (i - 1) * (2 * math.pi / ray_count)
		local ray_x = pos_x + ray_length * math.cos(angle)
		local ray_y = pos_y + ray_length * math.sin(angle)

		local did_hit, hit_x, hit_y = RaytracePlatforms(pos_x, pos_y, ray_x, ray_y)

		if did_hit then
			found_normal = true
			local normal_x, normal_y = hit_x - pos_x, hit_y - pos_y
			total_normal_x = total_normal_x + normal_x
			total_normal_y = total_normal_y + normal_y
			total_distance = total_distance + math.sqrt(normal_x^2 + normal_y^2)
		end
	end

	if found_normal then
		local average_normal_x = total_normal_x / ray_count
		local average_normal_y = total_normal_y / ray_count
		local average_distance = total_distance / ray_count
		return true, average_normal_x, average_normal_y, average_distance
	else
		return false, 0, 0, 0
	end
end

And there you go. This surface normal returns values in the same order as the vanilla function

Organise Spells or Perks into the Spell/Perk list

Made by Copi & Conga lyne

This function organises the Spells to integrate into the vanilla spell progress log (eg, a modded crit on hasteium spell can be placed next to vanilla crit on x spells) .

function OrganiseProgress()
    -- Based on Conga Lyne's implementation
    for insert_index = 1, #actions_to_insert do
        local action_to_insert = actions_to_insert[insert_index]
        -- Check if spells to inject after are defined
        if action_to_insert.inject_after ~= nil then
            -- Loop over actions
            local found = false
            for actions_index = #actions, 1, -1 do
                action = actions[actions_index]
                -- Loop over inject after options
                for inject_index = 1, #action_to_insert.inject_after do
                    if action.id == action_to_insert.inject_after[inject_index] then
                        found = true
                        break
                    end
                end
                if found then
                    table.insert(actions, actions_index + 1, action_to_insert)
                    break
                end
                if actions_index == 1 then
                    --Insert here as a failsafe incase the matchup ID can't be found.. some other mod might delete the spell we're trying to insert at
                    actions[#actions + 1] = action_to_insert
                end
            end
        else
            actions[#actions + 1] = action_to_insert
        end
    end
end

actions_to_insert is the table of your modded actions (spells)

After putting this function underneath your table of actions, be sure to put a new table entry in the spells you wish to give a position in the list as a table, this can be done so with the following line of text

inject_after = {"HITFX_CRITICAL_BLOOD", "HITFX_CRITICAL_OIL", "HITFX_CRITICAL_WATER", "HITFX_BURNING_CRITICAL_HIT"},

For a custom spell, it would look like this in full, pay attention to inject_after especially

    {
        id          = "HITFX_CRITICAL_DRUNK",
        inject_after = {"HITFX_CRITICAL_BLOOD", "HITFX_CRITICAL_OIL"},
        name 		= "$spell_critical_drunk_name",
        description = "$spell_critical_drunk_desc",
        sprite 		= "mods/Apotheosis/files/ui_gfx/gun_actions/critical_drunk.png",
        sprite_unidentified = "data/ui_gfx/gun_actions/electric_charge_unidentified.png",
        related_extra_entities = { "mods/Apotheosis/files/entities/misc/hitfx_critical_drunk.xml" },
        type 		= ACTION_TYPE_MODIFIER,
        spawn_level                       = "1,3,4,5", -- HITFX_CRITICAL_WATER
        spawn_probability                 = "0.2,0.2,0.2,0.2", -- HITFX_CRITICAL_WATER
        price = 70,
        mana = 10,
        --max_uses = 16,
        action 		= function()
            c.extra_entities = c.extra_entities .. "mods/Apotheosis/files/entities/misc/hitfx_critical_drunk.xml,"
            draw_actions( 1, true )
        end,
    },

In this example, the example modded spell called "HITFX_CRITICAL_DRUNK" will be inserted in the progress log after Critical on Bloody Enemies, if that spell doesn't exist (eg another mod removed the spell), it will be placed after Critical on Oiled Enemies instead

Here is a picture of what it looks like ingame:

Modding Utilities ProgressLogExample.png

Organise Creatures into the Enemy list

Made by Copi & Conga lyne

This function organises Creatures to integrate into the vanilla enemy progress log (eg, a modded slime enemy can be placed next to vanilla slime enemy in the progress log) .

function OrganiseProgressEnemy()
    --List of enemies you want to insert into progress
    --id_matchup is the filename of the enemy you want to insert after
    --name is the filename of your modded creature or creatures; take note you can insert multiple creatures at once by using \n as a seperator between filenames
    local insert_list = {
        {
            id_matchup="miner_chef",
            name="hiisi_thief",
        },
        {
            id_matchup="shotgunner",
            name="hiisi_minecart\nhiisi_minecart_tnt\nhiisi_giga_bomb",
        },
    }

    --turn _list.txt into a table called enemies
    local path, enemies = "data/ui_gfx/animal_icons/_list.txt", {}
    for enemy in ModTextFileGetContent(path):gmatch("[^\r\n]+") do
        enemies[#enemies+1] = enemy
    end

    -- Scan through insert_list, then for each entry in said table, scan through the enemies table for any filenames which match the id_matchup we're looking for
    for k=1,#insert_list
    do local v = insert_list[k]
        if v.id_matchup == nil then
            table.insert(enemies,v)
        else
            for z=1,#enemies
            do c = enemies[z]
                if c == v.id_matchup or z == #enemies then
                    table.insert(enemies,z + 1,v.name)
                    break
                end
            end
        end
    end

    --After inserting all new modded enemies into the table, we can concatenate it into a string and insert \n between each table entry
    --This will make the file have the same format it originally had when we read it's data so the game can use it
    ModTextFileSetContent(path, table.concat(enemies, "\n"))

    --And that's it!
    --Enjoy your newly organised enemy log
end

Here is a picture of what it looks like ingame:

Modding EnemyProgressOrganised.png

Retrieving the Cause of Death

Gets the cause of death message if called from OnPlayerDied. In some cases this can have a more accurate message than the one displayed in-game.

local function not_empty(s)
	return s ~= nil and s ~= ''
end

function GetCauseOfDeath()
	local raw_death_msg = StatsGetValue("killed_by") or ""
	local origin, cause = raw_death_msg:match("(.*) | (.*)")

	if origin then
		origin = GameTextGetTranslatedOrNot(origin)
	end

	local result = 'Noita'
	if not_empty(origin) and not_empty(cause) then
		if origin:sub(-1) == 's' then
			result = GameTextGet("$menugameover_causeofdeath_killer_cause_name_ends_in_s", origin, cause)
		else
			result = GameTextGet("$menugameover_causeofdeath_killer_cause", origin, cause)
		end
	elseif not_empty(origin) then
		result = origin
	elseif not_empty(cause) then
		result = cause
	end

	return result .. StatsGetValue("killed_by_extra")
end

Find out which mod overwrote a vanilla Noita file

function find_mod_data_override(path)
    local mod_ids = ModGetActiveModIDs()
    for idx=#mod_ids,1,-1 do
        local mod_id = mod_ids[idx]
        if ModDoesFileExist("mods/" .. mod_id .. "/" ..path) then
            return mod_id
        end
    end
    return nil
end

Usage:

local player_png_override = find_mod_data_override("data/enemies_gfx/player.png")
if player_png_override then
    GamePrint("Warning: Mod '" .. player_png_override .. "' overrode the player sprite. MyMod might not work properly.")
end