How to make pit puzzle, such as the chamber of pits?

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

so I'm testing antti's version, just for kicks, and got a very weird crash

my script so far:

Code: Select all

time = 1
function pitSequence()
  local sequence = { pitpuz7, pitpuz4, pitpuz1, pitpuz2, pitpuz3, pitpuz6, pitpuz5, pitpuz8, pitpuz9 }   --this doesn't contain ALL the pits, only the exact sequence or the path of closing and opening pits
  sequence[time]:close()    --close a pit in the sequence
--  sequence[time-3]:open()   --and then open a pit again a few steps behind
  time = time + 1
end
I just wanted to see it close them in my order - I have 9 pits:
1 2 3
4 5 6
7 8 9 z
x
so I have them closing 7 4 1, 2 3, 6 5, 8 9, so I can move from x to z
Anyway, without any method yet to shut down the timer, soon after 9 closed, I got a crash, and my mouse was stuck to the middle of the screen, even if I alt=tabbed to other windows open! I had to use keyboard shortcuts to close the editor, then my mouse was free again!

But I feel a little sense of déjà vu - is this already a known bug, that when using mouselook and you get a crash then the mouse is stuck near the screen center? I didn't see it on the "known issues" list for 1.2.11.

The error is:
sequence[time]:close() --close a pit in the sequence - "attempt to index a nil value" and it's right when the next timer tick happens after closing the last pit
Finished Dungeons - complete mods to play
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

I'm almost there!!! but I have to go to bed, and I'll be busy most of the day tomorrow. Here's what I've got so far, working partially:

Code: Select all

time = 1
function pitSequence()
  local sequence = { pitpuz7, pitpuz4, pitpuz1, pitpuz2, pitpuz3, pitpuz6, pitpuz5, pitpuz8, pitpuz9 }
  if time < 10 then
    sequence[time]:close()    --close a pit in the sequence
  end
  if time > 2 then
    sequence[time-2]:open()   --and then open a pit again a few steps behind
  end
  if levercount == 2 then
    time = time + 1
  else
    time = time - 1
  end
  if time == 12 then
    time = 1
    pitpuztimer:deactivate()
  end
end

levercount = 2
function leverPull()
	if pitpuzlever1:getLeverState() == activated and
	   pitpuzlever2:getLeverState() == activated then
	   levercount = 2
	   time = 1
	elseif pitpuzlever1:getLeverState() == deactivated and
	   pitpuzlever2:getLeverState() == deactivated then
	   levercount = 2
	   time = 1
	else
	   levercount = 1
	   time = 11
	end
end
The two levers are located on either end of the pit puzzle (like in the original game), along with a button on each end, and the levers are linked to the script with the leverpull function (the buttons are linked to a 1sec timer linked to the script with the pitSequence function).

I haven't yet set up the levers to cancel and open all the pits if they're in the middle of the sequence.

The forward sequence works well. The reverse sequence works partially, but I only barely rigged it so I haven't set up proper pit closing or stopping it at the right time, so it will crash with an attempt to index a nil value.
Finished Dungeons - complete mods to play
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

Okay, this works perfectly so far as I have tested, including pulling the lever halfway through to reset everything:

Code: Select all

time = 1
function pitSequence()
  local sequence = { pitpuz7, pitpuz4, pitpuz1, pitpuz2, pitpuz3, pitpuz6, pitpuz5, pitpuz8, pitpuz9 } -- doesn't have to include all pits, just the path you want
  if levercount == 2 then
    if time < 10 then
      sequence[time]:close() --close a pit in the sequence
    end
    if time > 2 then
      sequence[time-2]:open() --and then open a pit again two steps behind
    end
    time = time + 1
    if time == 12 then
      time = 1
      pitpuztimer:deactivate()
    end
  elseif levercount == 1 then
    if time > 2 then
      sequence[time-2]:close() --close a pit in the sequence
    end
    if time < 10 then
      sequence[time]:open() --and then open a pit again two steps behind
    end
    time = time - 1
    if time == 0 then
      time = 11
      pitpuztimer:deactivate()
    end
  end
end

levercount = 2
function leverPull()
  if pitpuzlever1:getLeverState() == "activated" and
    pitpuzlever2:getLeverState() == "activated" then
      levercount = 2
      time = 1
  elseif pitpuzlever1:getLeverState() == "deactivated" and
    pitpuzlever2:getLeverState() == "deactivated" then
      levercount = 2
      time = 1
  else
    levercount = 1
    time = 11
  end
  for i=1,9 do
    local pit = findEntity("pitpuz"..i)
    if pit then
      pit:open()
    end
  end
  pitpuztimer:deactivate()
