Stationeers

Stationeers

77 ratings
How to Program Anything with an IC10 for the Novice
By Wilhelm W. Walrus
Learn everything from slot instructions to stack iteration, starting with a light and switch. This guide uses simple and applicable examples to demonstrate all the core features of MIPS in Stationeers.
3
3
   
Award
Favorite
Favorited
Unfavorite
The Very Basics
If you've written a script or two, it may be wise to skip this section--we're just gonna control a light with a switch.
But otherwise, let's begin:

Computers Are Calculators
At the end of the day, everything a computer does can be reduced to a function of some numbers; but we can substitute those numbers for names to create assembly code, which is what we use in Stationeers. So, anything our machinery is doing in-game is a function of some named numbers.

LogicTypes
Some of these named numbers are LogicTypes (i.e. On, Setting, Activate). When writing a script, we are trying to use numbers from various devices to change the numbers associated with other devices. The better you understand how these LogicTypes relate to the functionality of each device, the better your scripts will be! For starters, try learning the units associated with certain LogicTypes, like kPa for Pressure or kelvin for Temperature.

The Hardest Part: Naming Things
To access the LogicTypes, we need to communicate with some devices. The simplest way to communicate with any device in Stationeers is to link the device to the housing using a screwdriver. Each housing has six slots: d0 through d5. There is also db, or the device that the chip is inside (usually an IC housing).

Besides communicating with a device, we also have to store values in variables to change them. To name our devices and variables, we use the alias instruction. This isn't required, but good names make writing code easier. There are only 16 numeric registers for variables (r0-r15), so use them wisely.
alias switch d0 #comments can explain your script. Use '#' to start one alias light d1 #device register alias state r0 #numeric register

Reading
The first thing we need to do to control the light is to get the state of the switch. To read a value from a device, we use the load instruction:
l r? d? LogicType l state switch Setting

Writing
If the switch is off, we want the light to be off. This means that we want the Setting of the switch to correspond directly to On for the light. To write a value, we use the set instruction:
s d? LogicType r? s light On state

Looping and Labels
Now, we just need to make those two instructions loop forever and our light control script will be done. To jump around to different lines, we can use the j instruction. We can give it a line number, but if we ever change the script, it might change the line number we have to use. Instead, we can create a label to dynamically store important line numbers. So our basic, infinite loop looks like:
Loop: l state switch Setting #'Loop:' is the labeled line. No comments on labels s light On state j Loop #jump to the line marked 'Loop:'

Ticks and Timing
The script above will work, but as of right now it runs 31 extra times per tick. A tick in Stationeers is half a second, and ticks are what the game engine uses to synchronize all the logic and physics systems. What this really means is that a device will only change up to twice per second. We can stop and wait until the next tick starts with the yield instruction. To wait even longer, we could use sleep. Or you can just wait for the script to complete 128 lines.
yield sleep a #where a=timeInSeconds

Light/Switch Controller
Putting that all together:
Doing More
I haven't seen many bases with just one wall light, so I assume you have more than one to control as well.

Set Batch Instructions
To communicate with many devices of one type at once, we can use the batch and batch name instructions. We can load the switch's Setting just like before, but things get more complicated with all these new lights. The set batch instructions take the form:
sb prefabHash LogicType r? sbn prefabHash nameHash LogicType r?

HASHing and Macros
To get the prefabHash and nameHash, we can use the HASH macro. This is a function which turns names into numbers. To get the prefabHash, just HASH the PrefabName (which can be found in the stationpedia). The nameHash is just the HASH of the name. So, if we have default wall lights and long wall lights, all named 'autolight' with a labeler, we can control everything with the following definitions:
define lightName HASH("autolight") #=some num define lightID HASH("StructureWallLight") #=some num define longLightID HASH("StructureLightLong") #=some num
Note that when we define something we cannot change it. lightName is not a variable in a register, it is a permanent value that is simply substituted everywhere that lightName occurs in the script.

Additional Controls
Now, lets make the logic of the On state more complicated. Lets say we want the lights to be on if the switch is set to on, or if a player is detected by an occupancy sensor. This way, we can leave the lights on when we go mine at night, so the monsters can't get under our bed:
or r0 isOn isOccupied

Switching Many Lights
To accomplish all this, we'll need to add a device to the script, a new variable, and for consistency, we'll change the name of the existing variable. Putting it all together, we get:
We could even switch the or instruction for an xor (exclusive or), which would allow us to turn the lights off even while a player is in the base by turning the switch on. Then, the lights will turn back on when the player exits the base:
To better predict the behavior of logical statements like or, consider looking up the truth table for the operation.

