diff --git a/Automation/ScriptingEngine/ScriptableComponents/Components/DistrictScriptableComponent.cs b/Automation/ScriptingEngine/ScriptableComponents/Components/DistrictScriptableComponent.cs
index 4af33006..dcb78cfe 100644
--- a/Automation/ScriptingEngine/ScriptableComponents/Components/DistrictScriptableComponent.cs
+++ b/Automation/ScriptingEngine/ScriptableComponents/Components/DistrictScriptableComponent.cs
@@ -3,6 +3,7 @@
// License: Public Domain
using System;
+using System.Collections.Generic;
using IgorZ.Automation.AutomationSystem;
using IgorZ.Automation.ScriptingEngine.Core;
using IgorZ.Automation.ScriptingEngine.Expressions;
@@ -10,6 +11,8 @@
using Timberborn.Bots;
using Timberborn.DwellingSystem;
using Timberborn.GameDistricts;
+using Timberborn.Population;
+using Timberborn.WorkSystem;
namespace IgorZ.Automation.ScriptingEngine.ScriptableComponents.Components;
@@ -18,10 +21,14 @@ sealed class DistrictScriptableComponent : ScriptableComponentBase {
const string BotPopulationSignalLocKey = "IgorZ.Automation.Scriptable.District.Signal.Bots";
const string BeaversPopulationSignalLocKey = "IgorZ.Automation.Scriptable.District.Signal.Beavers";
const string NumberOfBedsSignalLocKey = "IgorZ.Automation.Scriptable.District.Signal.NumberOfBeds";
+ const string UnemployedBeaversSignalLocKey = "IgorZ.Automation.Scriptable.District.Signal.UnemployedBeavers";
+ const string UnemployedBotsSignalLocKey = "IgorZ.Automation.Scriptable.District.Signal.UnemployedBots";
const string BotPopulationSignalName = "District.Bots";
const string BeaverPopulationSignalName = "District.Beavers";
const string NumberOfBedsSignalName = "District.NumberOfBeds";
+ const string UnemployedBeaversSignalName = "District.UnemployedBeavers";
+ const string UnemployedBotsSignalName = "District.UnemployedBots";
#region ScriptableComponentBase implementation
@@ -30,8 +37,9 @@ sealed class DistrictScriptableComponent : ScriptableComponentBase {
///
public override string[] GetSignalNamesForBuilding(AutomationBehavior behavior) {
- return behavior.GetComponentFast()
- ? [BeaverPopulationSignalName, BotPopulationSignalName, NumberOfBedsSignalName]
+ return behavior.GetComponentFast()
+ ? [BeaverPopulationSignalName, BotPopulationSignalName, NumberOfBedsSignalName,
+ UnemployedBeaversSignalName, UnemployedBotsSignalName]
: [];
}
@@ -45,6 +53,8 @@ public override Func GetSignalSource(string name, AutomationBehavio
BeaverPopulationSignalName => () => BeaverPopulationSignal(districtBuilding),
BotPopulationSignalName => () => BotPopulationSignal(districtBuilding),
NumberOfBedsSignalName => () => NumberOfBedsSignal(districtBuilding),
+ UnemployedBeaversSignalName => () => UnemployedBeaversSignal(districtBuilding),
+ UnemployedBotsSignalName => () => UnemployedBotsSignal(districtBuilding),
_ => throw new UnknownSignalException(name),
};
}
@@ -59,6 +69,8 @@ public override SignalDef GetSignalDefinition(string name, AutomationBehavior be
BeaverPopulationSignalName => BeaverPopulationSignalDef,
BotPopulationSignalName => BotPopulationSignalDef,
NumberOfBedsSignalName => NumberOfBedsSignalDef,
+ UnemployedBeaversSignalName => UnemployedBeaversSignalDef,
+ UnemployedBotsSignalName => UnemployedBotsSignalDef,
_ => throw new UnknownSignalException(name),
};
}
@@ -66,7 +78,8 @@ public override SignalDef GetSignalDefinition(string name, AutomationBehavior be
///
public override void RegisterSignalChangeCallback(SignalOperator signalOperator, ISignalListener host) {
var name = signalOperator.SignalName;
- if (name is not (BeaverPopulationSignalName or BotPopulationSignalName or NumberOfBedsSignalName)) {
+ if (name is not (BeaverPopulationSignalName or BotPopulationSignalName or NumberOfBedsSignalName
+ or UnemployedBeaversSignalName or UnemployedBotsSignalName)) {
throw new InvalidOperationException("Unknown signal: " + name);
}
host.Behavior.GetOrCreate().AddSignal(signalOperator, host);
@@ -111,6 +124,26 @@ public override void UnregisterSignalChangeCallback(SignalOperator signalOperato
};
SignalDef _numberOfBedsSignalDef;
+ SignalDef UnemployedBeaversSignalDef => _unemployedBeaversSignalDef ??= new SignalDef {
+ ScriptName = UnemployedBeaversSignalName,
+ DisplayName = Loc.T(UnemployedBeaversSignalLocKey),
+ Result = new ValueDef {
+ ValueType = ScriptValue.TypeEnum.Number,
+ ValueValidator = ValueDef.RangeCheckValidatorInt(min: 0),
+ },
+ };
+ SignalDef _unemployedBeaversSignalDef;
+
+ SignalDef UnemployedBotsSignalDef => _unemployedBotsSignalDef ??= new SignalDef {
+ ScriptName = UnemployedBotsSignalName,
+ DisplayName = Loc.T(UnemployedBotsSignalLocKey),
+ Result = new ValueDef {
+ ValueType = ScriptValue.TypeEnum.Number,
+ ValueValidator = ValueDef.RangeCheckValidatorInt(min: 0),
+ },
+ };
+ SignalDef _unemployedBotsSignalDef;
+
static ScriptValue BeaverPopulationSignal(DistrictBuilding districtBuilding) {
return ScriptValue.FromInt(districtBuilding.District?.DistrictPopulation.Beavers.Count ?? 0);
}
@@ -128,6 +161,27 @@ static ScriptValue NumberOfBedsSignal(DistrictBuilding districtBuilding) {
return ScriptValue.FromInt(statistics.FreeBeds + statistics.OccupiedBeds);
}
+ static ScriptValue UnemployedBeaversSignal(DistrictBuilding districtBuilding) {
+ var district = districtBuilding.District;
+ if (!district) {
+ return ScriptValue.FromInt(0);
+ }
+ PopDataCollector.CollectData(district, PopData);
+ return ScriptValue.FromInt(PopData.BeaverWorkplaceData.Unemployed);
+ }
+
+ static ScriptValue UnemployedBotsSignal(DistrictBuilding districtBuilding) {
+ var district = districtBuilding.District;
+ if (!district) {
+ return ScriptValue.FromInt(0);
+ }
+ PopDataCollector.CollectData(district, PopData);
+ return ScriptValue.FromInt(PopData.BotWorkplaceData.Unemployed);
+ }
+
+ static readonly PopulationDataCollector PopDataCollector = new();
+ static readonly PopulationData PopData = new();
+
#endregion
#region Implementation
@@ -145,6 +199,7 @@ static ScriptValue NumberOfBedsSignal(DistrictBuilding districtBuilding) {
sealed class DistrictChangeTracker : AbstractStatusTracker {
DistrictCenter _currentDistrictCenter;
+ readonly List _trackedWorkplaces = new();
void Start() {
var districtBuilding = GetComponentFast();
@@ -154,6 +209,7 @@ void Start() {
}
void UpdateDistrictCenter() {
+ UnsubscribeFromWorkplaces();
if (_currentDistrictCenter) {
_currentDistrictCenter.DistrictPopulation.CitizenAssigned -= OnCitizenAssigned;
_currentDistrictCenter.DistrictPopulation.CitizenUnassigned -= OnCitizenUnassigned;
@@ -166,9 +222,28 @@ void UpdateDistrictCenter() {
_currentDistrictCenter.DistrictPopulation.CitizenUnassigned += OnCitizenUnassigned;
_currentDistrictCenter.DistrictBuildingRegistry.FinishedBuildingRegistered += FinishedBuildingRegisteredEvent;
_currentDistrictCenter.DistrictBuildingRegistry.FinishedBuildingUnregistered += FinishedBuildingUnregisteredEvent;
+ SubscribeToWorkplaces();
+ }
+ }
+
+ void SubscribeToWorkplaces() {
+ foreach (var workplace in _currentDistrictCenter.DistrictBuildingRegistry.GetEnabledBuildings()) {
+ workplace.WorkerAssigned += OnWorkerAssignmentChanged;
+ workplace.WorkerUnassigned += OnWorkerAssignmentChanged;
+ _trackedWorkplaces.Add(workplace);
}
}
+ void UnsubscribeFromWorkplaces() {
+ foreach (var workplace in _trackedWorkplaces) {
+ if (workplace) {
+ workplace.WorkerAssigned -= OnWorkerAssignmentChanged;
+ workplace.WorkerUnassigned -= OnWorkerAssignmentChanged;
+ }
+ }
+ _trackedWorkplaces.Clear();
+ }
+
void OnDistrictChangedEvent(object obj, EventArgs args) {
UpdateDistrictCenter();
OnPopulationChangedEvent();
@@ -185,18 +260,35 @@ void OnCitizenUnassigned(object sender, CitizenUnassignedEventArgs args) {
void OnPopulationChangedEvent(Citizen citizen = null) {
if (!citizen || citizen.GetComponentFast()) {
ScheduleSignal(BotPopulationSignalName, ignoreErrors: true);
+ ScheduleSignal(UnemployedBotsSignalName, ignoreErrors: true);
}
if (!citizen || !citizen.GetComponentFast()) {
ScheduleSignal(BeaverPopulationSignalName, ignoreErrors: true);
+ ScheduleSignal(UnemployedBeaversSignalName, ignoreErrors: true);
}
}
+ void OnWorkerAssignmentChanged(object sender, WorkerChangedEventArgs args) {
+ ScheduleSignal(UnemployedBeaversSignalName, ignoreErrors: true);
+ ScheduleSignal(UnemployedBotsSignalName, ignoreErrors: true);
+ }
+
void FinishedBuildingRegisteredEvent(object sender, FinishedBuildingRegisteredEventArgs arg) {
ScheduleSignal(NumberOfBedsSignalName, ignoreErrors: true);
+ // Re-subscribe to pick up the new workplace's worker events.
+ UnsubscribeFromWorkplaces();
+ SubscribeToWorkplaces();
+ ScheduleSignal(UnemployedBeaversSignalName, ignoreErrors: true);
+ ScheduleSignal(UnemployedBotsSignalName, ignoreErrors: true);
}
void FinishedBuildingUnregisteredEvent(object sender, FinishedBuildingUnregisteredEventArgs arg) {
ScheduleSignal(NumberOfBedsSignalName, ignoreErrors: true);
+ // Re-subscribe to drop the destroyed workplace's worker events.
+ UnsubscribeFromWorkplaces();
+ SubscribeToWorkplaces();
+ ScheduleSignal(UnemployedBeaversSignalName, ignoreErrors: true);
+ ScheduleSignal(UnemployedBotsSignalName, ignoreErrors: true);
}
}
diff --git a/Automation/ScriptingEngine/ScriptableComponents/Components/WorkplaceScriptableComponent.cs b/Automation/ScriptingEngine/ScriptableComponents/Components/WorkplaceScriptableComponent.cs
index b708ce70..c9ee11b4 100644
--- a/Automation/ScriptingEngine/ScriptableComponents/Components/WorkplaceScriptableComponent.cs
+++ b/Automation/ScriptingEngine/ScriptableComponents/Components/WorkplaceScriptableComponent.cs
@@ -7,6 +7,7 @@
using IgorZ.Automation.ScriptingEngine.Core;
using IgorZ.Automation.ScriptingEngine.Expressions;
using Timberborn.BaseComponentSystem;
+using Timberborn.PrioritySystem;
using Timberborn.WorkSystem;
namespace IgorZ.Automation.ScriptingEngine.ScriptableComponents.Components;
@@ -15,19 +16,68 @@ sealed class WorkplaceScriptableComponent : ScriptableComponentBase {
const string RemoveWorkersActionLocKey = "IgorZ.Automation.Scriptable.Workplace.Action.RemoveWorkers";
const string SetWorkersActionLocKey = "IgorZ.Automation.Scriptable.Workplace.Action.SetWorkers";
+ const string SetPriorityActionLocKey = "IgorZ.Automation.Scriptable.Workplace.Action.SetPriority";
+ const string AssignedWorkersSignalLocKey = "IgorZ.Automation.Scriptable.Workplace.Signal.AssignedWorkers";
const string RemoveWorkersActionName = "Workplace.RemoveWorkers";
const string SetWorkersActionName = "Workplace.SetWorkers";
+ const string SetPriorityActionName = "Workplace.SetPriority";
+ const string AssignedWorkersSignalName = "Workplace.AssignedWorkers";
#region ScriptableComponentBase implementation
///
public override string Name => "Workplace";
+ ///
+ public override string[] GetSignalNamesForBuilding(AutomationBehavior behavior) {
+ var workplace = GetWorkplace(behavior, throwIfNotFound: false);
+ return workplace ? [AssignedWorkersSignalName] : [];
+ }
+
+ ///
+ public override Func GetSignalSource(string name, AutomationBehavior behavior) {
+ var workplace = GetWorkplace(behavior);
+ return name switch {
+ AssignedWorkersSignalName => () => ScriptValue.FromInt(workplace.NumberOfAssignedWorkers),
+ _ => throw new UnknownSignalException(name),
+ };
+ }
+
+ ///
+ public override SignalDef GetSignalDefinition(string name, AutomationBehavior behavior) {
+ var workplace = GetWorkplace(behavior);
+ return name switch {
+ AssignedWorkersSignalName => LookupSignalDef(
+ AssignedWorkersSignalName + "-" + workplace.MaxWorkers,
+ () => MakeAssignedWorkersSignalDef(workplace)),
+ _ => throw new UnknownSignalException(name),
+ };
+ }
+
+ ///
+ public override void RegisterSignalChangeCallback(SignalOperator signalOperator, ISignalListener host) {
+ if (signalOperator.SignalName is not AssignedWorkersSignalName) {
+ throw new InvalidOperationException("Unknown signal: " + signalOperator.SignalName);
+ }
+ host.Behavior.GetOrCreate().AddSignal(signalOperator, host);
+ }
+
+ ///
+ public override void UnregisterSignalChangeCallback(SignalOperator signalOperator, ISignalListener host) {
+ host.Behavior.GetOrThrow().RemoveSignal(signalOperator, host);
+ }
+
///
public override string[] GetActionNamesForBuilding(AutomationBehavior behavior) {
var workplace = GetWorkplace(behavior, throwIfNotFound: false);
- return workplace ? [RemoveWorkersActionName, SetWorkersActionName] : [];
+ if (!workplace) {
+ return [];
+ }
+ var workplacePriority = behavior.GetComponentFast();
+ return workplacePriority
+ ? [RemoveWorkersActionName, SetWorkersActionName, SetPriorityActionName]
+ : [RemoveWorkersActionName, SetWorkersActionName];
}
///
@@ -36,6 +86,7 @@ public override Action GetActionExecutor(string name, AutomationB
return name switch {
RemoveWorkersActionName => _ => ResetWorkersAction(workplace),
SetWorkersActionName => args => SetWorkersAction(workplace, args),
+ SetPriorityActionName => args => SetPriorityAction(behavior, args),
_ => throw new UnknownActionException(name),
};
}
@@ -47,12 +98,30 @@ public override ActionDef GetActionDefinition(string name, AutomationBehavior be
return name switch {
RemoveWorkersActionName => RemoveWorkersActionDef,
SetWorkersActionName => LookupActionDef(key, () => MakeSetWorkersActionDef(workplace)),
+ SetPriorityActionName => SetPriorityActionDef,
_ => throw new UnknownActionException(name),
};
}
#endregion
+ #region Signals
+
+ SignalDef MakeAssignedWorkersSignalDef(Workplace workplace) {
+ return new SignalDef {
+ ScriptName = AssignedWorkersSignalName,
+ DisplayName = Loc.T(AssignedWorkersSignalLocKey),
+ Result = new ValueDef {
+ ValueType = ScriptValue.TypeEnum.Number,
+ ValueFormatter = x => x.AsFloat.ToString("0"),
+ ValueValidator = ValueDef.RangeCheckValidatorInt(0, workplace.MaxWorkers),
+ ValueUiHint = GetArgumentMinMaxValueHint(0, workplace.MaxWorkers),
+ },
+ };
+ }
+
+ #endregion
+
#region Actions
ActionDef RemoveWorkersActionDef => _removeWorkersActionDef ??= new ActionDef {
@@ -77,6 +146,24 @@ ActionDef MakeSetWorkersActionDef(Workplace workplace) {
};
}
+ ActionDef SetPriorityActionDef => _setPriorityActionDef ??= new ActionDef {
+ ScriptName = SetPriorityActionName,
+ DisplayName = Loc.T(SetPriorityActionLocKey),
+ Arguments = [
+ new ValueDef {
+ ValueType = ScriptValue.TypeEnum.String,
+ Options = [
+ ("VeryLow", Loc.T("Priorities.VeryLow")),
+ ("Low", Loc.T("Priorities.Low")),
+ ("Normal", Loc.T("Priorities.Normal")),
+ ("High", Loc.T("Priorities.High")),
+ ("VeryHigh", Loc.T("Priorities.VeryHigh")),
+ ],
+ },
+ ],
+ };
+ ActionDef _setPriorityActionDef;
+
static void ResetWorkersAction(Workplace building) {
building.DesiredWorkers = 0;
building.UnassignWorkerIfOverstaffed();
@@ -95,6 +182,22 @@ static void SetWorkersAction(Workplace building, ScriptValue[] args) {
building.UnassignWorkerIfOverstaffed();
}
+ static void SetPriorityAction(AutomationBehavior behavior, ScriptValue[] args) {
+ AssertActionArgsCount(SetPriorityActionName, args, 1);
+ var priorityName = args[0].AsString;
+ if (!Enum.TryParse(priorityName, out var priority)) {
+ throw new ScriptError.ValueOutOfRange($"Unknown priority: {priorityName}");
+ }
+ var workplacePriority = behavior.GetComponentFast();
+ if (!workplacePriority) {
+ throw new ScriptError.BadStateError(behavior, "Building doesn't have WorkplacePriority");
+ }
+ if (workplacePriority.Priority == priority) {
+ return;
+ }
+ workplacePriority.SetPriority(priority);
+ }
+
#endregion
#region Implementation
@@ -108,4 +211,21 @@ static Workplace GetWorkplace(BaseComponent building, bool throwIfNotFound = tru
}
#endregion
+
+ #region Workplace change tracker
+
+ sealed class WorkplaceChangeTracker : AbstractStatusTracker {
+
+ void Start() {
+ var workplace = GetComponentFast();
+ workplace.WorkerAssigned += OnWorkerChanged;
+ workplace.WorkerUnassigned += OnWorkerChanged;
+ }
+
+ void OnWorkerChanged(object sender, WorkerChangedEventArgs args) {
+ ScheduleSignal(AssignedWorkersSignalName, ignoreErrors: true);
+ }
+ }
+
+ #endregion
}
diff --git a/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/enUS.txt b/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/enUS.txt
index 622765f3..e0e37917 100644
--- a/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/enUS.txt
+++ b/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/enUS.txt
@@ -138,6 +138,8 @@ IgorZ.Automation.Scriptable.Debug.Signal.Ticker,"ticker","Name of the condition
IgorZ.Automation.Scriptable.District.Signal.Beavers,"beavers in district","Name of the condition for the current beaver population checking"
IgorZ.Automation.Scriptable.District.Signal.Bots,"bots in district","Name of the condition for the current bot population checking"
IgorZ.Automation.Scriptable.District.Signal.NumberOfBeds,"beds in district","Name of the condition for the current number of beds checking"
+IgorZ.Automation.Scriptable.District.Signal.UnemployedBeavers,"unemployed beavers in district","Name of the condition for the current number of unemployed beaver workers"
+IgorZ.Automation.Scriptable.District.Signal.UnemployedBots,"unemployed bots in district","Name of the condition for the current number of unemployed bot workers"
IgorZ.Automation.Scriptable.Dynamite.Action.Detonate,"detonate dynamite","Name of the action that immediately detonates the dynamite"
IgorZ.Automation.Scriptable.Dynamite.Action.DetonateAndRepeat,"detonate dynamite and add another {0} times","Name of the action that detonates the dynamite and places new dynamites the specified number of times"
IgorZ.Automation.Scriptable.Floodgate.Action.SetHeight,"set gate height to {0}","Change the floodgate height to the specified value"
@@ -161,7 +163,9 @@ IgorZ.Automation.Scriptable.StreamGauge.Signal.Current,"current","Name of the co
IgorZ.Automation.Scriptable.StreamGauge.Signal.Depth,"depth","Name of the condition for the current water level checking"
IgorZ.Automation.Scriptable.Weather.Signal.Season,"season","Name of the condition for the current season checking"
IgorZ.Automation.Scriptable.Workplace.Action.RemoveWorkers,"remove workers","Name of the action that removes all workers from the workshop"
+IgorZ.Automation.Scriptable.Workplace.Action.SetPriority,"set worker priority to {0}","Name of the action that sets the workplace priority level"
IgorZ.Automation.Scriptable.Workplace.Action.SetWorkers,"assign {0} workers","Name of the action that sets the number of workers in the building"
+IgorZ.Automation.Scriptable.Workplace.Signal.AssignedWorkers,"assigned workers","Name of the condition for the current number of assigned workers"
IgorZ.Automation.Scripting.Expressions.AndOperator,"and","String to join components of the expression with condition AND. Used to represent the condition in the panel"
IgorZ.Automation.Scripting.Expressions.OrOperator,"or","String to join components of the expression with condition OR. Used to represent the condition in the panel"
IgorZ.Automation.Scripting.Expressions.NotOperator,"NOT","NOT operator name to represent the condition in the panel"
diff --git a/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/ruRU.txt b/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/ruRU.txt
index 341e4eba..32f84a25 100644
--- a/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/ruRU.txt
+++ b/ModsUnityProject/Assets/Mods/Automation/Data/Localizations/ruRU.txt
@@ -139,6 +139,8 @@ IgorZ.Automation.Scriptable.Debug.Signal.Ticker,"тикер","Name of the condit
IgorZ.Automation.Scriptable.District.Signal.Beavers,"бобров в районе","Name of the condition for the current beaver population checking"
IgorZ.Automation.Scriptable.District.Signal.Bots,"ботов в районе","Name of the condition for the current bot population checking"
IgorZ.Automation.Scriptable.District.Signal.NumberOfBeds,"постелей в районе","Name of the condition for the current number of beds checking"
+IgorZ.Automation.Scriptable.District.Signal.UnemployedBeavers,"безработных бобров в районе","Name of the condition for the current number of unemployed beaver workers"
+IgorZ.Automation.Scriptable.District.Signal.UnemployedBots,"безработных ботов в районе","Name of the condition for the current number of unemployed bot workers"
IgorZ.Automation.Scriptable.Dynamite.Action.Detonate,"активировать динамит","Name of the action that immediately detonates the dynamite"
IgorZ.Automation.Scriptable.Dynamite.Action.DetonateAndRepeat,"активировать динамит и повторить ещё {0} раза","Name of the action that detonates the dynamite and places new dynamites the specified number of times"
IgorZ.Automation.Scriptable.Floodgate.Action.SetHeight,"выставить высоту затвора в {0}","Change the floodgate height to the specified value"
@@ -162,7 +164,9 @@ IgorZ.Automation.Scriptable.StreamGauge.Signal.Current,"течение","Name of
IgorZ.Automation.Scriptable.StreamGauge.Signal.Depth,"глубина","Name of the condition for the current water level checking"
IgorZ.Automation.Scriptable.Weather.Signal.Season,"сезон","Name of the condition for the current season checking"
IgorZ.Automation.Scriptable.Workplace.Action.RemoveWorkers,"снять работников","Name of the action that removes all workers from the workshop"
+IgorZ.Automation.Scriptable.Workplace.Action.SetPriority,"установить приоритет работников на {0}","Name of the action that sets the workplace priority level"
IgorZ.Automation.Scriptable.Workplace.Action.SetWorkers,"назначить {0} работников","Name of the action that sets the number of workers in the workshop"
+IgorZ.Automation.Scriptable.Workplace.Signal.AssignedWorkers,"назначенные работники","Name of the condition for the current number of assigned workers"
IgorZ.Automation.Scripting.Expressions.AndOperator,"и","String to join components of the expression with condition AND. Used to represent the condition in the panel"
IgorZ.Automation.Scripting.Expressions.OrOperator,"или","String to join components of the expression with condition OR. Used to represent the condition in the panel"
IgorZ.Automation.Scripting.Expressions.NotOperator,"НЕ","NOT operator name to represent the condition in the panel"