Opening LOTS of doors

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
JohnWordsworth
Posts: 1397
Joined: Fri Sep 14, 2012 4:19 pm
Location: Devon, United Kingdom
Contact:

Re: Opening LOTS of doors

Post by JohnWordsworth » Fri Feb 14, 2014 10:23 pm

Ahh no, sorry - the above functions are simply definitions that you can then later use.

So, you might have a script_entity called "utils" with the above functions defined...

Code: Select all

-- Spawn a grid of the same type of entity.
-- @param entityName The entity to spawn.
-- @param level The level on the dungeon to spawn the entities on
-- @param minX The first X coordinate to spawn in
-- @param maxX The last X coordinate to spawn in
-- @param minY The first Y coordinate to spawn in
-- @param maxY The last Y coordinate to spawn in
-- @param facing All entities will be spawned with this facing
--
function spawn_grid(entityName, level, minX, minY, maxX, maxY, facing)
  for x=minX, maxX do
    for y=minY, maxY do
      spawn(entityName, level, x, y, facing);
    end
  end
end

-- Open all doors in the region between (minX, minY) and (maxX, maxY) on the given floor.
-- 
function open_all_doors(level, minX, minY, maxX, maxY)
  for x = minX, maxX do
    for y = minY, maxY do
      for e in entitiesAt(level, x, y) do
        if ( e.class == "Door" ) then
          if ( e:isClosed() ) then
            e:open();
          end
        end
      end
    end
  end
end
As long as you have this in your dungeon, you can then use those functions anywhere else to quickly and easily do what you need. So, to poison an entire room - you would do the following;

Code: Select all

utils.spawn_grid("poison_cloud", 1, 15, 15, 17, 17, 1);
That's it. The function 'spawn_grid' will take care of doing the work for you. And to open all of the doors in a room, you could use the above like the following;

Code: Select all

utils.open_all_doors(1, 15, 15, 17, 17);
Note that 'open_all_doors' isn't overly efficient, if you wanted to do a whole floor at once, you could write a different function for that using allEntities instead of entitiesAt.

Hope this helps, let me know if you have any other questions.
My Grimrock Projects Page with links to the Grimrock Model Toolkit, GrimFBX, Atlas Toolkit, QuickBar, NoteBook and the Oriental Weapons Pack.

User avatar
Eleven Warrior
Posts: 730
Joined: Thu Apr 18, 2013 2:32 pm
Location: Australia

Re: Opening LOTS of doors

Post by Eleven Warrior » Sat Feb 15, 2014 10:30 am

The hard way ahy..

Glew
Posts: 64
Joined: Sat Dec 21, 2013 7:57 pm

Re: Opening LOTS of doors

Post by Glew » Sat Feb 15, 2014 1:31 pm

Oh, okay. Thanks. Maybe I should have made my "Neewb" sticker visible :D
So you say I just copy paste that parametric function into any script entity in my dungeon and then I can call it from anywhere else the way you showed? I guess I will give it a try and see how this works.

edit:
Aaaand it doesn't work.

Code: Select all

function spawn_grid(entityName, level, minX, minY, maxX, maxY, facing)
  for x=minX, maxX do
    for y=minY, maxY do
      spawn(entityName, level, x, y, facing);
    end
  end
end

function poisonProba() -- this goes connected to a wall button (proba means "trial" in Hungarian fyi)
utils.spawn_grid("poison_cloud", 3, 4, 6, 7, 6, 1);
end
I connected a button to poisonProba and upon pressing it says "attempt to index global 'utils' (a nil value)
What is the meaning of this?

User avatar
JohnWordsworth
Posts: 1397
Joined: Fri Sep 14, 2012 4:19 pm
Location: Devon, United Kingdom
Contact:

Re: Opening LOTS of doors

Post by JohnWordsworth » Sat Feb 15, 2014 3:24 pm

No worries - I'll explain what's going on, as this is a really useful technique for building big dungeons and not having to repeat code over and over. Then I'll explain how to fix the problem (although, you should be able to fix it yourself then!).

So, when you place a script entity into your dungeon, you can access the global values and functions in that script entity from any other script entity in the game. You do this by putting "[other_script_entity_id].[variable]" or "[other_script_entity_id].[function]()". For example, if you had a script_entity with the name "useful_functions" and you have a function in that called "hello_world" you could call that from any other script. This doesn't do anything useful, but would look like...

SCRIPT ENTITY: useful_functions

Code: Select all

function hello_world() 
  print("Hello World");
end
.

With the above in your dungeon, any other script entity can call that method by doing the following...

Code: Select all

useful_functions.hello_world();
Why this works, is because the bit before the dot (.) is actually looking through your dungeon for another object with the same name. Because you have already made a script entity called 'useful_functions', it finds that script entity. Then you have called the function 'hello_world' on that script_entity, which exists - so it will run.

