[Solved] Help with entity.picture nil yet not nil

Place to get help with not working mods / modding interface.
Post Reply
User avatar
1000101
Burner Inserter
Burner Inserter
Posts: 14
Joined: Thu Oct 08, 2015 9:14 am
Contact:

[Solved] Help with entity.picture nil yet not nil

Post by 1000101 »

So I am updating a personal mod from 0.15.x to 0.17.x and I am running into an issue where entity.picture is throwing a lua error.

Code: Select all

    local basePic    = baseEntity.picture
    
    -- This dump block works fine
    local s = ""
    for index = 1, #basePic.layers do
        s = s .. "\n" .. basePic.layers[ index ].filename
    end
    error( s )
    
    -- Yet this line throws a nil error
    local picLayer   = basePic.layers[ 1 ]
For the life of me I don't understand why basePic would be resolved without issue for dumping the layers, but when trying to resolve the first layer does.

Am I just stupid? Did I miss something obvious? wth?
Last edited by 1000101 on Sat Aug 03, 2019 3:17 am, edited 1 time in total.
There are 10 kinds of people in this world, those that understand binary and those that don't.

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Help with entity.picture nil yet not nil

Post by darkfrei »

Try this:

Code: Select all

  -- for index = 1, #basePic.layers do
  --   s = s .. "\n" .. basePic.layers[ index ].filename
  -- end
  for index, layer in pairs (basePic.layers) do
    s = s .. "\n" .. layer.filename
  end

User avatar
1000101
Burner Inserter
Burner Inserter
Posts: 14
Joined: Thu Oct 08, 2015 9:14 am
Contact:

Re: Help with entity.picture nil yet not nil

Post by 1000101 »

The for block that dumps the layer filename works fine, it's just there to (a) show that the picture is actually resolved and (b) for checking I am referencing the correct layer. It's the single line below where I grab the actual layer I want that fails with the nil error. Below is the full function, but I don't think it will add anything other than context.

NOTE: Function has other issues (WIP upgrade from 0.15.x->0.17.x), but until I correct getting the picture from the base entity, they are moot.

Code: Select all

