Useful scripts repository

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
Post Reply
User avatar
Edsploration
Posts: 104
Joined: Wed Sep 19, 2012 4:32 pm

Re: Useful scripts repository

Post by Edsploration »

Teleport to a Random Location (on the same floor)

My method works around the logic of a monster spawner won't spawn a monster if the space is blocked. This is a way to detect if a space is solid rock or not, even if there are no entities there.

Options
allowTeleFrag = true | false :arrow: Whether to teleport on top of a monster, killing it (no XP gained this way), or reroll for a new target location.
allowRocked = true | false :arrow: Whether to teleport into rock, killing the whole party! OUCH! (1000 damage to all split 50% physical, 50% earth), or reroll for a new target location.
dodgeRockLuck = 0 to 1 :arrow: The probability to reroll instead of rocking the party. This parameter is to reduce the chance of landing in rock, because it can be a little too likely for dungeons with many rock spaces.

Setup
We need three pieces to get the random teleport working:
  1. A custom monster type definition. (The "probe".) This is used for a "valid location" check where a monster spawner is placed which then attempts to place the monster probe.

    Code: Select all

    cloneObject{
    	name = "teleport_probe",
    	baseObject = "snail",
    }
    
  2. A timer helper object. To teleport the party we must place a teleporter on top of it, and then delete the teleporter after a tiny delay.
    • The timer's ID must be "teleport_timer" to work with the script.
    • The timer interval can be short. I used 0.1 seconds.
    • The timer must call the tpCleanup() function, as well as deactivate itself.
  3. The teleport script itself! Call the teleportRandom() function to trigger a random teleport! You can use a button, pressure plate, item, spell, whatever can call it!

    Code: Select all

    function teleportRandom()
    
    	---- Define teleporter behavior ----
    	local allowTeleFrag = true      ----
    	local allowRocked = true        ----
    	local dodgeRockLuck = 0.8       ----
    	------------------------------------
    	
    	local rockedMessage = "Teleported into Rock! A gruesome death!"
    	
    	local allMonsters = {"crab", "crowern", "goromorg", "green_slime", "herder", "herder_big", "herder_small", "herder_swarm", "ice_lizard", "ogre", "scavenger", "scavenger_swarm", "shrakk_torr", "skeleton_archer", "skeleton_archer_patrol", "skeleton_warrior", "snail", "spider", "tentacles", "uggardian", "warden", "wyvern"}
    	
    	local targetX = math.floor(math.random()*32)
    	local targetY = math.floor(math.random()*32)
    	local targetFacing = math.floor(math.random()*4)
    	
    	local teleportSuccess = false
    	
    	spawn("spawner", party.level, targetX, targetY, 0, "probe"):setSpawnedEntity("teleport_probe"):activate()
    	
    	for i in entitiesAt(party.level, targetX, targetY) do
    		if i.name == "teleport_probe" then
    			teleportSuccess = true
    			i:destroy()
    			spawn("teleporter", party.level, party.x, party.y, 0, "teleport_random"):setTeleportTarget(targetX, targetY, targetFacing)
    			teleport_timer:activate()
    			break
    		end
    	end
    	
    	probe:destroy()
    	
    	if teleportSuccess == false then
    	
    		local rockTarget = false
    		local monsterTarget = false
    		
    		-- Test for reason of teleport fail --
    		if allowRocked or allowTeleFrag then
    			local tempBlockage = entitiesAt(party.level, targetX, targetY)
    			if tempBlockage() == nil then
    				rockTarget = true
    			end
    			for i in entitiesAt(party.level, targetX, targetY) do
    				for j=1,# allMonsters do
    					if i.name == allMonsters[j] then
    						monsterTarget = true
    						break
    					end
    				end
    				if monsterTarget == true then
    					break
    				end
    			end
    		end
    		
    		-- Should we rock the party? --
    		if allowRocked and rockTarget and math.random() > dodgeRockLuck then
    			spawn("teleporter", party.level, party.x, party.y, 0, "teleport_random"):setTeleportTarget(targetX, targetY, targetFacing)
    			teleport_timer:activate()
    			hudPrint(rockedMessage)
    			-- Everyone takes 1000 damage, 50% physical, 50% earth --
    			for i=1,4 do
    				party:getChampion(i):damage(500, "physical")
    				party:getChampion(i):damage(500, "poison")
    				party:getChampion(i):playDamageSound()
    			end
    			return
    		end
    		
    		-- Should we telefrag a monster? --
    		if allowTeleFrag and monsterTarget then
    			spawn("teleporter", party.level, party.x, party.y, 0, "teleport_random"):setTeleportTarget(targetX, targetY, targetFacing)
    			teleport_timer:activate()
    			return
    		end
    		
    		-- No suitable location found, try again --
    		teleportRandom()
    	end
    end
    
    function tpCleanup()
    	findEntity("teleport_random"):destroy()
    end
    
