Lua Basics

Lua is a dynamically-typed language which is interpreted by the game when executed. It's beyond the scope of this documentation to provide a thorough lua tutorial, but there will be plenty of exaxmples. Here are a few key things to keep in mind:

Comments

You can add comments to your code in order to make the intent clearer to future readers. Please do this! You may know what the complex code in your prog does today, but when another builder has to update it 4 years from now, what do you want them know?

Comments begin with a -- and can be on their own line or after some code on the same line.

-- This is a comment on its own line.

local testVar = 5 -- This is an inline comment.

Declaring Local Variables

In any program, you can define new variables. You should generally only do this inside functions registered via triggers, and always set them as a local variable to avoid unintentional conflicts with other code.

self:onSpeech("test", function(self)
  -- Correct. The first time you set testVar, it should have
  -- "local" in front.
  local testVar = 5

  -- Incorrect. badVar hasn't been set inside this scope, so the
  -- program will create a "global" variable which may mess with
  -- any other function using a variable with the same name.
  badVar = 5

  -- This is fine, since testVar was already declared earlier in
  -- the same function. This just changes it to a different value.
  testVar = 6
end)

If/Else Checks

Here are some examples of how to do if/else checks.

Operators

For numbers:

if someNumber == 1  -- Equals 1
if someNumber ~= 1  -- Not equals 1
if someNumber > 1   -- Greater than 1
if someNumber >= 1  -- Greater than or equal 1
if someNumber < 1   -- Less than 1
if someNumber <= 1  -- Less than or equal 1

For strings:

if someString == "Test"     -- Is Test (case sensitive)
if someString ~= "Test"     -- Is not Test
if someString               -- Is not nil/empty
if someString:find("Test")  -- String contains Test

Else / Else If

if testVar == 1 then
  -- Do something
elseif testVar == 2 then
  -- Do something
else
  -- Do something
end

Note that you always need a then after any condition. else does not need a then. The last block in the if/else chain must always be ended with an end.

Check for nil (empty) variables

local testShip = Ship.getFromName("Testing")

-- Check to see if testShip is nil rather than a real ship.
-- "Nil" is Lua-speak for empty/missing. It's important to check
-- for because calling functions on a nil variable will error.
if testShip then
  -- Do something because it's a real ship
else
  -- Do something if it's not found
end

You can also use if <variable> then ... on numbers or string variables. Numbers set to 0 and strings which are empty will all act like a nil variable.

Not / Not Equals

To reverse the meaning of an if check, put not before it:

if not testShip then
  -- Do something if testShip is nil
end

To see if something is not equal to something else, use ~=:

if testShip:getName() ~= "Testing" then
  -- Do something if testShip's name is not "Testing"
end

And / Or

if testShip and testShip:getName() == "Testing" then
  -- Do something if the ship is non-empty and its name is "Testing"
end

Normally, calling testShip:getName() would fail if testShip was nil because you can't call functions on a nil variable. However, checking for it to be non-nil first protects us from that. It won't even bother checking the ship's name if the first condition ends up being false because the ship is nil.

if testShip:getName() == "Testing" or testShip:getName() == "Other" then
  -- Do something
end

Calling functions

There are two main types of functions: Those which you can call globally, and those which you can call from a variable.

Invoking an object is an example of a global function. These usually look like TypeName.functionName(args) - note the period.

If, instead, you already have an object variable and you want to get its name, you could call myObject:getName(). Note the colon. Calling a function on a variable always goes like <var name>:<function>. myObject.getName(), with a period instead, would complain that there is no such function.

To see what functions are available on a given variable, go to the relevant module on the sidebar.

Constructing Strings

You can construct strings using other variables by using .. to concatenate them (join them together). For example:

local firstName = "Han"
local lastName = "Solo"

-- This will result in "Hello, Han Solo"
local greeting = "Hello, "..firstName.." "..lastName

The variables don't all have to be strings. You can put numbers in the string in the same way.

If you want to put a double quote in a string, you'll need to escape it using \. For example:

local greeting = "Hello, \"Han Solo\""

Code Blocks (Scopes)

Like most programming languages, Lua is constructed of blocks of code. All the code inside a function is a block. You can nest other blocks inside that, like if/else statements. You must indicate when each block is done, usually with end.

Each block is considered a "scope" for variables. If you declare a variable inside a block, it can be used or updated inside that block or any block inside it.

self:onSpeech("test", function(self, ch, fullText)
  -- Available anywhere after this line in the function
  local mainVar = 5

  if fullText:find("red") then
    local redVar = 6
    -- Here, we could use either mainVar or redVar.
  end

-- We could not use redVar here, since its block has ended
end)

Indentation

For readability, it's also best to indent all of the code inside a block so it's clear when it begins and ends.

Consider this example:

self:onSpeech("test", function(self, ch, fullText)
if ch:getName() == "Tester" then
if ch:getTopLevel() > 50 then
ch:echoAt("You said test and you're high-level!")
else
ch:echoAt("You should get to level 50 first.")
end
else
ch:echoAt("Who are you?")
end
end)

There's a function with a couple levels of if/else blocks inside, but at a glance it's difficult to see how the program flows. Now, with indentation:

self:onSpeech("test", function(self, ch, fullText)
  if ch:getName() == "Tester" then
    if ch:getTopLevel() > 50 then
      ch:echoAt("You said test and you're high-level!")
    else
      ch:echoAt("You should get to level 50 first.")
    end
  else
    ch:echoAt("Who are you?")
  end
end)

Now it's much easier to see what happens if their name is "Tester" and what happens otherwise.

When editing your code, be sure to indent with spaces, since the mud won't recognize tabs. Preferably, use two spaces instead of four, since the game's limited line lengths put space at a premium. These are preferences you can set in any code editor.

To save space, you can also write simple blocks on one line, but you still need to note the end of it.

-- This would exit a function early (return) if the
if not ch then return end

Loops

You can run a block a number of times by using a for loop. When running it a number of times, it's common to use i as the variable indicating which iteration you're on.

-- This loop will run 10 times and print a message each time
for i = 1, 10 do
  LOTJ.log("In iteration "..i)
end

Sometimes you have a list of things you want to loop over rather than a fixed number. For example, you might want to loop over every ship in a starsystem. In that case, the syntax looks like this:

local system = <some starsystem>
for ship in system:ships() do
  -- "ship" is now a local variable which you can use in this block
  LOTJ.log("Ship in system is"..ship:getName())
end
generated by LDoc 1.5.0 Last updated 2024-10-22 16:05:00