forging item on altar

Ask for help about creating mods and scripts for Grimrock 2 or share your tips, scripts, tools and assets with other modders here. Warning: forum contains spoilers!
Post Reply
bongobeat
Posts: 1076
Joined: Thu May 16, 2013 5:58 pm
Location: France

forging item on altar

Post by bongobeat »

I want some kind of forge, where you have to put different items on an altar, then it spawns the crafted items. (it's like forging the metor hammer in the game)

can someone help how to do it?

I have start this script, but it does not work correctly:

if you put a hammer, it spawn a key
if you put a rock, nothing happens
if you put both rock and hammer, it spawns 2 keys
SpoilerShow

Code: Select all

    function surfaceContains(surface, item)
       for _,i in surface:contents() do
          if i.go.name == item then return true end
       end
    end

    function forgeItems()
if surfaceContains(skyforge01.surface, "rock" and "forge_hammer") then
--
   if findEntity("skyforge01") ~= nil then
       for i, ent in skyforge01.surface:contents() do
          ent.go:destroy()
       end
   end
--			skyforge_timer1.timer:start()
			playSound("fireburst")
			spawner_56.spawner:activate()
--			skyforge_timer1.timer:stop()
			skyforge01.surface:addItem(spawn("gold_key").item)
       else
--
       end
    end
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: forging item on altar

Post by minmay »

You misunderstand the meaning of the "and" keyword in Lua. It evaluates to the expression on the left-hand side if the left-hand side is nil or false, and otherwise evaluates to the expression on the right-hand side. See http://www.lua.org/manual/5.1/manual.html. Therefore, the expression "rock" and "forge_hammer" will always evaluate to "forge_hammer". Your line is exactly equivalent to:

Code: Select all

if surfaceContains(skyforge01.surface, "forge_hammer") then
In addition, your function will destroy items that have nothing to do with the forging, which I assume is not desired. Here is a script that should do what you want.

Code: Select all

function forgeItems()
  local rock = nil, hammer = nil
  for _,i in skyforge01.surface:contents() do
    local n = i.go.name
    if n == "rock" then
      rock = i
      if hammer then break end -- have both items
    elseif n == "forge_hammer" then
      hammer = i
      if rock then break end -- have both items
    end
  end
  if hammer and rock then
    rock.go:destroy()
    hammer.go:destroy()
    playSound("fireburst")
    spawner_56.spawner:activate()
    skyforge01.surface:addItem(spawn("gold_key").item)
  end
end
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
bongobeat
Posts: 1076
Joined: Thu May 16, 2013 5:58 pm
Location: France

Re: forging item on altar

Post by bongobeat »

thanks for help :)

yeah, I'm getting confused with scripts. I can do some DIY with other scripts(<--- that's the name that google translator gave me for the word "bricolage"), but it becomes rapidetely out of my knowledge! :oops:

hmmm, what should be modified if:
1°I want 2 rocks (or more) and one hammer (<-- actually trying)

2° I want 3 different items? (this is, hummm... :oops: )

I got this script from my log1 mod: was easy to modify (of course it is not of mine), but it don't work anymore in log2
SpoilerShow

Code: Select all

function remove_alcoveItems(caller)

   local counter_1 = 0   
   local counter_2 = 0
   local counter_3 = 0
   local counter_4 = 0

   for i in afor14:containedItems() do
      if i.name == "raw_adamantite_big" then
            counter_1 = counter_1 + 1
      elseif i.name == "crafting_hammer" then
           counter_2 = counter_2 + 1
      elseif i.name == "grim_powergem" then
           counter_3 = counter_3 + 1
      elseif i.name == "adamantite_enriched" then
           counter_4 = counter_4 + 1
      else
           -- invalid object found
      end
   end

   if counter_1 == 1 and counter_2 == 1 and counter_3 == 1 and counter_4 == 3 then
if findEntity("afor14") ~= nil then
for i in afor14:containedItems() do
i:destroy()
end
afor14:destroy()
spawn("deep_temple_altar", 20, 2, 18, 1, "afor14")
playSound("Volcano")
spawn("fireburst", 20, 2, 18, 1)
spawn("shockburst", 20, 2, 18, 1)
afor14:addItem(spawn("dragonbane"))
playSound("discover_spell")
hudPrint("You have forged a dragonbane!")
hudPrint("But the forge seems to be damaged now!")
   end
end
       
       caller:destroy()
       
    end

    function alcoveHelper()

       local alcove_clean_up = "alcove_"..getStatistic("play_time")
       spawn("timer", party.level, 1,1,1, alcove_clean_up )
          :setTimerInterval(0.01)
          :addConnector("activate", "alcove_script_3" , "remove_alcoveItems" ) 
          :activate()

    end
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: forging item on altar

