#Advanced Automation

1 messages · Page 2 of 1

hallow crest
#

these buiilding produce a very large amount of hapyness ... i gain 5 to 10 level when i staryt it

south wedge
#

I guess, it's due to there was no way to put/remove workers based on the water level at the dump? But there is such an option now 🙂 The worker comes only to fill up the reservoir.

#

This would be a bit more complex setup, thought (gauge + custom signals).

hybrid hatch
#

I know floodgate triggers had something like it. I don't remember the details.

serene urchin
south wedge
#

Maybe add a signal to the fluid dump then...

south wedge
hallow crest
#

that's the idea ... but there is probably other thing that we can drive with only a timer

smoky timber
#

i just left a message on workshop page saying that my gatherer flags are suddenly giving me parse errors and at the same time the output constructer bricks are not showing

south wedge
# smoky timber

Have you checked the logs? If you didn't, please share them here. I'm pretty sure it's the range checking error. If my memory doesn't confuse me, the flag storage max capacity is 10.

smoky timber
# smoky timber

when i click test it says signal not supported inventory.outputgood.berries
i have not written any scripts myself as i do not understand how. i have only been using the constructor

smoky timber
south wedge
smoky timber
serene urchin
#

Here thge logs is:

%appdata%\..\LocalLow\Mechanistry\Timberborn\

Log: Player.log

south wedge
serene urchin
#

ps from pinned message in Mod users

south wedge
#

This seems to be "just a log". We need it at the moment you had the problem.

#

It's how the log works: it records what happens.

serene urchin
#

it did have:

[GathererFlag.Folktails@(90, 103, 5)] Failed to parse condition: (ge (sig Inventory.OutputGood.Berries) 2000)
Error: Signal not supported: Inventory.OutputGood.Berries
[GathererFlag.Folktails@(90, 103, 5)] Automation error reported by: IgorZ.Automation.Conditions.ScriptedCondition(Pre="";Expr="(ge (sig Inventory.OutputGood.Berries) 2000)")
[GathererFlag.Folktails@(90, 103, 5)] Signal change triggered, but the condition was broken: (ge (sig Inventory.OutputGood.Berries) 2000)
[GathererFlag.Folktails@(90, 103, 5)] Failed to parse condition: (le (sig Inventory.OutputGood.Berries) 1900)
Error: Signal not supported: Inventory.OutputGood.Berries
[GathererFlag.Folktails@(90, 103, 5)] Automation error reported by: IgorZ.Automation.Conditions.ScriptedCondition(Pre="";Expr="(le (sig Inventory.OutputGood.Berries) 1900)")
[GathererFlag.Folktails@(90, 103, 5)] Signal change triggered, but the condition was broken: (le (sig Inventory.OutputGood.Berries) 1900)
Load time: 52941ms (scene index: 2)
[GathererFlag.Folktails@(90, 103, 5)] Failed to parse condition: (ge (sig Inventory.OutputGood.Berries) 2000)
Error: Signal not supported: Inventory.OutputGood.Berries
[GathererFlag.Folktails@(90, 103, 5)] Failed to parse condition: (le (sig Inventory.OutputGood.Berries) 1900)
Error: Signal not supported: Inventory.OutputGood.Berries
smoky timber
serene urchin
#

where do you have to mod from? tried verify gamefiles to force mods to update?

smoky timber
#

Ive verified files since and it hasn’t fixed, i could try verifying again, ill even unsubscribe from the mod and resubscribe if I have to

smoky timber
serene urchin
#

well then we need a log where you have loaded a save and try set a new flag using constructor and an image of it

#

(so when photo is taken drag the log here)

smoky timber
serene urchin
#

ok then drag a log here

smoky timber
serene urchin
smoky timber
serene urchin
#

hmm i would sugest to try with only and see if it still is the same

  • Harmony
  • Mod settings
  • Timberapi
  • automation
#

could be a incompatible mod with automation mod

serene urchin
#

weird reinstall it?
mod.io remove it and download again
Steam unsubscribe and subscribe again

smoky timber
smoky timber
# smoky timber done multiple times already, and i just did it again after you said that, still ...

though adding a path instantly makes output:berries show up on the constructor, i havent checked if its(the script) showing as valid tho, im going to visit my save where i first encountered the error now and replace the path and see if it does anything

edit, save where i encountered error is still not fixed, even after replacing path
edit2, i can't be bothered trying to figure out why its showing no output:berries on my gatherer flag, as long as the issue doesnt happen to any other buildings... i just hope next content update to the mod somehow fixes the issue, or it just fixes itself somehow...

thanks for trying.

reef widget
#

Poor man's "sluice":

Stream Gauge "RiverPool"

broadcast depth
IF: (ge (sig StreamGauge.Depth) 0)
THEN: (act Signals.Set 'Gauge.RiverPool' (sig StreamGauge.Depth))

Stream Gauge "RiverNearPool"

broadcast depth
IF: (ge (sig StreamGauge.Depth) 0)
THEN: (act Signals.Set 'Gauge.RiverNearPool' (sig StreamGauge.Depth))

broadcast contamination
IF: (ge (sig StreamGauge.Contamination) 0)
THEN: (act Signals.Set 'Gauge.RiverNearPool.Contamination' (sig StreamGauge.Contamination))

Floodgate:

close on bad weather
IF: (ne (sig Weather.Season) 'temperate')
THEN: (act Floodgate.SetHeight 100)

open on good weather when the water level in the pool is lower than in the river and contamination in the river less than 2%
IF: (and (eq (sig Weather.Season) 'temperate') (le (sig Signals.Gauge.RiverPool) (sig Signals.Gauge.RiverNearPool)) (lt (sig Signals.Gauge.RiverNearPool.Contamination) 2))
THEN: (act Floodgate.SetHeight 0)
somber dagger
#

any way for using Access operators to query a building to know what property names are available to use?

serene urchin
somber dagger
#

You can see the default ones that way.. but not all of them I think

#

Ie the ones in the pulldown

south wedge
south wedge
reef widget
south wedge
#

A workaround for the gatherer flags. Wait till at least one item of the good appears in the stock. Then, create the rule. The flags have a tricky logic on how they set inventory. Still need to understand it.

tribal canopy
#

Hey @Igor, I commented on your pinned help comment on Steam about the circular execution error. Just thought I'd let you know I'm on the Discord server now in case you preferred to follow-up here. I already replied to your recent follow-up there.

south wedge
#

So, you apply that rule on two tanks? Hmm, why the eror comes then.

tribal canopy
#

By the way, is there any forum of shared user scripts? I'm curious what others are doing.

south wedge
#

The circular detection system should only trigger if the same rule gets executed twice in one call. But for now I donl't see how it happens.

tribal canopy
#

If you have trouble reproducing from my description, I can try sharing a save file where it triggers on game load.

south wedge
tribal canopy
tribal canopy
south wedge
tribal canopy
south wedge
#

My day is in its middle 🙂 I'm working on automation right now anyway.

tribal canopy
#

Just verified that my save loads with only Automation and it's advertised dependencies.

#

Just a random thought: If you could support list signals where consumers could add/remove themselves from the list signal (i.e. add/remove a string name) and producers could check how many entries are in the list, this would open up a lot of possibilities.

tribal canopy
# south wedge What is the use case?

Same use case: multi-producer/multi-consumer. However, with a list signal where consumers add/remove themselves you can quantify demand by looking at the size of list. You can have some producers unpause if there is any demand and others unpause only when there is high demand.

south wedge
#

I have a plan to add mutli-signal feature. I.e. you set the same singal from multiple actions to different values. Then, you can get the value: last, min, max, sum, count. Will it solve you case?

tribal canopy
south wedge
tribal canopy
#

It actually sounds more useful than my suggestion. If I understand it right, each container can set the signal to how much spare capacity it has, and then you can sum it to calculate the total spare capacity. I'm looking forward to this.

south wedge
tribal canopy
south wedge
#

I'm open for any suggestions. Not anything can be done, but all ideas deserve to be heard.

south wedge
#

