Noita Wiki
Advertisement
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 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 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

-- 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(Extremely Dense Rock)'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)


Advertisement