Event when item inserted into building

Place to get help with not working mods / modding interface.
Post Reply
BuilderOfAges
Inserter
Inserter
Posts: 35
Joined: Thu Aug 02, 2018 3:02 pm
Contact:

Event when item inserted into building

Post by BuilderOfAges »

I'm working on an idea for an "anvil". The idea is that, when some raw metal is placed inside, the hitpoints go to 1. Then a "hammer" tool that is actually a type of repair pack, is applied until the anvil is back to full health, and the raw metal is automatically replaced with processed metal. This would (kind of) simulate smithing: having to hammer the raw metal into shape.

But I can't find any events that get raised when a building's inventory changed. I could register a tick listener, but that seems a bit wasteful of performace to me. Is there any way to work around this?

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

Re: Event when item inserted into building

Post by DaveMcW »

There are a LOT of building inventory changes, asking the devs to add a listener would also be wasteful. So a tick listener is the best you can do.

quyxkh
Smart Inserter
Smart Inserter
Posts: 1029
Joined: Sun May 08, 2016 9:01 am
Contact:

Re: Event when item inserted into building

Post by quyxkh »

I think on_player_cursor_stack_changed should do pretty well, no? Check whether the player has your anvil selected and had a hammer in hand and there's one in the anvil now, then take it out and put it in hand or inventory and do the work, something along those lines?

BuilderOfAges
Inserter
Inserter
Posts: 35
Joined: Thu Aug 02, 2018 3:02 pm
Contact:

Re: Event when item inserted into building

Post by BuilderOfAges »

All true. Just on_player_cursor_stack_changed won't be enough, since you can insert directly from you own inventory by ctrl + click. But I'll just listen to multiple player inventory changes and see if I can make it work like that.

Some kind of on_player_insert event would be nice perhaps, and not as wasteful as an event that also triggers on inserters. But beggars can't be choosers.

quyxkh
Smart Inserter
Smart Inserter
Posts: 1029
Joined: Sun May 08, 2016 9:01 am
Contact:

Re: Event when item inserted into building

Post by quyxkh »

Maybe there's a way to make the anvil's gui not openable? I think that'd make the hand the only way to get things in or out.

I think I do agree with your point here, there's a player_dropped_item event and a built_entity event, maybe player_cursor_stack_changed could include the other inventory involved, where any items came from or went to?

quyxkh
Smart Inserter
Smart Inserter
Posts: 1029
Joined: Sun May 08, 2016 9:01 am
Contact:

Re: Event when item inserted into building

Post by quyxkh »

Here, this uses rocket silos (they've got stack size 1 for convenience here) and wooden chests as hammers and anvils,

Code: Select all

local empty={}
local open_guis = {}
local de=defines.events
script.on_init(function() global.open_guis=open_guis end)
script.on_load(function() open_guis=global.open_guis end)
script.on_event(de.on_gui_opened, function(ev)
    open_guis[ev.player_index]=(ev.entity or empty).unit_number or false
    end)
script.on_event(de.on_gui_closed, function(ev)
    local pidx,e = ev.player_index, ev.entity
    local p = game.players[pidx]
    open_guis[pidx]=false
    if (e or empty).name == 'wooden-chest' then
	local b,did  = e.get_inventory(1), false
	while b.remove'rocket-silo' == 1 do
	    -- the `true` parm says mark it lootable, it'll be put
	    -- back in player inventory if possible.
	    p.surface.spill_item_stack(p.position,'rocket-silo',true)
	    did = true
	    end
	if did then
	    game.print"no no, don't place rocket silos in wooden chests gently!"
	    end
	end
    end)
script.on_event(de.on_player_cursor_stack_changed, function(ev)
    local pidx,p,sel,stack
    pidx    = ev.player_index
    p	    = game.players[pidx]
    sel	    = p.selected
    stack   = p.cursor_stack
    if (sel or empty).name ~= 'wooden-chest' then return end
    if (sel or empty).unit_number == open_guis[pidx] then return end
    if (stack or empty).valid_for_read
       and stack.name ~= 'rocket-silo' then return end
    if sel.get_inventory(1).remove'rocket-silo' == 0 then return end
    -- stack changed, now has an empty hand or a rocket silo, and there
    -- was a rocket silo in the chest. I conclude the player dropped
    -- the rocket silo into it. Put it back in the player's hand or inventory
    game.print'bang!'
    if not stack.valid_for_read then
	stack.set_stack'rocket-silo'
    else
	p.insert'rocket-silo'
	end
    end)
edit: added init/load global maintenance, duh.

BuilderOfAges
Inserter
Inserter
Posts: 35
Joined: Thu Aug 02, 2018 3:02 pm
Contact:

Re: Event when item inserted into building

Post by BuilderOfAges »

Thank you! I'll try this out and get back to you.

quyxkh
Smart Inserter
Smart Inserter
Posts: 1029
Joined: Sun May 08, 2016 9:01 am
Contact:

Re: Event when item inserted into building

Post by quyxkh »

haha, viewtopic.php?f=25&t=62712 has a much better idea for your anvil gui: just don't let players open it.

Code: Select all

script.on_event(de.on_gui_opened,function(ev)
    if (ev.entity or {}).name=='wooden-chest' then game.players[ev.player_index].opened=nil end
    end)
to replace the barrage of on_gui_x code above, leaving:

Code: Select all

local empty={}
local de=defines.events
script.on_event(de.on_gui_opened, function(ev)
    if (ev.entity or empty).name == 'wooden-chest'
	then game.players[ev.player_index].opened = nil end
    end)
script.on_event(de.on_player_cursor_stack_changed, function(ev)
    local pidx = ev.player_index
    local p    = game.players[pidx]
    local sel, stack  = p.selected, p.cursor_stack -- hand content changed
    if	    (sel or empty).name == 'wooden-chest'  -- over an anvil
       and  ( not (stack or empty).valid_for_read  -- and hand's now empty
	      or stack.name == 'rocket-silo' )  -- or holding a hammer
       and  sel.get_inventory(1).remove'rocket-silo' == 1  and there's a hammer in the anvil
    then -- make it so player didn't drop the hammer, player swung it.
	game.print'bang!' 
	if stack.valid_for_read then
	    p.insert'rocket-silo'
	else
	    stack.set_stack'rocket-silo'
	    end
	end
    end)

BuilderOfAges
Inserter
Inserter
Posts: 35
Joined: Thu Aug 02, 2018 3:02 pm
Contact:

Re: Event when item inserted into building

Post by BuilderOfAges »

I modified it slightly and it works great! Here's my take on it (uses stdlib for the events):

Code: Select all

require "stdlib/event/event"

Event.register(defines.events.on_built_entity, function(e)
  if e.created_entity.name == "anvil" then
    e.created_entity.set_recipe("copper-crude")
  end
end)

Event.register(defines.events.on_gui_opened, function(e)
  if (e.entity or {}).name == "anvil" then
    game.players[e.player_index].opened = nil
    game.players[e.player_index].print{"messages.anvil-cannot-be-opened"}
  end
end)

Event.register(defines.events.on_player_cursor_stack_changed, function(e)
  local player = game.players[e.player_index]
  if (player.selected or {}).name ~= "anvil" then return end

  local anvil = player.selected
  local anvil_source = anvil.get_inventory(defines.inventory.assembling_machine_input)
  local hammer_count = anvil_source.remove("stone-hammer")
  if hammer_count == 0 then return end

  -- return the hammer(s) to the player
  player.insert{name = "stone-hammer", count = hammer_count}

  -- update progress
  local recipe = anvil.get_recipe()
  anvil.crafting_progress = anvil.crafting_progress + 0.34
  -- ingredients are not automatically removed when progress is artificial
  -- https://forums.factorio.com/viewtopic.php?f=25&t=62727
  if anvil.crafting_progress >= 1 then
    for _, ingredient in ipairs(recipe.ingredients) do
      anvil_source.remove{name = ingredient.name, count = ingredient.amount}
    end
  end

  -- play hammering sound
  for _, sound in ipairs(game.surfaces["nauvis"].find_entities_filtered{name="hammer-sound"}) do
    sound.destroy()
  end
  player.surface.create_entity({
    name = "hammer-sound",
    position = player.position,
  })
end)
It's not on the mod portal yet, but you can download the version of the mod that uses this here.

Thanks again for your help!

Post Reply

Return to “Modding help”