v2.2.2 (25 May 2025):

  • [Fix #84] Output good signals are not shown on the gathering flags.
reef widget
#

Bug: when editing long script rules that hang past the end of the box, the display of the line jumps to the start on anything other than the arrow keys, resulting in blind entry.

#

Also, my new monstrosity:
Scrap Pile (underground)

IF: (eq (sig Inventory.OutputGood.ScrapMetal) (sig Inventory.OutputGood.ScrapMetal))
THEN: (act Signals.Set 'Stockpile.Scrap' (sig Inventory.OutputGood.ScrapMetal))

Metal Pile (underground)

IF: (eq (sig Inventory.OutputGood.MetalBlock) (sig Inventory.OutputGood.MetalBlock))
THEN: (act Signals.Set 'Stockpile.MetalSupply' (sig Inventory.OutputGood.MetalBlock))

Log Pile (small)

IF: (le (sig Inventory.OutputGood.Log) 400)
THEN: (act Prioritizable.SetHaulers)

IF: (ge (sig Inventory.OutputGood.Log) 1600)
THEN: (act Prioritizable.ResetHaulers)

Scavenger Flag

IF: (and (gt (sig Signals.Stockpile.Scrap) 99000) (gt (sig Inventory.OutputGood.ScrapMetal) 1500))
THEN: (act Workplace.RemoveWorkers)

IF: (lt (sig Signals.Stockpile.Scrap) 10000)
THEN: (act Workplace.SetWorkers 100)

Smelter

IF: (or (and (ge (sig Signals.Stockpile.MetalSupply) 99000) (gt (sig Inventory.OutputGood.MetalBlock) 1100)) (and (eq (sig Signals.Stockpile.Scrap) 0) (lt (sig Inventory.InputGood.ScrapMetal) 200)))
THEN: (act Workplace.RemoveWorkers)

IF: (and (lt (sig Signals.Stockpile.MetalSupply) 75000) (ge (sig Inventory.InputGood.ScrapMetal) 200))
THEN: (act Workplace.SetWorkers 100)

Opening the panel for the smelter instantly drops the game from 60 FPS to 15 FPS

tribal canopy
#

I should try using Workplace.RemoveWorkers. I tried using Pausable.Pause when input runs out, but it results in disappearing input when a hauler picks up the last of the input from the stockpile.

reef widget
south wedge
#

For the flags, stopping is fine. They only give output, and with pause icon, you will see that the flag is not working.

tribal canopy
#

@south wedge I created a feature request for manual signals. Piisfox's scripts present another potential use case: The hardcoded thresholds could be replaced with manual signal for easier management.

#

@reef widget Just wanted to share that instead of (eq (sig Inventory.OutputGood.MetalBlock) (sig Inventory.OutputGood.MetalBlock)) I use (ge (sig Inventory.OutputGood.MetalBlock) 0) for brevity.

#

Also, (act Signals.Set 'Stockpile.MetalSupply' (add (sig Inventory.OutputGood.MetalBlock) 0)) let's you see the value set which I find more useful.

south wedge
#

Or maybe just create another boolean operator (sigchange ...)?

south wedge
#

A teaser. The next version will have import/export functionality.

#

And while working on this, I figured out we may want to have a "custom template" feature. That is, saving the rules and then applying them. Need to find a proper UI approach, though.

#

@reef widget @tribal canopy Folks, do you have GitHub accounts? I can add you as collaborators so that you could add things to Wiki.

south wedge
south wedge
#

A localized version is ready now.

tribal canopy
south wedge
#

Give me 30 mins, I'll make a preview build for import/export feature

#

Need to test two locales, lol.

south wedge
#

Maybe, I can detect multiline? ThinkingIT

#

v2.3.0 (pre-release):

  • [Feature] Add import/export feature for the rules.

An undocumented function: you can save rules with errors if click "Save" with Ctrl hold. Don't ask me why you would need it. If you don't need it, you don't use it 🙂

#

More on the undocumented functions. Hold Ctrl while hovering over the buildings during the templates application. The logs will have a reason why the template was rejected.

#

And remember the first rule of Fight Club.

reef widget
#

@south wedge Bad timing: today's experimental update broke something. I'll switch to stable for now.
As for Github: Piisfun

reef widget
#

This is a good example for logistics:

condition:(le (sig Inventory.OutputGood.Berries) 600)
action:(act Prioritizable.SetHaulers)

condition:(ge (sig Inventory.OutputGood.Berries) 2400)
action:(act Prioritizable.ResetHaulers)

Small warehouse for berries, set to obtain.
If (berries <= 6.00) (20% capacity) then prioritize having haulers refill it.
if (berries >= 24.00) (80% capacity) then cancel the priority flag.
Fill when empty, stop when full.

The only problem is that you can only directly copy the rule to a warehouse set to berries.

condition:(le (sig Inventory.OutputGood.Berries) 600)
action:(act Prioritizable.ResetHaulers)

condition:(ge (sig Inventory.OutputGood.Berries) 2400)
action:(act Prioritizable.SetHaulers)

This is the supply version: the only difference is that the actions are swapped.
Empty when full, stop when empty.

south wedge
reef widget
#

Just did.
One note for editing: in a <details> collapsable tag, if there is not a blank line after <summary>, the whole page breaks.

south wedge
tribal canopy
#

Moving from pause/unpause to remove/set workers has been a huge QoL improvement for me. Thanks again for the tip.

#

I'm still in the early game, but one thing I'm struggling to automate is the farmhouse. However, I imagine that will eventually become an always-on building once the food supply and population reach an equalibrium?

reef widget
#

I don't even bother to automate them; same for foresters.
There isn't a good way to determine when they need to work, the work itself is incredibly sporadic, and to top it all off, they are both core to basic survival.

For that, permamently working with some downtime is fine; the only time where there would be a significant benefit is at the beginning of the first cycle when you lack workers.

tribal canopy
#

Yeah, I'm currently at a stage where food storage is full and population is still low. Being able to lower the worker count to 2 or 1 helps, though.

#

The forester seemed easy enough to pause during the early growing period, but has been fairly busy since as you say.

reef widget
#

The other buildings that I have decided aren't worth trying to automate are:

  • Inventor
  • Observatory
  • Numbercruncher
  • Engine

For the first 3, there is no way to check science from a script, you usually need them to run in the background anyway, and simply setting them to low priority is enough for employment control.
The Engine is similar: there is no variables for power or battery capacity, which are the main things you would want to toggle on, other than possibly fuel availability. Besides, Igor also made Smart Power, which does all that and is naturally fully compatible, even if the two mods can't talk to one another.

tribal canopy
#

Appreciate the advice. Will probably save me some head scratching.

#

I've installed Smart Power, but at this stage I'm only using it to pause buildings when flow stops.

south wedge
#

v2.3.1 (26 May 2025):

  • [Change] Support Timberborn 0.7.9.0. Incompatible with the previous versions of Update 7.
  • [Feature] Add import/export feature for the rules.
  • [Fix #90] The placement tools prematurely place the building after an automation tool was used.
#

Note, that for the stable branch, the v2.2.2 was the last version. From now on, the updates will be made for 0.7.9.x only (experimental).

tribal canopy
#

hmm... I guess I'll give experimental a try... is it fairly stable?

south wedge
tribal canopy
#

immediate crash... haha

#

one of my mods must be incompatible

serene urchin
#

My power edition did break yesterday hopefully releases an update today

tribal canopy
serene urchin
#

No modded buildings that use power? Like deepsea pump in water extention or its pipe?

tribal canopy
#

No. I'm in early game and only have base-game buildings right now.

lime python
#

It matter if you have mods enabled, not if you have placed bukdings

tribal canopy
#

Thanks, will keep that in mind. For now I just returned to stable so I can play. Will debug experimental later.

reef widget
tribal canopy
sand thunder
#

Adding the ability to inport and export rules is awesome, thanks!
I'm not sure if you've thought about comments or have plans for those, but I made some suggestions as github issues:
https://github.com/ihsoft/TimberbornMods/issues/91
https://github.com/ihsoft/TimberbornMods/issues/92

GitHub

For common lisp: Single line comments start with ; (outside of string blocks) Multi-line comments start with #| and end with |# Even if they're stripped and not displayed in game anywhere it wo...

GitHub

In addition to #91 (allow comments in import) Similar to common lisp convention perhaps you could have: ;;;; Short title for a rule (display above it) ;;; Longer multi-line description for a rule, ...

sand thunder
#

Also, is there any way to detect what a storage is set to store? E.g return 'Water' or 'Grease' etc.

somber dagger
#

Would be kind of cool if t here was a Debug window in game, where you could send values from scripts to a window to monitor whatever you wanted. I suppose the window could pull from the log file as an option but you would have to initially place variables to the logfile with Debug.LogStr,Debug.LogNum .

south wedge
south wedge
south wedge
sand thunder
#

with hotkeys etc

south wedge
sand thunder
#

I'm using vs code with a lisp extension

south wedge
#

import/export format is not lisp-compliant.

sand thunder
#

ok

south wedge
#

Save you files with .sh extention 🙂 VS editor should start recognizing the right comment notion.

sand thunder
south wedge
#

Anyway, I can add ";" also.

sand thunder
#

Yeah IDK man, I was just going by the syntax being lisp like from where you said that - it doesn't matter, the language highlighter can be set or configured however

reef widget
#

{% (getstr SingleGoodAllower.AllowedGood) %}
Oh, perfect!

sand thunder
south wedge
#

Maybe it's a parser issue. Ill check.

#

As for the checking the type change, it will likely not work. If the type has changed, but you still have references to the other good id, that rule will throw a runtime error and stop working.

#

Why checking for it anyway? And you won't get notified about the change, since it's not a signal.

sand thunder
#

Yeah, ok, thanks.
I wonder what a good way to be able to track the amount and capacity for various goods would be.
That new Count and Sum on singals looks promising. Perhaps that can help.

I'd sort of hoped I could set something up so that any tanks set to Water would contribute to a current water and total, and be able to repurpose them and have it smart enough to handle it.

Yeah, I realised abot the no update due to not being a singal also - I was going to run stuff off a amount trigger from elsewhere.

sand thunder
#

In case anyone is interested or can figure out what's wrong, this was what I was fiddling with.
It'll largely be redundant when the aggragate signals with Max etc come out though.

The idea is to have a controller script that you put on a district center that keeps a tally of the storage amound, capacity and level (calculated 0-100) of a certain good - in this case water.

The tanks all run a script that will increase a Dirty signal when their value changes.
The controller (district center) script listens for this and manages the update.

  • sets buffer signals to 0
  • toggles update signal which the pumps tanks listen on
    • pumps tanks add their content and capacity to buffer signals (nothing listens on them)
      I tested and this interrupted rule excution of the Dirty rules - seemed single threaded
  • Dirty triggers on controler continue to
    • clear Dirty signal (triggers require >0 so should not recurse)
  • update actual amount, capacity and level (calculated) signals from buffers for Water which will have triggers later.

It all looks ok if you add the tank scripts first, but things start to error when you start changing stored water amount. IDK why.

#
## On Water Storage Tanks
# Add current contents and capacity to buffered totals when update is triggered
condition: (eq (sig Signals.A1.Inventory.Water.Update) (sig Signals.A1.Inventory.Water.Update))
action: (act Signals.Set 'A1.Inventory.Water.Buffer' (add (sig Inventory.OutputGood.Water) (sig Signals.A1.Inventory.Water.Buffer)))
condition: (eq (sig Signals.A1.Inventory.Water.Update) (sig Signals.A1.Inventory.Water.Update))
action: (act Signals.Set 'A1.Inventory.Water.Capacity.Buffer' (add (getnum Inventory.Capacity) (sig Signals.A1.Inventory.Water.Capacity.Buffer)))
# Set Dirty flag when amount changes
condition: (eq (sig Inventory.OutputGood.Water) (sig Inventory.OutputGood.Water))
action: (act Signals.Set 'A1.Inventory.Water.Dirty' (add 1 (sig Signals.A1.Inventory.Water.Dirty)))
#

That's it for me though. That import function is so handy, thankyou!

tribal canopy
sand thunder
#

I was thinking 'I bet DeepSeek could make a vs code extension to do highlighting and comment toggling with just config files' and it turned out it could!

I used .bar file extension (beaver automation rules) because tar was taken and I didn't have any better ideas so feel free to suggest alternatives.

south wedge
lime python
#

Common, your mods are no so big. Imagine Water Beavers using multiple versions 🤣

tribal canopy
tribal canopy
#

As someone noted in the Workshop comments, I've noticed that I have to Unsubscribe and re-Subscribe to get updates to this mod, while other mods (at least some) update automatically.

south wedge
serene urchin
tribal canopy
reef widget
# tribal canopy My experience was in stable.

The fact of the matter is it's still Steam that is the issue.

Auto-updates on Steam are notoriously unreliable for anything on the workshop, unless the games itself forces the update.

simple trout
#

I currently have 3 stockpiles for logs.
They all output their storage levels to a signal but they don't get combined right?
How would I combine the signals?

hybrid hatch
#

(technically it checks every time the menu is shown, but I think it's cached somewhere. I've never seen it update while the game is running)

somber dagger
simple trout
#

Yes, tried that. It will not add signals together. It does not allow string.

somber dagger
#

Thats weird as signals can be strings, or a number.

#

What does your output script looklike the sets the custom signal?

simple trout
#

I finaly got it to work...

#

(act Signals.Set 'INV.Main.LogsTotal' (add (sig Signals.INV.Main.Logs1) (add (sig Signals.INV.Main.Logs2) (add (sig Signals.INV.Main.Logs3) (add (sig Signals.INV.Main.Logs4) (sig Signals.INV.Main.Logs5))))))

south wedge
#

What I can release quick, is making "add" multi-argument operator. Today, it only accepts two.

south wedge
simple trout
#

Well... I complete forgot the Signals.Set function...

#

I'm new to programming :/

south wedge
#

Set changes the signal value, but when you use sig on a custom signal, it must return a number.

This worked fine to me:

(act Debug.LogNum (add (sig Signals.test1) (sig Signals.test2)))

It prints value -0.02. The value seems awkward, and this will be changed soon. In the current version, non-existing signals return -1. They should return 0.

south wedge
#

@reef widget @sand thunder The signals aggregation is added. It's a preliminary version. Probably unstable. Try it out. The supported suffixes: .Min, .Max, .Sum, and .Count. Works for custom signals only.

#

Logging may be a bit spammy. It's a debug build.

sand thunder
#

And noticed for stable it used the old version. I'll switch anyway.

sand thunder
#

There's also a 'RUNTIME ERROR' on the 1st rule on the district center in those saves when you load.

action:(act Signals.Set 'A1.Inventory.Water.Level' (div (sig Signals.A1.Inventory.Water.Sum) (sig Signals.A1.Inventory.Water.Capacity.Sum)))

It worked fine when I set it up.
It also tests fine (when I go to edit to rule script in game and hit Test I mean), but if you save the rules it will crash.

#

I was running into a similar problem with this before: #1190169064383991858 message

#

Other than that everything worked great though! Will be super handy.

somber dagger
#

With the sluice, you can turn flow on and off but currently no option to set it back to Auto.

south wedge
south wedge
#

It's either one or another.

somber dagger
reef widget
lime python
#

Sum means adding value of items, count is about items occurrence

south wedge
#

Basically, count tells you how many buildings can set the signal in question. Not how many actions, but how many unique buildings.

#

E.g. if you have a building with 3 Signals.Set actions in different rules, it will be count 1. Not 3.

#

Btw, for this reason saving a new rule with set action will trigger the signal update. I rpobabl need to change it and only update rules that depend specifically on "count".

reef widget
#

So if I have 3 storages of 1000 broadcasting, Count would return 3 and Sum 3000. okay.

south wedge
#

Wow! I got the errer after installing scrollable panel mod. Thta's very strange

#

Nm, it wasn't the mod that caused it. The crash error happens if you reload the game, i.e. load it after you already had a game loaded/started. A dumb bug with static variable initalization.

hybrid hatch
#

I assume you could make (arithmetic) Mean with Sum/Count but it might be nice to have it as a dedicated function.

#

I can't think of any reason to have geometric mean, or median, or mode

south wedge
#

Sum/Count = Average. Not Mean.

#

Ah. sorry. Confused with median.

hybrid hatch
#

Heh

south wedge
#

Median would have more meaning than Average in terms of checking what's in the storages "in average".

hybrid hatch
#

Yea, mean is average, median is middle, mode is most frequent

#

Ooh, because storages could have different capacities too

south wedge
#

If I have storage of 1000 and 10, then having average of 500 won't give the right picture

hybrid hatch
#

Mean = 505, yea

south wedge
#

If you setup a rule to keep it full for 70%, you will be constantly refilling that small one.

hybrid hatch
#

So really you want Sum / Capacity

#

As that's the "overall percentage full"

#

Which is probably what the bars at the top-left are calculated with

south wedge
#

Fixed loading issues. Fixed the crash. Temporarily adding helping function concat. It takes 2+ arguments and joins them as strings. E.g.: (act Debug.LogStr (concat 'A1.Count=' (sig Signals.A1.Inventory.Water.Count))). Don't get used to it, it's only for preview. I will extend log methods to accept multiple arguments.

#

fyi @sand thunder @reef widget

#

And speaking of resources counting in general. There is a gray area with the goods being held. While hauler carries the good, it's not accounted in any storage. This data is available in the target storage (reserved capacity), but for now there is no way to get it from the script. I wonder if it should be accounted as an available good amount? It won't be instant of the store stat, though.

reef widget
#

I think it is fine to not have it; it is generally a very small percentage, and when it isn't, you have worse issues (namely, being out of that resource).

The one exception is an edge case with pausing/unpausing a building based on the quantity of resources in a warehouse containing raw materials. If the quantity is above the limit, but a hauler picks it up to move it to the building and drops it below that limit, it is possible to create an infinite loop where the hauler picks it up, pausing the building, causing them to put it back in the warehouse, unpausing the building...

south wedge
#

Or, there can be difference: for output signals always take an instant value (what's is in the storage rigth now), but for the input signals consider what's coming. What do you think?

tribal canopy
#

It's not critical, but it would help. I was hitting this scenario before moving from pause/resume to remove/set workers. Now with remove/set workers, while operating with low inventory, when the worker picks up input from storage they get unassigned from their workplace because input supply is depleted, but when they finish delivery they get reassigned because there is now input available. This works fine so long as the same worker gets reassigned, but there's still a race condition where someone else could be assigned from who knows where on the map.

sand thunder
# reef widget I think it is fine to not have it; it is generally a very small percentage, and ...

Could/should this instead be fixed by programming different levels for pause and unpause? E.g Pause when wood < 100 Unpause when wood > 150.
Perhaps not doing that is the underlying issue. There's always the chance someone picks up the same raw resource for another job too.
I'm not sure it's worth adding more complexity etc. when it can be solved like that, which is good practice to prevent excessive toggling anyway. - my thoughts

sand thunder
reef widget
sand thunder
#

I'm just wondering what the rules would look like. E.g on the apothecary 'unpause if (inbound + in storage) > 20' or something.

south wedge
tribal canopy
sand thunder
sand thunder
south wedge
# sand thunder No, I was just thinking aloud really

Above, I suggested an idea to always account the reserved items into input signlas. Only factories and workshops have input signals, and only there it really matters. For the output signals, we can leave it as it is.

#

So you check input >= 10 units, and even if the stock only has 5, but there are 5 enroute, the check will pass.

sand thunder
#

Got it.
What was causing that 'RUNTIME ERROR' and crash for that 1st rule in the save BTW, out of interest? I suspect I had the same issue before as well - with the script I did before the aggregate stuff.

south wedge
sand thunder
#

Right that makes sense, that's great that's fixed then!

south wedge
sand thunder
#

I also can't think of a time when you'd need the current storage amount where having the 'soon to be delivered' included would be a problem. Maybe if you were being super pedantic about only unpausing buildings that could work RIGHT NOW and were on a really large map I guess, but I'd imagine in a lot of cases it would take less time for the goods to arrive than for the beaver to get to another job.

sand thunder
south wedge
#

Surprisingly, AI gave the right solution. Too bad Copilot can't merge the proposed code changes itself.

#

Btw, guess what value will Avg give if the there are no signal providers registered?

#

(if you would make it yourself, sum/count could throw division by zero).

sand thunder
#

There might be another div by zero actually, I just had a crash (I wasn't even trying).

In that 'rules added' save above, or any probably, when you delete the last tank it crashes.

#

Also, I was thinking to try the Copilot as well

south wedge
#

Div by zero should not crash. It should mark the rule "runtime error".

sand thunder
#

I wanted to add something to that extension I did and using the codespaces or whatever seemed like a good way...

#

Yeah, IDK what the cause is but its an uncaught exception

south wedge
sand thunder
#

Yep that's the one 🙂

south wedge
#

yeah, just got it. Started randomly removing buildinsg with ruels and here we go.

#

Investigating.

sand thunder
#

Thanks!

south wedge
#

Thank you! You helped finding it 🙂

sand thunder
#

I actually wondered how involved getting set up to be able to compile the mod on linux would be, you're using Windows for this right? And not a VM I could use by any chance?

#

If I could I would take a look myself...

#

@south wedge It looks like the error originates from the rule on the district center that uses the aggregate signal that had it's last 'setter' deleted.

south wedge
#

div by zero triggered the bug, but wasn't the reason

#

The collection si being modified while I iterate.

#

A popular mistake 🤣

south wedge
#

Damn. The issue is not that simple. The signals start traveling really complicted.

#

Haha. This is what happens.

  1. Removing all buildings that emit signal make the counter = 0.
  2. It results in div by zero in another building.
  3. The rule is marked as "failed", and it tried to be removed from the system.

Thing is, the removal happens in the middle of the process of the signal propagation. It's good it failed this way. Because the issue is much more deep. The failed rules can affect live signals - this is where the problem is.

#

Reliable way to reproduce: load the save ("running") and delete all three tanks. The order doesn't matter. The errors comes when the last one deleted.

sand thunder
#

Yeah I was thinking with everything being event driven and also effecting itself it might get messy haha. I was impressed it all just worked so far.

south wedge
#

Well, it's the first time I'm making my own eventing system. I actually like seeing all these troubles. It's fun!

sand thunder
#

I have a question, is there any way currently or planned to make 2 (or more) actions in one rule?
E.g If X I pause AND set signal pumps.running =0
Just to make it a bit nearer than having 2 rules with the same trigger.

south wedge
#

#2 is actually important. Most players like the game "with eyes".

south wedge
#

Speaking about AI. Do you know what is its worsts "problem"? So far, no models can really realize they make code. They interpret your code as just another version of "human language". And it creates a problem: they can make a code that looks just right! Except, it doesn't work. And you can spend hours finding out why, because the code "looks right" to you too. The models were trained on the same code samples as you, not a surprise.

#

Well, I got the error improved:

250530T234932.451 [EXCEPTION] [IgorZ.Automation.ScriptingEngine.ScriptableComponents.SignalDispatcher.CheckifSytemLocked] InvalidOperationException: SignalDispatcher is locked for changes: Signal group update: Signals.A1.Inventory.Water
#

It's just the first step to have it fixed.

sand thunder
#

haha yeah it can be touch and go.
I'm not sure if Copilot has a deep think type feature. I've found that works much better, but I've only done stuff where I tell it what I want and copy and paste code and errors to get it right.

Yesterday I was surprised though, I asked DeepSeek to give me a ublock Origin (adblocker) filter rule to make the steam workshop preview image full screen. It took 3 goes to get something that half worked then it nailed it. It was so good. I had to give it some html from the inspector but that's it.

south wedge
sand thunder
#

The other thing I was going to ask you for was to ignore newline characters when parsing the scripts so more complex rules could be formatted over several lines.

#

Again though it's just asthetics

south wedge
sand thunder
#

I tried it once and it gave me an error so I think it's not allowing it right now, but I'll check and let you know

south wedge
#

Ah, wait. If you apply it in the import - it won't work. Obviously. There is no parser there. It doesn't know if the statement continues on the next lime or not.

sand thunder
#

Yeah in the import

south wedge
#

Unlikely I even start on it. It's a full tokenizer/parser rework.

sand thunder
#

really! I thought maybe just alter a regex a little

#

Ok that's NP thanks for considering

south wedge
#

Recently I was reading about CEL. May be, one day, I will give it a chance. It will cover many complains.

sand thunder
#

BTW this was what I was trying or similar:

condition: (eq (sig Signals.A1.Inventory.Water) (sig Signals.A1.Inventory.Water))
action: 
    (act Signals.Set 'A1.Inventory.Water.Level' 
        (div 
            (sig Signals.A1.Inventory.Water.Sum) 
            (sig Signals.A1.Inventory.Water.Capacity.Sum)
        )
    )
south wedge
#

What I can do, add "multiline import section"

#

It's simple.

sand thunder
#

Ah ok, but don't worry if it is any work, thanks though.

#

CEL, I"ve not heard of that - just looked it up.

south wedge
#
condition: (eq (sig Signals.A1.Inventory.Water) (sig Signals.A1.Inventory.Water))
action: |||mutliline
    (act Signals.Set 'A1.Inventory.Water.Level' 
        (div 
            (sig Signals.A1.Inventory.Water.Sum) 
            (sig Signals.A1.Inventory.Water.Capacity.Sum)
        )
    )
|||
#

or such

south wedge
#

And there is C# port.

#
condition: (eq (sig Signals.A1.Inventory.Water) (sig Signals.A1.Inventory.Water))
action: <<<
    (act Signals.Set 'A1.Inventory.Water.Level' 
        (div 
            (sig Signals.A1.Inventory.Water.Sum) 
            (sig Signals.A1.Inventory.Water.Capacity.Sum)
        )
    )
>>>

Maybe quote the multi line blocks like this? Replacing \n\r with spaces is trivial. Then, it's a regular import logic.

sand thunder
#

Yeah that would be nice - make long actions and calculations more readable.

south wedge
#

Feel free to file a ticket 😉 I need to fix the crash now DamFT

sand thunder
south wedge
#

And the right answer to what will be "average" if there are no values. It will be 0.

#

Rationale: the custom signals in Automation have default value 0. "Default" means: the signal was not yet set by any provider.

sand thunder
#

BTW @south wedge did you see this: #1190169064383991858 message

south wedge
#

I'm a "windows" dude. My desktop is Windows.

#

I spent an hour an improved the error message!

#

(kidding). In fact I was playing another game DamIT

#

Jokes aside, a proper error message is a halfway to the issue resolution when you meet it the first time. And given my memory is not good these days, I prefer to make the code so that it's "not me". Many times confirmed: it works!

#

A new preview release! v2.4.2

Main feature: .Avg suffix to the signal names. Guess what it means.

But in general, it's just another preview. Some here and there changes. The crash on the buildings demolishing is now nicely reported. You still crash, but with a nice error message! Isn't it cool? birthdaypartyparrot

I'm going offline for 2 days for the family affairs. I'll be around in Discord (my phone is always with me!), but no coding.

sand thunder
south wedge
sand thunder
#

Alright, thanks for the tip. 🤞

south wedge
#

Hint on how to get troubles (just in case you wonder):

  1. Select a good on tank.
  2. Set a rule on this good.
  3. Change the good kekw
  4. Reload or somehow trigger the rule => runtime error.
#

It may or may not crash your game 🤣

sand thunder
#

Oh well, sounds like progress was made, I call that a win.

hybrid hatch
south wedge
#

FYI: I liked this idea! I'd love to migrate to C# CEL.

hybrid hatch
south wedge
#

I did only a brief searching. For now, all I need to know: we have a C# version!

#

In CEL, I like the main concept. "Let' make a COMMON language for the expressions". Damn, yeah!!!!

#

Btw, the link I gave is recognized by "our" AI (Gemini). If you ask Gemini to make a parser code, you get the code, referenced to this 3rd party.

#

(not an advertisement!)

#

I, honestly, like GPTv4 more.

hybrid hatch
#

Yea, I only found the other because it's on NuGet as Cel

south wedge
hybrid hatch
#

It was the top hit for "common expression language c#"

south wedge
#

I was looking for "cel". Lol

hybrid hatch
sand thunder
# south wedge What I can do, add "multiline import section"

You could also just use some regex search and replace to format the input and remove whitespace. ChatGPT gave me this which when tested in an online test thingie (technical term) returned:

condition: (eq (sig Signals.A1.Inventory.Water) (sig Signals.A1.Inventory.Water))
action: (act Signals.Set 'A1.Inventory.Water.Level' (div (sig Signals.A1.Inventory.Water.Sum) (sig Signals.A1.Inventory.Water.Capacity.Sum)))
using System;
using System.Text.RegularExpressions;

public static class Formatter
{
    public static string FormatText(string input)
    {
        // 1) Remove comments (“#” to end of line)
        string noComments = Regex.Replace(
            input,
            @"#.*",
            string.Empty,
            RegexOptions.Multiline
        );

        // 2) Strip all whitespace around parentheses and braces
        string noWS = Regex.Replace(
            noComments,
            @"\s*([()\{\}])\s*",
            "$1"
        );

        // 3) Add a space before each '(' or '{'
        string spaceBefore = Regex.Replace(
            noWS,
            @"([^\s])([({])",
            "$1 $2"
        );

        // 4) Insert newline before every "condition:" or "action:", except if at the very start
        string formatted = Regex.Replace(
            spaceBefore,
            @"(?<=.)(?=(?:condition:|action:))",
            "\n"
        );

        return formatted.Trim();
    }
}

class Program
{
    static void Main()
    {
        string testInput = @"
# This is a full-line comment
condition: (eq (sig Signals.A1.Inventory.Water) (sig Signals.A1.Inventory.Water))
action: 
    (act Signals.Set 'A1.Inventory.Water.Level' 
        (div #tricky!
            (sig Signals.A1.Inventory.Water.Sum) 
            (sig Signals.A1.Inventory.Water.Capacity.Sum)
        )
    )";

        string result = Formatter.FormatText(testInput);
        Console.WriteLine(result);
    }
}
``` (IDK how to make that smaller... oh well)
#

This was the input string:

# This is a full-line comment
condition: (eq (sig Signals.A1.Inventory.Water) (sig Signals.A1.Inventory.Water))
action: 
    (act Signals.Set 'A1.Inventory.Water.Level' 
        (div #tricky!
            (sig Signals.A1.Inventory.Water.Sum) 
            (sig Signals.A1.Inventory.Water.Capacity.Sum)
        )
    )
south wedge
#

The regex based approach has a down side: you cannot report the line where the error happen at. And it's the reason why I parse the input without regexps.

sand thunder
tribal canopy
#

Sometime I see the "No available workers in district." flag on building with worker slots set to zero with Workplace.RemoveWorkers. My current workaround is to manually pause then resume the building to clear it. Would it be worthwhile for me to open a feature request to clear this flag as a part of Workplace.RemoveWorkers, or this obviously infeasible?

south wedge
#

The game's notification logic is blind simple: zero workers? => show the notification! When automation sets the counter to 0, it tries to block this notification.

tribal canopy
south wedge
sand thunder
#

Out of curiosity, how regularly does stable get updated to exprimental?

south wedge
sand thunder
#

So every few months, maybe less often then?

south wedge
#

It solely depends on what game developers do. Making of a new update usually takes months, but if they do only tweaks - it can be weeks.

sand thunder
#

Ah ok. And it would be a massive PITA to make this work for stable huh?

#

I remember a comment a while ago about merge hell - that's what that was about i take it

south wedge
#

Yeah. it's not only code, it's also the whole process that eats time. You need to keep a totally isolated environment for the game. Or switch branches back and forth in Steam, wihc makes own troubles to the JetBrains (caches).

sand thunder
#

Yeah sounds like a headache. And you're not setup in a virual machine for this right?
We used to use those - VMWare for that exact reason - different VM and environment for different system.

#

They used to have a tool which you could run to turn a physical windows machine into a windows VM that worked pretty well - so if you were ever thinking to try it you could maybe use that. Also I think I saw their VMWare workstation product recently became free to use or something.

south wedge
tribal canopy
#

Looks like 0.7.9.0 landed on stable?

south wedge
#

yup

tribal canopy
#

Nice.

#

Do you anticipate multi-signal support to land on stable then, or the next experimental?

south wedge
#

You can use the pre-release on stable now. Nothing special is needed. For the release, I 'd like to add a couple more features before publishing it.

tribal canopy
#

Understood. I saw the issue closed emails and was curious how this all fit together. Thanks.

south wedge
#

v2.4.4 (June 3rd, 2025):

  • [Feature #86] Add multi-value support for the custom signals. Signal values can be aggregated via name suffix: .Min, .Max, .Sum, .Avg, and .Count. Not available in constructor.
  • [Feature #88] Allow operator add accepting multiple arguments.
  • [Feature #91] Allow extra comment styles in rule importing: prefixes ; and //; and multi-line comments /* ... */ and #| ... |#.
sand thunder
#

I was testing one of the older more complex scripts I did that always had unexplained issues to see if it now worked (#1190169064383991858 message) and got a crash when saving the rules on the district center.

It's saying there's a circular execution of a ...Dirty signal, but I don't think there is a problem with it.
This signal is meant to indicate when the water level is outdated and used to trigger an update. It's:

  • incremented by tanks when their contents change
  • set = 1 when district center rules are saved (on Init)
  • reset=0 after an update is done
    • this should not trigger recursion because rules require ...Dirty >= 1 to fire
#

To reproduce it add the following script to the district center in this save:

## Area A1 Controller (put on district center)

# Setup an Init signal that can be used as a trigger for other initialization rules
condition:(eq (sig District.Beavers) (sig District.Beavers))
action:(act Signals.Set 'Init' (sub 1 (sig Signals.Init)))

# Recalculate aggregated values by marking them as 'dirty'
condition: (eq (sig Signals.Init) (sig Signals.Init))
action:(act Signals.Set 'A1.Inventory.Water.Dirty' 1)

# When Water total is dirty clear the buffers and do an update
condition: (ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action: (act Signals.Set 'A1.Inventory.Water.Buffer' 0)
condition: (ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action: (act Signals.Set 'A1.Inventory.Water.Capacity.Buffer' 0)
# Toggle .Update to trigger tanks to recount their contents and capacity
condition: (ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action: (act Signals.Set 'A1.Inventory.Water.Update' (sub 1 (sig Signals.A1.Inventory.Water.Update)))

# At this point all storage tank triggers will have completed and buffers contain updated values
# Clear the dirty flag prior to updating the actual value so listners see a clean value
condition: (ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action:(act Signals.Set 'A1.Inventory.Water.Dirty' 0)
condition: (eq (sig Signals.A1.Inventory.Water.Dirty) 0)
action:(act Signals.Set 'A1.Inventory.Water.Level' (div (sig Signals.A1.Inventory.Water.Buffer) (sig Signals.A1.Inventory.Water.Capacity.Buffer)))
# Arbitary add to cause value to display in game UI
condition: (eq (sig Signals.A1.Inventory.Water.Dirty) 0)
action:(act Signals.Set 'A1.Inventory.Water.Capacity' (add 0 (sig Signals.A1.Inventory.Water.Capacity.Buffer)))
 # Arbitary add to cause value to display in game UI
condition: (eq (sig Signals.A1.Inventory.Water.Dirty) 0)
action:(act Signals.Set 'A1.Inventory.Water' (add 0 (sig Signals.A1.Inventory.Water.Buffer)))
#

Also, do you prefer stuff like this here or would it be easier if I made an issue? IDM either way.

#

This was on the Stable Timberborn version with latest update 2.4.4 BTW (none for Beta in steam properties)

south wedge
sand thunder
#

Alright, will put it there.

south wedge
#

Why it fails I can tell right now. The circular detection doesn't verify the condition. It triggers if the same rule gets called. It would be tricky to verify the condition since the signal execution system, knows nothing about the rules. There is a listener that needs to be called if a signal changes, and this is what it does. And listener can be anything, not necessarily a scripted rule. I'll think if this can be handled at the rule side instead ThinkingIT

As for the crash, it's indeed a bug. The game should not crash.

sand thunder
#

Ok, interesting, thanks

sand thunder
sand thunder
#

I was just wondering if it would be possible to make a .Mine aggregate function to get the value of a signal which the building itself set. It would be a decent workaround for settings. https://github.com/ihsoft/TimberbornMods/issues/95

GitHub

One example is I would like to be able to set a Priority for each pump that affects when it starts and stops. I'd like to be able to reference this Priority in multiple rules on that building. ...

south wedge
sand thunder
#

Yeah I know, it would be useful though. E.g to have a priority you set for stuff, so you can use the same rules but have a local signal that's different between buildings.

tribal canopy
#

A local signal could also double a mechanism to perform multiple actions on a single condition.

silent comet
#

Can you automate based on power consumption ?

serene urchin
south wedge
silent comet
wicked furnace
floral thicket
#

GetIsHazardousWeather is being called before WeatherService is loaded.

south wedge
south wedge
south wedge
#

v2.4.5 (pre-release, 3 June 2025):

  • [Feature] Special shor description for the rules that check signal value to itself: "signal change condition."
  • [Change] Rules that cannot execute on unfinished buildings are now grayed in the rule list.
  • [Fix #101] Crash from circular execution of rule.
  • [Fix #104] Game crashes with ModdableWeather mod.
  • [Fix #102] NullReferenceException: Object reference not set to an instance of an object.

I did pretty big changes to the save and rules activation system. In smoke tests, it looks good.

The unfinished buildings rules thing was always there (do not execute some rules if the building is unfinished). It just was never exposed. Now, it's exposed: UI explicitly shows if the rules not being executed (disabled).

#

@sand thunder @reef widget

#

And now, it's time got me to dig some resources on Moon 🙂

sand thunder
#

And thanks

tribal canopy
south wedge
#

Plus, it's Space! I came to TB from KSP (Kerbal Space Program).

tribal canopy
#

Cool. Will have to watch some YouTube videos. Thanks for sharing.

tribal canopy
#

I'm not familiar with installing mods outside of Steam, though I'm sure I'll figure it out, but if anyone has recommendations on how to manage the installation of pre-release mods please share.

south wedge
#

Let me make a quick wiki

tribal canopy
sand thunder
#

Looks like on windows it would be My Doccuments/Timberborn/Mods

south wedge
serene urchin
south wedge
serene urchin
#

You do know all 3 steam gog and epic have the local mods in the same location on windows?
%userprofile%/Documents/Timberborn/Mods

#

Linux has it in some other location as of wine not sure of mac

jaunty creek
#

mac is /Users/<name>/Documents/Timberborn/Mods

south wedge
south wedge
#

v2.5.0 (June 5th, 2025):

  • [Feature] Special short description for the rules that check signal value to itself: "signal change condition."
  • [Feature #150] Add Modulo (mod) Operator.
  • [Change] Rules that cannot execute on unfinished buildings are now grayed in the rule list.
  • [Fix #101] Crash from circular execution of rule.
  • [Fix #104] Game crashes with ModdableWeather mod.
  • [Fix #102] NullReferenceException: Object reference not set to an instance of an object.
sand thunder
#

Sent too soon. My assumptions are:

  • rules on a building with the same trigger execute in the order they appear
  • if a rule changes a signal any rules that trigger on that signal change will fire and complete before continuing to execute/trigger the next rule on the building that changed it
  • signal values are global and updated instantly (I guess this is obvious)
#

There's still something not right. Maybe one of those assumptions are not true.

south wedge
#

rules on a building with the same trigger execute in the order they appear
This cannot be guaranteed. All rules are executed by a callback when a value has changed. In a group of the same listeners withing the same building, you can expect some ordering. But otherwise, there is none.

#

In fact, I would strongly discourage implementing logic that assumes the order in which signals are executed.

#

If you feel you need to know the signals "order", let me know what is the use case. We probaly can have it worked around.

#

And just in case you are trying to make the "district capacity" counter. Don't 🙂 It's my next feature. I'm going to make a signal that returns district capacities on a good.

#

(but please, keep trying to make my mod failing!!! It's very important to make it stable)

sand thunder
#

Haha yeah ok sounds good.
I'm just trying to figure out the rules - like what I want to do is more like a script, and being able to take only 1 action per trigger means order matters

#

I put things on the same building with same trigger hoping that would give deterministic order

sand thunder
#

So do you think it's the rules with same trigger on the same building executing out of defined order that's causing issues with that save?

south wedge
sand thunder
#

I think recursion is a useful feature and I was planning to make use of it with priority stuff - reduce requested start priority untill enough buildings are running. Sounds like that wouldn't work with that limitation though.

sand thunder
#

And BTW, I know I can use Sum to do the stuff in that save, I use it now more as a proof of concept for testing execution order etc.

south wedge
sand thunder
#

Well, something is failing in that save. I think it should work.

south wedge
sand thunder
#

This is on the tanks (5)

condition:(eq (sig Signals.A1.Inventory.Water.Update) (sig Signals.A1.Inventory.Water.Update))
action:(act Signals.Set 'A1.Inventory.Water.Buffer' (add (sig Inventory.OutputGood.Water) (sig Signals.A1.Inventory.Water.Buffer)))

condition:(eq (sig Signals.A1.Inventory.Water.Update) (sig Signals.A1.Inventory.Water.Update))
action:(act Signals.Set 'A1.Inventory.Water.Capacity.Buffer' (add (getnum Inventory.Capacity) (sig Signals.A1.Inventory.Water.Capacity.Buffer)))

condition:(eq (sig Inventory.OutputGood.Water) (sig Inventory.OutputGood.Water))
action:(act Signals.Set 'A1.Inventory.Water.Dirty' (add 1 (sig Signals.A1.Inventory.Water.Dirty)))
#

This on the district center

condition:(eq (sig District.Beavers) (sig District.Beavers))
action:(act Signals.Set 'Init' (sub 1 (sig Signals.Init)))

condition:(eq (sig Signals.Init) (sig Signals.Init))
action:(act Signals.Set 'A1.Inventory.Water.Dirty' 1)

condition:(ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action:(act Signals.Set 'A1.Inventory.Water.Buffer' 0)

condition:(ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action:(act Signals.Set 'A1.Inventory.Water.Capacity.Buffer' 0)

condition:(ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action:(act Signals.Set 'A1.Inventory.Water.Update' (sub 1 (sig Signals.A1.Inventory.Water.Update)))

condition:(ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action:(act Signals.Set 'A1.Inventory.Water.Dirty' 0)

condition:(eq (sig Signals.A1.Inventory.Water.Dirty) 0)
action:(act Signals.Set 'A1.Inventory.Water.Level' (div (sig Signals.A1.Inventory.Water.Buffer) (sig Signals.A1.Inventory.Water.Capacity.Buffer)))

condition:(eq (sig Signals.A1.Inventory.Water.Dirty) 0)
action:(act Signals.Set 'A1.Inventory.Water.Capacity' (add 0 (sig Signals.A1.Inventory.Water.Capacity.Buffer)))

condition:(eq (sig Signals.A1.Inventory.Water.Dirty) 0)
action:(act Signals.Set 'A1.Inventory.Water' (add 0 (sig Signals.A1.Inventory.Water.Buffer)))
#

I'll write psudocode

#
# On all water tanks
if update, set water.buffer += my.water
if update, set capacity.buffer += my.capacity
if my.water changed, set dirty += 1

# On district center
if Init (rules saved), set dirty = 1
if dirty >= 1, set water.buffer = 0
if dirty >= 1, set capacity.buffer = 0
if dirty >= 1, toggle update
if dirty >= 1, set dirty = 0
if dirty = 0, set level = water.buffer / capacity.buffer
if dirty = 0, set capacity = capacity.buffer
if dirty = 0, set water = water.buffer

However capacity.buffer is 3x more than it should be

sand thunder
#

So it looks like the buffers aren't set to 0 before the tanks add their own water amount and capacity to them. In the log the actions appear in the correct order - you can see the action to set the buffer to 0
But following that the action to add to that same buffer shows adding to a non-zero value if I'm reading it right.

#

I'll double check signal names

#

They look right. Not sure why

Executing signal callback: SignalCallback(Host=IgorZ.Automation.Conditions.ScriptedCondition(Pre="";Expr="(eq (sig Signals.A1.Inventory.Water.Update) (sig Signals.A1.Inventory.Water.Update))"),OwnerBuilding=[SmallTank.IronTeeth@(29, 24, 4)], Name=Signals.A1.Inventory.Water.Update)
[SmallTank.IronTeeth@(29, 24, 4)] Setting signal value: source=SignalSource(Value=87000, HasFirstValue=True, ProvidersCount=1), value=129000
# Actually, I'm confused. This should be alternating adding own water and adding 30000 to a zeroed signal.
``` isn't adding to a zero value signal - it should be.
#

I wish I could debug this, step through and see everything.
From what I can tell from the log the 3rd assumption is failing and 1 and 2 hold true:

  1. rules on a building with the same trigger execute in the order they appear
  2. if a rule changes a signal any rules that trigger on that signal change will fire and complete before continuing to execute/trigger the next rule on the building that changed it
  3. signal values are global and updated instantly (I guess this is obvious)
sand thunder
sand thunder
#

Also, @south wedge , I watched a video recently about building on the moon. It was about casting regolith. It, like basalt (IIRC), is in fact perfect for casting as it's constituents all have a very similar melting point (you don't end up with different substances solidifying in pockets as it cools due to having vastly different melting points like some minerals or soils).

You can actually make a bunch of mirrors which could be just aluminium foil, also available from regolith, and point them at a crucible, dump a bunch of 'dirt' in it then when it's molten, pour it into a mold to make bricks or any shape you want really and have a high quality strong outcome.

My home town (Adelaide, Australia) turned out the have the only university that had put out papers on casting regolith where many other places had focused on 3D printing structures etc that required additional materials and water to be shipped in (huge cost and limitations).

No need. Just get dirt, melt it like you did the ants when you were a child and make your house, roads or whatever else from it. Done.

IDK if this helps in your Crust endevours but there it is. GL 🙂

#

And that's actual real science - true facts so to speak. I love that stuff.

reef widget
#

Hmmm...
Would there be any point to an automation flag for "building flooded"?

tribal canopy
#

Buildings are inoperable when flooded, right? So it would have to be used to set a custom signal that's used by another building?

reef widget
#

Or possibly an emergency drain.

sand thunder
#

@reef widget @tribal canopy did either of you ever try something like the script I posted above?

#

Or something that relied on #1190169064383991858 message to work?

tribal canopy
# sand thunder <@508446882519973889> <@791463935278710794> did either of you ever try somethi...

Nothing quite as complex... This is something I tried to do. However, I did run into this issue

GitHub

On Small Tank: condition:(ge (sig Inventory.OutputGood.Water) 0) action:(act Signals.Set 'D1.Have.Water' (add 0 (sig Inventory.OutputGood.Water))) condition:(ge (sig Inventory.OutputGood.Wa...

GitHub

Mods for Timberborn game. Contribute to ihsoft/TimberbornMods development by creating an account on GitHub.

sand thunder
#

14

#

What's different, progrematically or whatever?

#

If you know off hand?

tribal canopy
#

The most I did was try to have a centralized start/stop threshold managed by the District Center. It listens to storages advertising their size, and it sets a start/stop threshold as a percentage of district capacity, which are used by water pumps.

sand thunder
#

Did it work?

tribal canopy
#

At runtime yes, but on game load there were missing signals.

#

From my analysis, the signals aren't being restored on game load, but the aggregate signals (i.e. .Sum) are.

#

Igor has yet to confirm my observations.

sand thunder
#

What I did didn't work on load or runtime. I think it may be much the same, except perhaps I had an update singnal as well in the mix

#

That does sound like a load issue though and in my case it never worked

tribal canopy
#

Say you set the signal Test: action: (act Signals.Set 'Test' 100)

#

Then you save the game and reload it.

#

Signals.Test will read as 0 until the action is triggered again, but Signals.Test.Sum will correctly read as 1.

sand thunder
#

Yeah, that's a definite load issue

#

My problem was not related to saving or loading - it just didnt function as I'd expected

tribal canopy
#

Yeah, I've avoided potential circular dependencies after a very early issue: #1190169064383991858 message

sand thunder
#

I wondered if maybe you or @reef widget could see an error in my code/logic or could confirm it 'should' work

tribal canopy
#

Sure, if you just want a second set of eyes on it, I'll take a look. Not sure I'll be able to test it, though.

sand thunder
#

Absolutely!

#

This save:

#

#1190169064383991858 message

#

This psudo code:

#

#1190169064383991858 message

#

This is the actual code: #1190169064383991858 message]

#

I suspect at this point someone with debug capability will need to step though and find the point at which it's failing. I would absolutely love to but I've recently abandoned Windows for Linux and although I don't know for certain I think this kind of debugging is a lot harder to setup to say the least.

#

I guess for now I'm looking for 'on windows': 'it should work but doesn't

#

OS shouldn't matter for the game, just dev really

#

So @tribal canopy , I'm dying to ask, what did you find?

#

I'm impatient 😁

tribal canopy
#

Haha. Sorry, I was in the middle of something else I had to finish first. I'm pulling it up now.

sand thunder
#

so, I don't want to clap I just want to thumbsup, so yeah. IDK how get rid of the clap. 1st world problems

#

NVM, consider me the ultimate hero of everyone who reads this> The alpha of ahpha's the omega of omegas. The one true left clicker of left clickers. And what not

#

I just hope someone will relate to this discord convo and get a chuckle

tribal canopy
#

haha

#

logically, it seems sound... but it does make assumptions about ordering and coherency/thread-safety?

#

This looks very similar to logic I abandoned because it tripped the circular execution detection:

condition:(ge (sig Signals.A1.Inventory.Water.Dirty) 1)
action:(act Signals.Set 'A1.Inventory.Water.Dirty' 0)
sand thunder
#

Yes, that used to not work but I think that last change changed that.

tribal canopy
#

ah, okay

sand thunder
#

The way it was signals would trigger 1st then check expressions after... I think that was changed so they would only trigger if their expressions allowed.

tribal canopy
#

honestly, I think we can't rely on any of the += logic and need to leverate the .Sum builtin suffix.

sand thunder
#

I kinda get what you're saying. Absolutely most of what that does is done by Sum. But there's really only 2 triggers. the Dirty Trigger and the Update trigger that is toggled as a result.

#

I only use that script now as a proof of concept for execution order etc

tribal canopy
#

The simultaneous handling of the triggers is where I'd venture the problem is. It feels like you classic read-modify-write race condition.

sand thunder
#

IDK what that is, can you explain please?

#

the read modify write race condition

tribal canopy
#

as a proof of concept, did it work with a single water tank?

sand thunder
#

IDK I never tested with just 1

#

In my mind it should work with either but maybe that's worth testing

#

I think you may be onto something here: read-modify-write race condition.

tribal canopy
sand thunder
#

Just saying: what would be more useful than an example is something we could test

tribal canopy
#

Say actor A and actor B attempt to do a read-modify-write 'buffer" at the same time, you could get this sequence:

  1. A reads buffer as 0.
  2. B reads buffer as 0.
  3. A writes 0 + 1 to buffer
  4. B writes 0 + 1 to buffer
#

unless there's an explicit mechanism to make B read after A has done the write, you can never guarantee the above won't happen

#

by it's nature, the behavior isn't deterministic, so the only real test is to stress the system and see if it fails... I think you've done exactly that

sand thunder
#

That's pretty much what I concluded - that signas weren't truly gobal

tribal canopy
#

it's not that they're not global, it's that modifications aren't atomic

sand thunder
#

Isnj'

#

Isn't that the same?

tribal canopy
#

*atomic

sand thunder
#

Not challenging, wanting to learn, what's the differnece

#

Global = instant update everywhere

tribal canopy
#

B can only see what A writes to buffer... B cannot see that A is reading from the buffer about about to write to it...

#

it would be atomic if B is not allowed to read from buffer after A has read from it and is planning to write to it

#

an "atomic" read-modify-write means that nothing is allowed to happen between the read and the write

sand thunder
#

So, I understand namespaces. That's more generic perhaps. What you're describing is to do with a specific value. Which also makes snese

#

sense

tribal canopy
#

"global" just means what's written to it is visible everywhere

#

after A has read from it and is computing the value to write, it has not yet modified the value... so visibility isn't playing a role yet

sand thunder
#

The whole signal system is event driven, do you think the atomic value mismatch fuckery-for-lack-of a better word is like you describe?

tribal canopy
#

yeah

#

yeah, read-modify-writes are not generally compatible with event-driven systems

sand thunder
#

Ok so let me describe what I had in mind and please shoot it to shreads> There ware Singnal Objects that were defined on a global scale> they each had local static varibles, their value, that updated the instant they were set from anywhere. They were essentially global variables....

#

But, evidence suggests this is not the case

tribal canopy
#

yeah, in such a case you generally tend to need mutexes guarding the global variable to prevent this exact issue

#

this mod doesn't have anything analogous to mutexes

sand thunder
#

Ok, so what I assumed as a purely single threaded mod is in fact not a purely single threaded mod. basically

tribal canopy
#

the issue you're seeing suggests it isn't

sand thunder
#

Yeah, that would explain it.

#

IDK if there's other explainations

tribal canopy
#

the other explanation is caching

sand thunder
#

It's a real problem for any complicated script if we can't rely on one rule executing after another

sand thunder
tribal canopy
#

i.e. when a global signal is changed, it's value is copied into an object passed to the callback

#

if the value is cached and the cached before any callback is called, then the callbacks are called sequentially

sand thunder
#

It all seemed single threaded when I lightly tested it and going through the verbose logs confirmed that. If there's some async kind of thing messing this up it happened in a way that make the log still seem synconised as if it was single threaded

#

It would have been great if there was some write or trigger out of place, but I didn't see any...

tribal canopy
#

testing it with a single tank will pretty much confirm it's a read-modify-write race... regardless of the implementation detail that's causing it

sand thunder
#

Ok I'll try that

tribal canopy
#

if it seems to function with a single tank, but not multiple

sand thunder
#

You tried it?

tribal canopy
#

no, didn't try it... just clarifying expected outcome based on the hypothesis

sand thunder
#

Ok I will go and test with a single tank

#

Not 100% on what that will prove or disprove but will let you know

tribal canopy
#

it should prove that the logic is sound

sand thunder
#

Logic is sound, I already know that 😉

#

this is what I get when a hauler delivers water to a storage tank

Everything starts at 0 if I read it correctly, as it should.
So I'm not sure what this means.
I deleted all other tanks, there's only 1

#

I'm actually not sure that's relevant - it's only the dirty signal and the actual water amount is not included

tribal canopy
#

oh, you don't get a log for the buffer update?

sand thunder
#

no, apparently not

#

Unless it's hidden under one of the conditions listed there that lack actions

#

That would be the place where that would be expected

tribal canopy
#

I sometimes have a building act as a monitor for signals I'm debugging:

condition: (eq (sig Signals.Test) (sig Signals.Test))
action: (act Debug.LogNum (sig Signals.Test))
#

unfortunately, the debug logs don't let you pass a string to label the number you're logging, so I track what's being logged by which building is logging it lol

sand thunder
#

Ok, yeah I don't think logging will for sure detemine this problem because if it's due to execution order debug log rules will be of limited help

#

There's a new concat fuction or statement that helps a lot now

tribal canopy
#

if you're no longer suspecting a logical issue, there's no need to test with a single tank as I suggested

#

Igor should be able to just tell us if read-modify-writes are atomic

sand thunder
#

what do you mean a logical issue? I'm pretty sure the logic is sound But i can't be sure as I'm not sure about how things run

#

If you can't see any issue I'm sure we're at the point where debugging, breakpoints and all information is needed to diagonse this

tribal canopy
#

by logically sound, I mean that if not for the race condition I suspect it would work

#

if the system doesn't behave as you expect, I don't consider that a logic issue

sand thunder
#

Yeah, I'm not certain. Thanks for you help with this, I really appreciate it

tribal canopy
#

"logic" is the if-condition, the arithmetic, etc

#

no problem

sand thunder
#

Well I stated my assumptions: #1190169064383991858 message
and as far as I can tell only 3 is failing

#

And actually testing those is the entire point of that script, it's pretty much redundant otherwise.

tribal canopy
#

my impression is that your test has proven that operations are not atomic... the only further certainty we can get is reading and understanding the mod code, or the author telling us

sand thunder
#

BTW, does this link: #1190169064383991858 message work every time you click it?
It only works like 1/4 of the time for me.

#

Seems like 1/2 the time it goes to a different post.

Anyway, not atomic operations. Yeah, I still think they are. I'm not sure what's going on but I reckon it's all single threaded 100%. There will be some other explaination I expect.

But anyway, thanks for helping with this!

tribal canopy
#

Yeah, the link h as work for me each time I've clicked it.

reef widget
sand thunder
#

No, it's cachy browser on cachyos. I've no idea why it doesn't always scroll to the right place

#

Wait no, it's the discrod snap I think.

#

No the discord flatpak. That's what I installed and doens't work 100%.
It's from Discover store - on CachyOS - that's what I'm using because it doens't spy on you and works with games, apparently>
I got to say switching from Windows was a big win.

#

Glad I did. Even if there are some minor issues and stuff I don't yet get.

#

If anyone is considering doing so, my 2c, just do it. Fuck windows, that's messed up what they and everyone else is doing. There are alternatives, if you give a damn, use them.

#

And for the record, when I say the discord flatpak doesn't work 100% it does except when you click links it might not scroll right too them - stop short or something. Other than that, no issues.

sand thunder
wicked furnace
#

Got another crash from drought start, still a conflict with Moddable Weather. Just had a look with ilspy, seems that mod uses differently named classes for basegame weather, eg. GameDroughtWeather/GameBatideWeather/GameTemperateWeather, in addition to adding custom ones. This may need some more logic fixing, or perhaps a separate compatibility patch?

floral thicket
#

this will crash if the user uses any of the modded weather anyway. my mod still keep the original weather ID (not the type). So maybe just ignore if it encounter an unknown ID instead of throwing?

south wedge
south wedge
south wedge
#

As for the signal loading. Confirmed a bug. The "last" value is not restored. And not saved, lol. All signals per building are saved/restored properly, but "last" is a different thing - it depends on what happen in runtime. I had to store this value explicitly, and I didn't.

reef widget
south wedge
#

@sand thunder This preview properly saves/restores the last value. Note, that the old saves will load with 0 values for all signals. Sorry, I don't want to keep the compatibility code.

wicked furnace
#

@south wedge @floral thicket Any chance for weather mod compatibility, at least not to let the game crash on hazardous weather? If the weather signal-string doesn't need to be a constant/localizable, you could even just pull the weather's Id ... or just add a generic "unknown" hazardous weather/season constant for modded weathers/seasons

Sorry for the trouble

south wedge
wicked furnace
south wedge
#

Well, I see the palace where it fails. It's not actually a fail, it's a failsafe check to ensure the new seasons was properly recognized by Automation. The new seasons are not known to Automation. Thus - failure.

#

I can remove the check to not crash the game, but the season based eventing will not work.

wicked furnace
# south wedge Ah, this is a new error.

Seems they still kept the same Weather ID for those basegame weathers, see their comment above.
Guess you could switch to checking the Id rather than the class/Type ... if that's not a bad idea

south wedge
south wedge
#

v2.5.2 (pre-release, June 6th, 2025):

  • [Fix #103] Signals not restored on game load.
  • [Fix #104] Game crashes with ModdableWeather mod.

@floral thicket @wicked furnace

#

I did smoke tests. Seems working to me.

floral thicket
#

for example, Monsoon and Rain etc (and many unknown as mods can further add more)

south wedge
#

Technically, I can p[ass the ID "as is", but it will result in a very unpredicatble logic.

#

You said you keep ID intact. Isn't it the case for the Moonsoon?

floral thicket
#

well I don't know about the logic. but I am just stating what is happening.

floral thicket
#

but the game does not have Monsoon

tribal canopy
#

just curious, but why does the mod need to understand the string, rather than just allowing scripts do a string compare against an arbitrary string?

south wedge
#

There is no way to obtain "a full list of season ids".

wicked furnace
#

found another issue: if a goodId contains some 'special' characters, then the signal name for its quantity turns out invalid ... for example, cond: (eq (sig Inventory.OutputGood.Grapes_MorePlants) 100) throws an error of "bad symbol name"

floral thicket
#

because if you throw when the ID is unknown, then the crash will just happen again, because there are more weathers than the base game

south wedge
tribal canopy
south wedge
wicked furnace
south wedge
floral thicket
#

BadtideWeather, DroughtWeather, Monsoon, ProgressiveTemperate, Rain, ShortTemperate, SurprisinglyRefreshing, TemperateWeather

#

but I think it's a bad idea to make a list.

#

because anyone can add more in

#

and I plan to make more weathers soon anyway

south wedge
south wedge
south wedge
# floral thicket but I think it's a bad idea to make a list.

Also, consider this. You are a newbie who decided to use Automation. You go and read Wiki to know what signals you have. And there, in the weather signal section, what should I put to describe the returned value? "You will get an arbitrary string that depends on the mods set you have in the game?" It would be unlikely a good documentation style.

#

The game crash (due to the check), was the way how we knew the problem exists 🙂 Otherwise, it would be people coming and asking why didn't their scripts work.

reef widget
# floral thicket but I think it's a bad idea to make a list.

Do all weather types have a common base class?
I think the solution here is to have a mandatory field for the name that this mod (or any other that needs to access the weather system) can then detect and use to bind to them. Essentially a standard communication interface.

Then this mod should probably have moddable weather as an optional dependency (I think that exists, at least) so that the game will load them in the correct order by default.

floral thicket
#

yes, IModdedWeather, and IModdedHazardousWeather and IModdedTemperateWeather but I don't think he wants to check that because he has to refer to my DLL

#

or, guess he could check using Reflection and type name string

#

ModdableWeather.Specs.IModdedHazardousWeather if you want to use that route.

#

and I think it's not fair to ask one modder to support another mod's code

south wedge
#

Dependencies to the other mods is a headache to support. Let's try to keep it as simple as possible. For now, just strings is OK.

wicked furnace
#

I see you now do set the season signal to the "unknown" weather's id, but it can't be checked against

south wedge
#

Well, in fact I figured out that the non-standard IDs cannot be used (other than dumping them). The scripting system does type and value checking. I don't want to remove it, but it won't let you specifying the unknown season IDs in conditions. I have no good solution for it right now.

#

Maybe adding another signal with no value options, and hiding it in the constructor?

tribal canopy
#

a .Raw suffix that bypasses the checks?

south wedge
#

Or a global setting to bypass any checks ("advanced mode").

tribal canopy
#

"god mode"

#

"mod mode"

tribal canopy
reef widget
#

@heady summit An example with input automation:
This monitors the total number of logs in the district (or at least the sum total of logs in every log pile with the broadcast rule applied), and shuts down the lumbermill if there is less than a certain quantity, or if there in no place to store the planks (detected based on internal inventory).

This ensures that the lumbermill can never use up all of your logs, so there will always be enough logs to use as fuel for things like making food.

;;;;EVERY LOG PILE IN THE DISTRICT
;;;;Auto-detects the type of resource set in the pile/warehouse/tank.
condition:(eq (sig Inventory.OutputGood.{% (getstr SingleGoodAllower.AllowedGood) %}) (sig Inventory.OutputGood.{% (getstr SingleGoodAllower.AllowedGood) %}))
action:(act Signals.Set 'District1.{% (getstr SingleGoodAllower.AllowedGood) %}s' (sig Inventory.OutputGood.{% (getstr SingleGoodAllower.AllowedGood) %}))

;;;;LUMBERMILL
;;;;Rule 1: Stop the mill if the log supply is critically low OR the internal plank storage is full
condition:(or (le (sig Signals.District1.Logs.Sum) 75000) (gt (sig Inventory.OutputGood.Plank) 1200))
action:(act Workplace.RemoveWorkers)

;;;;Rule 2: Restart the mill once the log supply is stable low AND the internal plank storage has space
condition:(and (ge (sig Signals.District1.Logs.Sum) 100000) (le (sig Inventory.OutputGood.Plank) 1200))
action:(act Workplace.SetWorkers 200)
south wedge
south wedge
reef widget
# tribal canopy has anyone thought about how you might implement something like per-slot job pri...

Let's use the badwater rig as an example.

  1. Have every tank storing badwater broadcast DistrictName.Badwater as the quantity they have avaiable (say, 12,000 units total)
  2. The badwater rig listens to the tank output and changes the number employed to match:
    • <10% calls 10 workers
    • 50% calls 5 workers

    • 90% calls 2 workers

    • 100% releases all workers

Hmm... could the number of workers be used as an input in this?
That way the 10-worker surge could skip the 50% rule.
Alternatively, Perhaps a signal could be used as a flag to skip over it?

That said, the only workplaces I could see this sort of multi-stage job rule being useful on are:

  • Badwater Rig (10 workers)
  • Mine (10 workers)
  • Hauling Post (10 workers, based on number of beavers in the district, according to Haulers = beavers * 10% + 5)
heady summit
#

Hmm, thinking about my original idea (#🤖mod-creators message) now, could there be a feature that always applies the same settings to a type of building by default across games?

#

I think that might be the best bang for the buck, so to say

#

Or a "set as building default" button?

#

Importing/exporting is already there, so I imagine there could be a persistent, special folder with files named after the buildings that always get applied when they're built

south wedge
heady summit
south wedge
reef widget
#

Oh, right, I stumbled into something earlier.

Is it intended that applying a template/copying rules to a pile or warehouse in a stack also applies it to every warehouse above it?

south wedge
#

At least it was this way in U6 and early U7.

reef widget
south wedge
heady summit
reef widget
south wedge
#

Tbh, how many similar buildings at time you build? 5? 10? Not too much to be very bored by using the copy tool.

heady summit
heady summit
south wedge
#

FWIW, you can "go to the folder" right now and create your own templates. Did you try? 😉

south wedge
heady summit
south wedge
heady summit
#

yeah of course. I mean as far as I saw all I can do at the moment is copy/paste templates, so I'll be managing my own automation collection (wherever)

south wedge
#

I mean this: <mod folder>/Blueprints/Tools. These blueprints describe the autamation tools. You can create your own or modify existing.

heady summit
#

Ah, that I didn't know. Let me check

south wedge
heady summit
#

Very powerful stuff

#

gotta dig deeper

#

(and get sidetracked :P)

south wedge
#

Keep in mind that the mod's folder may get cleaned up by Workshop when updating the version. So you better keep the copies. Or try making a symlink with RO permissions.

tribal canopy
#

How about a "copy to similar buildings" like Smart Power has?

south wedge
#

In SmartPower, the similar buildings are searched by the network. What would be "similar" in case of automation? The buildings can be unfinished or non-connected to the district. Or they can be non-district buildings.

tribal canopy
tribal canopy
south wedge
south wedge
vagrant pivot
#

I'm trying to control the floodgates using the stream gauge. If the contamination is greater than 5%, the flootgates should be closed. Floodgates at a discharge point should be opened. If contamination is under 5% it should be the other way round. Unfortunately, I don't understand what I need to set on the stream gauge and the floodgates. I hope someone can help me. It's probably quite simple for the experts here, but unfortunately, I don't understand this scripting language at all. With the constructor I can`t find any possible solution, and writing the script by myself is beyond my possibilities.

reef widget
vagrant pivot
#

that would be so helpful, many thanks

reef widget
#

So, for the steam gauge part, there is a template that gives you all the options:

#

All I do is change the name of the output signal:

#

Typical stream gauge setup for me:
This broadcasts the signal for the master floodgate to receive,

reef widget
# vagrant pivot I'm trying to control the floodgates using the stream gauge. If the contaminatio...
;;;; STREAM GAUGE
;;;; Broadcast contamination level on change
template:Gauge.Signals
precondition:(?sig StreamGauge.Contamination)
condition:(ge (sig StreamGauge.Contamination) 0)
action:(act Signals.Set 'Gauge1.Contamination' (sig StreamGauge.Contamination))


;;;; MASTER FLOODGATE (I USED A DOUBLE FLOODGATE)
;;;; If the season is "Temperate" AND the contamination level is below 0.03 (3%)
condition:(and (eq (sig Weather.Season) 'temperate') (lt (sig Signals.Gauge1.Contamination) 3))
;;;; Set height to 1.75
action:(act Floodgate.SetHeight 175)
;;;; Once triggered, it stays at this height until the season changes

;;;; At the beginning of a drought, raise the floodgate to the max height: 2.00
condition:(eq (sig Weather.Season) 'drought')
action:(act Floodgate.SetHeight 200)

;;;; During a badtide, drop the floodgate to 0.00 to minimize contamination risk
condition:(eq (sig Weather.Season) 'badtide')
action:(act Floodgate.SetHeight 0)

;;;; Broadcast master floodgate height 
template:Floodgate.Sync
precondition:(?sig Floodgate.Height)
condition:(ge (sig Floodgate.Height) 0)
action:(act Signals.Set 'Floodgate1.Height' (sig Floodgate.Height))


;;;; ALL SLAVE FLOODGATES
;;;; any time the height of the master floodgate changes, update to match.
template:Floodgate.Sync
precondition:(?act Floodgate.SetHeight)
condition:(ge (sig Signals.Floodgate1.Height) 0)
action:(act Floodgate.SetHeight (sig Signals.Floodgate1.Height))
vagrant pivot
#

wow, that looks so confusing to me

#

many thanks for your help, I will give it a try

#

what do you mean by master flootgate and slave floodgate?

reef widget
serene urchin
#

maybe discharge point and other ThinkingIT

reef widget
# vagrant pivot what do you mean by master flootgate and slave floodgate?

Technical terms for communications protocols.

There is 1 master floodgate; it listens to the stream gauge and determines the correct height to move to, then broadcasts that height.

Every other floodgate that should move with it is a "slave floodgate"; they simply repeat the height that they were sent without any other logic.

This means that if you want to change the settings, you only need to adjust the 1 master floodgate, and also limits the number of floodgates being constantly pinged by changes at the stream gauge to 1.

vagrant pivot
#

It`s all Greek to me. Looks harder than rocket science. Will try it, really appreciate your help!

vagrant pivot
#

It works great, you are a genius!

south wedge
#

Yes, @reef widget is a true master of making scripts!

south wedge
#

Hey, @floral thicket . Regarding the custom seasons. In general, are you willing to integrate with Automation. If yes, I can think about a kind of API (reflections based) so that the external mods could provide extra values for the fixed value arguments. This will let to show them in the constructor, but the side back is that localization will also be needed (loc key).

floral thicket
#

I am not sure how that works, what do you need?

#

right now all the weathers are registered as interfaces btw

#

and are all singletons

#

as well as specs

south wedge
south wedge
floral thicket
#

or you can grab from the game's IContainer all Multibound IModdedHazardousWeather and IModdedTemperateWeather

#

but I think the former will be easier for you

south wedge
floral thicket
#

though ISpecService doesn't provide non <T> overload I believe, but you can use reflection jsut fine

floral thicket
south wedge
floral thicket
#

I don't know how I can help lol

south wedge
#

I don't like the "pull" approach in this case. If a mod adds something, it should "push" it forward. It will make the code a spagetti.

floral thicket
floral thicket
south wedge
floral thicket
#

or EvenBus which my mod does push when a weather starts/ends, but EventBus also use concrete types.

south wedge
#

IHazardousWeatherUISpecification is almost good, except it doesn;t have ID

floral thicket
south wedge
floral thicket
#

say I agree to give you a whole list of weather ID, how do I do that without a new type?

south wedge
floral thicket
#

EventBus also uses concrete type unfortunately

#

also EB is risky because of timing. say I send out the registered list, remember you won't receive it until PostLoad

south wedge
#

Technically, if you post to EB and "even" of type ComponentSpec, I will be able to catch it.

floral thicket
#

nope, EventBus code does that. It holds all events until PostLoad

#

you won't receive it until PostLoad, not because you register, but because EB's behavior

south wedge
#

I effectively means that anyone who registered prior to PostLoad will get all the events.

floral thicket
#

yes but don't you need that list to get your stuff ready?

#

I think the simplest way is use Reflection on your side on ISpecService lol 😛

south wedge
floral thicket
#

cast it to SpecService, scan the _cachedBlueprints for any of type with the name ModdedWeatherSpec

floral thicket
#

so you get the hold to the reference of the type ModdedWeatherSpec. then use it with SpecService.GetSpecs<T> using MakeGenericArgument with the reference to that type. and you can grab the Id.

south wedge
floral thicket
#

here you go:

    ImmutableArray<string> GetAllWeatherIds()
    {
        var spec = specService as SpecService ?? throw new Exception("ISpecService is no longer SpecService");
        var specType = spec._cachedBlueprints.Keys.FirstOrDefault(q => q.Name == "ModdedWeatherSpec");
        
        if (specType is null)
        {
            // No moddable weather
            return [];
        }

        var specs = (IEnumerable)typeof(ISpecService).GetMethod(nameof(ISpecService.GetSpecs))
            .MakeGenericMethod(specType)
            .Invoke(specService, []);
        var idProp = specType.GetProperty("Id");

        return [.. specs.Cast<object>().Select(q => (string)idProp.GetValue(q))];
    }
#

specService is an injected ISpecService

#

no need Harmony or DLL reference 😛

south wedge
#

One thing I don't like here is a hardcoded name of the spec type. Any new mod that adds a new value (a new season, but not only seasons can be added), and again change the code. It will end up in a pile of hardly maintainable code with time. But as a start it looks good.

#

Let me think on it.

floral thicket
#

if you want to you can refer to the DLL to use nameof, it's at compile time so your mod still won't actually need it, just a reference to the DLL when you compile.

south wedge
#

I mean, you and me are not the only modders in the world, right? Tomorrow, another modder creates a new mod with extra vlues to either seasons or anything else.

floral thicket
#

well my policy has always been "not guaranteed to work with other mods" lol

south wedge
#

So it's better to make a solution that can work going forward, and not only for your mod.

floral thicket
#

and because this mod adds something the game does not have.

south wedge
floral thicket
#

but I don't understand why it breaks... if you choose not to support, and simply check the Hazardous Weather ID, my mod still has it?

#

it does have new string values, but why does it matter? string are meant to be "arbitrary" anyway? at least it does not crash if my mod is there and it's good enough right?

south wedge
south wedge
south wedge
#

v2.5.5 (pre-release, June 14th, 2025):

  • [Feature] Allow disabling arguments and values checking in the scripts via settings.
  • [Feature] Add Debug.Ticker signal. You asked for it, you got it! Now you can use Debug.Ticker signal to get your rules executed every tick.
  • [Change] Improve circular execution detection.
  • [Fix #103] Signals not restored on a game load.
  • [Fix #104] Game crashes with ModdableWeather mod.
  • [Fix #107] Signal values may not be truly global.
  • [Fix #109] Add a debug option to re-evaluate scripts on a game load.
#

@sand thunder @reef widget @tribal canopy Folks, you are my best hope to verify if the scripting engine is in a good shape. I may agree with your opinions or don't, but I really value your input. Please, keep doing it 🙂

reef widget
#

Just in time for me to get back to the game after a couple of weeks off.

Does Debug.Ticker return a number, or is it just always true?
I'm just curious how hard it would be to abuse it to make an automated void pump (floodgates constantly opening and closing to delete water), which would probably require running every n ticks instead of every tick.

south wedge
reef widget
#

So theoretically, I could still use modulus on it?

south wedge
#

The value increases on every game tick

south wedge
#

Keep in mind, the ticks are not happening when the game is paused.

#

And I deliberately placed it into the Debug component 😉

crude rover
#

Hello all, is there a way to get a global storage figure for each item?

reef widget
#

Well, I tested out the void pump idea, and found that even with an unoptimized script where every single floodgate (around 56 of them) is running separately off of Debug.Ticker (112 rules total being called every tick), there was minimal lag, even at 7x speed.

That said, outside of an oscillator like this or a function writing to logs, it shouldn't see too much use, so it should be fine.

In terms of capacity, it was capable of keeping up with a strength 40 water source.

reef widget
crude rover
#

See I’m very much a noob to scripting, ChatGPT is helping lol, I have had a script for a warehouse to do the whole signal thing so if I was to have more than one storage then they all would need to generate a number to log then how would I get a script to sum up the total from the logs?

reef widget
crude rover
#

Would that work for the lumberjacks themselves if I want them to pause or resume based on global storage and their own?

reef widget
#

In that case...

#
;;;;If total logs > 1000 and flag inventory is full, stop
condition:(and (gt (sig Signals.District1.Logs.Sum) 100000) (gt (sig Inventory.OutputGood.Log) 1900))
action:(act Pausable.Pause)

;;;;If total logs <= 500, resume.
;;;;This doesn't check the flag because we can assume it got emptied.
condition:(le (sig Signals.District1.Logs.Sum) 50000))
action:(act Pausable.Unpause)
crude rover
#

Thanks I’ll try it another time as game just crashed, it was raining and I’m assuming it was a weather change that triggered it.

buoyant rapids
#

I had a few crashes with 2.5.3 and moddable weather. Is there anything new in 2.5.5?

south wedge
buoyant rapids
south wedge
buoyant rapids
#

I've read it. But 2.5.3 was supposed to also have that fix. So I want to make sure 🙂

south wedge
#

v2.5.6 (June 16th, 2025):

  • [Feature] Allow disabling arguments and values checking in the scripts via settings.
  • [Feature] Add Debug.Ticker signal. You asked for it, you got it! Now you can use Debug.Ticker signal to get your rules executed every tick.
  • [Feature] Load weather seasons from the game blueprints. This allows the modded seasons to work, given the mod that introduces them follows the specs naming convention.
  • [Change] Improve circular execution detection.
  • [Fix #103] Signals not restored on a game load.
  • [Fix #104] Game crashes with ModdableWeather mod.
  • [Fix #107] Signal values may not be truly global.
  • [Fix #109] Add a debug option to re-evaluate scripts on a game load.

Now, released.

#

@floral thicket In order for the modded seasons to work, their specs should end with WeatherSpec, and have fields Id and NameLocKey. As far as I can tell, with the current version it works fine.

reef widget
#

@south wedge Crash; seems to be this mod.

v0.7.10.0-5279ef7-xsw
Exception: ISpecService is no longer SpecService
IgorZ.Automation.ScriptingEngine.ScriptableComponents.WeatherScriptableComponent.LoadWeatherIds () (at <0f348ad9b7bc4b01b928a75bd49c1b20>:0)
IgorZ.Automation.ScriptingEngine.ScriptableComponents.WeatherScriptableComponent.PostLoad () (at <0f348ad9b7bc4b01b928a75bd49c1b20>:0)
Mods.MoreModLogs.SingletonSystemPatch.ErrorReporter (System.Action fn) (at /home/normanr/src/Timberborn-MoreModLogs/SingletonSystemPatch.cs:53)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.Timberborn.SingletonSystem.SingletonLifecycleService.PostLoadSingletons_Patch0(Timberborn.SingletonSystem.SingletonLifecycleService)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.Timberborn.SingletonSystem.SingletonLifecycleService.LoadAll_Patch1(Timberborn.SingletonSystem.SingletonLifecycleService)
Timberborn.SingletonSystem.SingletonLifecycleUnityAdapter.Start () (at <3bb50da0b9a949568631f734dea04cec>:0)

The exception occured while trying to load into a save from yesterday. Version 2.5.6
Reverting to Version 2.5.5 works fine.

south wedge
reef widget
#

One minute.

south wedge
#

Ah, wait. The error is about the service.

#

It seems, the last game update broke it.

#

I wonder if it fails in stable branch.

reef widget
wicked furnace
#

Also fyi, weather mods can also add new temperate seasons (Moddable Weather already does) - so just put out a disclaimer about those not being recognized in this mod as anything other than plain Temperate if you don't want to implement somehow detecting those.

floral thicket
#

wow, did they change the SpecService?

#

oops... I think one of my mod did change ISpecService to something else rip

#

ModdableBindito I think

#

my new implementation should have just inherited the original SpecService.

floral thicket
#

if you use Moddable Bindito, please update that mod and it should work with this mod now

south wedge
wicked furnace
south wedge
#

temperate stays to be a synonym to the "standard temeprate", but nothing stops from mamking more "temperates".

rigid thicket
south wedge
rigid thicket
rigid thicket
wicked furnace
#

@south wedge just fiy that update to "moddable bindito" theapologist316 just made yesterday ended up breaking timberapi instead... so that will take a lil while longer

floral thicket
#

but it should work for Automation now right?

south wedge
south wedge
floral thicket
#

it should, though it breaks TimberApi now instead lol

south wedge
#

People reported the crash today, after 2.5.6 released. Something is not right here.

south wedge
#

Ah, its not the mod itself, but another one. Now it makes sense.

south wedge
#

v2.5.7 (June 17th, 2025):

  • [Change] Don't crash if the modded seasons can't be loaded. Ignore the specs and use the standard seasons instead.
  • [Fix] Fix a crash during a game load under some circumstances.
lime python
#

Please, don't use don't crash, or eMka will push another update 🤣

south wedge
#

This time, it was'n him 🙂

reef widget
#

It's a good thing that I loaded into my save pointing directly at a building with a broken script...
I had to update all of the seasons to the new spec, and I use a lot of season-based automation.

south wedge
#

E.g. temperate should get converted to TemperateWeather automatically, without errors.

reef widget
south wedge
#

Or did you have rules for the modded seasons, and the modded seasons didn't load?

reef widget
#

No modded seasons.
As for parsing, it just threw parse errors.

south wedge
#

Could you please share the error message?

reef widget
#

One second, let me load the old save.

#

Basically: it never replaced them.

somber dagger
south wedge
#

It's not how it is supposed to work DamFT

south wedge
reef widget
#

I will once this thunderstorm (real life weather, not in-game) ends.
Be aware that you need greedy embers to load it.

#

I just mistook the "no-power" icon for the "broken script" icon.

wicked furnace
#

@south wedge by the way how do i override/add templates (in a separate local mod of course)? are there any rules for writing one up?

reef widget
#

The parser throws an uncaught exception if you try to save a rule that compares a string to a number.

reef widget
#

Uncaught exception on dividing by zero with this rule:

(ge (div (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Quantity.Sum) (add(sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Capacity.Sum) 1)) 95)

I got around it for now with

(add(sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Capacity.Sum) 1)

which ensures a minimum value of 1.

#

Also, Is there a way I could grab the name of the district? That would let me make the signal names completely procedurally.

south wedge
# wicked furnace <@429578251724128278> by the way how do i override/add templates (in a separate ...

There are no specific rules for making the templates. But keep in mind that they are applied via UI, and can be attempted to be applied to an incompatible building. For this, there is "precondtion" expression. Checkout how it's made for the standard templates. This expression should evaluate to "true" in order to allow the scripts be applied. Other than that, it's all about the tools specification semantics: unique tool IDs, the right classes and order indexes.

south wedge
reef widget
#

If a building has multiple recipes, is there any way to grab which output is "active"?

Take a gristmill, for example. It can make Wheat flour or Cattail flour, but only one at a time.

south wedge
#

I still didn;t decide on the output signals if they should or should not invalidate in case of the inventory output change. E.g. the stockpile good switch. Thoughts?

#

Maybe only consider the allower (or recipe selected) in constructor, but don't verify in parsing? ThinkingIT

reef widget
#

Most of the use-cases would be in template-style scripts anyway; I would consider it fine if it was only allowed it in preprocessor.
So basically "it must be valid when the rule is saved, and it is up to you to make sure the rule is valid later."

The only additional time I would probably consider checking recipe output vs. good listed in rules would be when loading a save file.

Two outputs from one recipe is also rare enough to just say "not allowed" on; the only faction I know of with recipes like that by default is Whitepaws.

south wedge
#

v2.5.8 (June 18th, 2025):

  • [Fix] Properly convert the legacy seasons names into the season IDs.
  • [Fix] Add a missing localization string for the settings dialog.
#

Snap, I forgot about the div by zero issue DamIT

south wedge
#

Maybe something like "this district ID", in which case it can be just a hash string.

#

(keep in mind this game is multilingual).

#

A good thing about hashes, it can be a constant. It won't change if you rename the district. A bad thing: it won't be human readable.

reef widget
# south wedge I never even thought of this! But yes, this info can be obtained. However, you c...

I'd prefer readable…
Maybe try parsing the name skipping spaces, and if it fails, then fall back to a hash?

Alternatively, when a district is built, store the hash in an array of all districts, and use Dist<array pos+1>. So Dist1, Dist2, etc. The list can be cleaned at the same time to prevent excessive growth from moving district centers.

Errors:
For floodgates, stream gauges, etc., that can't connect, "This structure cannot be connected to a district."
For any building that can connect but isn't, something allong the lines of "Please connect this to a district first"

Of course, a district center would never throw this.

vast depot
#

Loving this mod. I'm trying to automate a phoenix protocol right now. Is there a way to get the progress of a breeding pod as a trigger? I want to setup an automation to pause a group of pods and some other buildings when the pod progress hits 99%.

reef widget
#

What property would I use to get the maximum number of workers a building can have?

Ah... {%(getnum Workplace.MaxWorkers)%}

reef widget
#

Huh... It finished but didn't explode.

#

Seems to have been a one-time thing; the next one worked just fine.

south wedge
#

But wait till they leave, and the explosion should happen.

south wedge
#

But keep in mind, the condition will be executing every tick. It can affect performance. However, @reef widget did some tests and said the effect is not significant. Anyway, the ticker is a debug tool. So use it with care.

reef widget
#

This is getting a bit ridiculous...

;;;;Monitor Input/Output by percent. Broadcast a stop signal when below 20% input or above 95% output.
condition:(or (ge (div (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Quantity.Sum) (add(sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Capacity.Sum) 1)) 95) (le (div (sig Signals.District1.{%(getstr Inventory.InputGoods 0)%}.Quantity.Sum) (add(sig Signals.District1.{%(getstr Inventory.InputGoods 0)%}.Capacity.Sum) 1)) 20))
action:(act Signals.Set 'District1.{%(getstr Inventory.OutputGoods 0)%}.Make' 0)

;;;;Monitor Input/Output by percent. Broadcast a start signal when above 40% input or below 50% output.
condition:(and (le (div (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Quantity.Sum) (add(sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Capacity.Sum) 1)) 50) (ge (div (sig Signals.District1.{%(getstr Inventory.InputGoods 0)%}.Quantity.Sum) (add(sig Signals.District1.{%(getstr Inventory.InputGoods 0)%}.Capacity.Sum) 1)) 40))
action:(act Signals.Set 'District1.{%(getstr Inventory.OutputGoods 0)%}.Make' 1)

;;;;Stop on stop signal.
condition:(eq (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Make) 0)
action:(act Workplace.RemoveWorkers)

;;;;Start on start Signal
condition:(eq (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Make) 1)
action:(act Workplace.SetWorkers {%(getnum Workplace.MaxWorkers)%})
south wedge
#

You can make it a bit more readable by using multiple rules instead of "or" condition. But i's a huge setup anyway 🙂

reef widget
#

The first 2 rules only go on the first factory of a type; the last two act as repeaters for the first two rules.

Basically:
IF OUTPUT quantity/capacity > 0.95 OR INPUT quantity/capacity < 0.20 THEN stop
IF OUTPUT quantity/capacity < 0.50 AND INPUT quantity/capacity > 0.40 THEN start

And half of the space is preprocessor stuff to make it fit nearly any building with 1 input and 1 output.

south wedge
#

I was thinking about adding a "hysteresis" operator, but decided to skip it since it won't be used much. It seems I was wrong.

south wedge
#

Folks, this example (on the image) I was considering from the start. One day, there will be a building with too many rules. Any ideas to make the view shorter, yet informative?

I was considering two approaches (they are not mutual exclusive):

  1. Only show a list of signals, the building is reacting to.
  2. Create "a visual" view where text is replaced with icons. I like this approach a lot, but it's not scalable 😦

Any more ideas? Checkout the UI in the attached image. How we can make it shorter?

jaunty pumice
#

Is there a tutorial somewhere on how to use the mod more in depth?

south wedge
#

And there is a button in the game, that brings you there HappyFT

long yacht
#

Is there a way to disable scripts on a single building without deleting them?

reef widget
long yacht
#

sounds about like what I have been doing then. Was hopping for an easier toggle to override my rules on some buildings when I want to on a temp basis. tnx for the quick answer.

reef widget
long yacht
#

yeah, was thinking just copy them to another building then copy it back when i am done with my need to override.

somber dagger
vast depot
tribal canopy
crude rover
reef widget
#

Let's start here:

;;;;Broadcast capacity
condition: (eq (sig Constructable.OnUnfinished.State) 'finished')
action: (act Signals.Set 'District1.{%(getstr SingleGoodAllower.AllowedGood)%}.Capacity' {%(getnum Inventory.Capacity)%})

;;;;Broadcast quantity
condition:(eq (sig Inventory.OutputGood.{%(getstr SingleGoodAllower.AllowedGood)%}) (sig Inventory.OutputGood.{%(getstr SingleGoodAllower.AllowedGood)%}))
action:(act Signals.Set 'District1.{%(getstr SingleGoodAllower.AllowedGood)%}.Quantity' (sig Inventory.OutputGood.{%(getstr SingleGoodAllower.AllowedGood)%}))

The first rule causes a storage to send a signal stating how much it can hold, just once. (This will make more sense later).
The second rule has it broadcast its current stored quantity any time it changes.

;;;;The name of the good this storage accepts
{%(getstr SingleGoodAllower.AllowedGood)%}
;;;;The capacity of this warehouse
{%(getnum Inventory.Capacity)%}

These make it so that the script adapts based on the target warehouse; replace them and it becomes a lot more readable:

;;;;storing logs; capacity 180
condition: (eq (sig Constructable.OnUnfinished.State) 'finished')
action: (act Signals.Set 'District1.Log.Capacity' 180)

condition:(eq (sig Inventory.OutputGood.Log) (sig Inventory.OutputGood.Log))
action:(act Signals.Set 'District1.Log.Quantity' (sig Inventory.OutputGood.Log))
#

Next:

;;;;Monitor Input/Output by percent. Broadcast a stop signal when below 20% input or above 95% output.
condition:(or (ge (div (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Quantity.Sum) (add(sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Capacity.Sum) 1)) 95) (le (div (sig Signals.District1.{%(getstr Inventory.InputGoods 0)%}.Quantity.Sum) (add(sig Signals.District1.{%(getstr Inventory.InputGoods 0)%}.Capacity.Sum) 1)) 20))
action:(act Signals.Set 'District1.{%(getstr Inventory.OutputGoods 0)%}.Make' 0)

For a factory is a single input and output item:

;;;; input item name
{%(getstr Inventory.InputGoods 0)%}
;;;; output item name
{%(getstr Inventory.OutputGoods 0)%}

If we use this on a lumber mill (input: Log, output: Plank):

;;;;Monitor Input/Output by percent. Broadcast a stop signal when below 20% input or above 95% output.
condition:(or (ge (div (sig Signals.District1.Plank.Quantity.Sum) (add(sig Signals.District1.Plank.Capacity.Sum) 1)) 95) (le (div (sig Signals.District1.Log.Quantity.Sum) (add(sig Signals.District1.Log.Capacity.Sum) 1)) 20))
action:(act Signals.Set 'District1.Plank.Make' 0)

Still complicated.

;;;; read in quantity and capacity from storage; is (Quantity / (Capacity + 0.01)) >= 95%?
;;;; .Sum combines the output of everything sending that signal, making it work with multiple storages
(ge (div (sig Signals.District1.Plank.Quantity.Sum) (add(sig Signals.District1.Plank.Capacity.Sum) 1)) 95)

The seemingly random +0.01 is actually to prevent the denominator ever being zero; dividing by zero would be bad.

The action is to broadcast a signal:

action:(act Signals.Set 'District1.Plank.Make' 0)
action:(act Signals.Set 'District1.Plank.Make' 1)
#

All this Percent Quantity stuff is heavy, so it only runs on one factory.
These last two signals go to all factories producing that good, essentially allowing multiple factories to listen to the two massive control rules.

;;;;Stop on stop signal.
condition:(eq (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Make) 0)
action:(act Workplace.RemoveWorkers)

;;;;Start on start Signal
condition:(eq (sig Signals.District1.{%(getstr Inventory.OutputGoods 0)%}.Make) 1)
action:(act Workplace.SetWorkers {%(getnum Workplace.MaxWorkers)%})

One last automatic value:

{%(getnum Workplace.MaxWorkers)%}

Hopefully that one is obvious.

wicked furnace
#

@south wedge is there any chance you'll add setting sluices back to Auto-mode as an automation action ... or make the scriptableComponent pass along contamination & downstream depth (and their set limits) as signals ?

south wedge
reef widget
#

How would I set a precondition for an action NOT being available?
Specifically, I want a rule to be applied only for a building that can be paused, but does not have workers.

south wedge
reef widget
south wedge
#

Yeah, when the building doesn't have workers it doesn't have "Workplace" component.

#

You can check for (?act Workplace.ResetWorkers)

reef widget
#

But how to negate it?
What I want: (and (?act Pausable.Pause) (not (?act Workplace.ResetWorkers)))
But I can't find anything equivalent to not, a boolean inversion.

wicked furnace
south wedge
#

"Not" is not implemented, but one day I will add it. Preconditions don't need signals in the condition.

reef widget
south wedge
#

Technically, precondition can even fail with an error. It won;t be shown in UI, but will be logged.

south wedge
#

v2.6.0 (June 22nd, 2025):

  • [Change] Make input field in script editor multi-line.
  • [Change] Better handling the script errors in both UI and the execution phases.
  • [Change #106] Allow choosing if the values should be evaluated or "described" in the UI. Check the settings dialog.
  • [Fix #115] InvalidOperationException: Condition already activated.
crude rover
#

Would it be possible to have the constructor be able to incorporate and, or functions? I know it can kind of do it if you only have one function but if you have multiple nested scripts constructor can’t handle it.

reef widget
crude rover
cunning gull
#

@south wedge any way to turn on a building at a specific time of day? i'm specifically looking to only run extra number crunchers when everything else turns off

serene urchin
south wedge
cunning gull
#

I'll give that a try. Thanks for the interesting mod, I'm trying to see what I can achieve with it

tribal canopy
#

The way I use it is by setting Lunch Start to 0h and Lunch End to when I want work to actually start. Then Shift Hours to when I want work to end. If you're trying to share Beavers between buildings, I'm not sure Configurable Workplace or Smart Power will serve your needs.

south wedge
#

Folks, if you see too much spam in the games logs from Automation, please let me know. Sometimes, I forget to remove the debug logging which is not meant to get into the mod. Just now I found a debug log for the ticks (debug.ticker).

reef widget
#

Oh, my, that could certainly flood the log pretty quickly, given that it runs every tick.

south wedge
south wedge
#

Quick vote before I post a pre-release of Debug.DistrictStockTracker.<good id>. Should it:

  1. Only return what's in the stockpiles (i.e. what can be brought in and taken out).
  2. Return #1 + what's in the output inventories of manufactures.
    ?
south wedge
#

Option #2 is made. Usage:

(ge (sig Debug.DistrictStockTracker.AlgaeRation) 0)
(ge (sig Debug.DistrictStockTracker.AlgaeRation.Capacity) 0)

This is a ticker. The condition triggers once every tick regardless to if the value changed or not. The solution is non-optimal: the values are calculated each tick on all districts. I would assume it will impact performance on the big districts. Good for debugging, but not for the real game automation. So far, I haven't found a way to calculate it without iterating all goods in all inventories every tick.

glad radish
#

Not sure this is the right place for this, but I noticed there was a command already included that detonates dynamite and automatically places another in the same spot, could this be used to place a different object in the same place?

I tried building tubeway tunnels and it was very tedious waiting for the tunnel to explode then manually placing the next tube tile by tile, could this potentially be automated by this mod where the next piece of tubeway is placed after the tunnel is exploded?

reef widget
deft tulip
#

Is there any signal that tells the number of any item in the whole district?

#

lets say all the logs available in the district. Not just in one storage pile.

#

@south wedge

reef widget
deft tulip
#

Yeah using that only. Thanks. 🙂

deft tulip
#

@south wedge There is one bug If I use the copy tool and there are multiple storage stacked. It copies the rule to all storage above the selected storage.

south wedge
# deft tulip Is there any signal that tells the number of any item in the whole district?

There is no "good way" for now. One is creating custom signals on the stockpiles you're interested in. Another way, is in the pre-release (v2.6.1, see above). It's not performance effective, but you may give a try.

In general, not always you need to focus on the exact amount of something in the district. Create a stockpile in mode "obtain", and check the fill rate. If the rate is low, then obviosly you need to start more production. If the stockpile is close to 90%, you likely have enough and can stop production. This way you create one custom signal per good, and not all of them you need to automate anyway.

south wedge
reef widget
deft tulip
south wedge
reef widget
#

If you want to change any colors, find the matching item in that same dialog and click the button labeled "styler" near it.

south wedge
#

Import was ok, but I still don;t see it in the languages.

reef widget
south wedge
#

Ah, found it :0

#

Nice 👍

prisma marlin
#

Hey guys. I'm diving into the Automating mod and I'm looking to get some information out of the storage buildings. Does anyone know how to get the value for max capacity out of the buildings? I believe I can use an Access operator to get it, but I'm struggling to find the ComponentName and the PropertyName for each building. Maybe there is an easy way to get the max value from any building with the same line of code?

reef widget
# prisma marlin Hey guys. I'm diving into the Automating mod and I'm looking to get some informa...

You want how much can fit, not how much is currently stored?

{%(getnum Inventory.Capacity)%}

Full rule set (compatible with all piles, warehouses, and tanks)

;;;;Broadcast capacity
template:Piisfun.LogisticsBroadcast
precondition:(ne (getnum SingleGoodAllower.HasAllowedGood) 0)
condition:(eq (sig Constructable.OnUnfinished.State) 'finished')
action:(act Signals.Set 'District1.{%(getstr SingleGoodAllower.AllowedGood)%}.Capacity' {%(getnum Inventory.Capacity)%})
;;;;Broadcast quantity
template:Piisfun.LogisticsBroadcast
precondition:(ne (getnum SingleGoodAllower.HasAllowedGood) 0)
condition:(eq (sig Inventory.OutputGood.{%(getstr SingleGoodAllower.AllowedGood)%}) (sig Inventory.OutputGood.{%(getstr SingleGoodAllower.AllowedGood)%}))
action:(act Signals.Set 'District1.{%(getstr SingleGoodAllower.AllowedGood)%}.Quantity' (sig Inventory.OutputGood.{%(getstr SingleGoodAllower.AllowedGood)%}))
reef widget
#

Well, so far...

  • Storage: Priority Supply
  • Storage: Priority Obtain
  • Storage: Broadcast Quantity/Capacity*
  • Factory: Workplace control based on Input quantity*
  • Factory: Workplace control based on Output quantity*
  • Factory: Workplace control based on both Input and Output quantity*
  • Factory: Repeat to all other factories*
  • Generator/Amenity: Priority restock*
  • Generator/Amenity: Low stock shutdown*
  • Floodgates: Various combinations
    -# *Procedural signal naming

Any other obvious ones?

south wedge
reef widget
serene urchin
#

to bad you can not set Recipe with automation ThinkingIT

south wedge
timber portal
reef widget
# timber portal

If you are using scripts, and not the constructor.
Automation uses fixed-point decimal values, where 1 is read as 0.01.

Therefore, if you want 5 logs, you would need 500 (5.00), but if you are trying to set a floodgate to 1.75, then 175.

As for the constructor, it just expects decimal points.

timber portal
#

It's not always divided by 100, though

serene urchin
reef widget
#

The one major thing is zero, because 0 equals 000.

timber portal
#

I'll grab a screenshot in the morning, right now I'm meant to be sleeping

reef widget
serene urchin
#

or both 😛

timber portal
#

but I was literally copying the exact same subexpression and it would work in certain cases and not in others

#

I should figure out where the log files end up on linux

serene urchin
#

well if we have a specific case where it happen and a screenshot to show it should go along way to track down

reef widget
hybrid hatch
#

Somewhere like ~/.local/share/Steam/steamapps/compatdata/1062090/pfx/drive_c/Users/steamuser

reef widget
#

Why is the top level hidden...

timber portal
#

lots of stuff is in .local

#

isn't appdata hidden on windows?

timber portal
#

on an advanced breeding pod:

condition:(gt (sig District.Beavers) (sub (sig District.NumberOfBeds) 1))
action:(act Pausable.Pause)

condition:(lt (sig District.Beavers) (sub (sig District.NumberOfBeds) 10))
action:(act Pausable.Unpause)
#

I have to remove the smart power mod because it's impossible to click the edit button on certain buildings because it flows off the bottom of the screen, hopefully that doesn't break anything

#

on a gear workshop:

condition:(lt (sig Signals.Stockpile.Gear) (div (sig Signals.Stockpile.GearCap) 4))
action:(act Pausable.Unpause)
#

so it seems like maybe it's only in conditions, as it works as expected in all the actions I've tried

#

but even in conditions it only affects operands to math operators

#

maybe I should also ping @reef widget

timber portal
hybrid hatch
deft tulip
reef widget
timber portal
deft tulip
timber portal
#

You can import templates? Or do you mean importing on the building?

timber portal
deft tulip
reef widget
timber portal
#

I don't understand what you mean then

timber portal
deft tulip
reef widget
timber portal
#

That's exactly what I asked if I was supposed to do and got told no

deft tulip
reef widget
deft tulip
timber portal
#

Do you need that many ;s?

reef widget
#

Yes. That comment type is actually a LISP standard, which is what the automation scripting language is based on.

timber portal
#

huh, I've never seen a language with a 4 character comment introducer

#

I mean unless you count rem in windows shell scripts but that's not technically even a comment, it's just a line with no actual effect

reef widget
#

Ah yes, Command prompt and its executable comments.

timber portal
#

Anyway, did you look into why some of my rules weren't dividing literals by 100?

reef widget
#

I can't, that's something for @south wedge to look at when he has time.
Link: #1190169064383991858 message

timber portal
#

alright, I wasn't sure who was a dev on the mod or just someone who knows it well

south wedge
#

As for not dividing by 100. If this actually happens in execution, it's a bug. Based on the screenshot, the "describe" logic shows the script value (integer). This could be a bad choice after all. Try enabling "evaluate arguments value" setting in the mod. This way you will get the actual value used in the processing.