Useful scripts repository

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
Grimwold
Posts: 511
Joined: Thu Sep 13, 2012 11:45 pm
Location: A Dungeon somewhere in the UK

Re: Useful scripts repository

Post by Grimwold »

Configure any number of teleporters to send you to a random destination from a list of co-ordinates.

* Each teleporter should be named in the format plate_01_tele (where plate_01 will be the name of pressure plates around the teleporter

* each teleporter should have hidden pressure plates on each of it's open sides that are named in the format plate_01_A, plate_01_B etc. (the A, B etc are irrelevant so could be anything as long as each plate is different)

create a script entity and paste in the following code:

Code: Select all

function randomTele(teleporter_id)
  local rand_x = {15,17,15,17}
  local rand_y = {28,28,30,30}
  local rand_l = {1,1,1,1}
  local rand = math.random(1,math.min(#rand_x,#rand_y,#rand_l))
  teleporter_id:setTeleportTarget(rand_x[rand],rand_y[rand],party.facing,rand_l[rand])
end

function teleActivate(plate)
  tele_id = plate.id:sub(1,8) .. "_tele"
  local tele_port = findEntity(tele_id)
  randomTele(tele_port)
end
* In the script entity populate the tables rand_x; rand_y and rand_l with your sets of co-ordinates... so in this example as is, the destinations as (x,y,level) are (15,28,1); (17,28,1) (15,30,1) and (17,30,1)

* Connect every single pressure plate to this script entity with the action teleActivate

Notes -
* The script has a failsafe for If you end up putting too many or too few in any of the co-ordinate tables and only use as many destinations as it has complete data for.

* final facing in this example is determined by the direction the party are facing, but the script can be modified to use either a facing specified by the destination (we'd add another table rand_f) or by the facing of the teleporter itself. (we'd use teleporter_id.facing)


Script entity if wish to use teleport facing
SpoilerShow

Code: Select all

function randomTele(teleporter_id)
  local rand_x = {15,17,15,17}
  local rand_y = {28,28,30,30}
  local rand_l = {1,1,1,1}
  local rand = math.random(1,math.min(#rand_x,#rand_y,#rand_l))
  teleporter_id:setTeleportTarget(rand_x[rand],rand_y[rand],teleporter_id.facing,rand_l[rand])
end

function teleActivate(plate)
  tele_id = plate.id:sub(1,8) .. "_tele"
  local tele_port = findEntity(tele_id)
  randomTele(tele_port)
end
script entity to use facing based on destination co-ordinates.
SpoilerShow

Code: Select all

function randomTele(teleporter_id)
  local rand_x = {15,17,15,17}
  local rand_y = {28,28,30,30}
  local rand_l = {1,1,1,1}
  local rand_f = {0,1,3,2}
  local rand = math.random(1,math.min(#rand_x,#rand_y,#rand_l,#rand_f))
  teleporter_id:setTeleportTarget(rand_x[rand],rand_y[rand],rand_f[rand],rand_l[rand])
end

function teleActivate(plate)
  tele_id = plate.id:sub(1,8) .. "_tele"
  local tele_port = findEntity(tele_id)
  randomTele(tele_port)
end
Ancylus
Posts: 50
Joined: Thu Oct 11, 2012 5:54 pm

Re: Useful scripts repository

Post by Ancylus »

Pattern recognizer

This script recognizes a specific pattern of activations of buttons/plates/whatever. The main difference compared to other similar scripts posted here is that it doesn't reset entirely if the player deviates from the pattern or completes it. For example, if the pattern is 3-1-3-2, the script will accept 3-1-3-1-3-2, since it has the right four steps at the end. Or if the pattern is 3-1-3-1, the sequence 3-1-3-1-3-1 will complete it twice, once at the fourth step and again at the sixth.

The activating entities need a connector to the step function. Their ids have to end in unique numbers, but may be otherwise arbitrary. No other objects are required. The correct pattern is defined at the beginning of the step function, and completing it causes a call to the patternDetected function.

Code: Select all

-- This variable keeps track of how many steps in the pattern have been taken.
current = 0

function step(source)
	-- This table defines the pattern we are looking for.
	local pattern = {3, 1, 3, 2}
	
	-- Identify the source that triggered the step. Each potential source needs to have an id ending in a number that identifies its part (if any) in the pattern.
	local _, _, latestId = source.id:find("(%d+)$")
	local latestId = tonumber(latestId)
	
	-- Check whether the source is the next step in the pattern.
	current = current + 1
	if latestId == pattern[current] then
		
		-- Pattern advanced, check whether it is complete.
		if pattern[current + 1] == nil then
			-- To reset at completion, remove the call to backtrack and assign 0 to current instead.
			backtrack(pattern, latestId)
			patternDetected()
		end
		
	else
		backtrack(pattern, latestId)
	end
end

function backtrack(pattern, latestId)
	-- See if the latest steps match a shorter subsequence at the beginning of the pattern.
	local next
	for i = current-1, 1, -1 do
		next = i
		
		if latestId == pattern[i] then
			for j = 1, i-1 do
				if pattern[j] ~= pattern[current - i + j] then
					next = nil
					break
				end
			end
		else
			next = nil
		end
		
		if next ~= nil then
			break
		end
	end
	
	-- If a valid subsequence was found, set the current position to match it.
	if next ~= nil then
		current = next
	else
		current = 0
	end
end

function patternDetected()
	-- A complete pattern has been detected.
end
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: Useful scripts repository

Post by Xanathar »

Steal and restore the party inventory

These functions steals and restores the whole party inventory :twisted: . If already stolen, it doesn't steal anymore until a restore is called :)

Works with sacks, charge items, stack items, text scrolls, torches. Already tested in-game including saving/reloading (how it should be done for everything).
If any item is in the same slot at the moment of restore, the stolen item will be dropped to ground instead of restored to inventory.

Many thanks to Komag for the testing/feedbacks!

Known issues:
  • Scrolls with images are not restored correctly (since getScrollImage method does not exist).
  • Items in sacks/containers get rearranged.
SpoilerShow

Code: Select all

-- The stolen inventory
g_StoredInventory = { }
g_InventoryStolen = false


-- Restores the whole party inventory
function restoreInventory()
	for c = 1, 4 do
		for i = 1, 31 do 
			local idx = c * 100 + i
			
			local item = party:getChampion(c):getItem(i)
			
			if (g_StoredInventory[idx] ~= nil) then
				local spitem = nil
				if (item ~= nil) then
					loadItem(g_StoredInventory[idx], true)
				else
					party:getChampion(c):insertItem(i, loadItem(g_StoredInventory[idx], false))
				end
			end
		end
	end
	
	g_StoredInventory = { }
	g_InventoryStolen = false
end

-- Steals the whole inventory of party
function stealInventory()
	if g_InventoryStolen then return end

	for c = 1, 4 do
		for i = 1, 31 do 
			local idx = c * 100 + i
			
			local item = party:getChampion(c):getItem(i)
			
			if (item ~= nil) then
				g_StoredInventory[idx] = saveItem(item)
				party:getChampion(c):removeItem(i)
			end
		end
	end
	
	g_InventoryStolen = true
end


-- saves an item into the table
function saveItem(item)
	local itemTable = { }
	itemTable.name = item.name
	itemTable.stackSize = item:getStackSize()
	itemTable.fuel = item:getFuel()
	itemTable.charges = item:getCharges()
	itemTable.scrollText = item:getScrollText()
	
	local idx = 0
	for subItem in item:containedItems() do
		if (idx == 0) then
			itemTable.subItems = { }
		end
		
		itemTable.subItems[idx] = saveItem(subItem)
		idx = idx + 1
	end
	
	return itemTable
end

-- loads an item from the table
function loadItem(itemTable, atparty)
	local spitem = nil
	if (atparty) then
		spitem = spawn(itemTable.name, party.level, party.x, party.y, math.random(0, 3))
	else
		spitem = spawn(itemTable.name)
	end
	if itemTable.stackSize > 0 then
		spitem:setStackSize(itemTable.stackSize)
	end
	if itemTable.charges > 0 then
		spitem:setCharges(itemTable.charges)
	end				
	
	if itemTable.scrollText ~= nil then
		spitem:setScrollText(itemTable.scrollText)
	end
	
	spitem:setFuel(itemTable.fuel)
	
	if (itemTable.subItems ~= nil) then
		for _, subTable in pairs(itemTable.subItems) do
			local subItem = loadItem(subTable)
			spitem:addItem(subItem, false)
		end
	end
	
	return spitem
end
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
LordYig
Posts: 175
Joined: Wed Jul 04, 2012 5:45 pm

Re: Useful scripts repository

Post by LordYig »

Create Darkness Zone
extinguish torch held by the party and prevent the light spell to be cast

I heavily based this idea on the "Anti-Magic Zone" proposed by Grimwold with contributions from JohnWordsworth and Wolfrug.

The idea is the create a zone or a room where the party cannot use torches or the light spell.
This can still be improved because at the moment the player can still use a torch stored in the party inventory while standing still, but if the party moves the torch is extinguished.
Also the light effect of the Orb of radiance is not affected by this script, I really don't know how to affect it...
  1. Add required custom objects to objects.lua to signify the spaces where darkness prevails and create an empty torch to handle the extinguished torch effect.

    I just followed the exemple of the anti magic zone here, and added the empty torch that will replace any torch the party is actually holding.

    Code: Select all

    cloneObject{
       name = "darkness_zone",
       baseObject = "pressure_plate_hidden",
       editorIcon = 96,
    }
    
    cloneObject{
       name = "torch_extinguished",
       baseObject = "torch",
       fuel = 0,
    }
  2. Create a clone of the party in init.lua to add an onCastSpell hook and an onMove hook (or add the hooks to an existing party clone if you use one)

    Same here, I just followed existing exemple to add custom hooks to the party clone.

    Code: Select all

    cloneObject{
    	name = "party",
    	baseObject = "party",
    	onCastSpell  = function(caster, spell)
    		return darkness_script.CheckDarkness(caster, spell)
    	end,
    	onMove  = function(party,direction)
    		return darkness_script.CheckParty()
    	end,
    }
  3. (Optional but recommended) - Add custom sound effect simulating a torch beeing extinguished in the sounds.lua from your scripts folder.

    Search for a royalty free sound effect a fire, a cigarette or a match beeing extinguished on the internet and place the file in the sounds folder of your dungeon.
    I have found one here : http://www.freesound.org/
    If you use the sound be sure to uncomment the lines referring to playing the sound in the next step (i.e the lines : playSound("extinguish_torch"))

    Code: Select all

    defineSound{
        name = "extinguish_torch",
        filename = "mod_assets/sounds/extinguish.wav",
        loop = false,
        volume = 0.2,
        minDistance = 1,
        maxDistance = 1,
    }
  4. In your dungeon create a script entity called darkness_script and add the following functions

    Code: Select all

    function CheckDarkness(caster, spell)
    	if spell == "light" then
    		for entity in entitiesAt(party.level,party.x,party.y) do
    			if entity.name == "darkness_zone" then
    				hudPrint(caster:getName() .. "'s spell fizzles because of the surrounding darkness")
    				return false
    			end
    		end
    	end
    	return true
    end
    
    function CheckParty()
    	for entity in entitiesAt(party.level,party.x,party.y) do
    		if entity.name == "darkness_zone" then
    			CheckTorch(party,direction)
    		end
    	end
    	return true
    end
    
    function CheckTorch()
    	local currentItem
    	local champion
    	local printMessage = false
    	for champID=1,4 do
    		champion = party:getChampion(champID)
    		currentItem = champion:getItem(7)
    		if currentItem ~= nil then
    			if currentItem.name == "torch"
    			 or currentItem.name == "torch_everburning" then
    				champion:removeItem(7)
    				champion:insertItem(7,spawn("torch_extinguished"))
    				printMessage = true
    				--playSound("extinguish_torch")
    			end
    		end
    		currentItem = champion:getItem(8)
    		if currentItem ~= nil then
    			if currentItem.name == "torch"
    			 or currentItem.name == "torch_everburning" then
    				champion:removeItem(8)
    				champion:insertItem(8,spawn("torch_extinguished"))
    				printMessage = true
    				--playSound("extinguish_torch")
    			end
    		end
    	end
    	
    	if printMessage then
    		hudPrint("the party's torch is suddenly extinguished by some unknown force")
    	end
    end
  5. Place your newly defined darkness_zone objects on your map in the places where you want to prevent the party to use the light spell and torches.
When a player attempts to cast the light spell while stood on one of the darkness tiles the spell will fail and a message will be printed to the hud saying why the spell fizzled.
When the party move one tile in any direction from a darkness tile to another, all torches held by any champion in either hand will be replaced by an extinguished version of the torch.

For a better atmosphere boost in this part of your dungeon remove any light source near the darness zone and add a few silent monsters here and there...

---------------------------

by Wanderer » Fri Jan 18, 2013 2:39 pm
Hi, refering to the posting "Creating Darkness Zones" I have found a workaround to prevent the player to use Torches even when the player stands still.

Here is how I did it: While I have changed nothing to the functions etc. listet in the posting, I have created a Timer wich is activated when entering the darkness-zone-object in the dungeon (through connector) with a interval of 0. As long as the player stand on the Zone-object the timer checks the three main functions in the scriptentity (lua) of the Darknesszone Object.
(like it also be checked when entering the object by the object itself). But instead of only checking the three functions at once by entering (checkdarkness(), checkparty(), checktorch() ), the timer checks it as long as the player stand on the darkness-zone. When the player leaves the darkness-field the timer is deactivated and the player can use a torch again (or cast a light spell).

I have testet this now for a while and stepped through several darknessobjects in a 3x3 room, and it works really very well for me. Even before the player make a attempt to give someone a torch, the torch extinguishes. :mrgreen:
The only line what could be added to this thing is, to cast a darkness-spell also on the party (in case the party already has a Light-Spell casted before).

lg Wanderer
User avatar
crisman
Posts: 305
Joined: Sat Sep 22, 2012 9:23 pm
Location: Italy

Re: Useful scripts repository

Post by crisman »

How to script a monster which casts a spell while performing a turn attack

If you want to create a monster that cast a spell every time it's attacking, but this monster has turn attacks (such as the lizards, ogres, wardens), the simple getForward is not enough, because each time it will perform a turn attack, his facing has not yet changed, so the spell will be casted in front of him as usual, and not toward you as you would expect. To fix this, you must use this type of script, as an example is a lizard casting a fireburst. This scrip must be placed in the asset definition inside the monster.lua script.

Code: Select all

onAttack = function(self, attack)
local dx, dy = getForward(self.facing)
if attack == "attack" then
   spawn("fireburst", self.level, self.x+dx, self.y+dy, 0)
elseif attack == "turnAttackLeft" then
   spawn("fireburst", self.level, self.x+dy, self.y-dx, 0)
elseif attack == "turnAttackRight" then
   spawn("fireburst", self.level, self.x-dy, self.y+dx, 0)
end
   end,
User avatar
Edsploration
Posts: 104
Joined: Wed Sep 19, 2012 4:32 pm

Re: Useful scripts repository

Post by Edsploration »

Doors That Open Via Left Click *knock* *knock*

As seen in the beginning of The Big Friendly Warden dungeon
(viewtopic.php?f=14&t=3665&start=30#p41116)

Now with extra functionality
:arrow: Ability for the door to close and reset by a function call!
:arrow: Duplicate the script to work on any number of doors without conflicts!

Implement by generating these assets:
  1. A custom sound file by geoffbarkman found here: http://www.freesound.org/people/geoffba ... nds/53442/
    Place the .wav file in your mod_assets/sounds directory.
  2. A sound asset definition. Insert the following code into your sounds.lua file:

    Code: Select all

    defineSound{
       name = "door_knock",
       filename = "mod_assets/sounds/door_knock.wav",
       loop = false,
       volume = 1,
       minDistance = 1,
       maxDistance = 6,
    }
    
  3. A custom alcove definition. Insert the following code into your objects.lua file:

    Code: Select all

    defineObject{
    	name = "talk_alcove",
    	class = "Alcove",
    	anchorPos = vec(0, 0.5, -0.01),
    	anchorRotation = vec(0, 180, 0),
    	targetPos = vec(0, 0.5, -0.01),
    	targetSize = vec(0, 0, 0),
    	placement = "wall",
    	replacesWall = false,
    	editorIcon = 96,
    }
    
  4. An item asset definition. Insert the following code into your items.lua file:

    Code: Select all

    cloneObject{
    	name = "talk_box",
    	baseObject = "rock",
    	model = "assets/models/env/gobelin.fbx",
    }
    
  5. Makes use of the party onPickUpItem hook. Combine this code with your custom party definition. Or just insert this code into your init.lua file:

    Code: Select all

    cloneObject{
    	name = "party",
    	baseObject = "party",
    	onPickUpItem = function(party, item)
    		if item.id == "open_door_1" then
    			door_script_1:knock()
    			return false
    		end
    	end,
    }
    
  6. Place the actual door somewhere in your dungeon.
  7. And add a script entity which must
    • have its id match the call from the party's onPickUpItem hook (above).
    • be placed in front of the door and facing the door.
    • avoid id conflicts with other *knock* *knock* doors by changing the 6 strings at the top of the script.

      Code: Select all

      -- Place this script in front of and facing towards the door.
      -- Specify unique ids for the following:
      
      doorToOpen = "dungeon_door_wooden_1"	--must be the actual door's id
      doorLogicItem = "open_door_1"			--must match the logic in the party's onPickUpItem hook
      doorLogicAlcove = "door_alcove_1"		--must be unique
      firstTimer = "lever_timer_1"			--should be unique
      secondTimer = "latch_timer_1"			--should be unique
      thirdTimer = "open_door_timer_1"		--should be unique
      
      function resetDoor()
      	findEntity(doorToOpen):close()
      	if findEntity(doorLogicAlcove) == nil then
      		spawn("talk_alcove", self.level, self.x, self.y, self.facing, doorLogicAlcove)
      			:addItem(spawn("talk_box", nil, nil, nil, nil, doorLogicItem))
      	end
      end
      
      resetDoor()
      
      function knock()
      	if findEntity(firstTimer) ~= nil then
      		return
      	end
      	playSound("door_knock")
      	spawn("timer", self.level, self.x, self.y, 0, firstTimer)
      		:setTimerInterval(3)
      		:addConnector("activate", firstTimer, "deactivate")
      		:addConnector("activate", self.id, "lever")
      		:activate()
      end
      
      function lever()
      	playSound("lever")
      	spawn("timer", self.level, self.x, self.y, 0, secondTimer)
      		:setTimerInterval(1)
      		:addConnector("activate", secondTimer, "deactivate")
      		:addConnector("activate", self.id, "latch")
      		:activate()
      end
      
      function latch()
      	playSound("key_lock")
      	spawn("timer", self.level, self.x, self.y, 0, thirdTimer)
      		:setTimerInterval(1)
      		:addConnector("activate", thirdTimer, "deactivate")
      		:addConnector("activate", self.id, "openDoor")
      		:activate()
      end
      
      function openDoor()
      	findEntity(doorToOpen):open()
      	-- Cleanup Generated Assets --
      	findEntity(firstTimer):destroy()
      	findEntity(secondTimer):destroy()
      	findEntity(thirdTimer):destroy()
      	local temp = findEntity(doorLogicAlcove):containedItems()
      	temp():destroy()
      	findEntity(doorLogicAlcove):destroy()
      end
      
A few usage notes:

The door can be reset (closed and *knock* *knock* logic reset) by calling the script's resetDoor() function.

This script is designed to be usable in multiple places on multiple doors without interfering with each other. You just need to repeat steps 5-7 for each door.

The scripts may not work properly if one is placed on either side of the same door. This isn't hard to fix, but I didn't intend to support it here.
Last edited by Edsploration on Fri Nov 23, 2012 5:41 pm, edited 1 time in total.
Open Project -> Community FrankenDungeon: viewtopic.php?f=14&t=4276
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Useful scripts repository

Post by Komag »

Tick Tock function using NOT and a flipping True/False boolean

example by antti

here's a nice simple solution that is just a single function and only needs a single timer that is always on:

Code: Select all

tic = true
function ticking()
  if tic then
    playSound("click_down")
  else
    playSound("click_up")
  end
  tic = not tic
end
This flips the boolean from true to false and vice versa so that a different sound is played next time the function is called.

You can use this for any number of situations where you want two separate actions to always happen one after the other every time a function is triggered.
Finished Dungeons - complete mods to play
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: Useful scripts repository

Post by Xanathar »

Finding facing direction given start/end points

This functions get the "facing" direction given two sets of point (from and to) or dx and dy:
SpoilerShow

Code: Select all

function directionFromPos(fromx, fromy, tox, toy)
	local dx = tox - fromx
	local dy = toy - fromy
	return directionFromDelta(dx, dy)
end

function directionFromDelta(dx, dy)
	if (dx > dy) then dy = 0; else dx = 0; end

	if (dy < 0) then return 0; 
	elseif (dx > 0) then return 1;
	elseif (dy > 0) then return 2;
	else return 3; end
end
Can be handy for spawning in the correct direction when the target changes over time.
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
dasarby
Posts: 28
Joined: Sun Dec 30, 2012 10:46 pm

Re: Useful scripts repository

Post by dasarby »

"Light's out" style lever puzzle

As first outlined here: viewtopic.php?f=14&t=4718

This script is used to create a puzzle in which toggling a lever toggles any number of other levers. The puzzle is solved when all levers are down. It is sports the following:
  • Any number of levers
  • Any lever can be connected to any other lever, regardless of where it is placed in the game.
  • Easy to define function which to run when the puzzle is solved/unsolved
  • Option to make the puzzle solved when all levers are deactivated.
The script is pretty well commented, explaining how things work and how to configure it for your puzzle.

https://github.com/adharris/grimrock/bl ... Levers.lua

Code: Select all

-- Script for a "lights out" style lever puzzle, where toggling one lever also
-- toggles all "adjacent" levers.  Each lever can be connected to any number of
-- other levers.  The puzzle is "solved" when all levers are "activated" (in
-- LoG a lever is activated when it is down).  

-- USING THIS SCRIPT: 
-- Define adjacent levers using the "levers" variable.  Change the "Solved"
-- function to perform the action to take when the puzzle is solved.  Change
-- the "Unsolved" function to be the action to take when the puzzle is moved
-- out of the solved state.  Then, add connectors to each lever like such:

--  Event        Target             Aciton
--   any    ->    this script  ->    PullLever


-- levers is the list of lever adjacencies. Each item in the table should
-- corispond to a single lever used in the puzzle. The key should be the
-- id of each lever, and the value is a list of ids of the levers to toggle
-- when the key lever is pulled.  For example:
--     leverX={"LeverY", "LeverZ", "LeverW"}
-- will cause leverY, LeverZ, and LeverW to be toggled whenever leverX is
-- toggled. Notice that the keys are not surronded by quotes, while the
-- adjancet levers are.  There is no limit to the number of levers in the
-- puzzle or the number of levers adjancet to each lever.
levers = {
	lever1={"lever2"},
	lever2={"lever1", "lever3"},
	lever3={"lever2", "lever4"},
	lever4={"lever3", "lever5"},
	lever5={"lever4"}
}

-- The following function will be called when the puzzle is "solved",
-- meaning that all levers are "activated" (down).  For example, this
-- function could a door.
function Solved()
	puzzleDoor:open()
end

-- The following function will be called when the puzzle is in any state that
-- is not "solved".  For example, closing a door.  If the puzzle should stay
-- "solved" after the player solves the puzzle once, you can leave this function
-- blank.
function Unsolved()
	puzzleDoor:close()
end

-- Set the following variable to true in order to make the puzzle solved when
-- all levers are deactivated too.  This is useful if you only want to make
-- the player get all levers to the same state, rather than forcing all levers
-- to be down.  Note that if you do this, you should be sure to start some 
-- levers up and some down, so the puzzle doesn't start solved
solvedWhenDeactive = false


----------- You shouldn't need to change anything below this point ----------

-- locked is a semaphore to prevent an infinite loop. You can ignore it.
locked = false 

-- Pull lever is the action that should be taken when a lever in the puzzle is
-- toggled. Connect each lever in the puzzle to this function.
function PullLever(trigger)
	-- Only run if the semaphore is not set, in order to prevent infinite recursion
	if not locked then
		-- Set the semaphore
		locked = true

		-- If the triggering lever is in the adjaceny list, loop over all its
		-- adjacent levers and toggle them in turn.
		if levers[trigger.id] then
			for _, id in ipairs(levers[trigger.id]) do
				-- search for the lever by id, and toggle it.
				findEntity(id):toggle()
			end
		end
		-- There's no more chance for recursion, so clear the semaphore.
		locked = false

		-- Loop over each lever in the puzzle, as defined by the keys in the
		-- levers table, and check to see if every one is activated or every
		-- one is deactivated.
		local allActive = true
		local allDeactive = true
		for id, _ in pairs(levers) do
			-- find each lever by id, and check its state.
			local lever = findEntity(id)
			allActive = allActive and lever:getLeverState() == "activated"
			allDeactive = allDeactive and lever:getLeverState() == "deactivated"
		end

		-- If all levers are active, or if solvedWhenDeactive is set and all
		-- levers are deactive, call the Solved function.  Otherwise, call
		-- the Unsolved function.
		if allActive or (solvedWhenDeactive and allDeactive) then
			Solved()
		else
			Unsolved()
		end
	end
end
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Useful scripts repository

Post by Komag »

Give a DMG bonus (temp STR) based on DEX when using Missile Weapons:

You'll want to refer to http://www.grimrock.net/modding/asset-d ... reference/
You might use monster onDamage or onProjectileHit, or party onAttack (not onProjectileHit because that is when the party is hit). Party onAttack sounds like the closest match to what you want.

To use the hook you need to clone the party object and define the hook into it. Make sure you have downloaded the asset pack and look at the objects script to see the full original party object. You will do a clone and add the hook and then link to a script name that you put in your dungeon. This keeps the hook simple. Then in your dungeon, put all your code in the script item.

In your objects.lua file (in your dungeon scripts folder deep in your documents folder):

Code: Select all

cloneObject{
  name = "party",
  baseObject = "party",
  onAttack = function(champ,weapon)
    dexAttackScript.dexAttack(champ,weapon)
  end,
}
in the dexAttackScript script entity (the little "lua" item you put in the dungeon via the editor):

Code: Select all

str = 0
ch = 0

dexAttack(champ,weapon)
  ch = champ:getOrdinal()
  str = champ:getStatMax("strength")
  local dex = champ:getStatMax("dexterity")
  if weapon.name == "sling" or weapon.name == "short_bow" or weapon.name "crossbow" or weapon.name == "longbow" then
     champ:setStatMax("strength", str+(dex*0.5)) -- change 0.5 to whatever you want
     champ:setStat("strength", str+(dex*0.5))
  end
  spawn("timer",party.level,party.x,party.y,0,"dexAttackTimer")
   :setTimerInterval(0.03)
   :addConnector("activate", "dexAttackScript", "strReset")
   :activate()
end

strReset()
  party:getChampion(ch):setStatMax("strength", str)
  dexAttackTimer:destroy()
end
I wrote this off the cuff so I may have made some mistakes or completely forgot about some aspect :)

EDIT - this concept would probably work better use onEquipItem or onUnequipItem hooks, so that the bonus effect could be seen immediately right on your inventory screen. There might be other considerations too, such as whether they are holding skulls (if minotaur) and how that might affect current strength, whether they have any other dex enhancing hear equipped, etc


-----
original thread for discussion/questions:
viewtopic.php?f=14&t=4783
Finished Dungeons - complete mods to play
Post Reply