Learning Lua proper will undoubtedly allow you to make the most use out of GD’s scripts and be the most efficient in the process.
I’d recommend something like CodeAcademy for getting started with it.
I myself would not claim to know Lua, but all the same I’ve used scripts ample times in GD by looking at the way Crate has done things and copy/pasting/adjusting things from there. This was how, years ago, I made the Bounties Unlocked mod. More recently, this is how I made the not-so-secret secret-quest in Grim Armory, as well as the spawning mechanism for GA’s smith (of which I had a lot of help from the community). Both of these mods are open source if you want to peer inside.
One of my problems with learning a new programming language is finding the right environment, for me (or, in this case, you), to comfortably test things out and experiment in. (This is why I’ve never learned C++, as I’ve hated every environment thereof.)
Here’s a quick rundown of how scripts are executed in Grim Dawn:
Most, if not all, entities in Grim Dawn have a number of Script ‘hooks’ associated with them. These hooks “play” their linked script when their associated event triggers in-game. Here’s Alkamos, for instance:
This calls the /quests/homesteadstepsoftormentbosses.lua file of which the first (non-commented) line in that file looks like this:
gd.quests.homesteadStepsOfTormentBosses = {}
(This should look similar to the above)
The {} here defines a currently-empty list of functions as being associated with the ‘gd.quests.homesteadStepsOfTormentBosses’ variable. It’s a mouthful, but it is a variable declaration like any other.
This file then has a function some distance in called “Boss2Killed”:
function gd.quests.homesteadStepsOfTormentBosses.Boss2Killed()
The keyword ‘function’ tells the script that it’s a function, and this function is added to our aforementioned empty list of functions by the ‘.’ appended to the variable call. This will make more sense the more you play around with it.
What follows in this function are a different bunch of different commands from elsewhere in GD’s scripting library, being executed on different local and global variables. I’ll copy/paste the entirety of this function as it looks in Grim Armory, as it has been modified by me to be the starting point of Grim Armory’s secret quest:
--Secret Quest Drop Randomizer
local AlkRandomizer = 0
local RandomizerRan = false
function gd.quests.homesteadStepsOfTormentBosses.Boss2Killed()
GiveTokenToLocalPlayer("HS_STEPSOFTORMENT_CLEARED")
--- Vanilla GD script
if Server then
local door01 = Door.Get(doorBossB01)
local door02 = Door.Get(doorBossB02)
if door01 != nil then
door01:Open()
end
if door02 != nil then
door02:Open()
end
end
--- Grim Armory script
if Server then
if not RandomizerRan then
math.randomseed(Time.Now())
RandomizerRan = true
AlkRandomizer = math.random(100)
print("Ran randomizer:")
print(AlkRandomizer)
end
end
---33% chance to begin secret quest on killing Alkamos
if AlkRandomizer >= 67 then
local player = Game.GetLocalPlayer()
if (player != nil && Game.GetGameDifficulty() == Game.Difficulty.Legendary) then
local ptag = "tagMalNotif1"
local ptoken = "PROTO_BEGIN_QUEST"
local pitem = "records/items/gearrelic/secret_relic.dbr"
-- holy crap Zantai this code is unreadable when you don't extract this stuff out to variables wtf
if (player:HasToken(ptoken) == false && player:HasItem(pitem, 1, false) == false) then
if(player:IsSpaceAvailable(pitem)) then
UI.Notify(ptag)
player:GiveItem(pitem, 1, false)
player:GiveToken(ptoken)
end
end
end
end
end
Now, while this is all well and good, remember that whole ‘environment’ debacle I was talking about earlier? In order to actually attach this code to the game’s library of scripts, we need to ensure any script we want to run is “Loaded”. In this case, we’re modifying an existing script that vanilla content loads by default, so we’re all set. But if we were creating an original script file, as Grim Armory does with its “proto.lua” file in the /scripts/game/questsproto/ directory, we need to be sure to add this file to the grimdawn.lua file that Crate wrote:
gd = {}
// Endless Dungeon
Script.Load("scripts/game/endlessdungeon.lua")
// Quests
Script.Load("scripts/game/quests.lua")
--- Steps of Torment/Alkamos is loaded here, via the quests.lua file.
Script.Load("scripts/game/dungeonchestsgdx1.lua")
Script.Load("scripts/game/dungeonchestsgdx2.lua")
Script.Load("scripts/game/dungeonchests.lua")
Script.Load("scripts/game/events/waveevent.lua")
//Protosets
Script.Load("scripts/game/questsproto/proto.lua")
grimdawn.lua is itself loaded by main.lua, one directory higher:
/*
GRIM DAWN
scripts/main.lua
Scripting entrypoint.
For more information visit us at http://www.grimdawn.com
*/
-- Libs
Script.Load("scripts/libs/shared.lua")
-- Game
-- Also loads protoset scripts from quest files
Script.Load("scripts/game/grimdawn.lua")
-- Augur
Script.Load("scripts/augur/augur.lua")
main.lua is, presumably, loaded automatically when the game is launched.
Suffice to say that if you have a script of your own that you want to execute in-game, you need to both attach that script to an entity to call it when a certain trigger is met, AND you need to ensure the script is loaded into GD’s scripting engine.