--[[
    CloneChestEntity
    
    Clones the entity for a base chest with the proper name and size constraints.
    
    in:
        baseEntity : LuaEntity, eg data.raw["container"]["steel-chest"]
        config : See LongChestLib.lua :: GetChestConfig()
        
        old:
        --longName : string, eg "long-steel-chest"
        --chestWidth : int, eg: 6
        --chestHeight : int, eg: 1
    
    out:
        Nothing
    
]]
-- function CloneChestEntity( baseEntity, longName, chestWidth, chestHeight )
function CloneChestEntity( baseEntity, config )
    
    local scale = config.width * config.height
    local longX = ( config.width  / 2.0 ) - 0.5
    local longY = ( config.height / 2.0 ) - 0.5
    
    local maxHealth     = baseEntity.max_health     * scale
    local inventorySize = baseEntity.inventory_size * scale
    
    local basePic    = baseEntity.picture
    
    --[[  -- Shows the correct entity filenames for the layers
    local s = ""
    local s = ""
    for index, layer in pairs( basePic.layers ) do
        s = s .. "\n[ " .. index .. " ] = ".. layer.filename
    end
    --for index = 1, #basePic.layers do
    --    s = s .. "\n" .. basePic.layers[ index ].filename
    --end
    --for name, _ in pairs( baseEntity ) do
    --    s = s .. "\n" .. name
    --end
    error( s )
    ]]
    
    -- This line fails with a nil error despite the above debug blocks working fine
    local picLayer   = basePic.layers[ 1 ]
    
    -- Everything below this point is still a WIP as I upgrade the code (pointless to worry about it if the game errors out before getting this far)
    local picWidth   = picLayer.width
    local picHeight  = picLayer.height
    local overWidth  = picWidth  - 32
    local overHeight = picHeight - 32
    local longWidth  = 32 * config.width  + overWidth
    local longHeight = 32 * config.height + overHeight
    
    local picShift = picLayer.shift
    
    local baseCollision = baseEntity.collision_box
    local longCollision = {
        { baseCollision[ 1 ][ 1 ] - longX, baseCollision[ 1 ][ 2 ] - longY },
        { baseCollision[ 2 ][ 1 ] + longX, baseCollision[ 2 ][ 2 ] + longY }
    }
    
    local baseSelection = baseEntity.selection_box
    local longSelection = {
        { baseSelection[ 1 ][ 1 ] - longX, baseSelection[ 1 ][ 2 ] - longY },
        { baseSelection[ 2 ][ 1 ] + longX, baseSelection[ 2 ][ 2 ] + longY }
    }
    
    local baseCWCP = baseEntity.circuit_wire_connection_point
    local baseCCS = baseEntity.circuit_connector_sprites
    
    local basePicturePriority = picLayer.priority
    
    local longEntity = util.table.deepcopy( baseEntity )
    longEntity.name = config.name
    longEntity.icon = "__esm_Long_Chests__/graphics/icons/" .. config.name .. ".png"
    longEntity.minable = { mining_time = 1, result = config.name }
    longEntity.max_health = maxHealth
    longEntity.corpse = "medium-remnants"
    longEntity.collision_box = longCollision
    longEntity.selection_box = longSelection
    longEntity.inventory_size = inventorySize
    longEntity.picture =
        {
            layers =
            {
                filename = "__esm_Long_Chests__/graphics/entity/" .. config.name .. ".png",
                priority = basePicturePriority,
                width = longWidth,
                height = longHeight,
                shift = picShift
            }
        }
    if( baseCWCP ~= nil )then
        longEntity.circuit_wire_connection_point = {
            shadow =
            {
                red   = { baseCWCP.shadow.red[ 1 ]   + longX, baseCWCP.shadow.red[ 2 ]   + longY },
                green = { baseCWCP.shadow.green[ 1 ] + longX, baseCWCP.shadow.green[ 2 ] + longY }
            },
            wire =
            {
                red   = { baseCWCP.wire.red[ 1 ]   + longX, baseCWCP.wire.red[ 2 ]   + longY },
                green = { baseCWCP.wire.green[ 1 ] + longX, baseCWCP.wire.green[ 2 ] + longY }
            }
        }
    end
    if( baseCCS ~= nil )then
        longEntity.circuit_connector_sprites.connector_main.shift        = { baseCCS.connector_main.shift[ 1 ]       + longX, baseCCS.connector_main.shift[ 2 ]       + longY }
        longEntity.circuit_connector_sprites.led_red.shift               = { baseCCS.led_red.shift[ 1 ]              + longX, baseCCS.led_red.shift[ 2 ]              + longY }
        longEntity.circuit_connector_sprites.led_green.shift             = { baseCCS.led_green.shift[ 1 ]            + longX, baseCCS.led_green.shift[ 2 ]            + longY }
        longEntity.circuit_connector_sprites.led_blue.shift              = { baseCCS.led_blue.shift[ 1 ]             + longX, baseCCS.led_blue.shift[ 2 ]             + longY }
        longEntity.circuit_connector_sprites.blue_led_light_offset       = { baseCCS.blue_led_light_offset[ 1 ]      + longX, baseCCS.blue_led_light_offset[ 2 ]      + longY }
        longEntity.circuit_connector_sprites.red_green_led_light_offset  = { baseCCS.red_green_led_light_offset[ 1 ] + longX, baseCCS.red_green_led_light_offset[ 2 ] + longY }
        if( baseCCS.connector_shadow ~= nil )then
            longEntity.circuit_connector_sprites.connector_shadow.shift  = { baseCCS.connector_shadow.shift[ 1 ]     + longX, baseCCS.connector_shadow.shift[ 2 ]     + longY }
        end
    end
    data.raw[ baseEntity.type ][ config.name ] = longEntity

end
Edit: fwiw, using your debug dump loop resulted in the same output as mine but doesn't resolve the underlying issue of the assignment of picLayer throwing the nil error for basePic (baseEntity.picture).

Code: Select all

Fail to load mods: __esm_Long_Chests__/data/lua:14: __esm_Long_Chests__/prototypes/entity.lua:66: attempt to index local 'basePic' (a nil value)
Edit 2: Further modified the dump block as follows:

Code: Select all

    local s = ""
    for index, layer in pairs( basePic.layers ) do
        s = s .. "\n[ " .. index .. " ] = ".. layer.filename
    end
    error( s )
Got the expected output of:

Code: Select all

[ 1 ] = __base__/graphics/entity/wooden-chest/wooden-chest.png
[ 2 ] = __base__/graphics/entity/wooden-chest/wooden-chest-shadow.png
All of which brings be back to my head scratcher of, why does the debug dump work but "local picLayer = basePic.layers[ 1 ]" fail? :\
There are 10 kinds of people in this world, those that understand binary and those that don't.

User avatar
1000101
Burner Inserter
Burner Inserter
Posts: 14
Joined: Thu Oct 08, 2015 9:14 am
Contact:

Re: Help with entity.picture nil yet not nil