What About Load Batch Instructions?
Load batch instructions are slightly more complicated, as they possess an additional parameter, the BatchMode:
lb r? prefabHash LogicSlotType BatchMode lbn r? prefabHash nameHash LogicSlotType BatchMode
A register can only contain one value, yet we are loading many values at once with a batch load instruction, right? Well, the BatchMode tells the computer how to put all those values together. There are four batch modes:
  • Average
  • Sum
  • Minimum
  • Maximum

Power Crisis Detector
Let's flip things around and control a single light with a bunch of batteries to demonstrate the load batch instruction. This script will turn a warning light On if the Average Charge of all our batteries is less than 20%:
alias warningLight d0 define batteryId HASH("StructureBatteryLarge") define minChargeRatio 0.20 ExaminePower: lb r0 batteryId Ratio Average #you could also Sum Charge and div by Sum Maximum slt r0 r0 minChargeRatio #logical set less than. r0=1 (or true) if charge(r0)<min s warningLight On r0 yield j ExaminePower
Doing Slots
Let's briefly discuss device slots.

LogicSlotTypes
Besides having merely having some LogicTypes, a device may have Slots which in turn have LogicSlotTypes. For example, a hydroponics tray has a plant slot and a fertilizer slot. Stackers have an input slot, an output slot, and an internal slot. A water bottle filler has two bottle slots. To read and write to slots, use:
ls r? d? slotIndex LogicSlotType ss d? slotIndex LogicSlotType r?

A Brief Detour
For the coming water bottle filler script, you will need to understand the jal instruction. The jal instruction is part of the -al family of instructions. All of these instructions save the next line number to a special register, ra. In effect, we can create functions:
jal Function #will return here, incidentally creating infinite loop Function: s db Setting 1 j ra #go to line after '-al' instruction

Branching Everywhere
Sometimes, we only want to jump if a certain condition is met. A conditional jump is called a branch and there are many branching instructions. Here are a few:
beq x y Label #goto 'Label' (or line number) if x=y beq r0 9 DoEqualsNine beqz r0 DoEqualsZero bltz r0 DoLessThanZero

Automatic Water Bottle Filler
Lets say we want to automatically turn a water bottle filler on only when it has at least one bottle which needs filling. We can use Occupied and Quantity from each slot to see if a bottle is present and full:
alias bottler d0 alias slotIndex r15 CheckSlots: yield #we yield first since we might not make it all the way through the function move slotIndex 0 #set the slot index for the following function to 0 jal HandleSlot #jump to and maybe back from function move slotIndex 1 #check next slot jal HandleSlot j CheckSlots #loop HandleSlot: ls r0 bottler slotIndex Occupied ls r1 bottler slotIndex Quantity slt r1 r1 1.5 #r1=isBottleNotFull, since each bottle holds up to 1.5L and r0 r0 r1 #bottle present AND not full s bottler On r0 bnez r0 CheckSlots #if bottler just enabled, don't return to check remaining slots, j ra #just start over at main loop. Otherwise, return to the -al address (ra)
Well that works, and it demonstrates functions and branches like I wanted, but we can actually make it simpler just by using basic arithmetic. We just need to calculate how much water can be stored and compare it to how much really is stored:
alias bottler d0 alias bottleAmt r15 alias maxAmt r14 HandleBottler ls r0 bottler 0 Occupied ls r1 bottler 1 Occupied add r0 r0 r1 #r0=numOfBottles mul maxAmt r0 1.5 ls r0 bottler 0 Quantity ls r1 bottler 1 Quantity add bottleAmt r0 r1 slt r0 bottleAmt maxAmt s bottler On r0 yield j HandleBottler
The water bottle filler uses a very small amount of power, so this really isn't particularly useful unless you have space in another, more important script that can also contain this logic. However, the hydroponics station may be automated to turn the growlight off if there are no plants in any of the slots. But be careful not to check the fertilizer slots! This is left as an exercise to the reader.
Looping and the Stack
My computer has spinny rainbow lights, and I like that, so let's put up some spinny rainbow LED's in our space station. I'm going to do this the wrong way first to demonstrate device indexing, then we can see a better way to do it using the stack.

