Loot Chest+

by hanes

Automagically collects all the loot, on all surfaces. Useful in combination with artillery. Convenient for mods that add loot when aliens die, like Bob's mods, Necromant, Reika's EndgameCombat, Alien Loot Economy and Alien Space Science. The chest acts now as a passive provider chest and gets unlocked with logistic-robotics tech.

Content
a month ago
0.16 - 2.0
9.01K
Logistics Logistic network Storage

g [fixed] UPS intensive search

4 years ago
(updated 4 years ago)

Hi,

I tried using this on Space Exploration with Alien Loot Economy.

Firstly, it wasn't using surfaces correctly - it used the chest surface. The other thing is the extreme lag it gives every 10 seconds during the on_tick event.

I was able to modify my local copy to completely eliminate the on_tick handler, by modifying the on_entity_died event as follows:

script.on_event(defines.events.on_research_finished, function(event)
    auto_cons_check(event.research.force)
end)

-- When an entity dies, we add its position to the table of positions 
-- if we have the automated construction tech and if the mod is enabled
script.on_event(defines.events.on_entity_died, function(event)
        artifactList = {}
        if auto_cons_researched then
                if not global.lootChestPos then
                        global.lootChestPos = {}
                end
                if string.find(event.entity.name,"spitter") or string.find(event.entity.name, "biter") or string.find(event.entity.name, "worm") or string.find(event.entity.name, "nest") then
                        entityLoot = event.loot.get_contents() -- gives LuaInventory
                        log("[LCP]: entity loot: "..serpent.block(entityLoot))
                        test = entityLoot["artifact-ore"]
                        if test and test > 0 then
                                log("[LCP]: we found "..tostring(entityLoot["artifact-ore"]).." artifacts")
                                table.insert(artifactList, {name = "artifact-ore", count = test})
                        end
                end
          end

    --Read artifactList and insert into Loot Chest
        if #artifactList ~= 0 then
          local chest = global.lootChest
          local cannotInsert = false
          for _, itemStack in pairs(artifactList) do
                if(chest.can_insert(itemStack)) then
                  chest.insert(itemStack)
                  event.loot.clear()
                else
                  cannotInsert = true
                end
          end

          if cannotInsert then
                for _, plr in pairs(chest.force.players) do
                  plr.print("Cannot insert loot. Artifact loot chest is full.")
                end
          end
    end
end)

there's some limitations, mostly that i'm only searching for artifact-ore type, but i'm sure it would be easy enough for you to adapt this.

now the mod doesn't consume any UPS on my map, and the global table doesn't grow forever (resolves the save/load times).

4 years ago

Hi, nice work there. Although it might sound crazy, but I like that "feature" from my version that you could plonk down the chest and if there were some loots from the same session, it got tranfered nonetheless. So I'm unsure wether to change and what.

On first "fiddeling-around" with it, it seems I would have to add a big block for each loot, or am I missing something?
So for example a mod has alien-ore-1, alien-ore-2, and alien-ore-3 I can't just search for "ore".

Do I need one of these for each loot then?

if string.find(event.entity.name,"spitter") or string.find(event.entity.name, "biter") or string.find(event.entity.name, "worm") or string.find(event.entity.name, "nest") then
entityLoot = event.loot.get_contents() -- gives LuaInventory
log("[LCP]: entity loot: "..serpent.block(entityLoot))
test = entityLoot["artifact-ore"]
if test and test > 0 then
log("[LCP]: we found "..tostring(entityLoot["artifact-ore"]).." artifacts")
table.insert(artifactList, {name = "artifact-ore", count = test})
end
end

4 years ago