Post by 1000101 »

After more experimentation, seems like lua doesn't like assigning unnamed table entries to variables. Using "local picWidth = baseEntity.picture.layers[ 1 ].width" worked (at very least it didn't throw a lua error).

nvm, it didn't like it after all, instead I get the following error.

Code: Select all

local picWidth   = baseEntity.picture.layers[ 1 ].width
-- resulting error: attempt to index field 'picture' (a nil value)
ffs lua is garbage.
There are 10 kinds of people in this world, those that understand binary and those that don't.

User avatar
1000101
Burner Inserter
Burner Inserter
Posts: 14
Joined: Thu Oct 08, 2015 9:14 am
Contact:

Re: Help with entity.picture nil yet not nil

Post by 1000101 »

Even more wth...

This works without error (other than the explicit call to "error" showing the filenames AND that picLayer is assigned):

Code: Select all

function TestFunction( baseEntity )
    
    local basePic    = baseEntity.picture
    local picLayer   = nil
    
    local s = ""
    for index, layer in pairs( basePic.layers ) do
        s = s .. "\n[ " .. index .. " ] = " .. layer.filename
        if( index == 1 )then
            picLayer = layer
        end
    end
    if( picLayer ~= nil )then
        s = s .. "\npicLayer = " .. picLayer.filename
    end
    
    error( s )
    
end
However, this throws the lua error "attempt to index local 'basePic' (a nil value)" on the for loop:

Code: Select all

function TestFunction( baseEntity )
    
    local basePic    = baseEntity.picture
    local picLayer   = nil
    
    for index, layer in pairs( basePic.layers ) do
        if( index == 1 )then
            picLayer = layer
        end
    end
    
end
NOTE: In all cases, the baseEntity is data.raw[ "container" ][ "wooden-chest" ]
There are 10 kinds of people in this world, those that understand binary and those that don't.

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: Help with entity.picture nil yet not nil

Post by DaveMcW »

Your second example works for me. Are you doing something funky to wooden-chest before running it?

Code: Select all

function TestFunction( baseEntity )
    local basePic    = baseEntity.picture
    local picLayer   = nil

    for index, layer in pairs( basePic.layers ) do
        if( index == 1 )then
            picLayer = layer
        end
    end

    log(serpent.dump(picLayer))
end
TestFunction(data.raw["container"]["wooden-chest"])
do local _={filename="__base__/graphics/entity/wooden-chest/wooden-chest.png",priority="extra-high",width=32,height=36,shift={0.015625,-0.0625},hr_version={filename="__base__/graphics/entity/wooden-chest/hr-wooden-chest.png",priority="extra-high",width=62,height=72,shift={0.015625,-0.0625},scale=0.5}};return _;end

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Help with entity.picture nil yet not nil

Post by eradicator »

Same as @DaveMcW, TestFunction() works just fine.

Code: Select all

---- #### "indexing nil" forum error test
local function TestFunction( baseEntity )
    
    local basePic    = baseEntity.picture
    local picLayer   = nil
    
    for index, layer in pairs( basePic.layers ) do
        if( index == 1 )then
            picLayer = layer
        end
    end
    
end

TestFunction(data.raw[ "container" ][ "wooden-chest" ])
error('test ok')
I suspect you're doing something like

Code: Select all

for foo,bar in pairs{'wooden-chest','some-other-chest',...} do
  TestFunction(data.raw['container'][bar]
end
Where one of the chests in the list actually doesn't have a "picture", or at least it is of a different composition than you expect. I.e. has a layers subnode "picture.layers" etc. You need to show the code where the function is actually called.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
1000101
Burner Inserter
Burner Inserter
Posts: 14
Joined: Thu Oct 08, 2015 9:14 am
Contact:

Re: Help with entity.picture nil yet not nil

Post by 1000101 »

So I found the issue...and it is as I suspected in my original post...I'm stupid.

While the wooden-chest was my go-to test, it (nor the iron/steel) wasn't the issue, but the logistics chests were. The structure of the picture data changed for the basic chests which is what I was basing my code changes off of but the logistics chests changed to an "animation" which I hadn't (a) expected and (b) didn't think to look at. I need to add more sanity checks and pick the proper field as needed.

Thanks for helping my dumb ass.

Edit:

Follow-up (as if anyone cared)...

Everything works now, my yet-another-"large-chest"-mod works, now I just need to update my crappy 0.15.x graphics using the new 0.17.x graphics.

Again, thanks for helping.
There are 10 kinds of people in this world, those that understand binary and those that don't.

Post Reply

Return to “Modding help”