Ok, tough question. This is probably the most complicated thing in the script syntax.
But we've not (yet) managed to get rid of it.
Rule #1
In ManiaScript, class values are
always stored as "aliases". Whatever the symbol you use when setting them. The warning is just here to remind you that <=> should be used.
First case
Here's an example :
There's a array of players, sorted by descending score, called Players.
Important note : we're talking here about an API array, one that's pre declared as a system variable.
One can write :
Code: Select all
declare BestPlayer <=> Players[0];
// Alice is the best player, so BestPlayer "points" to Alice
You would expect that :
Code: Select all
declare BestPlayer <=> Players[0];
// Alice is the best player, so BestPlayer "points" to Alice
{
... // Some code doing stuff
}
log(BestPlayer.Login);
// Will log Alice, right ???
In ManiaScript, BestPlayer is an alias. So BestPlayer means "The player in first position in the array Players". That's why, if scores have been changed, maybe it does not mean Alice anymore.
Code: Select all
declare BestPlayer <=> Players[0];
// Alice is the best player, so BestPlayer "points" to Alice
Players[1].Score += 1000;
// Give 1000 points to the 2nd best player, which is Bob
// those 2 line are completely equivalent :
// they Will log Bob, because he has an higher score right now.
log(BestPlayer.Login);
log(Players[0].Login);
In such cases, it become more clear that Class objects does not behave as Integer or Text values.
That's why we thought it would be better to use another symbol when setting variables : to remind that's not a plain affectation.
Now what if you want to keep Alice in a variable ?
The following code will work "as expected".
Code: Select all
declare BestPlayerId = Players[0].Id;
// BestPlayerId is an Ident : will never change
Players[1].Score += 1000;
// Give 1000 points to the 2nd best player, which is Bob
log(Players[BestPlayerId].Login);
// Will log Alice
ProTip : Note that the "log" will be a bit more time-consuming than the previous way : we have to find Alice in the array of players, from the Ident.
ProTip #2 : Yes, this can also be written
Code: Select all
declare BestPlayer <=> Players[Players[0].Id];
// will be an alias to Players[AliceId] and not Players[0]. Huge difference !
Players[1].Score += 1000;
// Give 1000 points to the 2nd best player, which is Bob
log(BestPlayer.Login);
// Will log Alice. Will also costs more CPU, for the alias has to be resolved.
Ok, I can do both ways. But is the alias system always used ?
Unfortunately, there are some edge cases where the aliases become a bit tricky....
What happens if you declare yourself an array of Classes.
Code: Select all
// Players[0] => Alice
// Players[1] => Bob
declare MyArray = [Players[0], Players[1]];
declare MyVal <=> MyArray[0];
MyArray = [Players[1], Players[0]];
log(MyVal.Login); // It will log "Alice", not what you may expect
If fact, when you write "MyVal <=> MyArray[0]", it means "take the alias that is stored in MyArray[0] and copy it in MyVal". So MyVal is an alias to Player[0], and not MyArray[0];
This is because the value stored in MyArray is already an alias, so we copy the alias directly, instead of making an alias to the alias. There are technical reasons : we can not easily do alias to aliases T_T'
(By now, every sane person should be confused... so don't worry if you are...)
Other hidden tricky cases ?
Actually there are : Functions returning Class objects. As with arrays, we have to make a difference between API functions and functions declared in script.
When you call an API function, the result will be a "simplified" alias. Those are unambiguous aliases referring to the object's Id, inside of an API-defined array.
Code: Select all
declare MyLabel <=> GetFirstChild("Label");
// MyLabel is an alias to Page.MainFrame.Controls[IdOfTheFirstChildFound]
Is behaving exactly like
Code: Select all
declare MyLabel <=> Page.MainFrame.Controls[GetFirstChild("Label").Id];
So if there was an API function GetBestPlayer, the code
Code: Select all
declare BestPlayer <=> GetBestPlayer();
// Alice is the best player, so BestPlayer is an alias for Players[AliceId]
Players[1].Score += 1000;
// Give 1000 points to the 2nd best player, which is Bob
log(BestPlayer.Login); // Will log Alice
When dealing with script-defined functions, the aliases are directly copied (the same way it does when using script-defined arrays).
In both cases, the using a class value you obtained from a function call will
never call the function again.
Are there some solutions to clean that mess up ?
- 1st solution : let it the way it is now. If people manage to understand this post, I guess it means that this syntax is not
that awful.
- 2nd solution : always force the use of "simplified" aliases.
Code: Select all
declare BestPlayer <=> Players[0];
// should behave exactly as
declare BestPlayer <=> Players[ Players[0].Id ];
"Simplified" aliases behave roughly as pointers : the alias will always refer to the same player, and unless the players disconnect, it will always be valid. "declare BestPlayer" will always behave the way "declare BestPlayerId" did.
This solution has 1 problem though : it hides the "cost" of fetching players by Id. And in some cases, one may prefer handling the complexity if it allows more speed.
Any opinions on this would be greatly appreciated ^_^