Let's start off with our first tutorial: User selectable warp points.
This week we will be writing a script that provides users the ability to create their own warp points.
More specific, we are going to allow players to create a target marker, a destination marker, and to warp themselves so they can test if their warp points are valid.
Forum support thread: forum.mtavc.com
Please ask your questions in this forum thread.
Video footage: scriptvideo1.wmv - Windows Media Video 9 (640x480, 30fps)
Complete script: script1.lua
Turning on our script
MTA scripting is completely event based. Events are truly dynamic and you can actually implement your own within your scripts, but in this example we'll be using built in events, of which MTA has around 40. Events get "triggered" when all kinds of things happen in the game. We can tie our own functions to any event (by calling a function called addEventHandler). Each event can have any number of event handlers or none!
We'll create our first function called Script_onResourceLoad, that we will set up to be called when the server loads the resource that contains the map using the onResourceLoad event. We'll look at how MTA's 'resources' work exactly in a future post.
Then we will create a new global key that toggles our script off and on. We call bindKey to assign the I-key to the function "modeIO" we have yet to create.
addEventHandler ( "onResourceLoad", getRootElement(), "Script_onResourceLoad" ) function Script_onResourceLoad ( resource ) if ( resource == getThisResource() ) then -- for each player already in the server for index, player in getElementsByType ( "player" ) do -- binds the "i"-key to our function "modeIO" bindKey ( player, "i", "down", "modeIO" ) end end end |
Notice that we have now ended the function with the end keyword.
I-key available as well. We can use the onPlayerJoin event for this, which gets called for every player that joins:addEventHandler ( "onPlayerJoin", getRootElement(), "Script_onPlayerJoin" ) function Script_onPlayerJoin () -- binds the "i"-key to our function "modeIO" bindKey ( source, "i", "down", "modeIO" ) end |
Our global key is needed, so any other binds we will use below will not interfere with any of the original controls and vice versa (such as the mouse buttons for fire and aim) when the script it turned on.
Getting our cursor to work
Specifying a warp point with keys can be hard to use (and to script). And ideally, we would want to use a mouse cursor. Fortunately, MTA has some nice mouse cursor functions available, including the showCursor which we will use now.
function modeIO ( source, key, keyState ) -- function toggles the cursor if isCursorShowing ( source ) then -- if cursor was already showing: showCursor ( source, false ) -- hide it else -- if it wasn't showing: showCursor ( source, true ) -- show it end end |
Now since we have three functions and a mouse cursor available, the easiest would be to assign every function to a different mouse button. That's exactly what we're going to do.
MTA has an event called onPlayerClick that allows scripters to get the xyz-coordinates at the exact world position of the cursor when the user clicks. This event is, of course, only possible when the cursor is visible.
onPlayerClick-event:addEventHandler ( "onPlayerClick", getRootElement(), "Script_onPlayerClick" ) function Script_onPlayerClick ( key, keyState, element, x, y, z ) if keyState == "up" then return -- ignore if the button was released end |
Creating our markers
Before we do this, you need a little understanding of how MTA's element hierachy works (identical to "entities" in other games).
MTA uses elements for everything. Markers, objects, vehicles, players etc - They're all elements. This is easy, because it means you don't have to check the elements's type each time before calling a function. For example: if you want to destroy or set the position of a marker, a vehicle or an object, just call destroyElement or setElementPosition.
We only want 2 markers (per player), a "teleport"-marker, and a "destination"-marker. This will keep the script easy, while still providing enough functionality. To allow 2 markers per player, we will store the ID of the marker we create for each player using setElementData.
If a user presses the left mouse button, we create a marker called "teleport" which is going to be our portal that will teleport us. If a user presses the right mouse button, we create a "destination"-marker that will be the portal's destination.
Let's implement the left and right mouse button functionality:if key == "left" then -- on a left mousebutton -- destroy his teleport point, if any destroyElement ( getElementData ( source, "teleport" ) ) -- create a normal cylinder marker local marker = createMarker ( x, y, z, "cylinder", 2, 0, 255, 0, 50 ) -- mark the cylinder as our "teleport" type setElementData ( marker, "type", "teleport" ) -- link the creator (player) to the marker, and vice versa setElementData ( source, "teleport", marker ) setElementData ( marker, "owner", source ) elseif key == "right" then -- on a right mousebutton -- destroy his destination point, if any destroyElement ( getElementData ( source, "destination" ) ) -- create a glowing corona local marker = createMarker ( x, y, z+1, "corona", 1, 0, 255, 255, 50 ) -- mark the corona as our "destination" type setElementData ( marker, "type", "destination" ) -- link the creator (player) to the marker, and vice versa setElementData ( source, "destination", marker ) setElementData ( marker, "owner", source ) |
We use setElementData to store some data for the marker. Later on you will see that this is trivial to retrieve the owner and type of the marker.
setElementData and it's partner function getElementData both interact with the element data a function has. This data acts as a useful data store that stays attached to an element. You can put values into this store with a key using setElementData and access them later using getElementData.
elseif key == "middle" then -- on a middle mousebutton -- teleport the player to whereever he clicked setElementPosition ( source, x, y, z+1 ) end |
If you're wondering what the source keyword is: it refers to the element that issued the event, in this case the player. The source keyword is available or all events, though in some cases is irrelevant.
Teleporting our player
The markers we just created have their own events. To let a script know a marker has been "hit" (touched) by a player, we can use theonMarkerHit event. The source keyword will be set to the marker that issued the event:addEventHandler ( "onMarkerHit", getRootElement(), "Script_onMarkerHit" ) function Script_onMarkerHit ( player ) |
We now use the data we set in the Script_onPlayerClick function above, to see what kind of marker this is. This can be either "teleport" or "destination".
-- if the marker is a teleport point, proceed if getElementData ( source, "type" ) == "teleport" then -- get the owner linked to the "teleport" type marker local owner = getElementData ( source, "owner" ) -- get the destination point linked to the owner local destination = getElementData ( owner, "destination" ) -- if the destination point exists, proceed if destination then -- get the "destination" marker's position local x, y, z = getElementPosition ( destination ) -- finally, warp the player there setElementPosition ( player, x, y, z ) end end end |
Cleaning up
Finally, we will do some cleaning up for each player. After all, we do not want any markers to stick around after a player has left the server.Like the
onPlayerJoin event, there is also a onPlayerQuit event that gets called whenever a player leaves:addEventHandler ( "onPlayerQuit", getRootElement(), "Script_onPlayerQuit" ) function Script_onPlayerQuit ( reason ) --destroy his teleport point, if any destroyElement ( getElementData ( source, "teleport" ) ) -- destroy his destination point, if any destroyElement ( getElementData ( source, "destination" ) ) end |
Please redirect your questions to the forum thread that can be found on top of this page.



