Scripting Problem: Sequence Lock (solved)

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
Green Cube
Posts: 6
Joined: Mon Sep 17, 2012 10:25 pm

Scripting Problem: Sequence Lock (solved)

Post by Green Cube »

Hello,

let me start off by saying this is just my second day ever scripting something, so bear with me. :) I'm trying to open a door using levers being pulled in a specific order. Now I managed to pull it off this way.

Code: Select all

-- Sequence Lock Puzzle

function leverCheck()

	if	p_sequencelock_lever_1:getLeverState() == "activated" then
	
		if	p_sequencelock_lever_2:getLeverState() == "activated" then
		
			if	p_sequencelock_lever_3:getLeverState() == "activated" then
			
				if	p_sequencelock_lever_4:getLeverState() == "activated" then
				
					p_sequencelock_door:open()
					
				end
				
			elseif	p_sequencelock_lever_4:getLeverState() == "activated" then
			
				p_sequencelock_lever_1:toggle()
				p_sequencelock_lever_2:toggle()
				p_sequencelock_lever_4:toggle()
				
			end
			
		elseif	p_sequencelock_lever_3:getLeverState() == "activated" then
		
			p_sequencelock_lever_1:toggle()
			p_sequencelock_lever_3:toggle()
		
		elseif	p_sequencelock_lever_4:getLeverState() == "activated" then
		
			p_sequencelock_lever_1:toggle()
			p_sequencelock_lever_4:toggle()
			
		end
	
	elseif	p_sequencelock_lever_2:getLeverState() == "activated" then
	
		p_sequencelock_lever_2:toggle()
		
	elseif	p_sequencelock_lever_3:getLeverState() == "activated" then
	
		p_sequencelock_lever_3:toggle()
	
	elseif	p_sequencelock_lever_4:getLeverState() == "activated" then
	
		p_sequencelock_lever_4:toggle()		

	end
	
	if		p_sequencelock_lever_1:getLeverState() == "deactivated" or
			p_sequencelock_lever_2:getLeverState() == "deactivated" or
			p_sequencelock_lever_3:getLeverState() == "deactivated" or
			p_sequencelock_lever_4:getLeverState() == "deactivated" then
			
		p_sequencelock_door:close()
		
	end
	
end
The only reason this works, is because I am not allowing wrong levers to stay activated. This makes the puzzle incredibly easy however. How can I get this to work without stopping the user from pulling the wrong levers?
Last edited by Green Cube on Thu Sep 20, 2012 1:33 am, edited 1 time in total.
User avatar
Montis
Posts: 340
Joined: Sun Apr 15, 2012 1:25 am
Location: Grimrock II 2nd playthrough (hard/oldschool)

Re: Scripting Problem: Sequence Lock

Post by Montis »

I'm very sure you can make this with way less code.
Can you explain how you exactly you want the levers to behave?
When destiny calls, the chosen have no choice.

My completed dungeon (LoG1): Hypercube
User avatar
Green Cube
Posts: 6
Joined: Mon Sep 17, 2012 10:25 pm

Re: Scripting Problem: Sequence Lock

Post by Green Cube »

I want the levers to be activated and deactivated like any other lever. But if I activate them in a specific order, in my code it's just 1,2,3,4, the door is supposed to open. If the levers are being pulled in any other order, then nothing is supposed to happen.

For now in my code it works like this:
Lever 1 -> lever 2 -> lever 4 and all levers become deactivated again. This happens with all possible orders. The only one that works is lever 1 -> lever 2 -> lever 3 -> lever 4. If I just remove the parts where the levers are being deactivated again, the script doesn't care in which order the levers were being pulled. So 3,4,2,1 for example works too.
User avatar
Shroom
Posts: 98
Joined: Tue Mar 27, 2012 6:37 pm
Location: UK

Re: Scripting Problem: Sequence Lock

Post by Shroom »

Here is an example code I have created

You need to create a counter called counterPuzzle - set the initial value to 4 and add a connector to the door you want to open

Create a script_entity and add the following code

Code: Select all

function pullFirst()
  if counterPuzzle:getValue() == 4 then
    counterPuzzle:decrement()
  else
    resetAll()
  end
end

function pullSecond()
  if counterPuzzle:getValue() == 3 then
    counterPuzzle:decrement()
  else
    resetAll()
  end
end

function pullThird()
  if counterPuzzle:getValue() == 2 then
    counterPuzzle:decrement()
  else
    resetAll()
  end
end

function pullFourth()
  if counterPuzzle:getValue() == 1 then
    counterPuzzle:decrement()
  else
    resetAll()
  end
end

function resetAll()
  counterPuzzle:reset()
  lever_1:setLeverState("deactivated")
  lever_2:setLeverState("deactivated")
  lever_3:setLeverState("deactivated")
  lever_4:setLeverState("deactivated")
end
link lever_1 to pullFirst, lever_2 to pullSecond etc

and job done - this can be as many or few levers as you like - just change the intial value of the counter to match the number of levers, and add a code slice for each new lever, counting down as you go :)
Last edited by Shroom on Tue Sep 18, 2012 1:02 am, edited 2 times in total.
Lilltiger
Posts: 95
Joined: Sun Sep 16, 2012 1:12 am

Re: Scripting Problem: Sequence Lock

Post by Lilltiger »

Edit: The code is now fixed, and works with the new patch as well (no upvalues)

Main features:
  • Easy to add new levers, just add it to the order.
  • Easy to change the order of activation.
  • It resets all the levers to deactivated once you have tried as many times as the sequence is long