Those three options I mentioned are manually editable at the top of the code. They could easily be changed to arguments accepted by the function, but I did it this way because variable scoping in LUA is still weird and scary to me.
Open Project -> Community FrankenDungeon: viewtopic.php?f=14&t=4276
User avatar
Blichew
Posts: 157
Joined: Thu Sep 27, 2012 12:39 am

Re: Useful scripts repository

Post by Blichew »

Could we get a stick on this ? It's highly unacceptable that this thread was on 3rd page of modding forum :D
User avatar
Neikun
Posts: 2457
Joined: Thu Sep 13, 2012 1:06 pm
Location: New Brunswick, Canada
Contact:

Re: Useful scripts repository

Post by Neikun »

That's why the editing superthread is stickied. If this thread gets lost, you can find it there.
That way the forum doesn't get crowded with too many stickies.
"I'm okay with being referred to as a goddess."
Community Model Request Thread
See what I'm working on right now: Neikun's Workshop
Lead Coordinator for Legends of the Northern Realms Project
  • Message me to join in!
nichg
Posts: 48
Joined: Thu Oct 11, 2012 12:38 pm

Re: Useful scripts repository

Post by nichg »

Featherfall Effect

I was asked to repost this here. This is an effect that lets you give the party 'feather fall' for a fixed time, during which all falls through pits will deal no damage. It does not prevent the sound-effects associated with falling however.

The effect requires three things:

- You have to clone the party object in init.lua for the dungeon, and override the onMove and onDamage hooks:

Code: Select all