Now, in the solution I provided I suggest making a script_entity specifically called 'utils' and putting the code in there. This is a good place to put generic functions that you might want to use from anywhere in the dungeon, over and over. For instance, you might want to spawn a grid of objects in a few places - that's why I wrote a generic solution that you could use over and over (even in different dungeons).

So, the steps to getting this to work in your dungeon when you push a button say is the following...

1. You need to make a script_entity called 'utils' and put the code above in it. Functions in this script are useful across the entire dungeon - however, because you need to pass them parameters to get them to work, you can't call them directly from levers. So...

2. You will then need to make a second script_entity called 'level1' say (whatever you like really, but the idea is that you could put all of your traps into one script so that you can keep track of them.

In this second script, I would put something like the following;

Code: Select all

function poisonProba() 
  utils.spawn_grid("posion_cloud", 3, 4, 6, 7, 6, 1);
end
Note that this works because you have already made a script_entity called 'utils' with the method 'spawn_grid' in it.

3. Now hook the button / lever up to the 'level1' script and the 'poisonProba' function.
My Grimrock Projects Page with links to the Grimrock Model Toolkit, GrimFBX, Atlas Toolkit, QuickBar, NoteBook and the Oriental Weapons Pack.

Glew
Posts: 64
Joined: Sat Dec 21, 2013 7:57 pm

Re: Opening LOTS of doors

Post by Glew » Sun Feb 16, 2014 2:00 am

John, thanks. It is much appreciated. Now it makes more sense. I didn't know anything about how to call global variables from other script entities. (To be honest, I have little idea of absolutely any scripting, though it is satisfying when you can finally make something work).

One question comes to mind: is there any technical reason for not putting all and every script into one entity? Other than it being messy and hard to manage?

User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: Opening LOTS of doors

Post by Diarmuid » Sun Feb 16, 2014 9:42 am

Glew wrote:Other than it being messy and hard to manage?
You nailed it! :D

Also, as you cannot duplicate variables or functions inside a single script entity, you might end with stuff like:

function openTheThirdDoorOnTheLeftOnLevelFour()
[bla bla bla]
end

instead of putting a script entity near the door and having

function openDoor()

end

User avatar
JohnWordsworth
Posts: 1397
Joined: Fri Sep 14, 2012 4:19 pm
Location: Devon, United Kingdom
Contact:

Re: Opening LOTS of doors

Post by JohnWordsworth » Sun Feb 16, 2014 1:37 pm

As you said and Diarmuid replied, it's just about keeping things tidy really. It's best to 'logically' break up code into logical groups of similar functions. Some more advanced topics related to this matter...

1. Not only does it keep everything tidy, but a powerful idea in coding is 'Encapsulation'. You can write (or download from this forum) some complicated code and, as long as you know how to use it - ie. the 2 or 3 functions that you need to call, you don't really need to worry about *how* it does it. For instance, you might have a script which adds a notebook to your mod - which is 1,000 lines long - but you don't have to worry about those, you just need to drop the script in your dungeon and link it up and it works. Having all of that in one script_entity, you essentially put it into it's own box so you can update the script when updates come out, but it's just a black box otherwise.

2. Another cool advantage in Grimrock of having script_entity's being actual entities, is that you can call 'self.x' and the like on the script to get the actual location of the script entity. So, you might want to spawn a magic sword when the user presses a button. Instead of hard-coding the square where you want to spawn the sword like this "spawn('magic_sword', 1, 6, 6, 1);" you could instead do "spawn('magic_sword', self.level, self.x, self.y, self.facing)". If you later decide you want to spawn the sword somewhere else - you just move the script entity instead of having to edit the script.
My Grimrock Projects Page with links to the Grimrock Model Toolkit, GrimFBX, Atlas Toolkit, QuickBar, NoteBook and the Oriental Weapons Pack.

Glew
Posts: 64
Joined: Sat Dec 21, 2013 7:57 pm

Re: Opening LOTS of doors

Post by Glew » Sat Nov 15, 2014 9:07 pm

And now the time has come, when I am hopefully, finally able to use that script of double for loops... *fingers crossed

edit: It works! IT WORKS! :lol: :lol: :lol: :lol: :lol: :mrgreen: :mrgreen: :mrgreen: :mrgreen:
It's a thing of beauty!

For the benefit of future newbies, the way I done it:

Code: Select all

-- this goes in a script entity with id="utils"
function closeAllDoors(level, minX, minY, maxX, maxY, name) -- I need to close all doors for this mod
  for x = minX, maxX do
    for y = minY, maxY do
      for e in entitiesAt(level, x, y) do
        if e.name == name then 
-- I decided it is better this way, now I can place a bunch of doors of one type and open them, also it did not work with e.class, but that may be of some other bug I didn't notice. I also deleted the parentheses around the if as they seemed (and proved) to be redundant
           e:close()
          end
        end
      end
    end
end

Code: Select all

-- this goes in an other script
function cageTheAltar() 
utils.closeAllDoors(1, 17, 9, 21, 13, "temple_door_portcullis")
end
-- it closes every and each Temple door portcullises I placed in the room; but nothing else

Post Reply