Device Indexing
Using the alias'ed name of our device is what we should do 99% of the time, but sometimes you just want d0, or d3, or dX. Is there any way we can say dX in code? Yes! Simply define the device of a register. For example:
move r3 1 #store 1 in register 3 alias indexedDevice dr3 #indexedDevice = d1 s indexedDevice On 1 yield s dr3 On 0 #same device move r3 2 s dr3 On 0 #not the same device
We can even do this with registers, and we can do it recursively too!
move r3 2 move r2 0 move r0 rr3 #r0=0 alias device drr3 #device=d0

The Wrong Way
So let's start with just 4 lights. We want 1 light to be lit at a time, and we want the light to loop around from d3 to d0. We'll need to remember 3 values: the index of the device we are handling, the index of the led we want to be lit next, and finally, we need to remember that there are 4 lights in total, so we can terminate the loop properly:
alias light0 d0 #having the devices be named can still be helpful when alias light1 d1 #setting up the IC housing, but they won't come up in the script alias light2 d2 alias light3 d3 define numLeds 4 alias deviceIndex r15 alias litLedIndex r14 StartIteratingLeds: move deviceIndex 0 #start at the first device HandleThisLed: alias thisLed dr15 #r15 is the deviceIndex seq r0 deviceIndex litLedIndex #r0=1 if this device should be lit s thisLed On r0 add deviceIndex deviceIndex 1 blt deviceIndex numLeds HandleThisLed #keep looping while deviceIndex<=3 FinishTick: yield #finally, wait a tick, increment litLed, then loop again add litLedIndex litLedIndex 1 slt r0 litLedIndex numLeds #r0=true if litLedIndex is valid select litLedIndex r0 litLedIndex 0 #use litLedIndex as is if valid, otherwise reset to 0 j StartIteratingLeds

Pretty Colors
I did specify rainbow lights and those don't quite cut it. We need to write a different value for Color to each of the LEDs. We could check the Stationpedia to find the right number for the color we want, but we can actually program with an existing Color enumeration:
s light0 Color Color.Red s light1 Color Color.Blue
Using the existing enums can make your code more flexible and easier to read.

Using a Reference ID
Instead of communicating with devices using d0, d1... we can use the reference ID of a device to communicate directly with as many devices as we can fit into the script. It does have two significant drawbacks though: you can't interact with slots, and if you ever replace one of these devices, you will need to update the reference ID inside the script. I recommend having a laptop in-game before you start using direct references. That said, some people have almost their entire base one heavy cable network, which makes sd and ld even more useful. But I like transformers, and frankly you should too.
That first value shown with a $ is the hexadecimal representation. This is the one I tend to use, as it is often shorter, since hexadecimal is more information dense. Or you can use the decimal one, it doesn't really matter--its all converted to binary at the end of the day anyway. But you can't use the s and l instructions anymore, you must use the direct instructions sd and ld.
define lightRefID $3c1e #'$' is the hexadecimal preprocessor macro FlipPower: ld r0 lightRefID On seqz r0 r0 # 'seqz' is usually better than 'not' because 'not' is performed on each bit sd lightRefID On r0 yield j FlipPower

Stacking Stuff
The stack is the closest thing you have to an array in basic assembly. The script will start execution with an empty stack, and with the stack pointer sp=0.
We can push an item onto the stack, which will increase sp by 1, or we can pop an item from the stack, which will decrease sp by 1. Do note that pop won't actually delete the value at sp.
push 32 push 42 push 420 Loop: select sp sp sp 3 #move stack pointer back to the top (3) once sp=0 pop r0 s db Setting r0 #display stack value yield s db Setting sp #then display stack index (sp-1 from pop earlier) yield j Loop
Also, if we change sp, then push and pop will exchange with that new position on the stack.

Relative Jumping
If you want to reduce the line count of your script for whatever reason, you should start using relative jumps to avoid wasting lines on Labels. That will look something like:
move r0 42 add r0 r0 1 brlt r0 50 -1 #keep going back one line and adding 1 until r0=50 s db Setting r0