end
- link the two buttons to the timer, which is linked to the script with pitSequence
- link the two levers to the script directly with leverPull
Finished Dungeons - complete mods to play
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

another version, with much easier customization (with notes where to do it) for you:

Code: Select all

time = 1
steps = 9 -- CUSTOMIZE: HOW MANY STEPS YOU HAVE
thetimer = pitpuztimer -- CUSTOMIZE: ID OF THE TIMER

function pitSequence()
  local sequence = { pitpuz7, pitpuz4, pitpuz1, pitpuz2, pitpuz3, pitpuz6, pitpuz5, pitpuz8, pitpuz9 } -- CUSTOMIZE: IDS OF THE PITS YOU WANT TO CLOSE, IN ORDER
  if levercount == 2 then
    if time < steps + 1 then
      sequence[time]:close() --close a pit in the sequence
    end
    if time > 2 then
      sequence[time-2]:open() --and then open a pit again two steps behind
    end
    time = time + 1
    if time == steps + 3 then
      time = 1
      thetimer:deactivate()
    end
  elseif levercount == 1 then
    if time > 2 then
      sequence[time-2]:close() --close a pit in the sequence
    end
    if time < steps + 1 then
      sequence[time]:open() --and then open a pit again two steps behind
    end
    time = time - 1
    if time == 0 then
      time = steps + 2
      thetimer:deactivate()
    end
  end
end

levercount = 2
function leverPull()
  if pitpuzlever1:getLeverState() == "activated" and -- CUSTOMIZE: ID OF LEVER1
    pitpuzlever2:getLeverState() == "activated" then -- CUSTOMIZE: ID OF LEVER2
      levercount = 2
      time = 1
  elseif pitpuzlever1:getLeverState() == "deactivated" and -- CUSTOMIZE: ID OF LEVER1
    pitpuzlever2:getLeverState() == "deactivated" then -- CUSTOMIZE: ID OF LEVER2
      levercount = 2
      time = 1
  else
    levercount = 1
    time = steps + 2
  end
  for i=1,steps do
    local pit = findEntity("pitpuz"..i) -- CUSTOMIZE: PARTIAL ID OF PITS
    if pit then
      pit:open()
    end
  end
  thetimer:deactivate()
end
Finished Dungeons - complete mods to play
User avatar
antti
Posts: 688
Joined: Thu Feb 23, 2012 1:43 pm
Location: Espoo, Finland
Contact:

Re: How to make pit puzzle, such as the chamber of pits?

Post by antti »

Komag wrote:another version, with much easier customization (with notes where to do it) for you:

Code: Select all

time = 1
steps = 9 -- CUSTOMIZE: HOW MANY STEPS YOU HAVE
thetimer = pitpuztimer -- CUSTOMIZE: ID OF THE TIMER

function pitSequence()
  local sequence = { pitpuz7, pitpuz4, pitpuz1, pitpuz2, pitpuz3, pitpuz6, pitpuz5, pitpuz8, pitpuz9 } -- CUSTOMIZE: IDS OF THE PITS YOU WANT TO CLOSE, IN ORDER
  if levercount == 2 then
    if time < steps + 1 then
      sequence[time]:close() --close a pit in the sequence
    end
    if time > 2 then
      sequence[time-2]:open() --and then open a pit again two steps behind
    end
    time = time + 1
    if time == steps + 3 then
      time = 1
      thetimer:deactivate()
    end
  elseif levercount == 1 then
    if time > 2 then
      sequence[time-2]:close() --close a pit in the sequence
    end
    if time < steps + 1 then
      sequence[time]:open() --and then open a pit again two steps behind
    end
    time = time - 1
    if time == 0 then
      time = steps + 2
      thetimer:deactivate()
    end
  end
end

levercount = 2
function leverPull()
  if pitpuzlever1:getLeverState() == "activated" and -- CUSTOMIZE: ID OF LEVER1
    pitpuzlever2:getLeverState() == "activated" then -- CUSTOMIZE: ID OF LEVER2
      levercount = 2
      time = 1
  elseif pitpuzlever1:getLeverState() == "deactivated" and -- CUSTOMIZE: ID OF LEVER1
    pitpuzlever2:getLeverState() == "deactivated" then -- CUSTOMIZE: ID OF LEVER2
      levercount = 2
      time = 1
  else
    levercount = 1
    time = steps + 2
  end
  for i=1,steps do
    local pit = findEntity("pitpuz"..i) -- CUSTOMIZE: PARTIAL ID OF PITS
    if pit then
      pit:open()
    end
  end
  thetimer:deactivate()
