Page 1 of 1

[Script] Generic Lever/Button Sequence Puzzle

Posted: Fri Dec 07, 2012 2:18 am
by Grimwold
Another puzzle scripting framework to drop into your dungeon if you want this kind of puzzle. This one was requested by LordYig with a view to creating a combination lock.

The idea is that there are a series of levers (or buttons) that must be activated in a strict sequence to open the door. For buttons you simply have to push them in the right order (and there can be duplicates). For levers you have to flip them in the right order and there can be duplicates, provided you note the correct activate/deactivate status.

As with my other generic puzzle scripts, this can be used multiple times in the same dungeon, provided you change the counter variable to a unique name. You could even re-use the same levers/buttons and have multiple combinations, however if you do so I would recommend you disable the sound played when an incorrect entry is made, as this could be confusing for a multi-solution setup.

You will need to place a door and a number of levers (or buttons) in your dungeon. Then add a script entity and paste in the following code.

Code: Select all

counter_name = "lever_combo_counter_1"
activators = "lever"  -- button OR lever
door_name = "lever_combo_door_1"
failure_sound = "scramble_writer"

function checkLever(trigger)
  local lever_check = {"lever_6","lever_8","lever_7","lever_9","lever_9"}
  local lever_state = {"activated","activated","activated","activated","deactivated"}
  if initialisation == 1 then
    local unique_lever_check = removeDuplicates(lever_check)
    for i=1,#unique_lever_check do
      local lever_entity = findEntity(unique_lever_check[i])
      if lever_entity == nil then
        print(unique_lever_check[i] .. " is missing")
        break
      else
        if activators == "lever" then
          lever_entity:addConnector("any",self.id,"checkLever")
        elseif activators == "button" then
          lever_entity:addConnector("toggle",self.id,"checkLever")
        end
      end
    end
  end
  local range = 0
  if activators == "lever" then
    range = math.min(#lever_check,#lever_state)
  elseif activators == "button" then
    range = #lever_check
  end
  local counter_entity = findEntity(counter_name)
  local door_entity = findEntity(door_name)
  if counter_entity == nil then
    spawn("counter",self.level,self.x,self.y,self.facing,counter_name)
      :setInitialValue(range)
      :setValue(range)
      :addConnector("activate",door_entity.id,"open")
      :addConnector("deactivate",door_entity.id,"close")
  end
  local counter_entity = findEntity(counter_name)
  local lever_index = range + 1 - counter_entity:getValue()
  if trigger ~= nil then 
    if activators == "lever" then 
      if trigger.id == lever_check[lever_index] and trigger:getLeverState() == lever_state[lever_index] then
        counter_entity:decrement()
        return true
      else
        counter_entity:reset()
        -- print(counter_entity:getValue())
        if failure_sound ~= nil and failure_sound ~= "" then
          playSound(failure_sound)
        end
        for _,v in pairs(lever_check) do
          j = findEntity(v)
          if j:getLeverState() == "activated" then
            j:toggle()
          end
        end
        return false
      end
    elseif activators == "button" then
      if trigger.id == lever_check[lever_index] then
        counter_entity:decrement()
        return true
      else
        counter_entity:reset()
        if failure_sound ~= nil and failure_sound ~= "" then
          playSound(failure_sound)
        end
      end
    end
  end
  if initialisation == 1 then
    initialisation = 0
  end
end


function isInTable(table, element)
  for _,value in pairs(table) do
    if value == element then
      return true
    end
  end
  return false
end

function removeDuplicates(t)
  local t2 = {}
  local idx = 1
  for k,v in pairs(t) do
    if not isInTable(t2,v) then
      t2[idx] = v
      idx = idx+1
    end
  end
  return t2
end

initialisation = 1

checkLever()
Finally you will need to customise the following entries in the first few lines of the script:

counter_name = "lever_combo_counter_1"
replace the text lever_combo_counter_1 with a unique name for a counter. You do not need to place the counter in the dungeon, but you need to make sure that this ID is not used for anything else in your dungeon (and does not duplicate the ID of a counter in a subsequent copy of this script)

door_name = "lever_combo_door_1"
replace the text lever_combo_door_1 with the ID of the door you want to open with this puzzle. You must place this door somewhere in your dungeon.

activators = "lever"
put either lever OR button for this value, depending on whether you wish to use levers or buttons for this copy of the puzzle.

failure_sound = "scramble_writer"
The sound to play when a button/lever is activated out of sequence. A little 'warning' to the player that the puzzle has reset. Replace the text scramble_writer with the name of the sound you want to use (either an existing sound from the game, or a new one defined in sounds.lua). If you want to completely disable sound, simply leave the quotes empty, like "".

local lever_check = {"lever_6","lever_8","lever_7","lever_9","lever_9"}
replace the entries in this list with the IDs of the levers (OR buttons) that you placed in your dungeon. You can add as many entries here as you want and may duplicate them.

local lever_state = {"activated","activated","activated","activated","deactivated"}
If you are using buttons then this table is ignored, you can leave it as is or set it to an empty table e.g. local lever_state = {}
However, if you are using levers, you must replace the entries in this list with the states you require for the levers. They should be in the same order as the entries in levers above. So here, "lever_6" needs to be "activated" first (moved to the down position). When using duplicate levers, be careful of states as a lever must alternate between it's activated and deactivated state.

Re: [Script] Generic Lever/Button Sequence Puzzle

Posted: Fri Dec 07, 2012 2:36 am
by LordYig
This is great ! :)
Thanks Grimwold ! Image

I'll give it a try this week-end when I get the time to !

Re: [Script] Generic Lever/Button Sequence Puzzle

Posted: Thu Dec 20, 2012 4:57 pm
by Grimwold
A minor update to this script... you can now specify a sound to play when a button/lever is toggled out of sequence (warning the player that the puzzle has reset). You can also disable sound by leaving the failure_sound variable as an empty string.

Re: [Script] Generic Lever/Button Sequence Puzzle

Posted: Wed Aug 07, 2013 3:38 pm
by Tarerin
Hello there. I've read your post about the sequence puzzle with buttons and such, and I would like to ask you if these scripts will work for pressure plates as well. I was thinking about making a musical puzzle, where the player would have to play a certain melody to be able to get past. I am not exactly a genius when it comes to lua scripts, I know only basic things, but the variety of puzzles in my skill competence is not so wide as to make a whole variety of puzzles in my dungeon. I would appreciate your help. :)

Re: [Script] Generic Lever/Button Sequence Puzzle

Posted: Wed Aug 07, 2013 11:44 pm
by msyblade
You'll be happy to learn that Grimwold wrote quite a few of these puzzle type frameworks, including a pressure plate variant.
They are all fully working and customizable to however many buttons/levers/plates you want to use. And bonus! You can find them all gathered neatly here :

viewtopic.php?f=14&t=4564

Re: [Script] Generic Lever/Button Sequence Puzzle

Posted: Thu Aug 08, 2013 9:43 am
by Tarerin
Thanks a lot, this will be helpful a lot. :)