Page 1 of 11

Useful scripts repository

Posted: Fri Sep 14, 2012 11:18 am
by Montis
Hi,
since I'm trying to do many new things in the dungeon I'm creating, I use the scripts a lot. It sometimes takes a long while to figure out how stuff works so I thought to myself: why not share the good stuff with others, maybe they can use it too! Or if they don't need it like this, they can at least learn a bit how the scripting works in this game.
So here I'll be sharing some scripts that I think fit in that category.

And as others have posted their scripts too, I'll try to maintain the following index. If you want your script added, just post it in this thread. If your script is so large that it needs it's own thread and you want it added below point 3, just write me a PM.

1) Basic scripts

1.1) Dungeon scripts:
  1. Set the key for "dungeon_door_wooden_locked" or "temple_door_wooden_locked" per script
  2. To check the state of a pressure plate
  3. Put torches into two torch holders in order to activate a teleporter
  4. To require two pressure plates to be down before a door will open
  5. To have a door open only when two pressure plates are pressed down
  6. Combination Torch Lock
  7. Check if the right item is put in an alcove
  8. Double locks / two locks on one wall
  9. Re-create the "Checkered Room" (for practice)
  10. How to Add a Scroll With Text (or a sack with contents) on an Alcove
1.2) Party scripts:
  1. Temporarily boost max hit points and add hit points
  2. Temporarily disable all party members, to avoid damage
1.3) Item and inventory scripts:
  1. Steal an item being held or equiped by a party member
  2. Iterator for champion inventory
  3. Check if anybody in your party is carrying a particular type of item
  4. Another item-check function
1.4) Monster scripts:
  1. none yet :cry:
1.5) Effects and other scripts:
  1. Add playing a sound or custom sound to a script
  2. Post on-screen text with a character's name
  3. Make a script run once (such as at the start of a map)
  4. Carefully place an object in a square
  5. To turn on a colored light when the map starts
  6. Adding a custom colored light source object to the dungeon
  7. Insta-death square
  8. Getting rid of "First time playing Grimrock?" on new game
  9. Earthquake!

2) Advanced scripts

2.1) Dungeon scripts:
  1. How a script can take the place of multiple similar connectors all at once
  2. Spawn a new button/lever/etc and then connect it to something
  3. Three different alcoves for left/right/middle item placement
  4. Connecting things between different levels per script
  5. Look for things in an area
  6. Two digit password entry
  7. Two word password entry
  8. Creating a sequence lock in 8 easy steps
  9. Rotating 2 portals around a room clockwise
  10. Conditional wall text
  11. Destructible tunnel blockage (cave in)
  12. Use a single timer's repeating signal to perform a series of events in a row
  13. Make a pit sequence puzzle
  14. Teleport to a Random Location (on the same floor)
  15. Combination lock with buttons
2.2) Party scripts:
  1. Scripts that can test for a stat (like willpower) and do something with it (like grant experience)
  2. Remove (disable) a random character from the party
  3. A corridor where players have to face/step backwards to proceed
  4. Grant "pit immunity" by disabling characters temporarily
  5. Create Anti-Magic Zones
  6. Featherfall Effect
2.3) Monster scripts:
  1. When a party member dies, check for it's killer and open a door if it was a specific enemy
2.4) Item and inventory scripts:
  1. Create a "gambling" setup/machine
  2. Adding NPC traders to your dungeon - buy stuff! use Alcoves + script addition
  3. How to teleport your party with a one-use item
  4. Teleporting your party to a certain location, using an item with charges
  5. Check for and Remove a held item, then Return the same item to Inventory
  6. Torch Douser/Anti-Light Area
2.5) Effects and other scripts:
  1. Dot matrix number display
  2. Adjustable number display and password entry
  3. LED Digital Wall Display: 0 - 99
  4. Character dialogue, random champion name retrieval and returning how many champions are alive
  5. Wall hooks as an alcove that only hold items that can fit on it
  6. Exploding barrel block
  7. Get distance between two objects
  8. True Random Number Generator