end
I don't think you need the steps variable if it is there to just indicate how many pits you have in the sequence table. You could just replace them with #sequence, which gets the length of the table. It would simplify the customization a little bit as well.
Steven Seagal of gaming industry
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

Ah, thanks, I was wondering if there was a way to get that, didn't know about the #. I'm at the courthouse on jury duty right now, but I'll update this tonight
Finished Dungeons - complete mods to play
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

antti wrote:I don't think you need the steps variable if it is there to just indicate how many pits you have in the sequence table. You could just replace them with #sequence, which gets the length of the table. It would simplify the customization a little bit as well.
hmm, tried it, but it's used in the separate leverPull function, so it crashes with "attempt to get length of global 'sequence' (a nil value)

it works correctly if I just pull out the sequence and put it at the top to make it global, but is there any reason to avoid doing that?
Finished Dungeons - complete mods to play
User avatar
Montis
Posts: 340
Joined: Sun Apr 15, 2012 1:25 am
Location: Grimrock II 2nd playthrough (hard/oldschool)

Re: How to make pit puzzle, such as the chamber of pits?

Post by Montis »

I would say no, since no variables are really global.
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: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

here is the altered version with streamlined CUSTOMIZATION all at the top:

Code: Select all

thetimer = pitpuztimer -- CUSTOMIZE: ID OF THE TIMER
thelever1 = pitpuzlever1 -- CUSTOMIZE: ID OF THE FIRST LEVER
thelever2 = pitpuzlever2 -- CUSTOMIZE: ID OF THE SECOND LEVER
namepart = "pitpuz" -- CUSTOMIZE: PARTIAL ID OF PITS
sequence = { pitpuz7, pitpuz4, pitpuz1, pitpuz2, pitpuz3, pitpuz6, pitpuz5, pitpuz8, pitpuz9 } -- CUSTOMIZE: IDS OF THE PITS YOU WANT TO CLOSE, IN ORDER

time = 1
function pitSequence()
  if levercount == 2 then
    if time < #sequence + 1 then
      sequence[time]:deactivate() --close a pit in the sequence
    end
    if time > 2 then
      sequence[time-2]:activate() --and then open a pit again two steps behind
    end
    time = time + 1
    if time == #sequence + 3 then
      time = 1
      thetimer:deactivate()
    end
  elseif levercount == 1 then
    if time > 2 then
      sequence[time-2]:deactivate() --close a pit in the sequence
    end
    if time < #sequence + 1 then
      sequence[time]:activate() --and then open a pit again two steps behind
    end
    time = time - 1
    if time == 0 then
      time = #sequence + 2
      thetimer:deactivate()
    end
  end
end

levercount = 2
function leverPull()
  if thelever1:getLeverState() == "activated" and
    thelever2:getLeverState() == "activated" then
      levercount = 2
      time = 1
  elseif thelever1:getLeverState() == "deactivated" and
    thelever2:getLeverState() == "deactivated" then
      levercount = 2
      time = 1
  else
    levercount = 1
    time = #sequence + 2
  end
  for i=1,#sequence do
    local pit = findEntity(namepart..i)
    if pit then
      pit:activate()
    end
  end
  thetimer:deactivate()
end
tested and working well, nice and clean to implement :)
Finished Dungeons - complete mods to play
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: How to make pit puzzle, such as the chamber of pits?

Post by Komag »

the dungeon crashed when trying to load into the real game as a custom dungeon:
pitpuzscript: cannot serialize table 'thetimer' with metatable
stack traceback:
[C]: in function 'error'
[string "ScriptEntity.lua"]: in function 'saveValue'
[string "ScriptEntity.lua"]: in function 'saveState'
[string "GameMode.lua"]: in function 'saveGame'
[string "GameMode.lua"]: in function 'autoSave'
[string "GameMode.lua"]: in function 'newGame'
[string "GameMode.lua"]: in function 'startGame'
[string "NewGameMenu.lua"]: in function 'startGame'
[string "NewGameMenu.lua"]: in function 'update'
[string "GameMode.lua"]: in function 'update'
[string "Grimrock.lua"]: in function 'display'
[string "Grimrock.lua"]: in main chunk
Finished Dungeons - complete mods to play
Post Reply