The Right Way
Now, let's put that all together to build a stack-based LED controller. Well, without the color enumeration. Because the stack size is variable, it makes sense to treat Color like a normal number so we can iterate through them:
push $refId0 #light0 push $refId1 #light1... push $refId2 push $xyz push $abc push $123 push $4a3c push $23ff push $1223 #we can push as many devices as we can fit into the script, not just 6. This has 9 alias thisLed r15 #no longer using device index, and the refID is just a num alias litIndex r14 alias numLeds r13 #we can't define numLeds as sp because sp is a register and
#registers are undefined at compile time. Definitions must be known at compile time.
#So we have to use a register and remember not to change it. Ever.
move numLeds sp jal SetLightColors #only need to set colors once StartIteratingLeds: move sp numLeds #notice the missing label. we go to 'pop thisLed' from 'brgtz sp -3' pop thisLed seq r0 sp litIndex sd thisLed On r0 brgtz sp -3 yield #loop has ended, so we wait for the written values to update add litIndex litIndex 1 slt r0 litIndex numLeds select litIndex r0 litIndex 0 #then we change which light is lit j StartIteratingLeds SetLightColors: alias color r12 #this function only needs to run once. it iterates through colors define maxColor 6 #parallel to LEDs on the stack and writes the color to the LED move color maxColor move sp numLeds pop thisLed select color color color maxColor sd thisLed Color color beqz sp ra #return when all lights have a color set sub color color 1 jr -5
Other Stuff
These are advanced, tangential topics.

Optional Device Handling
Sometimes, you may want a device in a script to be optional. For instance, maybe a warning light really isn't required, it's just nice to have. We can do something like skip enabling a device by using bdns or bdse, or we can create logic around a device with sdns or sdse. For example,
alias warningLight d4 #a device which may or may not be set brdns warningLight 2 #move down two lines if device not set s warningLight On r7 #skips or continues here

Nested Function Calls
Like every other register, the address register, ra, can only hold one value, which means the following code will NOT work as you might expect, and it will never go to SomethingElse:
LetsDoSomething: s device Setting something jal DoAnotherThingInAFunction j SomethingElse DoAnotherThingInAFunction: s device Activate 1 jal HeresTheNestedFunction j ra #it gets stuck in an infinite loop at this line since the last jal set this to ra HeresTheNestedFunction: s device On 1 j ra
There are several ways we can handle this limitation. If you're not using the stack for anything else, we can keep pushing ra onto the stack every time we nest, then popping it every time we need to return from the previous address. This example WILL work as expected:
DoSomething: s d0 Setting r0 jal FirstFunction j SomethingElse FirstFunction: push ra jal SecondFunction pop ra j ra SecondFunction: push ra jal ThirdFunction pop ra j ra ThirdFunction: s device On 1 j ra
If the stack is occupied, you can simply select some other register to hold the value of ra, and then you move the value of that register back into ra:
Function: move r4 ra jal NestedFunction move ra r4 j ra

IC Communication
Some tasks are so complex that they may require several scripts, spread across several housings, all working together. For example, a recipe chip for a furnace build could request an ore by using the Setting of another IC housing that is responsible for fetching ores. So the recipe chip may contain the following lines:
alias vendorChipHousing d4 alias desiredOre r7 WaitForChipAvailable: l r0 vendorChipHousing Setting beqz r0 SetRequest #wait for the housing to have setting=0 before changing it yield j WaitForChipAvailable SetRequest: s vendorChipHousing Setting desiredOre yield j SomewhereElse
While the vendor chip may consume the Setting as such:
WaitForRequest: l r3 db Setting bnez r3 HandleRequest yield j WaitForRequest HandleRequest: jal PickSilo #this function gets the refId of the silo containing sd silo Open 1 #oreHash=r3 (not defined here) yield sd silo Open 0 s db Setting 0 #change the housing back to zero to indicate its done yield j WaitForRequest

Channel Data
In the previous section, we used the value of Setting to communicate one value between two chips. We can use the Channel data of any device to communicate and store many values at once.

Referring back to the previous example, lets change it to use Channel data so we can write an ore AND an amount of ore:
alias vendorChip d4 alias ore r15 alias amt r14 SetRequest: s d4:0 Channel0 ore #each network connection has 8 channels. s d4:0 Channel1 amt #get network connection indices from the stationpedia yield j SomewhereElse
And then the vendor chip would load these values like:
alias ore r15 alias amt r14 HandleRequest: l ore db:0 Channel0 l amt db:0 Channel1 j SomewhereElse
Keep in mind that Channel data is stored in the cable network, so if you make any changes to that cable network, any Channel data you wrote will be set to NaN (short for Not a Number, which is secretly just another number).