thanks, i thought it was like this from the beginning (I recall reading it's event-driven somewhere) so I was surprised when I finally got around to testing it and found extreme UPS drops.

the other thing I noticed was the huge size of its global table because I've had entities dying for a long time without having a single LootChest+ placed.

so, the changes I made, can actually be greatly simplified; because we're getting the Loot directly from the entity obituary, we don't need to search for it, or even validate the name.

i'll work on a newer version for you to take a look at.

4 years ago

Hm thinking about the global table problem... if the loot chest would be unlocked much earlier, like it was before IIRC with steel, then problem solved...somewhat at least.
But also I'm excited to see what you come up with. Sounds interesting and has that huge benefit of being very cheap UPS wise.

4 years ago
(updated 4 years ago)
script.on_event(defines.events.on_entity_died, function(event)
        local foundLoot = false
        if not global.artifactList then
                log("[LootChestUPS] Initialise artifactList")
                global.artifactList = {}
        end
    if string.find(event.entity.name,"spitter") or string.find(event.entity.name, "biter") or string.find(event.entity.name, "worm") or string.find(event.entity.name, "nest") then
                entityLoot = event.loot.get_contents() -- gives LuaInventory
                lootCount = 0
                for key,value in pairs(entityLoot) do
                        lootCount = entityLoot[key]
                        itemName = key
                        if lootCount and lootCount > 0 then
                                foundLoot = true
                                if not global.artifactList[itemName] then
                                        global.artifactList[itemName] = 0
                                end
                                global.artifactList[itemName] = global.artifactList[itemName] + lootCount
                                extraText = ""
                                event.loot.clear()
                                extraText = " Uncollected loot has accumulated."
                        end
                end
        else
                return
        end

    if not foundLoot or global.artifactList == {} then
                return
        end

    --Read global.artifactList and insert into Loot Chest
        --log("[LootChestUPS] artifactList: "..serpent.block(global.artifactList))
        local chest = global.lootChest
        if not chest.valid then
                local errorMsg = "[LootChestUPS] No loot chest is placed, loot can not be collected."
                if not global.validChestCheckCount then
                        global.validChestCheckCount = 0
                        game.print(errorMsg)
                end
                if global.validChestCheckCount > 3000 then
                        global.validChestCheckCount = 0
                        game.print(errorMsg)
                else
                        global.validChestCheckCount = global.validChestCheckCount + 1
                end
                return
        end
    local cannotInsert = false
        for _, itemStack in pairs(global.artifactList) do
                if itemStack > 0 then
                        parameters = {}
                        parameters["name"] = _
                        parameters["count"] = itemStack
                        if(chest.valid and chest.can_insert(parameters)) then
                          chest.insert(parameters)
                          global.artifactList[parameters["name"]] = 0
                          event.loot.clear()
                        else
                          cannotInsert = true
                        end
                end
        end
    if chest.valid and cannotInsert then
                for _, plr in pairs(chest.force.players) do
                  plr.print("Cannot insert loot. Artifact loot chest is full."..extraText)
                end
        end
end)
4 years ago

i've modified it so that it just grabs ALL loot.

so my solution is to keep a dictionary of the loots and increment the count instead of storing an ever-growing number of parameters, it will build the parameters on the spot when attempting insertion.

i've tested this with full chests, i've also ensured that there's a periodic notice to the player that it's unable to add to a chest, since none exist.

the main problem with this is that there's no handling for multiple player forces. everyone uses one loot chest.

4 years ago

Am testing it right now and so far looking pretty awesome! Pretty good solution to destroy the loots if no chest is found.
Not sure about the multiple forces stuff.. Solo-engineer here.. Is that working when everyone has a loot chest?

Anyways, I go and test some more now. Big thanks for your contributions.

4 years ago

yeah i play solo as well, so i'm not concerned about PvP until someone complains.

i'm working on having it print warnings to the user depending on whether or not they want the warnings.

4 years ago

i've tested with a plague rocket in K2+SE on a 100% threat planet, it really does a lot better when pulling everything into the chest.

4 years ago

I've uploaded my modifications as LootChestUPS mod, feel free to take them and integrate to yours.

4 years ago

Thanks a lot for your effort, very appreciated!
Instead of nuking the loots if no chest is found, could they be marked for deconstruction? would that make a difference?

In my short testing the mod is almost non existent, script-update wise - love it!

4 years ago
(updated 4 years ago)

in the latest version it has an optimised method of storing the uncollected loots.

instead of:

{..{name: .. count: ...}, { name: ..., count: ...}}

it's:

{
"name" = "count",
"name" = "count"
}

and the counts are incremented every time we find loot, set to 0 every time we insert some into the chest.

so if you're producing loot faster than you can pull it out, there will be some potential rounding errors if you have, say, 1000 in the queue and space for 980, i think the game just deletes the 20 that didn't fit.

it was possible to search the chest for free slots, and subtract what can fit from what's in the queue, but that requires a lot of compute power for a death rate that probably is already hurting UPS. i didn't see the point - this is what would be required to deconstruct the remaining ground items.

4 years ago

another UPS optimisation is to enable the choice to have the chest use active provider chest prototype instead of passive, which eliminates the need for an inserter to empty the chest.

4 years ago

not worth it then. ok but not really a problem in my eyes. In a normal run you don't lose much loot. In my last vanilla run, I maybe fought 10 bases until I researched the chest.
Ah another weird thing, a chest placed by nanobots isn't working. Does that need some revive or so?

4 years ago

it also has a problem with GhostPlacerExpress, if you figure that one out, let me know. it seems the event doesn't fire to handle loot chest assignment.

4 years ago

Will do! Until then I'll plonk it down myself.

Thanks again for your work!

4 years ago

no problem, thanks for the starting point. learned a lot. struggled with schall pickup towers for too long but space exploration's multiple surface thing just finally got to me

New response