API Quick Example - MrCatchworth/xrebirth-tactical-map GitHub Wiki

This document will explain simply how to add a small command to the map with your own extension.

File Structure

Your extension is just a game extension like any other, so it follows the usual folder structure. The simplest extension is an empty folder in the game's extensions folder with one file, content.xml. That file gives the name, description, version, and other metadata of your extension. You can look at Egosoft's example for more on that.

Now we add another file, mej_tacmap_orders.txt. This file contains your new commands, and the map loads it automatically when the game is loaded. Despite the .txt extension, this is a Lua script, and the code will be run in the game's UI environment.

Simple Command File

Let's set up our script to add nothing at all to the map, and work from there. That looks like this:

return {
    issuableOrders = {},
    gridOrders = {}
}

It looks odd, but you can indeed 'return' from a file. This is how you send your commands to be added to the map.

There are basically three types of command for the map - maybe two depending how you look at it. They consist of:

  • Commands that go in issuableOrders and are part of the contextual right-click. They get issued as soon as you have a selection and right-click something on the map.
  • Commands that go in gridOrders and take up a button on the bottom right of the map. These get issued when you have a selection and left-click on the button.
  • Commands that are the same as above, except they need a target in addition to just a selection. When you left-click the button, its text becomes highlighted, but nothing else happens. When you next right-click on something in the map, that command is then issued instead of going through the contextual right-click, and the highlight goes away.

For our example, let's add one un-targeted grid command for the bottom right of the map that makes your ships patrol their current zone. We therefore add a new table to gridOrders:

return {
    issuableOrders = {},
    gridOrders = {
        {
            buttonText = "Patrol Here",
            
            multicast = true,
            
            buttonFilter = function(order)
            end,
            
            requiresTarget = false,
            
            issue = function(order)
            end
        }
    }
}

Our new addition is not done yet, but it already has several members. Let's go through them:

  • buttonText: A string that says what text the button should have on it. You have the same global functions as the game's UI here, so you can (and ideally should) use ReadText rather than a literal string.
  • multicast: true or false. If this is false, the command will never be available when more than one ship is selected. This is needed when the command would open a new menu, but in our case there is no problem, so we set it true.
  • buttonFilter: A function that returns true or false. If you return true the button is clickable, if false then it is greyed out. You use this function to inspect the selection and the map's current state, and decide whether the player should be able to click the button right now.
  • requiresTarget: true or false again. This tells the map whether your grid command needs a target or not, as explained above.
    • In our case, we don't need a target, so we set this to false.
  • issue: A function that doesn't return anything, but specifies how to actually issue the command to selected ship(s).

Notice the two functions take only one argument rather than several. All the basic properties of the menu's state - selection, current space, target and such - are collected into an object and given to the functions in one go. By convention I call this an order object.

For all these functions, the subject in an order object is always just one ship. If there are several ships selected, the map calls the function repeatedly, once for each selected ship.

So far, everything is already set up besides buttonFilter and issue. First, we need to decide when the button should be enabled. We could do some funky logic to stop the player issuing this command when the selected ship has no way of fighting. But let's do the simple thing for now and say the button is always enabled:

...
buttonFilter = function(order)
    return true
end,
...

Now we just need to work out how to issue our command to a subject. Without getting bogged down in details, to follow the game's conventions we must:

  1. Signal the ship to stop
  2. Get its current zone, and convert it to the datatype the MD/AI system likes to see
  3. Signal the ship to start patrol, with a delay (the "attack enemies" signal doubles as the signal to patrol a zone)

That gives us the following code:

...
issue = function(order)
    SignalObject(order.subject, "stop order")
    local zone = ConvertStringToLuaID(tostring(GetContextByClass(order.subject, sameSpaceType, false)))
    SignalObject(order.subject, "attack enemies", zone, nil, 0.2)
end
...

And our overall file will look like this:

return {
    issuableOrders = {},
    gridOrders = {
        {
            buttonText = "Patrol Here",
            
            multicast = true,
            
            buttonFilter = function(order)
                return true
            end,
            
            requiresTarget = false,
            
            issue = function(order)
                SignalObject(order.subject, "stop order")
                local zone = ConvertStringToLuaID(tostring(GetContextByClass(order.subject, sameSpaceType, false)))
                SignalObject(order.subject, "attack enemies", zone, nil, 0.2)
            end
        }
    }
}

Try this in-game, and you should see the new button and issue the command to both one ship and multiple ships.

Further Reading

Obviously, we haven't gone over right-click commands or targeted grid commands - the idea is basically the same, just with a few different members in the table.

For more information, see the API documentation on this Wiki. The Vanilla Commands addon also shows several working examples of all kinds.