3) Scripting frameworks or other large scripts in their own thread
  1. Advanced Scripting Framework by JKos
  2. [Library] - GrimQ - a query library for Grimrock and Lua by Xanathar
  3. AI Framework by Ancylus
  4. Generic Grimrock Puzzle Frameworks by Grimwold
  5. SetDefaultParty: customize the starting party in your dungeon by Magus
  6. Tome Library by Ixnatifual
  7. Custom alcoves using LoG assets by Neikun, Batty and Crisman
  8. Set up for Randomized Loot in dungeon or monster drops
  9. New Spells-Thread
  10. [SCRIPT] Extended Spells (Spinners, Hooks...) by Diarmuid
  11. External Scripts / Organization / generated in-dungeon on-the-fly
  12. [FUNCTION] Find entities with text and method filtering by Lark

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 1:07 pm
by Komag
EDIT - with update BETA 1.2.9 we can set the "opened by" key just like any other door, so this script becomes unnecessary

To set the key for a "dungeon_door_wooden_locked" or a "temple_door_wooden_locked":
- add a script "lua" item to your map (anywhere, but near the door will help you remember) and add the line:

Code: Select all

dungeon_door_wooden_locked_1:setOpenedBy("iron_key")
- You would replace "dungeon_door_wooden_locked_1" with the ID for the door (if you gave it your own ID, such as "mycooldoor" then the line would be:

Code: Select all

mycooldoor:setOpenedBy("iron_key")
- You would replace "iron_key" what whatever type of key you want to use (not a specific key ID, but the generic Name)(if you want the door to open with a brass key, the line would be:

Code: Select all

mycooldoor:setOpenedBy("brass_key")
- The script need not be connected to the door or the key in any way.

-----
original thread for discussion/questions:
viewtopic.php?f=14&t=3087&hilit=dungeon ... ked#p31393

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 1:20 pm
by Komag
EDIT - with update BETA 1.2.7 we can read the state of pressure plates with:
PressurePlate:isUp()
PressurePlate:isDown()
so the following is probably not all that helpful anymore, but may still illustrate some of how scripts work
------------------------------
To check the state of a pressure plate:
Currently we can only get the state of a lever, so when we want to use pressure plates, we can connect a plate to controlling a lever using a script that toggles the lever, then you can always check the lever state with "mylever:getLeverState()"

- make plate
- make script (lua):

Code: Select all

-- plate lever toggle
function platetoggle()
     mylever:toggle()
end
- in plate Inspector, create connection to script, with "platetoggle" as the action

so far, this gets the pressure plate to activate a lever. Test it by placing a lever nearby (even on the wall right in front of the plate) to just see if it works right, making the lever go down and up when placing a rock on and off the plate)

Then you can move the lever to some little room far away so it isn't heard

- then make a script that checks the lever state and does something if it's activated

Code: Select all

-- do something special script
function leveraction()
     if mylever:getLeverState() == "activated" then
          superdoor:open()
     else
          superdoor:close()
     end
end
- go into mylever inspector and create a connection to the new script with "leveraction" as the Action
this would then open the door you have called "superdoor", so you can set this up for a test
So if you want it to do something else, you can change "superdoor:open()" to some other action

This whole setup above is ridiculous though because it's easy to just link a plate to a door. But it just illustrates how you can do some things with scripts.

-----
original thread for discussion/questions:
viewtopic.php?f=14&t=3063&p=31243#p31243

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 1:59 pm
by Komag
Put torches into two torch holders in order to activate a teleporter:

- create two empty torch_holder items and give them IDs "leftholder" and "rightholder" (and have enough torches in the map for the player to use)
- create a teleporter and give it the ID "coolteleporter". (Set teleporter Target to a nearby square for testing)
- add a script item "lua" to the map
- enter script:

Code: Select all

-- start teleporter if both torch holders are filled
function cooltele()
    if leftholder:hasTorch() == true and
    rightholder:hasTorch() == true then
        coolteleporter:activate()
    else
        coolteleporter:deactivate()
    end
end
- edit the properties of each torch, adding a connection to the "lua" script item, with "any" under Event and "cooltele" under Action
- test in game!

You can certainly use any unique IDs you wish, or just use the default ID created such as "torch_holder_1"

(this is yet another simple case where a counter would be easy to use instead, but a script could be added to and made more flexible)

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 2:03 pm
by Komag
check if the player put the right item in an alcove
(to open a door, for instance):

example by antti
Here's a script where I iterate through all the items on an alcove, checking for any daggers. You can use this with very little modifications to get what you want:

Code: Select all

function checkAlcoveForItems()
   -- change these variables to match your alcove's ID and the item you want to test for
   local itemName = "dagger"
   local alcoveID = dungeon_alcove_1
   
   -- iterate through all the contained items on alcove, checking for a matching name
   for i in alcoveID:containedItems() do
      if i.name == itemName then
         itemFound()
      else
         itemNotFound()
      end
   end
end

-- run this script _once for each item_ with a matching name
function itemFound()
   playSound("level_up")
end

-- run this script _once for each item_ without a matching name
function itemNotFound()
   spawn("poison_cloud", party.level, party.x, party.y, 0)
end
-----------------------------------------------------------------------
example by Billick

To set up a less robust script for a one time check of a dagger to open a door:

Code: Select all

function daggerCheck()
   for i in my_alcove:containedItems() do
      if i.name == "dagger" then
         my_door:open()
      end
   end
end
- then just connect the alcove to the script

----------------------------------------------------------------------
example by Emciel and Grimwold

if you want the door to close again if they take the item out of the alcove, you'll need to set it up this way:

Code: Select all

function daggerCheck()
   if my_alcove:getItemCount() == 0 then
      my_door:close()
   else
      for i in my_alcove:containedItems() do
         if i.name == "dagger" then
            my_door:open()
            break
         else
            my_door:close()
         end
      end
   end
end
This will close the door if nothing is in the alcove or if something is there that is not a dagger, and will open the door if the dagger is there, even if other things are there too. The "break" stops the "for-loop" section of the script from finding a non-dagger item (and closing the door) after finding a dagger first.
----------------------------------------------------------------------
another example, to show some subtle differences in scripting:

Code: Select all

function daggerCheck()
   if my_alcove:getItemCount() == 0 then
      my_door:close()
   else
      for i in my_alcove:containedItems() do
         if i.name == "dagger" then
            my_door:open()
            return
         end
      end
      my_door:close()
   end
end
This version has a "return" which stops the entire script from continuing after finding a dagger, regardless of other items in the alcove. This method avoids multiple "close" commands being sent to the door due to the script finding many non-dagger items before finding a dagger. Only if no dagger is found does the script ever reach the second "my_door:close()" line.
----------------------------------------------------------------------
last example, by petri

I would write it like this (less redundancy and cleaner control flow):

Code: Select all

function daggerCheck()
	local itemFound = false

	for i in my_alcove:containedItems() do
		if i.name == "dagger" then
			itemFound = true
		end
	end

	if itemFound then
		my_door:open()
	else
		my_door:close()
	end
end
This could also be generalized to:

Code: Select all

-- This function returns true if the entity contains a given item.
-- It works for any entity that implements the containedItems() method.
function containsItem(entity, item)
	for i in entity:containedItems() do
		if i.name == item then
			return true
		end
	end
	-- if we get here the item was not found
	return false
end

-- example usage
if containsItem(my_alcove, "dagger") then
	my_door:open()
else
	my_door:close()
end
-----
original thread for discussion/questions:
www.grimrock.net/forum/viewtopic.php?f=14&t=3083

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 2:05 pm
by Komag
how a script can take the place of multiple similar connectors all at once
(such as making one button fire 8 different fireballs)

example by petri:
-----------------
Sometimes it's easier to use a single script than many connectors. For example the following script when activated will open all doors with ids secret_door_1, secret_door_2, ... secret_door_10

Code: Select all

function activate()
  for i=1,10 do
    local door = findEntity("secret_door_"..i)
    if door then
      door:open()
    end
  end
end
-----
original thread for discussion/questions:
viewtopic.php?f=14&t=3065

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 2:09 pm
by Komag
Add a sound being played to a script
(this takes the original tutorial script for the simple combination lock and adds a sound)

example by Xzalander:
---------------------

Code: Select all

function pullLever()
   if combinationLever1:getLeverState() == "activated" and
   combinationLever2:getLeverState() == "activated" then
      combinationDoor:open()
      playSound("secret")
   else
      combinationDoor:close()
   end
end
----------------------
If you wanted to play a custom sound:
example by antti:
-------------
You need to define a new sound asset and refer to the asset's name from you level script. Add something like this to your sounds.lua:

Code: Select all

defineSound{
   name = "cool_sound",
   filename = "mod_assets/sounds/cool_sound.wav",
   loop = false,
   volume = 0.8,
}
And then the following to a script entity:
function playCoolSound()
playSound("cool_sound")
end

-----
original thread for discussion/questions:
viewtopic.php?f=14&t=3046&p=31246#p31246

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 2:13 pm
by Komag
Scripts that can test for a stat (like willpower) and do something with it (like grant experience)

example from Shroom:

Code: Select all

function teststat(stat, value, xpmin, xpmax)

   local astat = {}
   astat[1] = party:getChampion(1):getStat(stat)
   astat[2] = party:getChampion(2):getStat(stat)
   astat[3] = party:getChampion(3):getStat(stat)
   astat[4] = party:getChampion(4):getStat(stat)
   
   local aname = {}
   aname[1] = party:getChampion(1):getName()
   aname[2] = party:getChampion(2):getName()
   aname[3] = party:getChampion(3):getName()
   aname[4] = party:getChampion(4):getName()
   
   for i=1,4 do
      if astat[i] < math.random(value) then
         hudPrint(aname[i].." doesn't learn anything.")
      else
         local experiencegained = math.random(xpmin,xpmax)
              hudPrint(aname[i].." succeeds to understand what is going on and gain "..experiencegained.." XP." )
              party:getChampion(i):gainExp(experiencegained)
      end   
   end
end
I think you could pass variables like this? So the call would be :-
teststat("willpower",25,10,25)
---------------------

example from Filipsan:

Code: Select all

function teststat(stat, testmaxvalue, xpmin, xpmax)

   local astat = 0
   local aname = ""
   
   for i=1,4 do
      astat = party:getChampion(i):getStat(stat)
      aname = party:getChampion(i):getName()
      if astat < math.random(testmaxvalue) then
         hudPrint(aname.." doesn't learn anything.")
      else
         local experiencegained = math.random(xpmin,xpmax)
         hudPrint(aname.." succeeds to understand what is going on and gain "..experiencegained.." XP." )
         party:getChampion(i):gainExp(experiencegained)
      end   
   end
end
-----
original thread for discussion/questions:
viewtopic.php?f=14&t=3062

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 2:28 pm
by Komag
EDIT - with update BETA 1.2.7 counters now can deactivate when their value changes from zero to non-zero value, thus largely negating the need for the script example below. But it may still illustrate some of how scripts work
-----------------------------

To have a door open only when two pressure plates are pressed down:
You can link them to a counter set to 2 (see details here: http://www.grimrock.net/modding/inspect ... onnectors/) and have the counter open the door on 0. But removing something from the plate will not close the door, or if you have the counter toggle the door then only removing something and placing it again will close the door, not what you want.

So counters are useful, especially for one-off events, but they only send a signal when they hit 0, and they do not send the opposite signal when going back up to 1, for instance. To have more control, you can use a script as a counter:

example from petri:

Code: Select all

counter = 2

function activate()
  counter = counter - 1
  checkValue()
end

function deactivate()
  counter = counter + 1
  checkValue()
end

function checkValue()
  if counter == 0 then
    mydoor:open()
  else
    mydoor:close()
  end
end
If you link two pressure plates to this script (with Event activate -> Action activate and Event deactivate -> Action deactivate), then the plates and the door will behave as you would expect.

You can add any number of events to occur at this point. In my map my script ends like this:

Code: Select all

fuction checkValue()
    if counter == 0 then
        basementdoor:open()
        basementtele:activate()
        teletimer:activate()
        basementtimer:activate()
        autobackuptimer:activate()
    end
end
-----
original threads for discussion/questions:
viewtopic.php?f=14&t=3032
viewtopic.php?f=14&t=3125

Re: Useful scripts repository

Posted: Fri Sep 14, 2012 2:44 pm
by Komag
Post on-screen text with a character's name:

Code: Select all

local name1 = party:getChampion(1):getName()
hudPrint("You know "..name1..", you are an incredibly effective adventurer!")