cloneObject {
   name = "party",
   baseObject = "party",

   onMove = function(party, dir)
      var_storage.set_last_level(party.level)
      var_storage.decrement_feather()
      return true
   end,
 
   onDamage = function(champion, dmg, dmgtype)
      if party.level ~= var_storage.get_last_level() then
         if var_storage.get_feather() then
            -- Feather fall
            return false
         end
      end
      return true
   end
}
This refers to a scripting object var_storage that must exist somewhere in your dungeon. Every time the party moves, it decrements the duration of their feather fall (you could just as well tie this to a timer to make it realtime). Furthermore, it stores the party's current dungeon level in a global variable in the storage object. When the party falls, their level changes before they take damage, but after onMove is called, so if the stored level and current level are different in onDamage, we know that we've taken damage due to falling down a pit (ostensibly this would also detect coincidences like falling into a fireball or something as falling damage, so there's a small chance it creates some spurious behavior).

Now, place a scripting object called 'var_storage' somewhere in the dungeon, with the following code on it:

Code: Select all

last_level = 1
feather_fall = false
feather_dur = 0

function set_feather(dur)
  feather_fall = true
  feather_dur = dur
  hudPrint("You feel lighter.")
end

function decrement_feather()
   if feather_fall == true then
      feather_dur = feather_dur - 1
      if feather_dur <= 0 then
        feather_fall = false
        hudPrint("You feel heavier.")
      end
   end
end

function get_feather()
  return feather_fall
end

function set_last_level(lev)
  last_level=lev
end

function get_last_level()
  return last_level
end
This scripting object carries global variables indicating whether the party is under the feather fall effect or not, as well as the last level. Since they're global they should be saved, so feather fall will persist across save/load. You must call var_storage.set_feather(15), for example, in order to give the party the feather fall effect (for instance from a potion use script or a spell script, or if the party interacts with a particular fountain or whatever). That would last for 15 steps, and display a message when the effect ended.

In principle this setup could be used to create any number of custom conditions or persistent spell effects. I'm not sure yet how to give graphical feedback for the effects, but ostensibly you could do something with creating a light source with particle system and move it with the party so long as the effect is up, then destroying it when the effect ends. I think it'd be weird with teleporters and pitfalls though, since there you move after onMove is done.
Lmaoboat
Posts: 359
Joined: Wed Apr 11, 2012 8:55 pm

Re: Useful scripts repository

Post by Lmaoboat »

Torch Douser/Anti-Light Area
When triggered, will store all the fuel values for each torch in your inventory, or held with mouse, douse them, and return them to their previous values when restored. The script will also douse any torches that are thrown through on placed on the square of the script entity itself.

Code: Select all

spawn("timer",self.level,self.x,self.y,0,self.id.."gcheck")
:setTimerInterval(0.1)
:addConnector("activate", self.id, "groundcheck")
:activate()


TF = {}
TID = {}
function torchout()
	for i=1,4 do
      for j=1,31 do
         if party:getChampion(i):getItem(j) ~= nil then
            local istorch = party:getChampion(i):getItem(j)
            if istorch.name == "torch" and istorch:getFuel() ~= 0 then
				table.insert(TID,istorch)
				local fuel = istorch:getFuel()
				table.insert(TF,fuel)
				istorch:setFuel(0)
		            
            end
         end      
      end
   end
	local mtor = getMouseItem()
	if getMouseItem() ~= nil and mtor.name == "torch" then
		table.insert(TID,mtor)
		local fuel = mtor:getFuel()
		table.insert(TF,fuel)
		getMouseItem():setFuel(0)
		
	end
end


function torchrestore()
	print(TF[1])
	print(TID[1])
	for i=1,25 do
		local tor = TID[i]
		if tor ~= nil then
			tor:setFuel(TF[i])
		end
	end
	for i=1,4 do
		for j=7,8 do
			if party:getChampion(i):getItem(j) ~= nil then
				local istorch = party:getChampion(i):getItem(j)
				party:getChampion(i):removeItem(j)
				party:getChampion(i):insertItem(j, istorch)
			end
		end
	end
end


function groundcheck()
	for i in entitiesAt(self.level, self.x, self.y) do
		if i.name == "torch" and i:getFuel() ~= 0 then
			local fuel = i:getFuel()
			table.insert(TID,i)
			table.insert(TF,fuel)			
			i:setFuel(0)
		end
	end
end



Last edited by Lmaoboat on Sat Oct 13, 2012 2:36 am, edited 1 time in total.
YpsiNine
Posts: 36
Joined: Wed Oct 10, 2012 1:08 am

Re: Useful scripts repository

Post by YpsiNine »

True Random Number Generator
By Martin Thoresen

Since we can't access os.clock() or os.time() within Grimrock we can add a random seed by using the ID of a spawned object.

Code: Select all

function randomize(x, y)
  seed = spawn("dagger", 1, 1, 1, 1)  --spawns a dagger at a place where the party can't interfere
  math.randomseed(seed.id)            --uses the ID of the spawned dagger as a random seed
  result = math.random(x, y)          --here we do the random number generation
  print(result)                       --to see the number it picked while debugging, else just remove this line
  print(seed.id)                      --to see the seed it got while debugging, else just remove this line
  seed:destroy()                      --then we destroy the dagger
  return result                       --we need to return the result to whatever asks for it
end
Then we use the function for example like this:

Code: Select all

variable = randomize(1,3)
if variable == 1 then
  hudPrint("I got a 1")
elseif variable == 2 then
  hudPrint("I got a 2")
elseif variable == 3 then)
  hudPrint("I got a 3")
