Contents |
---|
Overview |
Creating your own Macro |
Settings |
The Debug Log |
Code Mods |
‘Hello World' - A Coding Example |
DMHub is moddable using Lua, a popular programming language for modding games and other apps. This guide aims to get you going modding DMHub. Its contents are also entirely applicable to the Draw Steel Codex which is based on DMHub.
This guide won't explain every programming concept or Lua feature in detail, but it will try to be approachable enough that even someone new to programming can follow it. This guide does assume you are familiar with DMHub and have it set up and working on your machine.
To get the most out of this guide, you should read it in order, trying to understand each section before moving on. It is recommended to try any examples or commands for yourself as you read along. The sections depend heavily on each other so if you don’t understand one section it may be difficult for you to understand the next.
The goal of this tutorial is to teach you how to create your own macros! So let's dive into what a macro is and how you can create one.
DMHub’s Chat Panel allows you to run macros by beginning with a /
. For example, typing /roll 2d6
will roll 2d6. Another useful macro is /clear
which will clear the chat.
Macros are very useful and the first thing we are going to look at coding with a mod are some macros. Macros can also be bound to keyboard shortcuts. For instance, you can type /bind Ctrl+P roll 2d6
to make it so whenever you press Ctrl+P the macro /roll 2d6
will execute. (Make sure you don’t have any text elements focused when you test this, e.g. don't have your cursor in the chat or debug panels. Keybinds are ignored when you are entering text.).
You can always see a list of your key bindings by typing /showbinds
and you can use /resetbinds
to reset all your key bindings to the defaults.
DMHub has a notion of global variables (words/names that store numbers) called “settings variables”. For example, all of the Settings found in DMHub’s Setting dialogs are just a nice user interface which allows a user to change the values of the settings variables.
The "Font Size" setting for example is controlled by a Setting Variable called fontsize
. If you change the value of fontsize from 40 to 60, the size of the font will go from 40 to 60.
Try this out in the chat panel right away! type /fontsize
to see the current size of the font used all over DMHub.
Now type /fontsize 120
to change the size of your font to 120. Try some different numbers!
You could change the value of fontsize by typing, for instance, /fontsize 140
in chat. You will see your font size will change, and if you return to the settings dialog the value ascribed to Font Size will be changed.
You will notice that the Font Size in settings only allows certain reasonable values for font size to be set. But if you change the font size setting directly there aren’t any such restrictions. This means you can set the font size to unreasonable values. As a developer you won’t have the same safety mechanisms in place as when you’re a user!
To get ready for modding in DMHub, there are two settings you should modify to enable some developer features.
To mod in DMHub you should type the following commands:
/dev true
/debug true
These will set you into development mode and also turn debugging for DMHub on. This turns on several features which might slow the app down a little or be annoying for regular users but are important if you are going to be modding DMHub. These settings are saved so you should only have to use them once on a computer. Since these are settings like any others, you can type /dev
and /debug
to inspect the current values of these settings.
When programming, it is very important to be able to print information from the code you are writing and read that information. To facilitate this, DMHub has a Debug Log panel that will display debugging information. It is one of the features that is enabled once you turn /dev true on
You should open the Debug Log panel and use it whenever you are developing code for DMHub.
You can drag the Debug Log to place it among your panels in a place that is comfortable for you and also resize it.
TIP! Filter away unnecessary debug messages by giving your important ones a "tag" in front of them. For example by writing MYPANEL:: in front of all your prints in your lua code (example: print("MYPANEL:: panel done loading") ) and then writing "MYPANEL" in the search bar of the debug log panel you will only be able to see the logs with that "tag".
In these examples I will be using DBG: (for “debug”) as the tag.
A nice empty debug log is where you want to start any DMHub development.
DMHub’s Core Engine takes care of core functionality such as rendering the map and making tokens move smoothly. The code for this is not accessible outside of DMHub’s core development team.
Most game rules, user interface elements, and many other aspects of DMHub are implemented in what we call Code Mods. A Code Mod is a package of Lua code which is run when you enter into a game within DMHub. We try to “eat our own dog food” when developing DMHub, so lots and lots of DMHub is implemented by the core development team using Code Mods. You can view all the code for our code mods and you can make your own code mods.
To access DMHub’s code mods, go to the Compendium (found inside the Tools menu) and near the bottom is a section for Code Mods, select it to view all the Code Mods that are loaded into the game you are in.
Try pressing View for one of the files to view the Lua code for that file. Windows will be directed to open the file and will use whatever program you have set up to view .lua files. If you don’t have anything set up, Windows will ask you to choose which program to use to open lua files. You can use any text editor you are comfortable with. If you haven’t coded before and don’t have a text editor you normally use, I recommend Visual Studio Code
Try viewing some Lua files and become comfortable with being able to navigate between the code mods and view files. Note that you can also use the “Search” functionality to search for text within files. Try searching for “savingthrow” to find code that might interact with saving throws.
Now that you can access Code Mods within DMHub, it’s time to make your own Code Mod. To do so, simply press the little Plus Icon (“+”) at the bottom of the list of mods. Give your mod an appropriate name and description.
You will see that your mod automatically has one file called “Main”. Click on this to rename it to something appropriate.
Now that you can access Code Mods within DMHub, it’s time to make your own Code Mod. To do so, simply press the little Plus Icon (“+”) at the bottom of the list of mods. Give your mod an appropriate name and description.
You will see that your mod automatically has one file called “Main”. Click on this to rename it to something appropriate.
After you check out your mod you will see that the View button next to the Lua file in your mod will have changed to an Edit button. You can press it to open the file and begin editing it. Any changes you make to the file will be executed by DMHub as soon as you save it.
Changes you make to your Code Mod will be available to you. If you are happy with changes to a Code Mod and want other users in the game to start using the changes you have made, you can enter a comment detailing your changes and then choose Check In Code to upload your code and save it permanently.
Check In Code will appear only once you enter a comment.
You can then check out your code again if you want to make further changes.
Now that you are changing Lua code it is possible you might cause DMHub to freeze or become unusable. If this happens it is important to be able to recover from this. You can restart DMHub and go to the settings from the title screen. Choose the option Disable Local Mods. This will put DMHub into a safe mode where any uncommitted changes you have made to mods will be ignored. This will allow you to enter your game safely and use the Compendium to access your Code Mods and edit the code and try to fix whatever problems you have caused. Once you think you have a solution you can turn off Disable Local Mods and try out your code again.
If you have made some changes in code mods and things start behaving strangely in your game in any way it is recommended to see if selecting Disable Local Mods fixes these problems.
In a grand tradition of programming, the first thing you should code in DMHub is it printing Hello, World to the debug log. To do this, edit the Lua file for your checked out code mod. You will see the file starts with a single line:
local mod = dmhub.GetModLoading()
This line has its uses but you can simply ignore it for now. Instead you can start adding code under it. Specifically, add this:
print("DBG: Hello, world")
So your code should now look like this:
local mod = dmhub.GetModLoading()
print("DBG: Hello, world")
(Of course if you chose a different ‘tag’ to use for messages in the debug log instead of DBG use that instead).
Switch back to DMHub after saving the file and you should see this in the Debug Log:
DMHub executes your entire Lua file again if it detects changes have been made. Try editing the file and change the line to say
print("DBG: Hello, world!!")
DMHub will automatically re-execute your code when you save it and thus print the new version of the message in the Debug Log.
To do useful things with a code mod, you don't really want code to be executed when the game is loaded, or when you change your mod. Instead, you want to have code run when some kind of event happens. In our case we are going to make a macro called hello. When someone types /hello
this is our event that we will respond to by printing a message.
Use this code to create our hello macro:
Commands.hello = function(args)
print("DBG: Hello to you too!")
end
Add this to your Lua code and save it. Nothing will happen right away, but type /hello
in DMHub's chat panel and you will see the message displayed. You have created your first macro. Any code inside the function will be run whenever the user types /hello
.
A macro can accept arguments. For instance, if you type /roll 2d6 + 4
then "roll" is the name of the macro and 2d6 + 4 are the "arguments". When we use function(args)
in the Lua code we wrote, "args" means that we are putting the arguments to the macro in a variable called "args". Let's modify our code to look like this:
Commands.hello = function(args)
if args == "" then
print("DBG: Hello, can you please tell me your name?")
else
print("DBG: Well hello there", args)
end
end
Now the user can provide their name to the macro. For instance, /hello David
and their name will be printed. If they don't provide a name, their name will be asked for.
In this code, we use variables and an if statement. We check if args is equal to an empty string (a word or a sentence - a "string" of characters/letters). If you aren't familiar with any of these concepts it is recommended to do some reading about them and try to understand.
Take this code and try experimenting with it. Play around with it and try out different things.
To do useful things you will want to access information about the game. Characters on the map. Users who are logged in. Which characters are selected. DMHub comes loaded with functionality to allow you to do this. The best way to find out how to do something is to use the code mod searching feature and try to find existing examples.
As an example of what you can do, we are going to modify our hello macro to address each selected token and tell them how many hitpoints they have. Modify your hello macro code to look like this:
Commands.hello = function(args)
if #dmhub.selectedTokens == 0 then
print("DBG: No tokens selected!")
else
for _,token in ipairs(dmhub.selectedTokens) do
if token.name == nil then
print("DBG: Hello there my anonymous friend you have", token.properties:CurrentHitpoints(), "hitpoints")
else
print("DBG: Hello there", token.name, "you have", token.properties:CurrentHitpoints(), "hitpoints")
end
end
end
end
This code will look at all the tokens we have selected. It will tell us if we don't have any tokens selected. If we do have tokens selected it will address each of them by name and tell them how many hitpoints they have.
Try it out. Select tokens and type /hello
in chat.
DMHub makes available dmhub.selectedTokens
which is a list of all the tokens that are currently selected. If there are no selected tokens it will be an empty list.
If you aren't so familiar with Lua, you should note that #dmhub.selectedTokens
is the way to tell how many items are in a list. Going for _,token in ipairs(dmhub.selectedTokens)
probably looks mildly terrifying. What it does is says we want to run the following code once for each of the tokens and it creates a temporary variable called token which will refer to the currently referenced token
in the list.
Of course, printing things out to a debug console isn't an actually useful macro in the real world. Let's make a macro that approaches something you might want to put in a game.
We will make a macro called fullheal
that heals all selected tokens. Before we do this, w eneed to ensure that we have a map setup with a couple of tokens so we can test the macro once we've made it.
You can do that like this:
Commands.fullheal = function(args)
if #dmhub.selectedTokens == 0 then
print("DBG: Heal: No tokens selected!")
else
for _,token in ipairs(dmhub.selectedTokens) do
local name = token.name or "Anonymous"
token:ModifyProperties{
description = "Full Heal",
execute = function()
local amount = token.properties:MaxHitpoints() - token.properties:CurrentHitpoints()
if amount <= 0 then
print("DBG: Token", name, "Does not require healing")
else
token.properties:Heal(amount, "Healed by Full Heal macro")
end
print("DBG: Healed", name, "by", amount, "hitpoints to full health")
end,
}
end
end
end
After adding this to your Code Mod, if you type /fullheal
in chat, all selected tokens will be healed. It's good practice when developing code to put print statements describing what you are doing in detail as if things don't work as expected the print statements will give you an idea what is going on. For instance, if you forgot to select any tokens, you wouldn't be confused about why your code isn't working since the print statement would tell you.
There is quite a lot going on here and it would probably take a little to digest! We'll try to step through some different details.
The dmhub
variable is a variable DMHub provides which contains a large variety of member variables such as selectedTokens
which gives you a list of all of the selected tokens. Effectively the dmhub variable represents the DMHub engine and the functionality it provides. The dmhub
variable comes laden with many variables and functions which do different things. You can search through the other code mods to try to find the different kinds of things you can do.
DMHub also tries to make it possible to inspect just about any variable by using print in Lua. So in your Lua code, you can just add this:
print("DBG: dmhub variable:", dmhub)
and it will print out information about the variable. Since this dmhub
variable is a complex object with many fields, it will be printed as a link which you can click on to get a list of every field and function within the variable.
PICTURE
This can give you some pretty good ideas about the functionality that is available. You can search for interesting looking fields in Code Mods to find examples of how to use the field.
The dmhub variable provides several ways to access tokens including selectedTokens
which we used above. You can likewise print out tokens which are themselves objects rich with functionality that allow you to inspect and modify in interesting ways.
In our fullheal
macro we had this code:
token:ModifyProperties{
description = "Full Heal",
execute = function()
local amount = token.properties:MaxHitpoints() - token.properties:CurrentHitpoints()
if amount <= 0 then
print("DBG: Token", name, "Does not require healing")
else
token.properties:Heal(amount, "Healed by Full Heal macro")
end
print("DBG: Healed", name, "by", amount, "hitpoints to full health")
end,
}
This is the code that actually heals the token. When you want to change a token's properties, it is important to do so by using the ModifyProperties
that token provides to do so.
When you use ModifyProperties
, you provide an execute function which is responsible for carrying out the changes to the token’s properties.
ModifyProperties
takes care of watching all the changes you make to the token's properties and ensuring they are recorded permanently and sent to other player’s computers.
When testing something like the full heal macro it's a good idea to press f5. It will refresh the game and flush all temporary changes. You should test that the creature's health is still changed after pressing f5 to ensure that the changes persisted correctly.
This guide should have given you a good overview of DMHub's modding system and how to develop a macro. Remember, you can bind a macro to keys, using for example /bind ctrl+h fullheal
to make it easy to use at any time. Once you check in your code mod others will have access to it too, and you can package it up and put it in a module and share it with other users if you would like.
By searching through DMHub's existing Code Mods you can find recipes for all sorts of interesting things. We're excited to see what you create!