Writing Gamemodes: Difference between revisions

From Multi Theft Auto: Wiki
Jump to navigation Jump to search
(New page: This Guide tries to outline the process of how to write a proper gamemode. If you just started with scripting for MTA, you may want to check the other tutorials at the [[Scripting|Scriptin...)
 
(Undo revision 58223 by WolfPiotrK2 (talk))
(16 intermediate revisions by 12 users not shown)
Line 1: Line 1:
This Guide tries to outline the process of how to write a proper gamemode. If you just started with scripting for MTA, you may want to check the other tutorials at the [[Scripting|Scripting Page]] first.
This guide tries to outline the process of how to write a proper gamemode. If you just started with scripting for MTA, you may want to check the other scripting tutorials at the [[Main Page]] first.
==Introduction==
==Introduction==
A Gamemode is a resource that, once started, controls all of the gameplay. This may include telling the players what to do, spawning players, creating teams, defining what the players have to do to win or to get points and much more. For some of the functionality a gamemode has to provide, there are other resources that can help it to do that.
A gamemode is a resource that, once started, controls all of the gameplay. This may include telling the players what to do, spawning players, creating teams, defining what the players have to do to win or to get points and much more. Examples are Race and Deathmatch.  


==What does "proper gamemode" mean?==
==What does "proper gamemode" mean?==
To put it simply, a proper gamemode is one that makes full use of MTA's .map file system. This means that you can have the gamemode, but change the .map file of key parts of the gamemode so that there can be lots of maps for this gamemode.
To put it simply, a proper gamemode is one that makes full use of MTA's .map file system. This means that the gamemode code does not have any map-specific data hardcoded in it, like positions of players or cars. Instead, the gamemode should be able to load .map files which define these data. This way the gamemode can have multiple maps; also, people can create .map files for the gamemode with MTA's map editor, which is much more convenient than writing code.


An obvious example of what i define as a "proper gamemode" is that of Race mod - MTA:Race. It allows usermade maps with lots of possibilities within the .map file. To alter spawnpoints, or objects etc, the user does not need to look at editing the gamemode itself.
An obvious example of a "proper gamemode" is MTA:Race. It allows usermade maps with lots of possibilities within the .map file. To alter spawnpoints, objects etc., the user doesn't need to edit the gamemode itself.


Examples of what i dont consider gamemodes, are ones that have everything hardcoded within the script. This goes for vehicles, spawnpoints, objects.
===Map Files===
Map files are basically XML documents with a .map extension. They define an environment to play one or more specific gamemodes in. They are however not supposed to change the rules of the game - those are defined by the gamemode.


===Map Files===
Each element in a map corresponds to a node in the .map file. There is standard syntax for common things like spawnpoints, objects and vehicles; however, for "special", gamemode specific information, you need to invent your own syntax.
So what kind of things should the map maker have access to in the .map file? Obviously you dont want them changing key parts of your gamemode. You should stick to obvious things - spawnpoints, vehicles etc. In other words, the user should be able to move everything that happens in your gamemode to another location. It should be fairly obvious to you what kind of things the user should or should not have access to.


====Example====
====Example====
For example, in a Capture the Flag gamemode, you will have important things defined in .map - vehicles, spawnpoints, objects. You will also need to allow the user to define where the flag goes - so they should be able to place a flag element in the .map. As the scripter, you can decide the syntax of the flag element in the .map file (i will come onto how to use custom elements later on). E.g.
Let's take a Capture the Flag gamemode as an example. A map for this gamemode needs to mainly define spawnpoints and flag locations, and eventually objects and vehicles. A simplified map file could look like this:
<syntaxhighlight lang="xml"><spawnpoint name="spawnpoint1" posX="1959.5487060547" posY="-1714.4613037109" posZ="877.25219726563" rot="63.350006103516" model="0"/>
<syntaxhighlight lang="xml">
<pickup name="Armor 1" posX="1911.083984375" posY="-1658.8798828125" posZ="885.40216064453" type="armor" health="50" respawn="60000"/>
<map>
<flag posX="1959.5487060547" posY="-1714.4613037109" posZ="877.25219726563" team="blue" />
    <spawnpoint id="spawnpoint1" posX="1959.5487060547" posY="-1714.4613037109" posZ="877.25219726563" rot="63.350006103516" model="0"/>
    <pickup id="Armor 1" posX="1911.083984375" posY="-1658.8798828125" posZ="885.40216064453" type="armor" health="50" respawn="60000"/>
    <flag posX="1959.5487060547" posY="-1714.4613037109" posZ="877.25219726563" team="blue" />
    ...
</map>
</syntaxhighlight>
</syntaxhighlight>


Here you can see two MTA elements - spawnpoint and a pickup. More importantly, this .map has a 'flag' element. This means the user can easilly create their own maps by altering these elements. Again, as the scripter you can make up whatever element name you like.
Here you can see two MTA elements - a spawnpoint and a pickup. More importantly, this .map has a custom "flag" node which defines the position and color of the flag. The spawnpoint and pickup can be handled by existing external resources, custom elements have to be processed by the gamemode.


To summarise - we want mass mapper input as we saw in MTA:Race. Users should NOT even have to touch the script file itself at all.
To summarize - we want mass mapper input as we saw in MTA:Race. Users should NOT have to touch the gamemode script itself at all.


====Example of getting the .map information====
====Example of getting the .map information====
So how would you retrieve information about these "custom elements" from the map file? It is actually very simple(this assumes you have at least some knowledge on Lua and tables).
As mentioned above, your gamemode needs de retrieve custom elements that are defined in a map file and process them. This is quite easy as demonstrated below.
<syntaxhighlight lang="lua">
<syntaxhighlight lang="lua">
--retrieve a table of flag elements
-- retrieve a table with all flag elements
local flagElements = getElementsByType ( "flag" )
local flagElements = getElementsByType ( "flag" )
--loop through the flag elements
-- loop through them
for key, value in pairs(flagElements) do
for key, value in pairs(flagElements) do
--get our info
-- get our info
local posX = getElementData ( value, "posX" )
local posX = getElementData ( value, "posX" )
local posY = getElementData ( value, "posY" )
local posY = getElementData ( value, "posY" )
local posZ = getElementData ( value, "posZ" )
local posZ = getElementData ( value, "posZ" )
local team = getElementData ( value, "team" )
local team = getElementData ( value, "team" )
--create an object according to the flag position
-- create an object according to the flag position
createObject ( 1337, posX, posY, posZ )
createObject ( 1337, posX, posY, posZ )
--output the team that we created a base for
-- output the team that we created a base for
outputChatBox ( "Base for team "..team.." created" )
outputChatBox ( "Base for team " .. team .. " created" )
end</syntaxhighlight>
end
The getElementsByType function retrieves a table of all the elements of a certain type. This works for custom elements or real MTA elements.
</syntaxhighlight>
getElementData can be used to retrieve the xml attributes set in the .map file.
The [[getElementsByType]] function retrieves a table of all the elements of a certain type (the type corresponds to the node name in the .map file). This works for both custom types and built-in MTA types (like "vehicle" or "player").
In this example, all im doing is creating an object at that position, and outputting the team. In reality, you will be creating the real base or doing whatever is necessary.
[[getElementData]] can be used to retrieve the xml attributes set in the .map file.
In this simple example, an object is created at the flag's location and a message is outputted in the chatbox. In reality, you will of course need to do more during map loading, like in this case setting up collision shapes to detect players taking the flag.




==Map manager==
==Map manager==
Having read the section above it should be clear that a gamemode should always consist of two parts:
Having read the section above it should be clear that a gamemode should always consist of two parts:
* The gamemode resource that stays always the same
* The gamemode resource that always stays the same
* Many different maps resources that give the gamemode map-specific information
* Many different maps resources that give the gamemode map-specific information


Now instead of writing a map-loader for every single gamemode, the [[Map manager]] provides functions to load gamemodes and maps. Simply put, when you enter the correct command (for example 'gamemode ctf ctf-italy') it will start both resources 'ctf' and 'ctf-italy' while triggering an event to tell the 'ctf' resource that a map was loaded. The 'ctf' resource can then access the information 'ctf-italy' contains and start spawning players etc.
Now instead of writing a map-loader for every single gamemode, the [[Map manager]] provides functions to load gamemodes and maps. Simply put, when you enter the correct command (for example 'gamemode ctf ctf-italy') it will start both resources 'ctf' and 'ctf-italy' while triggering an event ([[onGamemodeMapStart]]) to tell the 'ctf' resource that a map was loaded. The 'ctf' resource can then access the information 'ctf-italy' contains and start spawning players etc.


===How to use the mapmanager===
===How to use the mapmanager===
To use the mapmanager service, your gamemode resource has to be tagged as such first. You'll be setting type attribute to "gamemode" in its <info> tag, inside meta.xml. Also, you can set the name attribute to set a friendly name that will be shown on ASE instead of the resource name.
To use the mapmanager service, your gamemode resource has to be tagged as such first. More specifically you'll be setting the "type" attribute of its <info> tag to "gamemode" inside meta.xml. Also, you can set the "name" attribute to a friendly name (like "Capture the flag") that will be shown on ASE instead of the resource name.
<syntaxhighlight lang="xml">
<syntaxhighlight lang="xml">
<!--meta.xml in "cowcatapult" gamemode-->
<!-- meta.xml in "cowcatapult" gamemode -->
<meta>
<meta>
<info type="gamemode" name="Cow catapulting 2.0"/>
    <info type="gamemode" name="Cow catapulting 2.0"/>
</meta>
</meta>
</syntaxhighlight>
</syntaxhighlight>


If your gamemode is going to load custom maps, you should add handlers for
If your gamemode is going to load custom maps, you should add handlers for
* onGamemodeMapStart( resource startedMap )
* onGamemodeMapStart
* onGamemodeMapStop ( resource stoppedMap ) (if any unloading is necessary)
* onGamemodeMapStop (if any unloading is necessary)
These are fired when a map for your gamemode is started or stopped, and pass the map resource as a parameter.
These are fired when a map for your gamemode is started or stopped, and pass the map resource as a parameter.
Within that handler, you can extract all info you need from this resource's map files and configs.
Within the handler function for these events you can extract all info you need from the resource's map files and configuration files.


====Example====
====Example====
<syntaxhighlight lang="lua">
<syntaxhighlight lang="lua">
function startCtfMap( startedMap ) -- startedMap contains a reference to the resource of the map
function startCtfMap( startedMap ) -- startedMap contains a reference to the resource of the map
mapRoot = getResourceRootElement( startedMap ) -- get the root node of the started map
    local mapRoot = getResourceRootElement( startedMap )       -- get the root node of the started map
local flagElements = getElementsByType ( "flag" , mapRoot ) -- get all flags in the map and store them in a table
    local flagElements = getElementsByType ( "flag" , mapRoot ) -- get all flags in the map and store them in a table
-- go on loading information like in the example above
    -- go on loading information like in the example above
    -- spawn players etc.
-- spawn players etc.
end
end
addEventHandler("onGamemodeMapStart", getRootElement(), startCtfMap)
addEventHandler("onGamemodeMapStart", getRootElement(), startCtfMap)
</syntaxhighlight>
</syntaxhighlight>
Line 84: Line 87:
Maps are separate resources. This is done so no editing of the gamemode resource is ever necessary in order to create a custom map, and also allows you to pack map-specific scripts/config files with them.
Maps are separate resources. This is done so no editing of the gamemode resource is ever necessary in order to create a custom map, and also allows you to pack map-specific scripts/config files with them.


To make a map compatible with your gamemode, open its resource's meta.xml and tag it as well:
To make a map compatible with your gamemode, open its resource's meta.xml and tag it as well: the "type" attribute must be set to "map", and the "gamemodes" attribute must be a comma-separated list (no spaces) of gamemode resource names that the map works with.
type attribute must be set to "map", and gamemodes attribute must be a comma-separated list of gamemode resource names (without spaces).
<syntaxhighlight lang="xml">
<syntaxhighlight lang="xml"><!--map's meta.xml-->
<!--map's meta.xml-->
<meta>
<meta>
<info type="map" gamemodes="cowcatapult,assault,tdm"/>
    <info type="map" gamemodes="cowcatapult,assault,tdm"/>
</meta>
</meta>
</syntaxhighlight>
</syntaxhighlight>
Line 106: Line 109:
There are basicially two ways to use the helpmanager:
There are basicially two ways to use the helpmanager:
* Provide a simple text that explains how to use your gamemode
* Provide a simple text that explains how to use your gamemode
* Request a GUI element from the helpmanager that will be displayed in it's own tab in the helpmanager window and lets you add any GUI elements to it. This is the recommended way for gamemodes that need to display more complex information that requires to be in it's own GUI.
* Request a GUI element from the helpmanager that will be displayed in its own tab in the helpmanager window and lets you add any GUI elements to it. This is the recommended way for gamemodes that need to display more complex information that needs its own GUI.


Read the helpmanager page for details on how to do it.
Read the [[Resource:Helpmanager|helpmanager help page]] for details on how to do it.


===Scoreboard===
===Scoreboard===
Scoreboard displays players and teams currently ingame. You add custom columns to it to provide map specific information. For example the column 'points' in the 'ctf' gamemode could represent the players points gained through kills or captures. As usual, see the scoreboard help page for more information.
Scoreboard displays players and teams currently ingame. You add custom columns to it to provide map specific information. For example the column 'points' in the 'ctf' gamemode could represent the player's points gained through kills or captures. As usual, see the [[Resource:Dxscoreboard|scoreboard help page]] for more information.


===Map cycler===
===Map cycler===
The map cycler controls what gamemodes and maps are played on a server. You can specifiy for example how many times in a row a map will be played until it switches to the next. To achieve this, you need to tell the map cycler when your gamemode finished (e.g. when a round ends).
The map cycler controls what gamemodes and maps are played on a server. You can specifiy for example how many times in a row a map will be played until it switches to the next. To achieve this, you need to tell the map cycler when your gamemode finished (e.g. when a round ends).
[[hu:Writing Gamemodes]]
[[it:Scrivere una gamemode]]
[[ru:Writing Gamemodes]]
[[de:Gamemodes schreiben]]
[[Category:Tutorials]]

Revision as of 12:49, 16 August 2018

This guide tries to outline the process of how to write a proper gamemode. If you just started with scripting for MTA, you may want to check the other scripting tutorials at the Main Page first.

Introduction

A gamemode is a resource that, once started, controls all of the gameplay. This may include telling the players what to do, spawning players, creating teams, defining what the players have to do to win or to get points and much more. Examples are Race and Deathmatch.

What does "proper gamemode" mean?

To put it simply, a proper gamemode is one that makes full use of MTA's .map file system. This means that the gamemode code does not have any map-specific data hardcoded in it, like positions of players or cars. Instead, the gamemode should be able to load .map files which define these data. This way the gamemode can have multiple maps; also, people can create .map files for the gamemode with MTA's map editor, which is much more convenient than writing code.

An obvious example of a "proper gamemode" is MTA:Race. It allows usermade maps with lots of possibilities within the .map file. To alter spawnpoints, objects etc., the user doesn't need to edit the gamemode itself.

Map Files

Map files are basically XML documents with a .map extension. They define an environment to play one or more specific gamemodes in. They are however not supposed to change the rules of the game - those are defined by the gamemode.

Each element in a map corresponds to a node in the .map file. There is standard syntax for common things like spawnpoints, objects and vehicles; however, for "special", gamemode specific information, you need to invent your own syntax.

Example

Let's take a Capture the Flag gamemode as an example. A map for this gamemode needs to mainly define spawnpoints and flag locations, and eventually objects and vehicles. A simplified map file could look like this:

<map>
    <spawnpoint id="spawnpoint1" posX="1959.5487060547" posY="-1714.4613037109" posZ="877.25219726563" rot="63.350006103516" model="0"/>
    <pickup id="Armor 1" posX="1911.083984375" posY="-1658.8798828125" posZ="885.40216064453" type="armor" health="50" respawn="60000"/>
    <flag posX="1959.5487060547" posY="-1714.4613037109" posZ="877.25219726563" team="blue" />
    ...
</map>

Here you can see two MTA elements - a spawnpoint and a pickup. More importantly, this .map has a custom "flag" node which defines the position and color of the flag. The spawnpoint and pickup can be handled by existing external resources, custom elements have to be processed by the gamemode.

To summarize - we want mass mapper input as we saw in MTA:Race. Users should NOT have to touch the gamemode script itself at all.

Example of getting the .map information

As mentioned above, your gamemode needs de retrieve custom elements that are defined in a map file and process them. This is quite easy as demonstrated below.

-- retrieve a table with all flag elements
local flagElements = getElementsByType ( "flag" )
-- loop through them
for key, value in pairs(flagElements) do
	-- get our info
	local posX = getElementData ( value, "posX" )
	local posY = getElementData ( value, "posY" )
	local posZ = getElementData ( value, "posZ" )
	local team = getElementData ( value, "team" )
	-- create an object according to the flag position
	createObject ( 1337, posX, posY, posZ )
	-- output the team that we created a base for
	outputChatBox ( "Base for team " .. team .. " created" )
end

The getElementsByType function retrieves a table of all the elements of a certain type (the type corresponds to the node name in the .map file). This works for both custom types and built-in MTA types (like "vehicle" or "player"). getElementData can be used to retrieve the xml attributes set in the .map file. In this simple example, an object is created at the flag's location and a message is outputted in the chatbox. In reality, you will of course need to do more during map loading, like in this case setting up collision shapes to detect players taking the flag.


Map manager

Having read the section above it should be clear that a gamemode should always consist of two parts:

  • The gamemode resource that always stays the same
  • Many different maps resources that give the gamemode map-specific information

Now instead of writing a map-loader for every single gamemode, the Map manager provides functions to load gamemodes and maps. Simply put, when you enter the correct command (for example 'gamemode ctf ctf-italy') it will start both resources 'ctf' and 'ctf-italy' while triggering an event (onGamemodeMapStart) to tell the 'ctf' resource that a map was loaded. The 'ctf' resource can then access the information 'ctf-italy' contains and start spawning players etc.

How to use the mapmanager

To use the mapmanager service, your gamemode resource has to be tagged as such first. More specifically you'll be setting the "type" attribute of its <info> tag to "gamemode" inside meta.xml. Also, you can set the "name" attribute to a friendly name (like "Capture the flag") that will be shown on ASE instead of the resource name.

<!-- meta.xml in "cowcatapult" gamemode -->
<meta>
    <info type="gamemode" name="Cow catapulting 2.0"/>
</meta>

If your gamemode is going to load custom maps, you should add handlers for

  • onGamemodeMapStart
  • onGamemodeMapStop (if any unloading is necessary)

These are fired when a map for your gamemode is started or stopped, and pass the map resource as a parameter. Within the handler function for these events you can extract all info you need from the resource's map files and configuration files.

Example

function startCtfMap( startedMap ) -- startedMap contains a reference to the resource of the map
    local mapRoot = getResourceRootElement( startedMap )        -- get the root node of the started map
    local flagElements = getElementsByType ( "flag" , mapRoot ) -- get all flags in the map and store them in a table
    -- go on loading information like in the example above
    -- spawn players etc.
end
addEventHandler("onGamemodeMapStart", getRootElement(), startCtfMap)

Making maps compatible

Maps are separate resources. This is done so no editing of the gamemode resource is ever necessary in order to create a custom map, and also allows you to pack map-specific scripts/config files with them.

To make a map compatible with your gamemode, open its resource's meta.xml and tag it as well: the "type" attribute must be set to "map", and the "gamemodes" attribute must be a comma-separated list (no spaces) of gamemode resource names that the map works with.

<!--map's meta.xml-->
<meta>
    <info type="map" gamemodes="cowcatapult,assault,tdm"/>
</meta>

Once you have everything set up, admins will use these two commands to start/stop gamemodes: /gamemode gamemodeName [mapName] (optional parameter allows picking an initial map, defaults to none) /changemap mapName [gamemodeName] (optional parameter specifies the gamemode to start the map with, defaults to the current one)

Map manager exports a few more access functions which you don't have to use, but may be useful.

What else should you do

There are several other resources that gamemodes should use/be compliant with.

Helpmanager

The helpmanager is ought to be the standard interface for players when they need help. If you use the helpmanager to display your gamemode's help, every player that used helpmanager before (e.g. in other gamemodes) will immediately know how to get there. It also displays help for different resources running resources in one window, if necessary.

There are basicially two ways to use the helpmanager:

  • Provide a simple text that explains how to use your gamemode
  • Request a GUI element from the helpmanager that will be displayed in its own tab in the helpmanager window and lets you add any GUI elements to it. This is the recommended way for gamemodes that need to display more complex information that needs its own GUI.

Read the helpmanager help page for details on how to do it.

Scoreboard

Scoreboard displays players and teams currently ingame. You add custom columns to it to provide map specific information. For example the column 'points' in the 'ctf' gamemode could represent the player's points gained through kills or captures. As usual, see the scoreboard help page for more information.

Map cycler

The map cycler controls what gamemodes and maps are played on a server. You can specifiy for example how many times in a row a map will be played until it switches to the next. To achieve this, you need to tell the map cycler when your gamemode finished (e.g. when a round ends).