Page 1 of 1

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

Posted: Fri Aug 02, 2019 6:45 pm
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?

Re: Help with entity.picture nil yet not nil

Posted: Fri Aug 02, 2019 8:05 pm
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

Re: Help with entity.picture nil yet not nil

Posted: Fri Aug 02, 2019 10:02 pm
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? :\

Re: Help with entity.picture nil yet not nil

Posted: Fri Aug 02, 2019 10:29 pm
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.

Re: Help with entity.picture nil yet not nil

Posted: Fri Aug 02, 2019 11:06 pm
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" ]

Re: Help with entity.picture nil yet not nil

Posted: Fri Aug 02, 2019 11:25 pm
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

Re: Help with entity.picture nil yet not nil

Posted: Sat Aug 03, 2019 2:52 am
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.

Re: Help with entity.picture nil yet not nil

Posted: Sat Aug 03, 2019 3:17 am
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.