A Bit About the Bit
I lied, we actually can store multiple separate numbers with just a single register. For example, 1,607 is one-thousand-six-hundred-seven, but if we bisect the digits, we can get 16 and 07. A binary digit is a bit (0 or 1), and many bits make up the complete binary number. We can shift these bits left or right to manipulate the digits of a register, effectively fitting multiple numbers into a single, larger number.
sll register value x #shift logical left: move all bits left by x, replacing rightmost bits with 0 sra register value x #shift arithmetic right: move all bits right by x, replacing leftmost bits with
#a copy of the most significant bit
I hope some of the applications of bit-shifting are obvious in light of the prior section, but the next section covers how to pack a binary number in greater detail for stack instructions.

Stack Instructions
IC housings are not the only devices which have a stack memory. Fabricators, like the autolathe, and logic sorters also have a stack memory. To interact with a stack outside of the one for the IC, we need to use the get and put instructions. But we can also use get and put on db to manage stack memory without pushing or popping from sp.
put d? stackIndex r? get r? d? stackIndex
We can also clear the stack with clr:
clr d?
As a quick example, lets write a non-looping block of logic to set a logic sorter to blacklist Ice, so it will filter everything else away. This will be done using the reference ID as well to demonstrate putd:
define logicSorter $34ac alias instruction r0 move instruction SorterInstruction.FilterSortingClassCompare #fill the first byte sll r1 NotEquals 8 #move the byte representing the compare operation over 8 bits sll r2 SortingClass.Ices 16 #move the 16 bit number for sorting class over 2 bytes or instruction instruction r1 #use logical or to combine the values or instruction instruction r2 putd logicSorter 0 instruction #put the assembled value at the first stack index
To assemble an instruction, you will need to know where to place what information. All the stack instructions for a device can be found in its Stationpedia page:
Some Tables
Here are all of the set instructions:
Form
Details
s d? logicType r?
set
sd referenceID logicType r?
set direct
sb deviceHash logicType r?
set batch
sbn deviceHash nameHash logicType r?
set batch name
ss d? slotIndex logicSlotType r?
set slot
sbs deviceHash slotIndex logicSlotType r?
set batch slot

Here are all of the load instructions:
Form
Details
l r? d? logicType
load
ld r? referenceId logicType
load direct
lb r? deviceHash logicType batchMode
load batch
lbn r? deviceHash nameHash logicType batchMode
load batch name
ls r? d? slotIndex logicSlotType
load slot
lbs r? deviceHash slotIndex logicSlotType batchMode
load batch slot
lbns r? deviceHash nameHash slotIndex logicSlotType batchMode
load batch name slot
lr r? d? reagentMode reagentHash
load reagent

Here are all* of the branch and jump instructions:
*relative branch/jump instructions (i.e. jr, breq,brdns), jump return instructions (i.e. jal, bapzal, bneal), and all zero (-z-) instructions except beqz have been omitted for brevity
Form
Details
j a
jump to line a
beq a b c
goto c if a==b
beqz a b
goto b if a==0
bne a b c
goto c if a!=c
blt a b c
goto c if a<b
bgt a b c
goto c if a>b
ble a b c
goto c if a<=b
bge a b c
goto c if a>=b
bap a b c d
goto d if a=~b **
bna a b c d
goto d if a!~b **
bnan a b
goto b if a is NaN
bdns d? a
goto a if device not set
bdse d? a
goto a if device is set
**the extent to which a is approximately equal or not equal to b is defined by c. See the Stationpedia entry for the full definition of bap and bna along with the associated s-, -r-, -z-, and -al instructions.

Here are all* of the logical instructions:
*all zero instructions (-z) except seqz have been omitted.
Form
Details
select r? a b c
r?=c if a==0 else=b
seq r? a b
r?=1 if a==b else=0
seqz r? a
r?=1 if a==0 else=0
sne r? a b
r?=1 if a!=b else=0
slt r? a b
r?=1 if a<b else=0
sgt r? a b
r?=1 if a>b else=0
sle r? a b
r?=1 if a<=b else=0
sge r? a b
r?=1 if a>=b else=0
sap r? a b c
r?=1 if a=~b else=0 **
sna r? a b c
r?=1 if a!~b else=0 **
snan r? a
r?=1 if a is NaN else=0
sdns r? d?
r?=1 if device not set else=0
sdse r? d?
r?=1 if device is set else=0
and r? a b
bitwise and of a and b
or r? a b
bitwise or of a and b
not r? a
bitwise not of a
nor r? a b
bitwise nor of a and b
xor r? a b
bitwise xor of a and b
sll r? a b
shift left logically
srl r? a b
shift right logically
sla r? a b
shift left arithmetically
sra r? a b
See 'A Bit About the Bit' in 'Other
Stuff' for more on bit-shifting

