![]() |
You must be logged in to post messages. Please login or register The University |
Welcome! You are not logged in. Please Login or Register. | No replies |
Age of Kings Heaven » Forums » The University » RMS Tutorials: Parser Pitfalls |
Bottom |
Topic Subject: | RMS Tutorials: Parser Pitfalls |
![]() Squire |
![]() RMS Tutorials: Parser Pitfalls************************************************************ This tutorial covers several occasions where the random map script parser works in unexpected ways. Most of this is interesting, but not particularly useful or important to know about. However, when it comes to dead logical branches and comments (and especially comments in dead branches), there are some takeaways I will provide on how to avoid issues. There is a summary at the end. This article assumes you are familiar with random map scripting; if that is not the case, you should read my This article covers the quirks discussed ************************************************************ New RM scripters often mess up their comments, because coming from other programming languages they assume comments work the same way. Examples:
Basically, /* and */ must be separated from anything else by some sort of separator, such as a space, tab, or newline. Every comment must be both opened and closed; there is no way to comment out a line by just typing a character at the beginning of the line. Simple enough, you just have to know about it. Also note that RMS syntax highlighters may highlight comments even when they are not actually comments, due to the limitations of syntax highlighting in the relevant text editors. Just watch out for that and be aware of it. ************************************************************ A branch is a piece of your script that may or may not be entered. It is either gated by an ************************************************************ Example:
The expected behavior if you look at this code would be that it should set the base terrain to snow if SOMETHING is defined, and if SOMETHING is not defined the base terrain should be the default. However, if you test this in the HD Edition or in (pre-UserPatch 1.5) AoC, the base terrain will ALWAYS be snow. The "else" in the comment is being interpreted as an /* when SOMETHING is true, pick snow, then it finds an choose the default (grass) */ because it cannot do anything useful with any of those words. Then it finds base_terrain SNOW and applies that.
If you are writing a script that should function properly in the HD Edition or in pre-UP1.5 versions of the CD AoC, then you should never use Be careful when creating a map-pack consisting of a bunch of scripts, because this effectively puts entire scripts into branches! There is more though:
In the HD Edition in cases 1 and 2, the base terrain will the default (grass). In case 3, the base terrain will be SNOW. That all seems correct ... or does it? Well not exactly. Cases 2 and 3 are identical, except for the comment, yet they produce different results. So that tells us that the parser is making use of the second "if" and realizing that it dealing with nested if-branches. Long story short, don't use "if" in comments in branches either. ************************************************************ if-branches aren't the only kind of branch though. Example:
In everything other than the Definitive Edition, the base terrain will always be DIRT. The parser looks at the first branch, but doesn't pick it because its chance is 0. It then ignores everything in this dead branch, including the /* until it runs into either a percent_chance 100 base_terrain DIRT and does that. At that point, we already have 100%, so WATER cannot be picked anymore.What if we are clever and put our comment first?
Nope, we still get DIRT. So, don't put ************************************************************ You thought we were done. Far from it. I was recently working on a massive map randomizer, and suddenly my map started producing cliffs, when it was not supposed to be doing so. So I whittled away several thousand lines of code and was left with this:
And that was enough to generate cliffs whenever DE_AVAILABLE was not defined. I was baffled. So I simplified it even more:
Let me show you:
It's a bit complicated, so sit tight. Every rms command/attribute/statement (let us call them all "tokens") has a numerical ID. Every terrain (and also every object) also has a numerical ID. When the parser encounters a constant (like LEAVES) it replaces with the ID (in this case 5) and interprets it in context. When such a constant follows DLC_FORESTAUTUMN is a new terrain in the Definitive Edition; thus it is not defined in HD Edition; and TESTING12345 is never defined. So the LEAVES is being interpreted in the context of something undefined, rather than in the context of GRASS_SNOW shares an ID with < , so that is where the cliffs came from.When I realized this, I was aghast. Let us look at some fun examples to puzzle your way through:
Both of these examples combine everything we have learned so far and ultimately are a roundabout way of writing < in the HD Edition.This was my first failed attempt:
Simply defining a new constant so that you don't have to use the word
By defining the constants (even if you are in the HD Edition, where the associated terrains don't exist), the parser is no longer encountering something undefined, and interprets it as an argument for Just make sure that you never actually use those terrains in the HD Edition, or the game will crash. Define any terrain constants in your script if they do not exist in any of the versions of the game that you want your script to work in. All of this prompted me to figure out the IDs for all RMS tokens, and to determine what other nasty surprises might be in store for us. So without further ado, here are my findings: ![]() So, in addition to not using Yes, ridiculous as it sounds, you can end a comment with HOUSE
************************************************************ I didn't mention this before, but you may have noticed a bunch of UserPatch constants in the table above. Let us take a simplified example of some custom regicide code:
When generated in UP this will make it so that losing your king triggers defeat, even when not playing regicide, but when not in UP this will make your entire map blank. You may have guessed it: AMOUNT_GOLD shares an ID with Shield the definition of UP constants, so they are not defined when not needed.
Shield UP constant definitions so the UP constants are only defined on UP. Also note that for WololoKingdoms most UP constants are defined in the gamedata, so you only need to define them manually for basegame UP1.5 ************************************************************ The Definitive Edition adds new RMS tokens. Most of these are not problematic when it comes to backwards compatibility, because the parser just gets rid of them when it does not recognize them. However, there are a few problem cases. Specifically, those that accept objects or terrains as arguments. That would be
When not on the Definitive Edition, I tried to shield the
There is a way to make this code run properly on both DE and not DE, but the solution is not pretty:
When NOT_DE is active, there is an extra Now let us look at a
When not in DE, The fix is relatively easy for the HD Edition, because comments in dead branches are ignored:
However, this fix does not work in UP because comments in in dead branches are actually seen as proper comments, so we have to put in a bit more work to make it fully compatible across all versions:
It looks silly, but it works. It provides the */ only when needed to cancel out the comment opened by SHORE_FISH. If your map only needs to work on DE, ignore all this. For backwards compatibility, you will need to look at a case-by-case basis for ************************************************************ In case that was too long or too complicated, here are the main takeaways: -Always make sure your script is syntactically correct -Never allow HOUSE to be in commented code -You can freely use comments in logical branches -You can use comments in if-branches, but not in random-branches (or if you do, at least make sure they do not contain any constants or tokens) -You can freely define and use UP constants -Do not use comments in logical branches; or if you do, at least make sure they do not contain any constants or tokens (watch specifically for -Do not use comments in logical branches; or if you do, at least make sure they do not contain any constants or tokens (watch specifically for -Shield UP constant definitions with if UP_EXTENSION -Do not use comments in logical branches; or if you do, at least make sure they do not contain any constants or tokens (watch specifically for -Check occurrences of I hope that covers everything. Good luck and feel free to ask any questions or point out mistakes I made in this guide! To leave you, here is a fully functional forest nothing map for the HD Edition. Don't believe me? Try it out!
......../¯/\ ......./ / / \ Check out my Blacksmith submissions as well as my stuff in the Steam Workshop. ....../ / /\\ \ ...../ /_/_\\ \ Proud guardian of the Definitive Random Map Scripting Guide ..../_____\\\ \ ....\\\\\\\\\\\\\/ and the Random Map Scripting Links and FAQ thread. [This message has been edited by Zetnus (edited 05-28-2020 @ 09:24 PM).] |
Age of Kings Heaven » Forums » The University » RMS Tutorials: Parser Pitfalls | Top |
You must be logged in to post messages. Please login or register |
Age of Kings Heaven | HeavenGames
Copyright © 1997–2021 HeavenGames LLC. All rights reserved.
v2.4.2