end
Lmaoboat
Posts: 359
Joined: Wed Apr 11, 2012 8:55 pm

Re: Useful scripts repository

Post by Lmaoboat »

Updated my Darkness script so you can throw torches through it.
Custode
Posts: 15
Joined: Wed Oct 10, 2012 8:29 pm

Re: Useful scripts repository

Post by Custode »

Combination lock with buttons

First: spawn some buttons and connect them via script (this save lots of connections in the map view).
Second: create a function with the argument (sender).
Every if statement covers one possibility, so if there are three buttons then there will be three possibilities each, pressed first, second or third.
Now you just have to get the counter value inside the if statements and decrease it only if the button is pressed in the correct sequence.
Example: if you want to press the first button for last you have to decrease the counter only when it is at the value of 1 and reset it in all the other cases.
Lastly create a single counter, set its value to the number of buttons you have and connect it to the script with activate.
With this method there is no need for a reset switch or multiple counters.

Code: Select all

spawn("wall_button", level, x, y, facing, "button_1"):addConnector("toggle", "script", "buttonPress")
spawn("wall_button", level, x, y, facing, "button_2"):addConnector("toggle", "script", "buttonPress")
spawn("wall_button", level, x, y, facing, "button_3"):addConnector("toggle", "script", "buttonPress")

--this example will have a combination of: 2 3 1 

function buttonPress(sender)
	if sender.id == "button_1" and counter:getValue() == 3 then
		counter:setValue(3)
	elseif sender.id == "button_1" and counter:getValue() == 2 then
		counter:setValue(3)
	elseif sender.id == "button_1" and counter:getValue() == 1 then
		counter:setValue(0)
	elseif sender.id == "button_2" and counter:getValue() == 3 then
		counter:setValue(2)
	elseif sender.id == "button_2" and counter:getValue() == 2 then
		counter:setValue(3)
	elseif sender.id == "button_2" and counter:getValue() == 1 then
		counter:setValue(3)
	elseif sender.id == "button_3" and counter:getValue() == 3 then
		counter:setValue(3)
	elseif sender.id == "button_3" and counter:getValue() == 2 then
		counter:setValue(1)
	elseif sender.id == "button_3" and counter:getValue() == 1 then
		counter:setValue(3)
	elseif counter:getValue() == 0 then
		door:open() -- or whatever you want to do
	end
end
Original thread: viewtopic.php?f=14&t=3026
User avatar
Lark
Posts: 178
Joined: Wed Sep 19, 2012 4:23 pm
Location: Springfield, MO USA

Re: Useful scripts repository

Post by Lark »

Automatic Elevator Script
I posted my "Automatic Elevator Script" at viewtopic.php?f=14&t=3723 for comments, enhancement requests, and etcetera. Download the custom sounds and etcetera, edit the operational parameters, and then stack the script on multiple levels to create an elevator between those floors. Enjoy!

Thank you, -Lark
User avatar
Lark
Posts: 178
Joined: Wed Sep 19, 2012 4:23 pm
Location: Springfield, MO USA

Re: Useful scripts repository

Post by Lark »

Automatic Elevator Script, Version 2

Version 2 is now available with more options and it's contained in a single script!
viewtopic.php?f=14&t=3723#p38402

FEATURES and ENHANCEMENTS:
  • Place only one script at the top of the elevator stack to create the whole stack
  • Button location definitions are now in a table which also define all floors below
  • Optional electric eye resets door close timer by walking in or out of the elevator
  • Optional door open button is placed on the back wall of the elevator
  • An optional elevator light can be included
  • The default buzz sound was reduced to about 10% of the original volume (others included)
  • Script reset included for level exits before sequences finish
  • Optional realistic elevator car tracking - arrival delayed when car is elsewhere
  • Optional LED level display in the back of the elevator car
  • Realistic door behavior
  • See comments in the code for parameters and usage guidelines
Post Reply