Difference between revisions of "Startbox API"

From Zero-K
Jump to navigation Jump to search
(More examples)
(→‎Box counts: Added the minimap so the reader doesn't have to click to look at it.)
 
(10 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
== Start boxes ==
 
== Start boxes ==
  
Each team has a defined starting area in which its players can spawn their commanders - these are called start boxes. Map makers can define how these areas look like by including a config file inside their map archive.
+
Each team has a defined starting area in which its players can spawn their commanders—these are called '''start boxes'''. Map makers can define how these areas look by including a configuration file inside their map archive.
  
 
== The config file ==
 
== The config file ==
  
The file which you need to include is  
+
The file that you need to include is
 
  mapconfig/map_startboxes.lua
 
  mapconfig/map_startboxes.lua
  
 
It consists of Lua code which returns 2 tables: one with the start box layout for the current match and one that contains possible box counts.
 
It consists of Lua code which returns 2 tables: one with the start box layout for the current match and one that contains possible box counts.
  
For examples see the bottom of this page.
+
For [[#Examples|examples]] see the bottom of this page.
  
 
== Box counts ==
 
== Box counts ==
Line 16: Line 16:
 
Let's start from the last table because it is the simplest. This is just a list of how many teams the map is designed for.
 
Let's start from the last table because it is the simplest. This is just a list of how many teams the map is designed for.
  
Let's take [http://zero-k.info/Maps/Detail/8162 Throne] as an example. It has an outer ring with 7 starting areas so it can be played as a 7-team FFA. There are also rings with 3 and 5 areas so 3- and 5-team FFAs are also possible setups. Finally, you could play a duel between any two positions from the same ring. (The real Throne config is a fair bit more complicated than that but you get the idea.)
+
[[File:Throne v1.minimap contrasty.jpg|thumb|High-contrast Throne mini-map showing the map's "rings."]]
 +
Let's take [http://zero-k.info/Maps/Detail/8162 Throne] map as an example. It has an outer ring with 7 starting areas, so it can be played as a 7-team <abbr title="free for all">FFA</abbr>. There are also rings with 3 and 5 areas so 3- and 5-team FFAs are also possible setups. Finally, you could play a duel between any two positions from the same ring. (The real Throne config is a fair bit more complicated than that, but you get the idea.)
  
 
Thus, the possible team counts are 2, 3, 5, and 7. Let's define a Lua table for that:
 
Thus, the possible team counts are 2, 3, 5, and 7. Let's define a Lua table for that:
local playercounts = {2, 3, 5, 7}
+
<syntaxhighlight lang="lua">local playercounts = {2, 3, 5, 7}</syntaxhighlight>
 
 
  
 
This is mostly important for fancy FFA maps. Simple duel/teamgame maps will usually just have 2 as the only value here:
 
This is mostly important for fancy FFA maps. Simple duel/teamgame maps will usually just have 2 as the only value here:
local playercounts = {2}
+
<syntaxhighlight lang="lua">local playercounts = {2}</syntaxhighlight>
 
 
  
 
== The boxes ==
 
== The boxes ==
Line 30: Line 29:
 
Now, the most tedious part. The boxes do not need to actually be boxes, they can be any arbitrary shape (even concave or disjoint). This is achieved by having the boxes defined as a set of polygons.
 
Now, the most tedious part. The boxes do not need to actually be boxes, they can be any arbitrary shape (even concave or disjoint). This is achieved by having the boxes defined as a set of polygons.
  
There is a widget which lets you draw the polygons ingame, it's called "Startbox Editor" and is under the "For Developers" tab in the widget list (press Alt+F11, and don't touch anything else unless you know what you're doing).
+
There is a widget which lets you draw the polygons in-game, it's called "Startbox Editor" and is under the "For Developers" section in the widget list (press {{key combo|Alt|F11}} to open the list, and don't touch anything else in there unless you know what you're doing).
  
To draw a polygon, use LMB - either drag a line or click points:
+
To draw a polygon using the left mouse button—either drag a line or click points:
  
 
[[File:Startbox API 1.jpg]]
 
[[File:Startbox API 1.jpg]]
  
Once you draw a full loop, press RMB to close it. Make sure the line does not cross itself. Finished polygons are a bit darker.
+
Once you draw a full loop, press the right mouse button to close it. Make sure the line does not cross itself. Finished polygons are a bit darker.
  
 
[[File:Startbox API 2.jpg]]
 
[[File:Startbox API 2.jpg]]
Line 44: Line 43:
 
[[File:Startbox API 3.jpg]]
 
[[File:Startbox API 3.jpg]]
  
If you want to delete the last created polygon, press D.
+
If you want to delete the last created polygon, press {{key press|D}}.
Once you are satisfied with the area, press S. Now you can draw the area for another team.
+
Once you are satisfied with the area, press {{key press|S}}. Now you can draw the area for another team.
  
 
Once you create all the boxes, go to your Spring directory and find the infolog. There you will see a bunch of code like
 
Once you create all the boxes, go to your Spring directory and find the infolog. There you will see a bunch of code like
boxes = {
+
<syntaxhighlight lang="lua">boxes = {
 
     stuff
 
     stuff
},
+
},</syntaxhighlight>
  
Each of those tables is a team's startbox. The config is an array (indexed from 0) of each team's startbox data. The box data from the generator will go to a team's config entry. Like this:
+
Each of those tables is a team's start box. The config is an array of each team's start box data. The box data from the generator will go to a team's config entry. Like this:
local main_config = {
+
<syntaxhighlight lang="lua">local main_config = {
     [0] = {
+
     {
 
         boxes = {
 
         boxes = {
 
             stuff
 
             stuff
Line 60: Line 59:
 
         -- more entries will go there later!
 
         -- more entries will go there later!
 
     },
 
     },
     [1] = {
+
      
 +
    -- another team's box data
 +
    {
 
         boxes = {
 
         boxes = {
 
             stuff
 
             stuff
 
         },
 
         },
 
     },
 
     },
}
+
}</syntaxhighlight>
 
 
The layout is a normal Lua table so you can use all the usual table manipulation methods. For example you could add more area based on how many people there are, or generate them dynamically.
 
  
 +
The layout is a normal Lua table so you can use all the usual table manipulation methods. For example, you could add more areas based on how many people there are, or generate them dynamically.
  
=== The recommended start points ===
+
== The recommended start points ==
  
This sub-table defines recommended start points within each box. These are points on which CAI bots will spawn. The first point is special: human players who did not pick start position will also spawn here and so will any extra CAI if there are more of them than the spots; as such having at least one startpoint in each box is mandatory. These points are also marked on people's UI as circles.
+
This sub-table defines recommended start points within each box. These are points on which CAI bots will spawn. The first point is special: human players who did not pick start position will also spawn here and so will any extra CAI if there are more of them than the spots; as such having at least one start point in each box is mandatory. These points are also marked on people's UI as circles.
  
Defining these is very simple. It's very similar to how start areas were defined. Ingame you can hold Space over a point to see its coordinates. These are written down for boxes similarly to the areas:
+
Defining these is very simple. It's very similar to how start areas were defined. In game, you can hold Space over a point to see its coordinates. These are written down for boxes similarly to the areas:
local main_config = {
+
<syntaxhighlight>local main_config = {
     [0] = {
+
     {
 
         boxes = {
 
         boxes = {
 
             stuff
 
             stuff
Line 86: Line 86:
 
         },
 
         },
 
     },
 
     },
     [1] = {
+
     {
 
         boxes = {
 
         boxes = {
 
             stuff
 
             stuff
Line 95: Line 95:
 
         },
 
         },
 
     },
 
     },
}
+
}</syntaxhighlight>
  
 
Of course, the start positions should match the areas, though they don't have to.
 
Of course, the start positions should match the areas, though they don't have to.
  
== Putting these together ==
+
== Team names ==
  
 
You can give each team a name, alongside a shorter backup if the first one is too large. Here's how:
 
You can give each team a name, alongside a shorter backup if the first one is too large. Here's how:
  
  [0] = {
+
<syntaxhighlight lang="lua">{
 
     boxes = { stuff },
 
     boxes = { stuff },
 
     startpoints = { stuff },
 
     startpoints = { stuff },
     nameLong = "South-West",
+
     nameLong = "Southwest",
 
     nameShort = "SW",
 
     nameShort = "SW",
}
+
}</syntaxhighlight>
  
It serves to identify the team so while it's good to use something that describes placement, you can get creative. For example:
+
It serves to identify the team, so while it's good to use something that describes placement, you can get creative. For example:
* "North", "South-East"
+
* "North", "Southeast"
 
* "5 o'clock"
 
* "5 o'clock"
 
* real-world style maps could have names like "Russia" or "America"
 
* real-world style maps could have names like "Russia" or "America"
 
* asymmetric maps can use landmarks, for example "Lake" vs "Desert"
 
* asymmetric maps can use landmarks, for example "Lake" vs "Desert"
* for missions it's good to use storyline names, eg "Empire" vs "Dynasty"
+
* for missions, it's good to use storyline names, e.g. "Empire" vs "Dynasty"
  
 
== Summary ==
 
== Summary ==
  
Now just return the config table and the playercounts table.
+
Now just return the ''config'' table and the ''playercounts'' table.
  return main_config, playercounts
+
<syntaxhighlight lang="lua">return main_config, playercounts</syntaxhighlight>
  
 
The exact format for the config table that should get returned is
 
The exact format for the config table that should get returned is
  
{
+
<syntaxhighlight lang="lua">{
     [0] = { -- a single team's data. Indexed from 0
+
     { -- a single team's data
 
         boxes = {
 
         boxes = {
 
             { -- a polygon
 
             { -- a polygon
Line 146: Line 146:
 
         nameShort = "short",
 
         nameShort = "short",
 
     },
 
     },
     [1] = { -- another team's data
+
     { -- another team's data
 
         boxes = { ... },
 
         boxes = { ... },
 
         startpoints = { ... },
 
         startpoints = { ... },
Line 153: Line 153:
 
     },
 
     },
 
     -- more teams
 
     -- more teams
}
+
}</syntaxhighlight>
  
 
== Advanced stuff ==
 
== Advanced stuff ==
The config has access to Spring Lua API so it is possible to do a lot of dynamic things. In particular, you can use math.random without worry.
+
The config has access to Spring Lua API, so it is possible to do a lot of dynamic things. In particular, you can use <syntaxhighlight lang="lua" inline>math.random</syntaxhighlight> without worry.
 +
 
 +
You can make stuff dependent on the game mode. There are functions which return the game type, such as "<syntaxhighlight lang="lua" inline>Spring.Utilities.Gametype.is1v1()</syntaxhighlight>".<!-- The full list USED TO BE here: https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Utilities/teamFunctions.lua#L74-->
  
An important thing is that #Spring.GetAllTeams() will return a wrong value due to fake teams and Gaia. Instead, use Spring.Utilities.GetTeamCount() to get the real number of teams.
+
An important thing is that <syntaxhighlight lang="lua" inline>#Spring.GetAllTeams()</syntaxhighlight> will return a wrong value due to fake technical teams and Gaia. Instead, use <syntaxhighlight lang="lua" inline>Spring.Utilities.GetTeamCount()</syntaxhighlight> to get the real number of teams.
  
 
== Examples ==
 
== Examples ==
Line 164: Line 166:
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/SpeedMetal.lua SpeedMetal] - the simplest layout, just two squares.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/SpeedMetal.lua SpeedMetal] - the simplest layout, just two squares.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/DeltaSiegeDry.lua DSD] - huge polygons, but otherwise very simple.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/DeltaSiegeDry.lua DSD] - huge polygons, but otherwise very simple.
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Isle%20of%20Grief%200.2.lua Isle of Grief] - shows a smart approach, one of the boxes is not defined rigidly but is a mirror of the other.
+
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Sever%201.lua#L343 Sever] - shows an example of smaller boxes for 1v1 and larger for teams.
 +
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Isle%20of%20Grief%200.2.lua Isle of Grief] - shows a smart approach, one of the boxes is not defined rigidly but is a mirror of the other (only really feasible for rough boxes or perfectly symmetrical maps).
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Aum_1.1.lua Aum] - another small trick, this time 5-way radial symmetry.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Aum_1.1.lua Aum] - another small trick, this time 5-way radial symmetry.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Throne%20v1.lua Throne] - multiple boxes defined, with a system to pick some of them based on how many teams there are.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Throne%20v1.lua Throne] - multiple boxes defined, with a system to pick some of them based on how many teams there are.
Line 170: Line 173:
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Mearth_v4.lua#L6 Middle-Earth] - interesting team names.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Mearth_v4.lua#L6 Middle-Earth] - interesting team names.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Adansonia%20v2.lua Adansonia v2] - shows synchronizing boxes across map versions.
 
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/Adansonia%20v2.lua Adansonia v2] - shows synchronizing boxes across map versions.
 +
* [https://github.com/ZeroK-RTS/Zero-K/blob/master/LuaRules/Configs/StartBoxes/duck.lua Duck] - boxless config for silly maps where everybody can start anywhere.
  
 
[[Category:Development]]{{Navbox manual}}
 
[[Category:Development]]{{Navbox manual}}

Latest revision as of 11:47, 3 September 2022

Start boxes

Each team has a defined starting area in which its players can spawn their commanders—these are called start boxes. Map makers can define how these areas look by including a configuration file inside their map archive.

The config file

The file that you need to include is

mapconfig/map_startboxes.lua

It consists of Lua code which returns 2 tables: one with the start box layout for the current match and one that contains possible box counts.

For examples see the bottom of this page.

Box counts

Let's start from the last table because it is the simplest. This is just a list of how many teams the map is designed for.

High-contrast Throne mini-map showing the map's "rings."

Let's take Throne map as an example. It has an outer ring with 7 starting areas, so it can be played as a 7-team FFA. There are also rings with 3 and 5 areas so 3- and 5-team FFAs are also possible setups. Finally, you could play a duel between any two positions from the same ring. (The real Throne config is a fair bit more complicated than that, but you get the idea.)

Thus, the possible team counts are 2, 3, 5, and 7. Let's define a Lua table for that:

local playercounts = {2, 3, 5, 7}

This is mostly important for fancy FFA maps. Simple duel/teamgame maps will usually just have 2 as the only value here:

local playercounts = {2}

The boxes

Now, the most tedious part. The boxes do not need to actually be boxes, they can be any arbitrary shape (even concave or disjoint). This is achieved by having the boxes defined as a set of polygons.

There is a widget which lets you draw the polygons in-game, it's called "Startbox Editor" and is under the "For Developers" section in the widget list (press ‎Alt + F11‎ to open the list, and don't touch anything else in there unless you know what you're doing).

To draw a polygon using the left mouse button—either drag a line or click points:

Startbox API 1.jpg

Once you draw a full loop, press the right mouse button to close it. Make sure the line does not cross itself. Finished polygons are a bit darker.

Startbox API 2.jpg

Now you can add another polygon if you want. These will all be a part of the same team.

Startbox API 3.jpg

If you want to delete the last created polygon, press D. Once you are satisfied with the area, press S. Now you can draw the area for another team.

Once you create all the boxes, go to your Spring directory and find the infolog. There you will see a bunch of code like

boxes = {
     stuff
},

Each of those tables is a team's start box. The config is an array of each team's start box data. The box data from the generator will go to a team's config entry. Like this:

local main_config = {
     {
         boxes = {
             stuff
         },
         -- more entries will go there later!
     },
     
     -- another team's box data
     {
         boxes = {
             stuff
         },
     },
}

The layout is a normal Lua table so you can use all the usual table manipulation methods. For example, you could add more areas based on how many people there are, or generate them dynamically.

The recommended start points

This sub-table defines recommended start points within each box. These are points on which CAI bots will spawn. The first point is special: human players who did not pick start position will also spawn here and so will any extra CAI if there are more of them than the spots; as such having at least one start point in each box is mandatory. These points are also marked on people's UI as circles.

Defining these is very simple. It's very similar to how start areas were defined. In game, you can hold Space over a point to see its coordinates. These are written down for boxes similarly to the areas:

local main_config = {
     {
         boxes = {
             stuff
         },
         startpoints = {
             {123, 456}, -- X, Z
             {654, 321},
             -- as many as you want, it's good if there are as many as you want people per team
         },
     },
     {
         boxes = {
             stuff
         },
         startpoints = {
             {789, 789},
             {987, 987},
         },
     },
}

Of course, the start positions should match the areas, though they don't have to.

Team names

You can give each team a name, alongside a shorter backup if the first one is too large. Here's how:

{
     boxes = { stuff },
     startpoints = { stuff },
     nameLong = "Southwest",
     nameShort = "SW",
}

It serves to identify the team, so while it's good to use something that describes placement, you can get creative. For example:

  • "North", "Southeast"
  • "5 o'clock"
  • real-world style maps could have names like "Russia" or "America"
  • asymmetric maps can use landmarks, for example "Lake" vs "Desert"
  • for missions, it's good to use storyline names, e.g. "Empire" vs "Dynasty"

Summary

Now just return the config table and the playercounts table.

return main_config, playercounts

The exact format for the config table that should get returned is

{
     { -- a single team's data
         boxes = {
             { -- a polygon
                 {123, 456}, -- a vertex of the polygon. X, Z.
                 {234, 567}, -- another vertex
                 {345, 678},
                 -- more vertices
             },
             { -- another polygon
                 -- vertices
             },
             -- more polygons
         },
         startpoints = {
             {123, 456}, -- X, Z
             {654, 321},
             -- more points
         },
         nameLong = "longer string with team name",
         nameShort = "short",
     },
     { -- another team's data
         boxes = { ... },
         startpoints = { ... },
         nameLong = "...",
         nameShort = "...",
     },
     -- more teams
}

Advanced stuff

The config has access to Spring Lua API, so it is possible to do a lot of dynamic things. In particular, you can use math.random without worry.

You can make stuff dependent on the game mode. There are functions which return the game type, such as "Spring.Utilities.Gametype.is1v1()".

An important thing is that #Spring.GetAllTeams() will return a wrong value due to fake technical teams and Gaia. Instead, use Spring.Utilities.GetTeamCount() to get the real number of teams.

Examples