Qmicozium
Little Mouse
Hello,
Today we're going to make a simple minigame with full explaination of every line of the code.
This tutorial is meant for people that have some basic knowledge about Lua or programming in general.
So, we'll make a minigame similar to survivor but cannonballs will be spawned automaticly. A difficulty level will grow slowly. Each player will have 2 lives and the last mouse standing will be the winner.
Let's start!
First of all we need a map on which players will be playing. In Lua we can load a new map using a @MapCode or using an XML code.
I made a supersimple map and I'm going to load its XML code. A function that loads a new map is called tfm.exec.newGame().
After executing this code, our map will load.
Now, we need to spawn the cannonballs. We want to spawn them on the left side of the screen and they should go right.
A function that spawns a shaman object is called tfm.exec.addShamanObject(), it also has some arguments inside of ( ) - first of them is an ID of the object.
ID of a cannonball is 17 (https://mforum.ist/threads/lua-documentation.16).
Another 2 arguments are X position and Y position, look at the picture down here to understand how does it work:
.
And we want to spawn this cannonball somewhere over there:

So X should be ~50 and Y should be ~220.
Spawning a cannonball on this position will look like that:
This code works fine, it spawns a cannonball where we want to but it's facing up.
To make it go right, we'll use 4th argument of this function - angle. We need to rotate a cannonball 90 degrees:
Okay, it was quite easy.
Now we need to spawn this cannonball every 0.5 second and the only thing we need to do is insert this function inside of an eventLoop function:
eventLoop is a function that executes itself every 0.5 second so this code will spawn the cannonballs every 0.5 second.
It works fine, but the game should start from something easier, at the beggining we should spawn the cannonballs like every 2 seconds.
To do this we need this code:
And I think this is the most complicated piece of code in this tutorial.
At the beggining of the code we have 4 variables:
Rest of the code you can analyze yourself, you don't need any programming knowledge to understand it.
The code describes itself, don't worry if you don't undestand it straightaway. Spend some more time on it.
You can print out some values, it can help. I did it for you, so you can use this code for analyzing purposes:
So now, we have a working auto-dificulty system!
That's not the end - as I said at the beggining there should be no mice joining during the game.
The best way to do it, is creating a table with all the mice that were in the room when the game started.
We'll use eventNewGame() here to detect when the game starts and then, put every player to this table.
A table-variable with all the mice should be placed at the beggining of the code, I called it inGame.
I use a FOR loop to go thru the list of players in the room and add every player to inGame variable we definied before.
But how to prevent players from joining during the game?
The easiest way is just killing them over and over. We need 2 events here - eventNewPlayer() and eventPlayerRespawn()
In eventNewPlayer() we should just kill a player and in eventPlayerRespawn() we should check if the respawned player is inside of inGame table and if he isn't, then we just kill him.
But what if the player is in the game but he leaves the tribehouse and then re-joins?
He will still be inside of inGame table - it's not good! We can easily fix it by adding another event - eventPlayerLeft() in which we'll remove from inGame table the player that left the room:
Fixed!
Now, the entire code looks like this:
Looks great! The last thing we need to implement is 2 lives for each player.
Let's kill two birds with one stone. I'm sure you remember inGame table. When the game starts we are doing
So what if instead of true (or instead of banana) we would hold a number of lives of every player?
Let's check it! Inside of function eventNewGame() replace true with number 2:
Also, we need to create another event - eventPlayerDied() and inside of it we'll check 2 things - if the player is inside of inGame and if the player has more than 1 life left.
If both conditions are true, we'll respawn a player and decrease his lives with 1. Else, we will remove him from inGame so he won't be able to play until the game ends.
Done!
But what with the winner?! Every time when any player dies or leaves, we need to check if there are more than 1 players alive. If so, game continues but if there are only 1 player left, it means that we have a winner! But checking if there's only 1 player in 2 places will require repeating our code, which is not good, I think it'd be better if we check it inside of eventLoop.
Unfortunately, Lua doesn't have any built-in function to count elements of a table with non-numeric keys, we have to make this function ourselves.
This function counts given table, then checks how many elements does it have - if only 1 - it returns the last player standing nickname, if 0 - returns nil (to avoid bugs) and if more than 1, it returns false.
So: nil = error, false = no winner.
Okay, now we need to put this function into eventLoop()
The code is almost done. After each game we should reset changed variables to default.
To reset all of them easily, we can put them all to the function (they can't be local!).
Notice, I haven't put boostDelay variable here because it wasn't changed anywhere in the script. It's a "constant variable".
From now to reset these variables, you just call set() function.
These variables should be reseted when the the game starts, so inside of eventNewGame() function.
2 small changes and our minigame will be ready!
I think that it would be much more interesting if we add some randomness to it. Random spawn height and random cannonball angle would be great!
It's easy, we need math.random() function.
Let's go back to our cannonball spawn function and change it a bit.
And the last thing for this tutorial is beauty of our code. Notice, that we repeat our XML code 3 times. If you wanted to change it, you would need to change it in 3 places. It doesn't sound scary enough, I know but sometimes you will use one value like 10, 20 or even 100 times so it's good to get used to this.
Let's just put the XML code into some variable and then use this variable instead of XML itself, it should be easy for you!
That's all in this tutorial, I hope I helped you with Lua learning! A minigame is ready to play.
Full code:
There is still some stuff in this script that could be upgraded but currently this tutorial is much bigger than I expected so I leave it as it is.
Remember! You need at least 2 players, otherwise minigame won't work correctly!
Qmicozium
Today we're going to make a simple minigame with full explaination of every line of the code.
This tutorial is meant for people that have some basic knowledge about Lua or programming in general.
So, we'll make a minigame similar to survivor but cannonballs will be spawned automaticly. A difficulty level will grow slowly. Each player will have 2 lives and the last mouse standing will be the winner.
Let's start!
First of all we need a map on which players will be playing. In Lua we can load a new map using a @MapCode or using an XML code.
I made a supersimple map and I'm going to load its XML code. A function that loads a new map is called tfm.exec.newGame().
code_language.lua:
tfm.exec.newGame('<C><P F="1" /><Z><S><S L="800" P="0,0,0.3,0.2,0,0,0,0" T="6" Y="400" X="400" H="50" /><S L="10" o="fffffffff" P="0,0,0,0,0,0,0,0" T="12" Y="-367" c="3" X="550" H="1500" /></S><D><DS X="737" Y="361" /></D><O /></Z></C>')
Now, we need to spawn the cannonballs. We want to spawn them on the left side of the screen and they should go right.
A function that spawns a shaman object is called tfm.exec.addShamanObject(), it also has some arguments inside of ( ) - first of them is an ID of the object.
ID of a cannonball is 17 (https://mforum.ist/threads/lua-documentation.16).
Another 2 arguments are X position and Y position, look at the picture down here to understand how does it work:
.

And we want to spawn this cannonball somewhere over there:

So X should be ~50 and Y should be ~220.
Spawning a cannonball on this position will look like that:
code_language.lua:
tfm.exec.addShamanObject(17, 50, 220)
To make it go right, we'll use 4th argument of this function - angle. We need to rotate a cannonball 90 degrees:
code_language.lua:
tfm.exec.addShamanObject(17, 50, 220, 90)
Okay, it was quite easy.
Now we need to spawn this cannonball every 0.5 second and the only thing we need to do is insert this function inside of an eventLoop function:
code_language.lua:
function eventLoop()
tfm.exec.addShamanObject(17, 50, 220, 90)
end
It works fine, but the game should start from something easier, at the beggining we should spawn the cannonballs like every 2 seconds.
To do this we need this code:
code_language.lua:
local spawnDelay = 2
local boostDelay = 5
local toSpawn = 0
local toBoost = 0
function eventLoop()
if (toBoost == boostDelay and spawnDelay > 0.5) then
spawnDelay = spawnDelay - 0.5
toBoost = 0
end
if (toSpawn == 0) then
tfm.exec.addShamanObject(17, 50, 220, 90)
toBoost = toBoost + 1
toSpawn = spawnDelay
end
toSpawn = toSpawn - 0.5
end
At the beggining of the code we have 4 variables:
- spawnDelay - delay between cannonballs, set to 2 (seconds) by default
- boostDelay - number of cannonballs that need to be spawned, to decrease spawnDelay
- toSpawn - counter nr. 1 used in our code, we use it to check how many seconds left to spawn another cannonball
- toBoost - counter nr. 2 used in our code, we use it to check how many cannonballs left to decrease spawnDelay
Rest of the code you can analyze yourself, you don't need any programming knowledge to understand it.
The code describes itself, don't worry if you don't undestand it straightaway. Spend some more time on it.
You can print out some values, it can help. I did it for you, so you can use this code for analyzing purposes:
code_language.lua:
local spawnDelay = 2
local boostDelay = 5
local toSpawn = 0
local toBoost = 0
function eventLoop()
if (toBoost == boostDelay and spawnDelay > 0.5) then
print('<vp>I\'m decresing spawnDelay!</vp>')
spawnDelay = spawnDelay - 0.5
toBoost = 0
end
if (toSpawn == 0) then
tfm.exec.addShamanObject(17, 50, 220, 90)
toBoost = toBoost + 1
toSpawn = spawnDelay
print('<rose>spawnDelay = ' .. spawnDelay .. ' (' .. boostDelay-toBoost .. ' cannonballs left to decrease spawnDelay)</rose>')
end
toSpawn = toSpawn - 0.5
print('toSpawn = ' .. toSpawn)
end
That's not the end - as I said at the beggining there should be no mice joining during the game.
The best way to do it, is creating a table with all the mice that were in the room when the game started.
We'll use eventNewGame() here to detect when the game starts and then, put every player to this table.
A table-variable with all the mice should be placed at the beggining of the code, I called it inGame.
code_language.lua:
local inGame = {}
........
function eventNewGame()
for player in next, tfm.get.room.playerList do
inGame[player] = true
end
end
I use a FOR loop to go thru the list of players in the room and add every player to inGame variable we definied before.
But how to prevent players from joining during the game?
The easiest way is just killing them over and over. We need 2 events here - eventNewPlayer() and eventPlayerRespawn()
In eventNewPlayer() we should just kill a player and in eventPlayerRespawn() we should check if the respawned player is inside of inGame table and if he isn't, then we just kill him.
code_language.lua:
function eventNewPlayer(player)
tfm.exec.killPlayer(player)
end
function eventPlayerRespawn(player)
if (inGame[player] ~= nil) then
tfm.exec.killPlayer(player)
end
end
But what if the player is in the game but he leaves the tribehouse and then re-joins?
He will still be inside of inGame table - it's not good! We can easily fix it by adding another event - eventPlayerLeft() in which we'll remove from inGame table the player that left the room:
code_language.lua:
function eventPlayerLeft(player)
inGame[player] = nil
end
Now, the entire code looks like this:
code_language.lua:
local spawnDelay = 2
local boostDelay = 5
local toSpawn = 0
local toBoost = 0
local inGame = {}
tfm.exec.newGame('<C><P F="1" /><Z><S><S L="800" P="0,0,0.3,0.2,0,0,0,0" T="6" Y="400" X="400" H="50" /><S L="10" o="fffffffff" P="0,0,0,0,0,0,0,0" T="12" Y="-367" c="3" X="550" H="1500" /></S><D><DS X="737" Y="361" /></D><O /></Z></C>')
function eventLoop()
if (toBoost == boostDelay and spawnDelay > 0.5) then
spawnDelay = spawnDelay - 0.5
toBoost = 0
end
if (toSpawn == 0) then
tfm.exec.addShamanObject(17, 50, 220, 90)
toBoost = toBoost + 1
toSpawn = spawnDelay
end
toSpawn = toSpawn - 0.5
end
function eventNewGame()
for player in next, tfm.get.room.playerList do
inGame[player] = true
end
end
function eventNewPlayer(player)
tfm.exec.killPlayer(player)
end
function eventPlayerRespawn(player)
if (inGame[player] == nil) then
tfm.exec.killPlayer(player)
end
end
function eventPlayerLeft(player)
inGame[player] = nil
end
Looks great! The last thing we need to implement is 2 lives for each player.
Let's kill two birds with one stone. I'm sure you remember inGame table. When the game starts we are doing
inGame[player] = true. And when we check if the player is inGame, we are doing if (inGame[player] ~= nil) - we just check if the value exists. So instead of inGame[player] = true we could do inGame[player] = 'BANANA' and it'd work too because we just check if this value exists (nil = doesn't exist).So what if instead of true (or instead of banana) we would hold a number of lives of every player?
Let's check it! Inside of function eventNewGame() replace true with number 2:
code_language.lua:
function eventNewGame()
for player in next, tfm.get.room.playerList do
inGame[player] = 2
end
end
Also, we need to create another event - eventPlayerDied() and inside of it we'll check 2 things - if the player is inside of inGame and if the player has more than 1 life left.
If both conditions are true, we'll respawn a player and decrease his lives with 1. Else, we will remove him from inGame so he won't be able to play until the game ends.
code_language.lua:
function eventPlayerDied(player)
if (inGame[player] ~= nil and inGame[player] > 1) then
tfm.exec.respawnPlayer(player)
inGame[player] = inGame[player] - 1
else
inGame[player] = nil
end
end
Done!
But what with the winner?! Every time when any player dies or leaves, we need to check if there are more than 1 players alive. If so, game continues but if there are only 1 player left, it means that we have a winner! But checking if there's only 1 player in 2 places will require repeating our code, which is not good, I think it'd be better if we check it inside of eventLoop.
Unfortunately, Lua doesn't have any built-in function to count elements of a table with non-numeric keys, we have to make this function ourselves.
code_language.lua:
function checkWinner(t)
local i = 0
for _ in pairs(t) do i = i + 1 end
if (i == 1) then
for player in next, t do
return player
end
elseif (i == 0) then
return nil
end
return false
end
So: nil = error, false = no winner.
Okay, now we need to put this function into eventLoop()
code_language.lua:
function eventLoop()
winner = checkWinner(inGame)
if (winner == nil) then
-- 0 players, weird, lets just start another game
tfm.exec.newGame('<C><P F="1" /><Z><S><S L="800" P="0,0,0.3,0.2,0,0,0,0" T="6" Y="400" X="400" H="50" /><S L="10" o="fffffffff" P="0,0,0,0,0,0,0,0" T="12" Y="-367" c="3" X="550" H="1500" /></S><D><DS X="737" Y="361" /></D><O /></Z></C>')
elseif (winner ~= false) then
-- if it isn't false, then we have a winner!
print('WINNER: ' .. winner)
tfm.exec.newGame('<C><P F="1" /><Z><S><S L="800" P="0,0,0.3,0.2,0,0,0,0" T="6" Y="400" X="400" H="50" /><S L="10" o="fffffffff" P="0,0,0,0,0,0,0,0" T="12" Y="-367" c="3" X="550" H="1500" /></S><D><DS X="737" Y="361" /></D><O /></Z></C>')
end
...........
end
The code is almost done. After each game we should reset changed variables to default.
To reset all of them easily, we can put them all to the function (they can't be local!).
code_language.lua:
function set()
spawnDelay = 2
toSpawn = 0
toBoost = 0
end
From now to reset these variables, you just call set() function.
These variables should be reseted when the the game starts, so inside of eventNewGame() function.
code_language.lua:
functon eventNewGame()
set()
......
end
2 small changes and our minigame will be ready!
I think that it would be much more interesting if we add some randomness to it. Random spawn height and random cannonball angle would be great!
It's easy, we need math.random() function.
Let's go back to our cannonball spawn function and change it a bit.
tfm.exec.addShamanObject(17, 50, math.random(190, 250), math.random(80, 100))
And the last thing for this tutorial is beauty of our code. Notice, that we repeat our XML code 3 times. If you wanted to change it, you would need to change it in 3 places. It doesn't sound scary enough, I know but sometimes you will use one value like 10, 20 or even 100 times so it's good to get used to this.
Let's just put the XML code into some variable and then use this variable instead of XML itself, it should be easy for you!
That's all in this tutorial, I hope I helped you with Lua learning! A minigame is ready to play.
Full code:
code_language.lua:
local boostDelay = 5
local inGame = {}
local xml = '<C><P F="1" /><Z><S><S L="800" P="0,0,0.3,0.2,0,0,0,0" T="6" Y="400" X="400" H="50" /><S L="10" o="fffffffff" P="0,0,0,0,0,0,0,0" T="12" Y="-367" c="3" X="550" H="1500" /></S><D><DS X="737" Y="361" /></D><O /></Z></C>'
function set()
spawnDelay = 2
toSpawn = 0
toBoost = 0
end
function checkWinner(t)
local i = 0
for _ in pairs(t) do i = i + 1 end
if (i == 1) then
for player in next, t do
return player
end
elseif (i == 0) then
return nil
end
return false
end
tfm.exec.newGame(xml)
function eventLoop()
winner = checkWinner(inGame)
if (winner == nil) then
-- 0 players, weird, lets just start another game
tfm.exec.newGame(xml)
elseif (winner ~= false) then
-- if it isn't false, then we have a winner!
print('WINNER: ' .. winner)
tfm.exec.newGame(xml)
end
if (toBoost == boostDelay and spawnDelay > 0.5) then
spawnDelay = spawnDelay - 0.5
toBoost = 0
end
if (toSpawn == 0) then
tfm.exec.addShamanObject(17, 50, math.random(190, 250), math.random(80, 100))
toBoost = toBoost + 1
toSpawn = spawnDelay
end
toSpawn = toSpawn - 0.5
end
function eventNewGame()
set()
for player in next, tfm.get.room.playerList do
inGame[player] = 2
end
end
function eventNewPlayer(player)
tfm.exec.killPlayer(player)
end
function eventPlayerRespawn(player)
if (inGame[player] == nil) then
tfm.exec.killPlayer(player)
end
end
function eventPlayerLeft(player)
inGame[player] = nil
end
function eventPlayerDied(player)
if (inGame[player] ~= nil and inGame[player] > 1) then
tfm.exec.respawnPlayer(player)
inGame[player] = inGame[player] - 1
else
inGame[player] = nil
end
end
Remember! You need at least 2 players, otherwise minigame won't work correctly!
Qmicozium
Last edited:
