Category Archives: ManiaScript

Posts regarding Nadeo’s ManiaScript language

Map Crafting

Today I’ll show you the basic idea behind MapType scripts.
These are necessary in order to build maps that you can use for your game mode. You can’t just use any map as there are usually some requirements a map needs to implement. In the case of Defusing 2 different spawns for the teams and at least one bomb goal will be needed.

So I created the map type script accordingly which you can find here: DefusingArena.Script.txt
I called it DefusingArena because that’s the pattern for map types.

For the map type script I don’t extend another script because it isn’t very complex.

Some important facts that need to be mentioned:

#RequireContext CSmMapType

This directive defines the class in which the script is running. For the game mode the ModeBase script does this assignment for CSmMode. It’s a necessary statement in order to run the script as a map type or a game mode.

CustomEditAnchorData = True;

Enables the editing of anchors. In the map type you have access to anchors that hold metadata about spawns and goals. So you can enable the map makers to assign objectives like the corresponding team of spawns and names of goals.

yield;

yield; causes the script to sleep for a moment. It’s basically a server tick. During yield the environment variables are updated like for example the PendingEvents.
Keep in mind that events are only available for one single server tick, that’s why I process them after each tick in order not to “overlook” one.
In the game mode script yield is called inside the ModeBase script so we don’t have to worry about it.

In my own function UpdateValidability() I check for the presense of all necessary spawns and goals. It’s called whenever the map is modified. By updating the ValidationStatus of a map the flag at the bottom right of the map editor is changed accordingly.
It’s a key feature of map types as invalid maps can’t be used for the game mode and therefore the map type script requires the map maker to build a valid and usable map.

ValidabilityRequirementsMessage = "Error message";
ValidationStatus = CSmMapType::ValidationStatus::NotValidable;

These statements turn the flag red and show an error message when the user clicks on the red flag. That’s useful in order to show the map maker what’s wrong with his map so that he can fix it.
If all requirements are met I set the ValidationStatus to Validated which will turn the flag green.

When the map maker switches into anchor editing mode and clicks on an anchor there’s an EditAnchor event risen.
I handle the event by showing a manialink that offers possibilities to the metadata of spawns or goals.

The manialink is running an additional script which will need to communicate with the actual map type script.

For this process key-value-coding is used. The map type declares variables for the object ManialinkPage that is also available for the manialink script.
As soon as the user clicks on a button like 1 or 2 (representing the team assigned to a spawn) a variable is changed by the manialink script and the map type script recognises it because it’s observing the variable. This key-value-variable could be understood as a “shared” variable between the map type script itself and the manialink script.

The new value will then be assigned to the anchor and when the map is saved and loaded by a game mode the spawn and goal blocks will have the set .Order and .Tag

I will take a closer look on manialink scripts later.

For the development I always build a “ScriptTestMap” for the specific mode. This map serves as a playground for the coming work on the game mode. It usually only has the needed amount of spawns and goal so that you can reach them really quickly and don’t have to run from one side of a big map to the other in order to capture a goal for example.

Closing up I would like to point you to the Links Page I’ve created on which you can find further information about ManiaScript. Especially the ManiaScript Class Documentation is very helpful. It can be generated with the ManiaPlanet game client and covers all available classes and their properties.

For example you can find the validation variables in the class CMapType in case you forget them.
I myself use the documentation all the time in order to look up how exactly the properties are called and for looking up which other useful values the classes offer.

Cheerio,
steeffeen

***Labels***

In my “Defusing” series I’ve mentioned a ManiaScript technique called “Labels” which might be confusing for many developers because of its uniqueness.
Actually I myself needed some time as well in order to fully get the point of it.

A code example:

Void LogSomething() {
declare Message = "Hello World!";
+++BeforeLog+++
log(Message);
}

***BeforeLog***
***
Message ^= " It's me!";
***

Log:
Hello World! It’s me!

So what’s happening there and what do all the plusses and stars mean?

You can declare Labels between your usual statements by writing: +++LabelName+++
TGYoshi describes these declarations as “hooks” which fits pretty well.

Only declaring a label doesn’t do anything unless you actually implement it.

That’s done by writing:

***LabelName***
***
// Your code
***

So you start the implementation block by surrounding the label’s name with 3 *. Directly following is the actual implementation (code) which is surrounded by 3 * as well.

Now whenever a label-“hook” is found inside your coding the implementation of the label will be executed.
Imagine it just as if the code of the implementation is copy-pasted to the location of the declaration (hook).

In the code example the label “+++BeforeLog+++” manipulates the “Message” logged by the function “LogSomething()”.

Let me point out some things that are important to know regarding this technique:

  • Labels aren’t functions/callbacks: You can’t directly pass them parameters.
  • Labels Implementations don’t have an own scope:
    => Variables visible where the label is declared are also visible inside the label’s implementation.
    => Variables declared inside the label’s implementation are also visible afterwards outside of the implementation.
  • You can declare the same label multiple times in your script in order to execute it at different locations in your code.
  • If you implement a label that hasn’t been declared your script is still valid but your label implementation won’t be executed.
  • If you implement the same label twice (or more often) all implementations will be executed at every declaration (“hook”).
  • If another script is extending yours it will have access to all the labels declared in your script (#Extend directive).
  • Using “-” instead of “+” for the label declaration (---LabelName---) will reduce the execution of implementations to the most derived one, other implementations will be ignored.
  • (The ManiaScript compiler has an error(?): You will need to add at least one function declaration below your last label implementation, otherwise a syntax error occurs.)

Nadeo is widely using labels for their ShootMania game modes.
The “ModeBase” script (included in the game’s files) declares many different labels which mark specific moments during the execution of game modes.
If you want to build your own game mode you can extend the ModeBase and use these labels in order to execute your own code for example as soon as the script started or when a map has been loaded.

Another example is the “ModeSport” script which is extended by the game modes Elite and Heroes. It’s offering the basic round-based game play used for the modes (among other things). Additionally the ModeSport script itself is extending the ModeBase script so you can find an even deeper chain of extending scripts.

The using of labels and the extending of scripts reduces the amount of code that has to be written and improves the clarity of scripts because the code of the base script can be used for several other scripts. It can be compared to object-oriented class hierarchies.

Please don’t hesitate to comment in case you have any questions regarding Labels.

Regards,
steeffeen

It’s a Shooter!

While being able to move around is a great thing I’m still missing to be able to hit other players in order to get points and feel great!

The first thing I need are actual opponents.
In the pasts some friends of mine always helped me testing by playing my target or the subject of expriments. Even while creating the 1-line script of the previous posts they joined on my ingame server looking for discovering something new. There have been game modes that I created live while 3 of my friends where playing on my server waiting for new stuff. ;)
Shout-out to them. Thanks!

Of course they aren’t always available so I will need other targets. For that purpose bots come in handy.
There are some things you need to know about bots in ShootMania but I will cover that in another post.
For now I will simply add the following line to my script setting the desired number of bots:

Users_SetNbFakeUsers(2, 2);

With that each team has 2 bots that can be shot.
So far they don’t give points and they can’t even get eliminated though!

When a player performs actions like shooting or hitting other players events are risen. These events need to be handled and processed as by default they are ignored.
In the PlayLoop I will loop through these events and perform actions depending on the type of the events.
At this time only OnHit and OnArmorEmpty events are of interest.

An OnHit event means that a player has hit another player, I will give points based on the damage that was done by the hit. Pay attention that armor and damage is based on a ratio of 100. That means that the usual 2 armor/health you have in most game modes are actually 200 armor in the script.
That means I will divide the damage by 100 in order to get the amount of points I will grant for the hit.

Additionally I will “Discard” (ignore/destroy) events which cover self or team hits.
That’s possible by simply comparing the Shooter and the Victim of the events.

OnArmorEmpty events inform you that a player lost all of his armor, most of the times by being hit by another player but also by falling into OffZone.
If I would Discard these events the player couldn’t die.

Any valid event will be accepted by calling PassOn() with it as parameter. This will make the server process the event so that different actions will be executed. The Shooter will see +1 for each Damage dealt, the victim losing all of his armor will be eliminated and the event feed on the left hand side of the screen will get updated covering the activities that took place.

For managing the points of players I use the script “Score.Script.txt” by Nadeo that gives helpful functions, like summing up points of the current and previous rounds.
The Score object of a player has properties called Points and RoundPoints which are shown separately on the scoreboard. The function AddPoints() of the Score script adds the given amount of points to the RoundPoints.
You need to “inform” this script about the various moments in the game play like starts and ends of rounds because it can’t use the labels like our game mode script does. Currently the end round won’t be called because our round never ends, we will need to add more code in order implement correct rounds later.

What’s worth mentioning is that it’s needed to enable Rounds in the ModeBase in order to use the according labels (StartRound etc).

MB_UseSectionRound = True;

Please see the ModeBase script itself in order to learn more about the structure of it.

I will explain less and less about the script execution in the coming posts as it takes very much time and as it’s kind of boring for more experienced readers. Hopefully you will still be able to follow the development, don’t hesitate to ask if I missed explaining an important fact.

The current version of the script is available here: Defusing2.Script.txt

Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.

– Martin Golding

See you around!
steeffeen

Labels are Life

I start the next step with the almost empty script opened in the game as I create my scripts 99% directly inside the game.
(Hint: While having started a match with an own script hitting F12 on the keyboard opens the script editor, making it possible to edit it ingame. Pressing Ctrl+G shows the log.)

If you want to edit your script outside of the game you will need to restart ManiaPlanet after any change as it caches files constantly and it therefore won’t see changes in the code.

I would like to be able to walk around the map needing me to be spawned.
For this act I will use the PlayLoop label of the ModeBase script.

Each label defined in the ModeBase can be used as a point to insert our own code.
Label locations are defined via +++Name+++ or ---Name--- and the actual code for the label is surrounded by a structure like this:

***Name***
***
// Our code
***

Using --- means that only the most derived implementation of the label will be used while +++ uses every implementation. (Other scripts could extend our own script leading to more derivation levels.)

The PlayLoop is the place where you can insert code that’s executed all the time during the actual gameplay. There are other labels like for example StartServer or StartRound in which it’s possible to prepare the used resources or EndMatch in which you will probably announce the winner of the match.

During the PlayLoop I loop through all players in order spawn or unspawn them if needed by checking their SpawnStatus and possible team change or spectating requests.

For spawning I will use an own extra function in order to keep the code more clean as it might get more complex in the future.
Furthermore I use the file SM.Script.txt by Nadeo for the actual spawning action, the script needs to be included with the #Include directive first.

#Include "Libs/Nadeo/ShootMania/SM.Script.txt" as SM
This makes it possible to access functions declared in this script via the namespace “SM”.

Currently the players get simply spawned in the first spawn block I can find.

In order to be able to move around 3 more statements are needed.

UseClans = True;
Activates a team based game play which will be needed for Defusing.

StartTime = Now;
The StartTime defines the begin of the match. Players can only spawn and perform actions after the match has started, which basically means that Now needs to be bigger than or equal to StartTime.

UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
Let players move around with their characters. There are other sequences like for example the Intro or the Podium sequence during which the players will only be able to watch mediatracker clips.

After adding all this stuff and hitting “Save and Test” the script is saved, the match restarted and my character is actually being spawned. I’m able to move around! Yeeyy!

You can find the current version of the script here: Defusing1.Script.txt
I’ve tried to add meaningful comments. ;)

Greetings,
steeffeen

A new World is awaiting us!

Let’s start the ride to our goal of creating a new game mode: Defusing!

As I mentioned I will begin with a clean new file, so I need that.
I simply create an empty text file in the folder Documents/ManiaPlanet/Scripts/Modes/ShootMania/ and will edit it from now on.

Pay attention to the name of the file as it has to end with ".Script.txt" to be considered a valid script file by the ManiaPlanet game.
I simply name my file "Defusing.Script.txt" as the game mode will be called “Defusing”.

The only thing I need to do in order to get an actually running script is adding 1 line of code:

#Extends "Modes/ShootMania/ModeBase.Script.txt"

This line says that I will use another script as base and my own script is only extending the other script.
The ModeBase.Script.txt is a script file that provided by Nadeo and is therefore included in the game itself.
Its structure is handy in order to create a clear and clean game mode script. You will see later why that is.

Now I can already run the script in the game!
SM Storm -> Multiplayer -> Create -> Choose the correct file via the “Script” field -> Hit “Launch” -> Choose any map of your choice -> Click on “Play”

There we are! Our Defusing-Script is running in the game and we just discover a new world (map).
Of course nothing happens so far as the ModeBase we’re extending is only a wrapper and doesn’t actually do anything actively within the match.
The result looks like that.

And the script like that: Defusing0.Script.txt

My next aim will be to let players be spawned in a team-based matter.

If you give someone a program, you will frustrate them for a day; if you teach them how to program, you will frustrate them for a lifetime.

– David Leinweber

So see you next time!
steeffeen

Defusing Incoming!

I would like to start a blog series showing you how I usually create a ShootMania game mode using ManiaScript.

Before I started playing ShootMania there were other games which I’ve enjoyed like Age of Empires, Need for Speed or Grand Theft Auto. And probably the first First-Person-Shooter for me was Counter-Strike Source from which I would like to transfer the well known Game Mode “Bomb Defuse” to ShootMania.
In fact there’s already a game mode called “Sabotage” which basically covers what I will try to implement.

So why create another version of the game mode?
The author of Sabotage unfortunately doesn’t play ShootMania anymore and so the game mode script became old and doesn’t use all the beauty that’s possible in the meantime.
I think playing an updated and improved version of the game mode would be way more fun.

Why start all over again instead of simply improving the old script?
Well, the biggest reason: I like to do things on my own. Okay, I LOVE to do that.
Other than that the old script would have to be changed very much because of the differences in developing possibilities between when the script has been written (July 2012) and now.
That’s why I will start the Game Mode with a clean empty file.

You can’t trust code that you did not totally create yourself.

– Ken Thompson

So stay tuned for an exciting adventure of scripting and testing from 0 characters to probably more than 1000 lines of codes.
FYI: The current version of my SpeedBall Game Mode has 2284 lines. ;)

Cheerio,
steeffeen