Page 1 of 1

Event when item inserted into building

Posted: Wed Sep 26, 2018 3:38 pm
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?

Re: Event when item inserted into building

Posted: Wed Sep 26, 2018 5:16 pm
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.

Re: Event when item inserted into building

Posted: Wed Sep 26, 2018 11:32 pm
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?

Re: Event when item inserted into building

Posted: Thu Sep 27, 2018 9:01 am
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.

Re: Event when item inserted into building

Posted: Thu Sep 27, 2018 3:34 pm
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?

Re: Event when item inserted into building

Posted: Thu Sep 27, 2018 5:12 pm
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.

Re: Event when item inserted into building

Posted: Thu Sep 27, 2018 9:57 pm
by BuilderOfAges
Thank you! I'll try this out and get back to you.

Re: Event when item inserted into building

Posted: Fri Sep 28, 2018 8:34 pm
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)

Re: Event when item inserted into building

Posted: Sat Sep 29, 2018 4:13 pm
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!