Post by minmay »

Ah, you just reminded me that rocks are stackable, so actually that original script will have an undesirable result (destroying a whole stack of rocks when it should only destroy one). Here is a general solution with examples that show how to use any combination of items, and is hopefully simple enough for you to use. You would connect the surface to the forgeSurface() function:

Code: Select all

-- Check a surface for a specific combination of items.
-- surface: the surface to search.
-- itemNames: an array of strings that should correspond to the names
--   of the items you want. Duplicates are not allowed.
-- If all the items were found, an array of arrays of item
-- references is returned. The arrays of item references are
-- in the same order as the strings in itemNames, so if
-- itemNames[1] is "rock", the first array will be an array of
-- references to all rocks on the surface.
-- If not all of the items were found, false is returned.
function surfaceContains(surface,itemNames)
	local found = {}
	for _,item in surface:contents() do
		for index,itemName in ipairs(itemNames) do
			if item.go.name == itemName then
				if found[index] then
					table.insert(found[index],item)
				else
					found[index] = {item}
				end
				break
			end
		end
	end
	for i = 1,#itemNames do
		if not found[i] then return false end
	end
	return found
end

-- Check a surface for eligible item combinations, and turn them
-- into a new item if any are found.
function forgeSurface(surface)

	-- Example: make a boulder from 3 rocks.
	local foundItems = surfaceContains(surface,{"rock"})
	if foundItems then
		-- We found at least one "rock" item on the surface, but
		-- we still need to count the rocks and see how many we
		-- have.
		if countItems(foundItems[1]) >= 3 then
			-- We have 3 rocks.
		
			-- Destroy exactly 3 rocks.
			destroySomeOf(foundItems[1],3)
			
			-- Add the boulder.
			surface:addItem(spawn("boulder").item)
			
			-- We don't need to do anything else.
			-- (Here is where you might add a sound,
			-- special effect, etc.)
			return
		end
	end
	
	-- Example 2: make a spiked club from 2 rocks, 3 branches,
	-- and a warhammer.
	-- Note: since Example 1 is checked first, and uses 3 rocks,
	-- if there are 3 rocks on the altar then a boulder will
	-- always be created from them, even if there are enough
	-- items to make a spiked club instead. To prevent this, you
	-- can easily just switch the order.
	local foundItems = surfaceContains(surface,{"rock","branch","warhammer"})
	if foundItems then
		-- We found at least one "rock" item on the surface, but
		-- we still need to count the rocks and see how many we
		-- have.
		local rocks = countItems(foundItems[1]) >= 2

		-- Branches are NOT stackable items. Therefore, the number
		-- of elements in the array will always be the number of
		-- branches; we do not need countItems().
		local branches = #foundItems[2] >= 3
		
		-- We only wanted 1 warhammer, so we don't need any extra
		-- checks: surfaceContains would have returned false in
		-- the first place if there were no warhammers at all.
		if rocks and branches then
				-- Destroy exactly 2 rocks.
				destroySomeOf(foundItems[1],2)
				
				-- Destroy exactly 3 branches.
				destroySomeOf(foundItems[2],3)
				
				-- Destroy exactly 1 warhammer.
				destroySomeOf(foundItems[3],1)
				
				-- Add the club.
				surface:addItem(spawn("spiked_club").item)
				
				-- We don't need to do anything else.
				-- (Here is where you might add a sound,
				-- special effect, etc.)
				return
		end
	end
end

-- Count the items in an array.
-- items: array of items to be counted
-- Returns the total number of items in the array, accounting for
-- stack size, so an array of one rock object with a stack size
-- of 3 would return 3.
function countItems(items)
	local total = 0
	for _,item in ipairs(items) do
		total = total+item:getStackSize()
	end
	return total
end

-- Destroy some of the items in an array. Items appearing
-- later in the array will be destroyed first.
-- items: array of items to be destroyed
-- quantity: number of items to destroy
-- Returns true if the requested number of items was destroyed,
-- false if the array did not contain enough items to satisfy the
-- quantity (all the items in the array were still destroyed).
function destroySomeOf(items,quantity)
	local destroyed = 0
	for i = #items,1,-1 do
		local diff = quantity-destroyed
		local item = items[i]
		if diff < item:getStackSize() then
			item:setStackSize(item:getStackSize()-diff)
			return true
		else
			destroyed = destroyed+item:getStackSize()
			item.go:destroy()
		end
		if quantity == destroyed then return true end
	end
	return false
end
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
bongobeat
Posts: 1076
Joined: Thu May 16, 2013 5:58 pm
Location: France

Re: forging item on altar

Post by bongobeat »

thanks a lot!
this is an awesome script! :shock:
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
Post Reply