Edit: Shroom's way is also fine, but more hassle to adjust to different situations and to change the order then my version, while his is easier to understand.

Code: Select all

-- Create a "static" variable to track the current step, start at 1 as lua tables starts at index 1
static_step = 1
static_failed = false

-- Create a timer to handle the deactivation
timer = spawn("timer", 1, 6,20,2, "timer"..self.id)
timer:setTimerInterval(0.5)
timer:addConnector("activate", self.id, "reset")
	
order = { lever_2, lever_3, lever_4, lever_5 }

function checkLevers()
   -- Check if this step failed
   if( order[static_step]:getLeverState() == "deactivated" ) then
      -- Set progress to failed
      static_failed = true
   end

   -- Wait until a full sequence have gone, then report if it was the wrong one or not
   if( static_step == #order ) then
      -- If the lever check failed, reset it
      if( static_failed == true ) then
         timer:activate()
         -- Reset the steps, we set it to 0 because we increase it at the end of this function
         static_step = 0
      else
          -- All checks passes successful, open the door
         door:open()
      end
   end

   -- Step ahead
   static_step = static_step + 1
end

function reset()
	for id, lever in ipairs(order) do
		lever:setLeverState("deactivated")
	end
	static_failed = false
	timer:deactivate()
end
Last edited by Lilltiger on Tue Sep 18, 2012 5:34 pm, edited 3 times in total.
User avatar
Montis
Posts: 340
Joined: Sun Apr 15, 2012 1:25 am
Location: Grimrock II 2nd playthrough (hard/oldschool)

Re: Scripting Problem: Sequence Lock

Post by Montis »

I have the smallest code of em all. Or well, maybe not anymore. :P

Create a counter next to the lua script entity and set it to the number of inputs you want the player to make. The levers need to be named in the same pattern e.g. sequenceLock_1 to sequenceLock_4.
Lets name the counter you just placed myCustomSequenceCounter. On activation of myCustomSequenceCounter, tell the door to open. Also add an additional connector to itself to tell the counter to reset when activated.

Then connect each lever to the following lua script entity action on the "any" event:
SpoilerShow

Code: Select all

function sequenceRiddle()
   -- change the following to what entity type you want to use for your riddle
   local triggerEntity = "lever"
   -- all entity IDs of your activation entities should start with the below, followed by a number
   local namingPattern = "sequenceLock_"
   -- define the sequence in which you want your entities to be activated
   local sequenceOfThings = {"1","2","4","2","3"}
   -- define the ID of your sequenceCounter
   -- important: this sequence counter needs to have the same initial value
   -- as the number of sequences you defined above
   local sequenceCounterID = "myCustomSequenceCounter"
   -- from here on, it shouldn't be needed to alter the rest of the code

   local curSeq = #sequenceOfThings+1-findEntity(sequenceCounterID):getValue()
   for i in entitiesAt(party.level, party.x, party.y) do
      if i.name == triggerEntity then
         if i.id == namingPattern..sequenceOfThings[curSeq] then
            findEntity(sequenceCounterID):decrement()
         else
            findEntity(sequenceCounterID):reset()
         end
      end
   end
end
The only "downside" is, that there mustn't be multiple levers or activation entities in one square (like one on each side of the square) or else the code will not work.
Last edited by Montis on Tue Oct 23, 2012 10:58 am, edited 5 times in total.
When destiny calls, the chosen have no choice.

My completed dungeon (LoG1): Hypercube
Lilltiger
Posts: 95
Joined: Sun Sep 16, 2012 1:12 am

Re: Scripting Problem: Sequence Lock

Post by Lilltiger »

Montis: having the smallest is not always a good thing ;)
User avatar
Montis
Posts: 340
Joined: Sun Apr 15, 2012 1:25 am
Location: Grimrock II 2nd playthrough (hard/oldschool)

Re: Scripting Problem: Sequence Lock

Post by Montis »

Only that it "fits" almost everywhere :roll:

Ok I'm gonna stop.
When destiny calls, the chosen have no choice.

My completed dungeon (LoG1): Hypercube
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Scripting Problem: Sequence Lock

Post by Komag »

Nice to see all these alternatives, it really helps to learn how some moderately advanced scripting works. And these would make great additions to the Useful Scripts Repository!
viewtopic.php?f=14&t=3099&start=30
Finished Dungeons - complete mods to play
User avatar
Shroom
Posts: 98
Joined: Tue Mar 27, 2012 6:37 pm
Location: UK

Re: Scripting Problem: Sequence Lock

Post by Shroom »

Lilltiger wrote:Here is some code that might help, it's untested, but it should work, although might be some typos.

Main features:
  • Easy to add new levers, just add it to the order.
  • Easy to change the order of activation.
  • It resets all the levers to deactivated once you have tried as many times as the sequence is long
Main Disadvantages:
  • The code is untested :D
Edit: Shroom's way is also fine, but more hassle to adjust to different situations and to change the order then my version, while his is easier to understand.
My code is very basic - I am learning lua and it does show how with no specific language knowledge you can do a lot - but using arrays/tables as shown is much more powerful - I took a minute to have a look (and suggest you do if your into it http://www.lua.org/pil/) and this is really a very simple language if you already use java or c/c#. I think the main thing I took from this is the use of tables - very powerful (http://www.lua.org/pil/2.5.html)

Montis's code is also very good - it shows how objects can be manipulated by concatenation of strings

Code: Select all

         if i.id == "p_sequencelock_lever_"..sequenceCounter:getValue() then
Both have a lot of potential for very complicated traps and I thank you both for sharing - you have helped me a lot!
Post Reply