Here are all of the arithmetic instructions:
Form
Details
move r? a
set register to a
add r? a b
a+b
sub r? a b
a-b
mul r? a b
a*b
div r? a b
a/b
mod r? a b
a%b
exp r? a
e^a
sqrt r? a
a^0.5
log r? a
log(a)
abs r? a
|a|
max r? a b
bigger val
min r? a b
smaller val
trunc r? a
whole number
floor r? a
next lowest integer
ceil r? a
next highest integer
round r? a
closest integer
rand r?
random
sin r? a
sine
asin r? a
arcsin
cos r? a
cosine
acos r? a
arccosine
tan r? a
tan
atan r? a
arctan
atan2 r? a b
arctan(a/b)

Here are all of the stack instructions:
Form
Details
pop r?
get the value of the stack at sp and decrement sp
push a
put a on the stack at sp, then increment sp
peek r?
check the value of the stack at sp
poke a b
put b on the stack at a
put d? a b
write b to the device's stack at a
putd referenceId a b
see above
get r? d? a
get the value of the device's stack at a
getd referenceId d? a
see above
clr d?
clear the stack memory of the device
clrd referenceId
see above

Here are all of the miscellaneous instructions:
Form
Details
alias str r?|d?
name device or register
define str a
name an immutable value
yield
wait until the next tick to continue
sleep a
wait a seconds to continue
rmap r? d? reagentHash
get itemHash for reagentHash for device
hcf
literally explode
The End
That's it I think. For more code examples, go here. If you still suck at surviving, go here.
17 Comments
Wilhelm W. Walrus  [author] 23 Aug, 2024 @ 6:41pm 
The stack is not an array, it can merely be used like an array. Arrays are data structures, which are just ways to organize data. Arrays are just a numbered list of elements. A stack is also a data structure. It is an ordered list of elements where the first item put on the stack is the last item to be taken off the stack. But because you can access any stack position using 'get' and 'put,' you can disregard the 'stacking' of stack and use it like an array.
Dr. Democracy 23 Aug, 2024 @ 10:44am 
Thanks for taking the time to write this out. I learned a fair bit that I missed from video tutorials, and I do prefer written guides.

Overall great, and I'l be sure to come back to this the next time I attempt to script something. Perhaps when you explain what a stack is, and you say it is an array, you could explain what an array is as well :)
Kastuk 22 Aug, 2024 @ 2:05pm 
Animated pictures of coding results isgreat to capture the pupils!
Frission 8 Aug, 2024 @ 3:08pm 
Much appreciated <3 I tried to make it work on my own first and just started getting "error on line x" with my version x_x I think it's because I put in "Sum Charge" and "Sum Maximum" instead of "Charge Sum" and "Maximum Sum" xD
Wilhelm W. Walrus  [author] 8 Aug, 2024 @ 1:18pm 
No, I just didn't feel like opening the game to check what the actual PrefabName was. I think that's right, but I wanted to make sure it was clear that there is some uncertainty. Strings are woefully uncomplicated in Stationeers, and you can't do anything like concatenate or manipulate strings or patterns.
Klausberger 8 Aug, 2024 @ 4:55am 
Curious:
Is that "?" a Wildcard in the Hash of the two types? Are there others?
Wilhelm W. Walrus  [author] 8 Aug, 2024 @ 4:03am 
In this case, Sum of Charge is a better way to compare the logic types. Otherwise, to use the Ratio, we would have to find out how many batteries of each type there are. We could find it programmatically by doing sumthing like Sum of On, but what if we want to count the batteries that are off?
[code]
define batId HASH("StructureBattery?")
define bigId HASH("StructureBatteryLarge?")

lb r0 batId Charge Sum
lb r1 bigId Charge Sum
add r0 r0 r1 #sum all charge in watts
lb r1 batId Maximum Sum
lb r2 bigId Maximum Sum
add r1 r1 r2 #sum max possible charge
div r0 r0 r1
mul r0 r0 100 #r0=% of charge remaining
[/code]
Frission 7 Aug, 2024 @ 11:26am 
Okay, say I've got a blend of regular and large station batteries and want to keep using the power crisis script, how do I combine them?
Frission 2 Aug, 2024 @ 2:54pm 
Use them to buy caaaaaaaaaaaat stickers :D
Wilhelm W. Walrus  [author] 2 Aug, 2024 @ 12:17am 
I promise not to spend them all in one place