Mindustry 8 pre-release is now available. The new functionality of Mindustry Logic is already supported by Mindcode when setting the language target to 8 either by command-line argument, or by the #set target = 8m; directive.
The Mindustry Logic v8 instruction set and corresponding Mindcode functions are described in Function reference for Mindustry Logic 8.1.
To run the code produced by Mindcode Logic 8, you need to use one of the pre-release versions, or one of the development versions of Mindustry (a "bleeding-edge" version).
Caution
If you save a game or a campaign in a pre-release or development version of Mindustry, you will no longer be able to open this game/campaign in older versions of the game. Additionally, there exists a possibility that a future version of Mindustry, including an official Mindustry release, won't be compatible with a particular version you're using (especially when using development versions). There thus might be no official release that would be able to read your game state.
It is strongly recommended to back up the state of your current game (Settings/Game Data/Export data) before running any pre-release or development version of Mindustry.
You can download and install the pre-release version in the same way as an official release, for example, from Itch.
How to run a development version of Mindustry:
- Download a Java installation package from https://github.com/Anuken/MindustryJreBuilds/releases/tag/v1.
- Extract the package into a directory on your computer.
- Download a development version of Mindustry from https://github.com/Anuken/MindustryBuilds/releases.
- This link leads to all the versions of mindustry ever released. Use the latest version if you don't know which to use, or version 25368.
- Download the
Mindustry-BE-Desktop-<build number>.jarfile
- Run the following command:
java.exe -jar Mindustry-BE-Desktop-<build number>.jar
- Use the full path to
java.exefrom the directory into which you've placed the files in step 2. - Use the full path including the correct name of the file you've downloaded in step 3.
The changes that affect Mindcode the most are described here. For a comprehensive list of changes to Mindustry Logic, see Changes to Mindustry Logic.
Mindustry now handles the numeric literals with double precision. Loss of precision, and the inability to encode some numeric constants into mlog, no longer happen.
Color literals for named colors (e.g., %[red]), are also supported by Mindcode.
A number of properties to be used with the sensor instructions were added. Mindcode supports using new properties out of the box, so no special support for these was needed.
The format instruction allows dynamic text formatting. It is possible to output special placeholders {0} to {9} into the text buffer. The format instruction then replaces a placeholder with actual value. Example:
print `Test: {1}{0}`
format `A`
format `B`
printflush message1
will output Test: BA to message1.
The printf function is designed to use this mechanism: printf(fmt, a, b, c) gets translated to
print fmt
format a
format b
format c
The upside is that fmt can be a variable and the formatting still works. The downside is that it generally isn't possible to optimize the format instructions, even when their parameters get resolved to a constant value. (This would mean manipulating the placeholders in instructions that produced the text buffer, which is not universally possible with a static analysis.) The existing compile time formatting (e.g., println($"Position: $x, $y");) will optimize to better code better if some or all of the parameters resolve down to constant values.
Apart from the printf(), Mindcode supports new format() function, which just outputs the format instruction for each of its arguments.
The format instruction searches the text buffer, looking for a placeholder with the lowest number. The first occurrence of this placeholder is then replaced by the value supplied to the format. This means that each format only replaces one placeholder: printf("{0}{0}{1}", "A", "B") followed by printflush therefore outputs AB{1} and not AAB. On the other hand, printf("A{0}B", "1{0}2", "X") outputs A1X2B - the placeholder inserted into the text buffer by the format instruction is used by the subsequent format. That opens up a lot of possibilities for building outputs dynamically; for example, to print numbers with thousands separators:
#set target = 8m;
// Formats a number into the text buffer, without external memory.
// The text buffer must not contain placeholders {0} and {1}. It must contain at least one other placeholder ({2} or higher).
def myFormatNumber(n)
n = floor(n);
if n < 0 then
format("-{2}"); // Prepend the minus sign
n = abs(n);
end;
while n > 999 do
mod = n % 1000;
// Insert placeholder for the next group, thousands separator, leading zeroes (if any) and a placeholder for this group.
format(mod < 10 ? "{2},00{1}" : mod < 100 ? "{2},0{1}" : "{2},{1}");
format(mod);
n \= 1000;
end;
// Put the rest of the number into the remaining placeholder
format(n);
end;
// Prints the number straight away
// The text buffer must not contain any placeholders lower than {3}.
def myPrintNumber(n)
print("{2}");
myFormatNumber(n);
end;
println("The numbers are {2} and {3}.");
myFormatNumber(floor(rand(1000000000)));
myFormatNumber(-floor(rand(1000000000)));
myPrintNumber(floor(rand(100000)));
Note: formatNumber and printNumber functions, identical to those above, are part of the system library.
The Print Merging optimization was enhanced to use the new formatting mechanism where possible. For example, println($"Minimum: $min, middle: $mid, maximum: $max") in language targets earlier than 8 compiles into
print `Minimum: `
print min
print `, middle: `
print mid
print `, maximum: `
print max
print `\n`
The new Print Merging optimization utilizing format saves three instructions by producing
print `Minimum: {0}, middle: {0}, maximum: {0}\n`
format min
format mid
format max
To prevent the new Print Merging optimization interfering with custom uses of the format instruction, it isn't used if a string constant containing a {0} substring, or some other specific substrings that might lead to the code creating {0} in the text buffer, are detected in the program. This leaves the placeholders {1} to {9} to be used freely by the user. It even allows interleaving the old-fashioned prints with the new format with no restrictions:
#set target = 8m;
param a = 10; // prevent a from being propagated as a constant
println("{2} {1}"); // if you use "{1} {0}" instead, the output will be the same, but different optimization will happen
format("Before");
println($"Value: $a");
format("After");
This program will compile to
set a 10
print "{2} {1}\n"
format "Before"
print "Value: {0}\n"
format a
format "After"
and will output
After Before
Value: 10
The format instruction is also supported by the emulator, allowing you to experiment with the new print support right away in the web app.
The printchar instruction allows outputting individual characters to the text buffer. The character is identified by its ASCII code. The ASCII code can be specified directly (printchar(65);), using Mindcode's character literal (printchar('!');) or using the ascii() function (printchar(ascii("Today"));).
When an object is used as instruction argument, the corresponding icon is output instead: printchar(@mega);.
The Print Merging optimization handles the printchar instructions when possible:
#set target = 8m;
for i in 0 ... 4 do
itemType = lookup(:item, i);
printchar(itemType);
amount = vault1.sensor(itemType);
println(" ", amount);
end;
printflush(message1);
produces
print " {0}\n {0}\n {0}\n {0}\n"
sensor :amount vault1 @copper
format :amount
sensor :amount vault1 @lead
format :amount
sensor :amount vault1 @metaglass
format :amount
sensor :amount vault1 @graphite
format :amount
printflush message1
To make using the printchar() function easier, character literals were added to Mindcode.
Since the v8 Build 150 Beta pre-release, Mindustry supports the select instruction facilitating conditional assignments.
Note
The Mindcode target which supports the select instruction is 8.1.
Mindcode doesn't provide direct access to the select instruction, but optimizes conditional expressions to use the select instruction when possible. Using the ternary operator with simple values (variable = condition ? trueValue : falseValue) in Mindcode typically results in the select instruction being used.
The strictNotEqual condition is supported neither in the op, nor in the jump instructions, and prior to the select instruction, this condition needs to be evaluated in two instructions. The select instruction allows simplifying it:
op strictNotEqual resul a bcan be expressed asselect result strictEqual a b false true.jump target strictNotEqual resul a bcan be expressed asselect @counter strictEqual a b @counter target.
The jump strictNotEqual instruction can't be emulated using a select instruction when symbolic labels are used, as symbolic label can't be used with the select instruction. Furthermore, it is possible to deactivate this mechanism using the emulate-strict-not-equal option.
The read and write instructions were updated to support reading and writing data to other blocks and objects apart from memory blocks.
Mindustry 8 allows reading and writing variables of another processor, addressing them by name. This functionality is provided as an extension to the read and write instructions, which can take processors instead of memory cells/banks, and string values representing variable names instead of numerical index.
This functionality is accessible in Mindcode via the new read() and write() methods:
#set target = 8m;
x = processor1.read("x");
y = processor1.read("y");
processor1.write(x + y, "z");
compiles into
read :x processor1 "x"
read :y processor1 "y"
op add *tmp2 :x :y
write *tmp2 processor1 "z"
Unlike external variables, access to another processor's variables is not limited to numeric values. All possible variable values are correctly transferred using these new instructions.
Thanks to the ability to access other processors' variables, Mindcode now supports remote functions and variables, as described here.
Mindustry 8 supports accessing individual characters of string values as UTF-16 numeric values using the read instruction. Characters are indexed starting at 0. The instruction returns null when the index is out of bounds.
This functionality is accessible in Mindcode via the char() function.
A length of a string can be obtained by sensing it's @size property. This functionality is accessible in Mindcode either using the @size property, sensor function or the specialized strlen() function.
Similarly to strings, it is possible to read characters from message blocks using the read instruction. The mechanics is the same as in the case of strings: both char() and strlen() functions can be used with message blocks:
#set target = 8m;
print("ABCD");
printflush(message1);
println(strlen(message1));
println(char(message1, 0));
printflush(message2);
The ability to access individual characters of string values is a convenient way to store data. Mindcode provides the encode() function to easily encode data into strings, and also uses the functionality internally to implement very space-efficient jump tables.
Mindustry 8 allows reading and writing individual canvas pixels using read and write. Canvas is an Erekir-specific block, so this is only relevant when working with mixed tech or when creating world processors for Erekir maps. The indexes need to be in the 0 ... 64 range, and individual pixel values are in the 0 ... 8 range (there are only eight different colors supported by canvases).
In the latest BE, a small but important change was made to the wait instruction:
- When the instruction has a zero argument, it still skips the rest of the current frame but isn't executed again on the next frame.
- When the instruction skips the rest of the frame (i.e., the instruction yields the execution), the accumulator isn't decreased.
The important change is the second one: when an instruction waits for a specified amount of time, it is guaranteed that the accumulator will have enough capacity to execute at least the number of instructions corresponding to the wait time (and the processor's current instructions-per-tick value) uninterrupted during the next update.
This can be used to ensure that a section of code will be executed atomically (without interruption). Mindcode provides atomic code blocks for this purpose.
This instruction prints the contents of the text buffer onto the display. So, instead of printflush(message1); you'll use drawPrint(x, y, alignment); drawflush(display1); to output the text on the display instead of a message block.
The new draw print instruction is represented by the drawPrint() function, as print() is already taken.
The text is drawn using a monospace font. Each character being printed represents one graphical operation. The graphics buffer has a capacity of 256 operations, after which it doesn't accept new ones and must be applied using drawflush.
New draw rotate, draw translate and draw scale can be used to rotate, translate or scale the graphical output. draw reset resets the transformations.
These instructions are represented by rotate(), translate(), scale() and reset() functions. By issuing reset(); rotate(90); translate(0, -176); it is possible to rotate the output by 90 degrees counterclockwise when drawing to the large display.
Figuring out the correct transformations isn't always easy. Issuing incorrect ones may result in all the output being drawn off-screen, which might be challenging to diagnose. For this reason, the following system functions are defined:
rotateLeftLarge(),rotateRightLarge(),upsideDownLarge(): ensures the output to the large display will be rotated left, right or upside down by issuingreset(),rotate()andtranslate()as needed.flipVerticalLarge(),flipHorizontalLarge(): flips output to the large display vertically or horizontally.rotateLeftSmall()and so on: the same, but intended for drawing on a small display.rotateLeft(display): will inspect the display and apply the correct transformation for small/large ones.smallToLarge(),largeToSmall(): will set up scaling so that output to a small display will be mapped completely to the large one or vice versa. Especially thelargeToSmall()transformation results into a graphics which is still nicely readable on a smaller display.
Note: these new instructions map either to functions or to operators. When compiling for Mindustry 7, the new operators are available by default, and the new functions are available through system libraries graphics and math, implemented in a backwards compatible way. When the function is provided by the target processor, it is used instead of the library implementation. This way you can start using these operators and functions when compiling for Mindustry 7 and seamlessly transition to Mindustry 8 without having to update your code.
Reverses the packcolor instruction. Prior to version 8, the same operation may be performed by the unpackcolor() function from the graphics library.
Positive modulo: like modulo, except when the divisor is positive and the dividend is negative, still returns a positive number. The instruction is mapped to the %% operator. Prior to version 8, the operator is emulated using available instructions.
Unsigned right-shift operator. The difference between the signed (>>) and unsigned (>>>) right shift operators concerns negative numbers: the signed operator copies the value of the leftmost bit (the sign bit) to lower bits when shifting, while the unsigned operator shifts in zeroes.
The instruction is mapped to the >>> operator. Prior to version 8, the operator is emulated using available instructions.
The instruction op logn result a b computes the logarithm of a in base b. Prior to version 8, the same operation may be performed by the logn() function from the math library.
Provides the signum of the argument (-1, 0 or 1 for a negative, zero or positive argument). Prior to version 8, the same operation may be performed by the sign() function from the math library.
Rounds the argument to the closest integer. Prior to version 8, the same operation may be performed by the round() function from the math library.
Allows determining whether a given weather type is active or activate/deactivate it. Supported weathers are @snowing, @rain, @sandstorm, @sporestorm, @fog and @suspend-particles
The instruction has a new output parameter that says whether displaying the message succeeded (it doesn't if another message is still being shown). Passing in special value @wait makes the instruction wait for the previous message to disappear.
The instruction has a new parameter to specify whether the explosion triggers additional effects.
Plays a sound, either in a specific position on the world map or with a global volume and pan.
Show the full list of available sounds.
@sfx-acceleratorCharge@sfx-acceleratorConstruct@sfx-acceleratorLaunch@sfx-acceleratorLightning1@sfx-acceleratorLightning2@sfx-beamHeal@sfx-beamLustre@sfx-beamMeltdown@sfx-beamParallax@sfx-beamPlasma@sfx-beamPlasmaSmall@sfx-blockBreak1@sfx-blockBreak2@sfx-blockBreak3@sfx-blockExplode1@sfx-blockExplode1Alt@sfx-blockExplode2@sfx-blockExplode2Alt@sfx-blockExplode3@sfx-blockExplodeElectric@sfx-blockExplodeElectricBig@sfx-blockExplodeExplosive@sfx-blockExplodeExplosiveAlt@sfx-blockExplodeFlammable@sfx-blockExplodeWall@sfx-blockHeal@sfx-blockPlace1@sfx-blockPlace2@sfx-blockPlace3@sfx-blockRepair@sfx-blockRotate@sfx-chargeCorvus@sfx-chargeLancer@sfx-chargeVela@sfx-click@sfx-coreLand@sfx-coreLaunch@sfx-door@sfx-drillCharge@sfx-drillImpact@sfx-explosion@sfx-explosionAfflict@sfx-explosionArtillery@sfx-explosionArtilleryShock@sfx-explosionArtilleryShockBig@sfx-explosionCleroi@sfx-explosionCore@sfx-explosionCrawler@sfx-explosionDull@sfx-explosionMissile@sfx-explosionNavanax@sfx-explosionObviate@sfx-explosionPlasmaSmall@sfx-explosionQuad@sfx-explosionReactor@sfx-explosionReactor2@sfx-explosionReactorNeoplasm@sfx-explosionTitan@sfx-healWave@sfx-loopBio@sfx-loopBuild@sfx-loopCircuit@sfx-loopCombustion@sfx-loopConveyor@sfx-loopCultivator@sfx-loopCutter@sfx-loopDifferential@sfx-loopDrill@sfx-loopElectricHum@sfx-loopExtract@sfx-loopFire@sfx-loopFlux@sfx-loopGlow@sfx-loopGrind@sfx-loopHover@sfx-loopHover2@sfx-loopHum@sfx-loopMachine@sfx-loopMachine2@sfx-loopMachineSpin@sfx-loopMalign@sfx-loopMineBeam@sfx-loopMissileTrail@sfx-loopPulse@sfx-loopRegen@sfx-loopShield@sfx-loopSmelter@sfx-loopSpray@sfx-loopSteam@sfx-loopTech@sfx-loopThoriumReactor@sfx-loopThruster@sfx-loopUnitBuilding@sfx-massdriver@sfx-massdriverReceive@sfx-mechStep@sfx-mechStepHeavy@sfx-mechStepSmall@sfx-padLand@sfx-padLaunch@sfx-plantBreak@sfx-rain@sfx-rockBreak@sfx-shieldBreak@sfx-shieldBreakSmall@sfx-shieldHit@sfx-shieldWave@sfx-shipMove@sfx-shipMoveBig@sfx-shockBullet@sfx-shockwaveTower@sfx-shoot@sfx-shootAfflict@sfx-shootAlpha@sfx-shootArc@sfx-shootArtillery@sfx-shootArtillerySap@sfx-shootArtillerySapBig@sfx-shootArtillerySmall@sfx-shootAtrax@sfx-shootAvert@sfx-shootBeamPlasma@sfx-shootBeamPlasmaSmall@sfx-shootBreach@sfx-shootBreachCarbide@sfx-shootCleroi@sfx-shootCollaris@sfx-shootConquer@sfx-shootCorvus@sfx-shootCyclone@sfx-shootDiffuse@sfx-shootDisperse@sfx-shootDuo@sfx-shootEclipse@sfx-shootElude@sfx-shootEnergyField@sfx-shootFlame@sfx-shootFlamePlasma@sfx-shootForeshadow@sfx-shootFuse@sfx-shootHorizon@sfx-shootLancer@sfx-shootLaser@sfx-shootLocus@sfx-shootMalign@sfx-shootMeltdown@sfx-shootMerui@sfx-shootMissile@sfx-shootMissileLarge@sfx-shootMissileLong@sfx-shootMissilePlasma@sfx-shootMissilePlasmaShort@sfx-shootMissileShort@sfx-shootMissileSmall@sfx-shootNavanax@sfx-shootOmura@sfx-shootPayload@sfx-shootPulsar@sfx-shootQuad@sfx-shootReign@sfx-shootRetusa@sfx-shootRipple@sfx-shootSalvo@sfx-shootSap@sfx-shootScathe@sfx-shootScatter@sfx-shootScepter@sfx-shootScepterSecondary@sfx-shootSegment@sfx-shootSmite@sfx-shootSpectre@sfx-shootStell@sfx-shootSublimate@sfx-shootTank@sfx-shootToxopidShotgun@sfx-stepMud@sfx-stepWater@sfx-tankMove@sfx-tankMoveHeavy@sfx-tankMoveSmall@sfx-uiBack@sfx-uiButton@sfx-uiChat@sfx-uiFavorite@sfx-uiNotify@sfx-uiUnlock@sfx-unitCreate@sfx-unitCreateBig@sfx-unitExplode1@sfx-unitExplode2@sfx-unitExplode3@sfx-walkerStep@sfx-walkerStepSmall@sfx-walkerStepTiny@sfx-waveSpawn@sfx-wind@sfx-wind2@sfx-wind3@sfx-windHowl@sfx-wreckFall@sfx-wreckFallBig
These instructions serve for displaying markers on the map or minimap, as part of creating interactive map objectives.
Adds a map locale property value to the text buffer.
« Previous: System library | Up: Contents | Next: Parallel processing »