• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyclopsMC / IntegratedDynamics / 19267223570

11 Nov 2025 01:30PM UTC coverage: 53.046% (+0.04%) from 53.006%
19267223570

push

github

rubensworks
Merge remote-tracking branch 'origin/master-1.21-lts' into master-1.21

2875 of 8772 branches covered (32.77%)

Branch coverage included in aggregate %.

17354 of 29363 relevant lines covered (59.1%)

3.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.66
/src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/Operators.java
1
package org.cyclops.integrateddynamics.core.evaluate.operator;
2

3
import com.google.common.collect.*;
4
import com.google.re2j.Matcher;
5
import com.google.re2j.Pattern;
6
import com.google.re2j.PatternSyntaxException;
7
import com.mojang.brigadier.exceptions.CommandSyntaxException;
8
import com.mojang.logging.LogUtils;
9
import lombok.Lombok;
10
import net.minecraft.ResourceLocationException;
11
import net.minecraft.core.component.DataComponentPatch;
12
import net.minecraft.core.component.DataComponentType;
13
import net.minecraft.core.component.DataComponents;
14
import net.minecraft.core.component.TypedDataComponent;
15
import net.minecraft.core.registries.BuiltInRegistries;
16
import net.minecraft.nbt.*;
17
import net.minecraft.network.chat.Component;
18
import net.minecraft.network.chat.MutableComponent;
19
import net.minecraft.resources.ResourceLocation;
20
import net.minecraft.util.ProblemReporter;
21
import net.minecraft.util.StringUtil;
22
import net.minecraft.world.entity.AgeableMob;
23
import net.minecraft.world.entity.Entity;
24
import net.minecraft.world.entity.LivingEntity;
25
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
26
import net.minecraft.world.entity.ai.attributes.Attributes;
27
import net.minecraft.world.entity.animal.Animal;
28
import net.minecraft.world.entity.decoration.ItemFrame;
29
import net.minecraft.world.entity.item.ItemEntity;
30
import net.minecraft.world.entity.monster.Enemy;
31
import net.minecraft.world.entity.player.Player;
32
import net.minecraft.world.entity.projectile.ProjectileUtil;
33
import net.minecraft.world.entity.vehicle.AbstractMinecart;
34
import net.minecraft.world.item.BlockItem;
35
import net.minecraft.world.item.Item;
36
import net.minecraft.world.item.ItemStack;
37
import net.minecraft.world.item.TooltipFlag;
38
import net.minecraft.world.item.crafting.RecipeType;
39
import net.minecraft.world.level.ClipContext;
40
import net.minecraft.world.level.block.Block;
41
import net.minecraft.world.level.block.SoundType;
42
import net.minecraft.world.level.block.state.BlockState;
43
import net.minecraft.world.level.block.state.properties.Property;
44
import net.minecraft.world.level.material.Fluid;
45
import net.minecraft.world.level.storage.TagValueOutput;
46
import net.minecraft.world.phys.*;
47
import net.neoforged.neoforge.capabilities.Capabilities;
48
import net.neoforged.neoforge.common.IShearable;
49
import net.neoforged.neoforge.common.SoundActions;
50
import net.neoforged.neoforge.energy.IEnergyStorage;
51
import net.neoforged.neoforge.event.EventHooks;
52
import net.neoforged.neoforge.fluids.FluidStack;
53
import net.neoforged.neoforge.items.IItemHandler;
54
import net.neoforged.neoforge.server.ServerLifecycleHooks;
55
import org.apache.commons.lang3.tuple.Pair;
56
import org.apache.commons.lang3.tuple.Triple;
57
import org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;
58
import org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;
59
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
60
import org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;
61
import org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;
62
import org.cyclops.commoncapabilities.api.ingredient.*;
63
import org.cyclops.cyclopscore.helper.IModHelpers;
64
import org.cyclops.cyclopscore.helper.IModHelpersNeoForge;
65
import org.cyclops.cyclopscore.nbt.path.INbtPathExpression;
66
import org.cyclops.cyclopscore.nbt.path.NbtParseException;
67
import org.cyclops.cyclopscore.nbt.path.NbtPath;
68
import org.cyclops.integrateddynamics.IntegratedDynamics;
69
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
70
import org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;
71
import org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorRegistry;
72
import org.cyclops.integrateddynamics.api.evaluate.variable.*;
73
import org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;
74
import org.cyclops.integrateddynamics.core.evaluate.IOperatorValuePropagator;
75
import org.cyclops.integrateddynamics.core.evaluate.OperatorBuilders;
76
import org.cyclops.integrateddynamics.core.evaluate.variable.*;
77
import org.cyclops.integrateddynamics.core.helper.Helpers;
78
import org.cyclops.integrateddynamics.core.helper.L10NValues;
79
import org.cyclops.integrateddynamics.core.helper.NbtHelpers;
80
import org.cyclops.integrateddynamics.core.ingredient.ExtendedIngredientsList;
81
import org.cyclops.integrateddynamics.core.ingredient.ExtendedIngredientsSingle;
82
import org.slf4j.Logger;
83

84
import java.util.*;
85
import java.util.stream.Collectors;
86

87
/**
88
 * Collection of available operators.
89
 *
90
 * @author rubensworks
91
 */
92
public final class Operators {
×
93

94
    private static final Logger LOGGER = LogUtils.getLogger();
2✔
95
    public static final IOperatorRegistry REGISTRY = constructRegistry();
2✔
96

97
    private static IOperatorRegistry constructRegistry() {
98
        // This also allows this registry to be used outside of a minecraft environment.
99
        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {
4✔
100
            return IntegratedDynamics._instance.getRegistryManager().getRegistry(IOperatorRegistry.class);
6✔
101
        } else {
102
            return OperatorRegistry.getInstance();
2✔
103
        }
104
    }
105

106
    public static void load() {}
1✔
107

108
    /**
109
     * ----------------------------------- LOGICAL OPERATORS -----------------------------------
110
     */
111

112
    /**
113
     * Short-circuit logical AND operator with two input booleans and one output boolean.
114
     */
115
    public static final IOperator LOGICAL_AND = REGISTRY.register(OperatorBuilders.LOGICAL_2.symbol("&&").operatorInteract("and")
9✔
116
            .function(variables -> {
1✔
117
                ValueTypeBoolean.ValueBoolean a = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
118
                if (!a.getRawValue()) {
3✔
119
                    return ValueTypeBoolean.ValueBoolean.of(false);
3✔
120
                } else {
121
                    return variables.getValue(1, ValueTypes.BOOLEAN);
5✔
122
                }
123
            }).build());
1✔
124

125
    /**
126
     * Short-circuit logical AND operator with two input booleans and one output boolean.
127
     */
128
    public static final IOperator LOGICAL_OR = REGISTRY.register(OperatorBuilders.LOGICAL_2.symbol("||").operatorInteract("or")
9✔
129
            .function(variables -> {
1✔
130
                ValueTypeBoolean.ValueBoolean a = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
131
                if (a.getRawValue()) {
3✔
132
                    return ValueTypeBoolean.ValueBoolean.of(true);
3✔
133
                } else {
134
                    return variables.getValue(1, ValueTypes.BOOLEAN);
5✔
135
                }
136
            }).build());
1✔
137

138
    /**
139
     * Logical NOT operator with one input booleans and one output boolean.
140
     */
141
    public static final IOperator LOGICAL_NOT = REGISTRY.register(OperatorBuilders.LOGICAL_1_PREFIX.symbol("!").operatorInteract("not")
9✔
142
            .function(variables -> {
1✔
143
                        ValueTypeBoolean.ValueBoolean valueBoolean = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
144
                        return ValueTypeBoolean.ValueBoolean.of(!valueBoolean.getRawValue());
8✔
145
                    }
146
            ).build());
1✔
147

148
    /**
149
     * Short-circuit logical NAND operator with two input booleans and one output boolean.
150
     */
151
    public static final IOperator LOGICAL_NAND = REGISTRY.register(
13✔
152
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LOGICAL_AND).build(
7✔
153
                    "!&&", "nand", "nand", IConfigRenderPattern.INFIX, "logical"));
154

155
    /**
156
     * Short-circuit logical NAND operator with two input booleans and one output boolean.
157
     */
158
    public static final IOperator LOGICAL_NOR = REGISTRY.register(
13✔
159
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LOGICAL_OR).build(
7✔
160
                    "!||", "nor", "nor", IConfigRenderPattern.INFIX, "logical"));
161

162
    /**
163
     * ----------------------------------- ARITHMETIC OPERATORS -----------------------------------
164
     */
165

166
    /**
167
     * Arithmetic ADD operator with two input numbers and one output number.
168
     */
169
    public static final IOperator ARITHMETIC_ADDITION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol("+").operatorName("addition").interactName("add")
11✔
170
            .function(
1✔
171
                variables -> ValueTypes.CATEGORY_NUMBER.add(variables.getVariables()[0], variables.getVariables()[1])
11✔
172
            ).build());
1✔
173

174
    /**
175
     * Arithmetic MINUS operator with two input numbers and one output number.
176
     */
177
    public static final IOperator ARITHMETIC_SUBTRACTION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol("-").operatorName("subtraction").interactName("subtract")
11✔
178
            .function(
1✔
179
                variables -> ValueTypes.CATEGORY_NUMBER.subtract(variables.getVariables()[0], variables.getVariables()[1])
11✔
180
            ).build());
1✔
181

182
    /**
183
     * Arithmetic MULTIPLY operator with two input numbers and one output number.
184
     */
185
    public static final IOperator ARITHMETIC_MULTIPLICATION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol("*").operatorName("multiplication").interactName("multiply")
11✔
186
            .function(
1✔
187
                variables -> ValueTypes.CATEGORY_NUMBER.multiply(variables.getVariables()[0], variables.getVariables()[1])
11✔
188
            ).build());
1✔
189

190
    /**
191
     * Arithmetic DIVIDE operator with two input numbers and one output number.
192
     */
193
    public static final IOperator ARITHMETIC_DIVISION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol("/").operatorName("division").interactName("divide")
11✔
194
            .function(
1✔
195
                variables -> ValueTypes.CATEGORY_NUMBER.divide(variables.getVariables()[0], variables.getVariables()[1])
11✔
196
            ).build());
1✔
197

198
    /**
199
     * Arithmetic MAX operator with two input numbers and one output number.
200
     */
201
    public static final IOperator ARITHMETIC_MAXIMUM = REGISTRY.register(OperatorBuilders.ARITHMETIC_2_PREFIX.symbol("max").operatorName("maximum").interactName("max")
11✔
202
            .function(
1✔
203
                variables -> ValueTypes.CATEGORY_NUMBER.max(variables.getVariables()[0], variables.getVariables()[1])
11✔
204
            ).build());
1✔
205

206
    /**
207
     * Arithmetic MIN operator with two input numbers and one output number.
208
     */
209
    public static final IOperator ARITHMETIC_MINIMUM = REGISTRY.register(OperatorBuilders.ARITHMETIC_2_PREFIX.symbol("min").operatorName("minimum").interactName("min")
11✔
210
            .function(
1✔
211
                variables -> ValueTypes.CATEGORY_NUMBER.min(variables.getVariables()[0], variables.getVariables()[1])
11✔
212
            ).build());
1✔
213

214
    /**
215
     * Arithmetic INCREMENT operator with one input numbers and one output number.
216
     */
217
    public static final IOperator ARITHMETIC_INCREMENT = REGISTRY.register(OperatorBuilders.ARITHMETIC_1_SUFFIX.symbol("++").operatorInteract("increment")
9✔
218
            .function(
1✔
219
                variables -> ValueTypes.CATEGORY_NUMBER.increment(variables.getVariables()[0])
7✔
220
            ).build());
1✔
221

222
    /**
223
     * Arithmetic DECREMENT operator with one input numbers and one output number.
224
     */
225
    public static final IOperator ARITHMETIC_DECREMENT = REGISTRY.register(OperatorBuilders.ARITHMETIC_1_SUFFIX.symbol("--").operatorInteract("decrement")
9✔
226
            .function(
1✔
227
                variables -> ValueTypes.CATEGORY_NUMBER.decrement(variables.getVariables()[0])
7✔
228
            ).build());
1✔
229

230
    /**
231
     * Arithmetic MODULO operator with two input numbers and one output number.
232
     */
233
    public static final IOperator ARITHMETIC_MODULUS = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol("%").operatorInteract("modulus")
9✔
234
            .function(
1✔
235
                variables -> ValueTypes.CATEGORY_NUMBER.modulus(variables.getVariables()[0], variables.getVariables()[1])
11✔
236
            ).build());
1✔
237

238
    /**
239
     * ----------------------------------- INTEGER OPERATORS -----------------------------------
240
     */
241

242
     private static final ValueTypeInteger.ValueInteger ZERO = ValueTypeInteger.ValueInteger.of(0);
3✔
243

244
    /**
245
     * ----------------------------------- DOUBLE OPERATORS -----------------------------------
246
     */
247

248
    /**
249
     * Integer SQRT operator with two input numbers and one output number.
250
     */
251
    public static final IOperator DOUBLE_SQRT = REGISTRY.register(OperatorBuilders.DOUBLE_1_PREFIX.symbolOperatorInteract("sqrt")
7✔
252
            .function(variables -> {
1✔
253
                ValueTypeDouble.ValueDouble a = variables.getValue(0, ValueTypes.DOUBLE);
6✔
254
                return ValueTypeDouble.ValueDouble.of(Math.sqrt(a.getRawValue()));
5✔
255
            }).build());
1✔
256

257
    /**
258
     * Integer POW operator with two input numbers and one output number.
259
     */
260
    public static final IOperator DOUBLE_POW = REGISTRY.register(OperatorBuilders.DOUBLE_2.symbolOperatorInteract("pow")
7✔
261
            .function(variables -> {
1✔
262
                ValueTypeDouble.ValueDouble a = variables.getValue(0, ValueTypes.DOUBLE);
6✔
263
                ValueTypeDouble.ValueDouble b = variables.getValue(1, ValueTypes.DOUBLE);
6✔
264
                return ValueTypeDouble.ValueDouble.of(Math.pow(a.getRawValue(), b.getRawValue()));
7✔
265
            }).build());
1✔
266

267
    /**
268
     * ----------------------------------- RELATIONAL OPERATORS -----------------------------------
269
     */
270

271
    /**
272
     * Relational == operator with two inputs of any type (but equal) and one output boolean.
273
     */
274
    public static final IOperator RELATIONAL_EQUALS = REGISTRY.register(OperatorBuilders.RELATIONAL
6✔
275
            .inputTypes(2, ValueTypes.CATEGORY_ANY).renderPattern(IConfigRenderPattern.INFIX)
4✔
276
            .symbol("==").operatorInteract("equals")
4✔
277
            .function(
2✔
278
                variables -> ValueTypeBoolean.ValueBoolean.of(variables.getValue(0).equals(variables.getValue(1)))
9✔
279
            )
280
            .typeValidator((operator, input) -> {
1✔
281
                // Input size checking
282
                int requiredInputLength = operator.getRequiredInputLength();
3✔
283
                if(input.length != requiredInputLength) {
4✔
284
                    return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,
8✔
285
                            operator.getOperatorName(), input.length, requiredInputLength);
13✔
286
                }
287
                // Input types checking
288
                IValueType temporarySecondInputType = null;
2✔
289
                for(int i = 0; i < requiredInputLength; i++) {
7✔
290
                    IValueType inputType = input[i];
4✔
291
                    if (inputType instanceof IValueTypeNumber) {
3✔
292
                        inputType = ValueTypes.CATEGORY_NUMBER;
2✔
293
                    }
294
                    if(inputType == null) {
2!
295
                        return Component.translatable(L10NValues.OPERATOR_ERROR_NULLTYPE, operator.getOperatorName(), Integer.toString(i));
×
296
                    }
297
                    if(i == 0) {
2✔
298
                        temporarySecondInputType = inputType;
3✔
299
                    } else if(i == 1) {
3!
300
                        if(!ValueHelpers.correspondsTo(temporarySecondInputType, inputType)) {
4✔
301
                            return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPE,
8✔
302
                                    operator.getOperatorName(), Component.translatable(inputType.getTranslationKey()),
11✔
303
                                    Integer.toString(i), Component.translatable(temporarySecondInputType.getTranslationKey()));
8✔
304
                        }
305
                    }
306
                }
307
                return null;
2✔
308
            })
309
            .build());
1✔
310

311
    /**
312
     * Relational &gt; operator with two input integers and one output boolean.
313
     */
314
    public static final IOperator RELATIONAL_GT = REGISTRY.register(OperatorBuilders.RELATIONAL_2
6✔
315
            .inputTypes(2, ValueTypes.CATEGORY_NUMBER).symbol(">").operatorName("gt").interactName("greaterThan")
8✔
316
            .function(
1✔
317
                variables -> ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NUMBER.greaterThan(variables.getVariables()[0], variables.getVariables()[1]))
12✔
318
            ).build());
1✔
319

320
    /**
321
     * Relational &gt; operator with two input integers and one output boolean.
322
     */
323
    public static final IOperator RELATIONAL_LT = REGISTRY.register(OperatorBuilders.RELATIONAL_2
6✔
324
            .inputTypes(2, ValueTypes.CATEGORY_NUMBER).symbol("<").operatorName("lt").interactName("lessThan")
8✔
325
            .function(
1✔
326
                variables -> ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NUMBER.lessThan(variables.getVariables()[0], variables.getVariables()[1]))
12✔
327
            ).build());
1✔
328

329
    /**
330
     * Relational != operator with two inputs of any type (but equal) and one output boolean.
331
     */
332
    public static final IOperator RELATIONAL_NOTEQUALS = REGISTRY.register(
13✔
333
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(RELATIONAL_EQUALS).build(
7✔
334
                    "!=", "notequals", "notEquals", IConfigRenderPattern.INFIX, "relational"));
335

336
    /**
337
     * Relational &gt;= operator with two inputs of any type (but equal) and one output boolean.
338
     */
339
    public static final IOperator RELATIONAL_GE = REGISTRY.register(
17✔
340
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_OR).apply(RELATIONAL_EQUALS, RELATIONAL_GT).build(
7✔
341
                    ">=", "ge", "greaterThanOrEquals", IConfigRenderPattern.INFIX, "relational"));
342

343
    /**
344
     * Relational &lt;= operator with two inputs of any type (but equal) and one output boolean.
345
     */
346
    public static final IOperator RELATIONAL_LE = REGISTRY.register(
17✔
347
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_OR).apply(RELATIONAL_EQUALS, RELATIONAL_LT).build(
7✔
348
                    "<=", "le", "lessThanOrEquals", IConfigRenderPattern.INFIX, "relational"));
349

350
    /**
351
     * ----------------------------------- BINARY OPERATORS -----------------------------------
352
     */
353

354
    /**
355
     * Binary AND operator with two input integers and one output integers.
356
     */
357
    public static final IOperator BINARY_AND = REGISTRY.register(OperatorBuilders.BINARY_2.symbol("&").operatorName("and").interactName("binaryAnd")
11✔
358
            .function(variables -> {
1✔
359
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
360
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
361
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() & b.getRawValue());
7✔
362
            }).build());
1✔
363

364
    /**
365
     * Binary OR operator with two input integers and one output integers.
366
     */
367
    public static final IOperator BINARY_OR = REGISTRY.register(OperatorBuilders.BINARY_2.symbol("|").operatorName("or").interactName("binaryOr")
11✔
368
            .function(variables -> {
1✔
369
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
370
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
371
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() | b.getRawValue());
7✔
372
            }).build());
1✔
373

374
    /**
375
     * Binary XOR operator with two input integers and one output integers.
376
     */
377
    public static final IOperator BINARY_XOR = REGISTRY.register(OperatorBuilders.BINARY_2.symbol("^").operatorInteract("xor")
9✔
378
            .function(variables -> {
1✔
379
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
380
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
381
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() ^ b.getRawValue());
7✔
382
            }).build());
1✔
383

384
    /**
385
     * Binary COMPLEMENT operator with one input integers and one output integers.
386
     */
387
    public static final IOperator BINARY_COMPLEMENT = REGISTRY.register(OperatorBuilders.BINARY_1_PREFIX.symbol("~").operatorInteract("complement")
9✔
388
            .function(variables -> {
1✔
389
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
390
                return ValueTypeInteger.ValueInteger.of(~a.getRawValue());
6✔
391
            }).build());
1✔
392

393
    /**
394
     * Binary &lt;&lt; operator with two input integers and one output integers.
395
     */
396
    public static final IOperator BINARY_LSHIFT = REGISTRY.register(OperatorBuilders.BINARY_2.symbol("<<").operatorName("lshift").interactName("leftShift")
11✔
397
            .function(variables -> {
1✔
398
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
399
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
400
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() << b.getRawValue());
7✔
401
            }).build());
1✔
402

403
    /**
404
     * Binary &gt;&gt; operator with two input integers and one output integers.
405
     */
406
    public static final IOperator BINARY_RSHIFT = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(">>").operatorName("rshift").interactName("rightShift")
11✔
407
            .function(variables -> {
1✔
408
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
409
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
410
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() >> b.getRawValue());
7✔
411
            }).build());
1✔
412

413
    /**
414
     * Binary &gt;&gt;&gt; operator with two input integers and one output integers.
415
     */
416
    public static final IOperator BINARY_RZSHIFT = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(">>>").operatorName("rzshift").interactName("unsignedRightShift")
11✔
417
            .function(variables -> {
1✔
418
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
419
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
420
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() >>> b.getRawValue());
7✔
421
            }).build());
1✔
422

423
    /**
424
     * ----------------------------------- STRING OPERATORS -----------------------------------
425
     */
426

427
    /**
428
     * String length operator with one input string and one output integer.
429
     */
430
    public static final IOperator STRING_LENGTH = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX.symbol("len").operatorInteract("length")
9✔
431
            .output(ValueTypes.INTEGER).function(variables -> {
3✔
432
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
433
                return ValueTypeInteger.ValueInteger.of(a.getRawValue().length());
5✔
434
            }).build());
1✔
435

436
    /**
437
     * String concat operator with two input strings and one output string.
438
     */
439
    public static final IOperator STRING_CONCAT = REGISTRY.register(OperatorBuilders.STRING_2.symbol("+").operatorInteract("concat")
9✔
440
            .function(variables -> {
1✔
441
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
442
                ValueTypeString.ValueString b = variables.getValue(1, ValueTypes.STRING);
6✔
443
                return ValueTypeString.ValueString.of(a.getRawValue() + b.getRawValue());
7✔
444
            }).build());
1✔
445

446
    /**
447
     * String contains operator which checks whether a given (literal) string is contained in the given string.
448
     */
449
    public static final IOperator STRING_CONTAINS = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperatorInteract("contains")
7✔
450
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
451
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
452
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
453
                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().contains(search.getRawValue()));
7✔
454
            }).build());
1✔
455

456
    /**
457
     * String match operator which checks whether a given regular expression is contained within a string.
458
     */
459
    public static final IOperator STRING_CONTAINS_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("contains_regex").interactName("containsRegex")
9✔
460
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
461
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
462
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
463
                try {
464
                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
465
                    return ValueTypeBoolean.ValueBoolean.of(m.find());
4✔
466
                } catch (PatternSyntaxException e) {
1✔
467
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
468
                            pattern.getRawValue()));
2✔
469
                }
470
            }).build());
1✔
471

472
    /**
473
     * String match operator which checks whether a given regular expression matches a string.
474
     */
475
    public static final IOperator STRING_MATCHES_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("matches_regex").interactName("matchesRegex")
9✔
476
            .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
477
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
478
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
479
                try {
480
                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
481
                    return ValueTypeBoolean.ValueBoolean.of(m.matches());
4✔
482
                } catch (PatternSyntaxException e) {
1✔
483
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
484
                            pattern.getRawValue()));
2✔
485
                }
486
            }).build());
1✔
487

488
    /**
489
     * String operator which returns the integral index of the first position where the search string appears in the given string.
490
     */
491
    public static final IOperator STRING_INDEX_OF = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("index_of").interactName("indexOf")
9✔
492
        .output(ValueTypes.INTEGER).function(variables -> {
3✔
493
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
494
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
495
                return ValueTypeInteger.ValueInteger.of(str.getRawValue().indexOf(search.getRawValue()));
7✔
496
            }).build());
1✔
497

498
    /**
499
     * String operator which returns the integral index where the a substring matching the regular expression appears in the given string.
500
     */
501
    public static final IOperator STRING_INDEX_OF_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("index_of_regex").interactName("indexOfRegex")
9✔
502
        .output(ValueTypes.INTEGER).function(variables -> {
3✔
503
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
504
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
505
                try {
506
                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
507
                    if (m.find()) {
3✔
508
                        return ValueTypeInteger.ValueInteger.of(m.start());
4✔
509
                    } else {
510
                        return ValueTypeInteger.ValueInteger.of(-1);
3✔
511
                    }
512
                } catch (PatternSyntaxException e) {
1✔
513
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
514
                            pattern.getRawValue()));
2✔
515
                }
516
            }).build());
1✔
517

518
    /**
519
     * String match operator which checks whether a given string matches the beginning of the given string.
520
     */
521
    public static final IOperator STRING_STARTS_WITH = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator("starts_with").interactName("startsWith")
9✔
522
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
523
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
524
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
525
                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().startsWith(search.getRawValue()));
7✔
526
            }).build());
1✔
527

528
    /**
529
     * String match operator which checks whether a given string matches the end of the given string.
530
     */
531
    public static final IOperator STRING_ENDS_WITH = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator("ends_with").interactName("endsWith")
9✔
532
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
533
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
534
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
535
                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().endsWith(search.getRawValue()));
7✔
536
            }).build());
1✔
537

538
    /**
539
     * String operator which splits on the given (literal) delimiter the input string .
540
     */
541
    public static final IOperator STRING_SPLIT_ON = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator("split_on").interactName("splitOn")
9✔
542
        .output(ValueTypes.LIST).function(variables -> {
3✔
543
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
544
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
545
                List<String> pieces = Arrays.asList(str.getRawValue().split(java.util.regex.Pattern.quote(search.getRawValue())));
8✔
546
                List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
547
                for (String piece : pieces) {
10✔
548
                    values.add(ValueTypeString.ValueString.of(piece));
5✔
549
                }
1✔
550
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
551
            }).build());
1✔
552

553
    /**
554
     * String operator which splits on the given (regular expression) delimiter the input string.
555
     */
556
    public static final IOperator STRING_SPLIT_ON_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("split_on_regex").interactName("splitOnRegex")
9✔
557
        .output(ValueTypes.LIST).function(variables -> {
3✔
558
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
559
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
560
                try {
561
                    List<String> pieces = Arrays.asList(Pattern.compile(pattern.getRawValue()).split(str.getRawValue()));
8✔
562
                    List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
563
                    for (String piece : pieces) {
10✔
564
                        values.add(ValueTypeString.ValueString.of(piece));
5✔
565
                    }
1✔
566
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
567
                } catch (PatternSyntaxException e) {
1✔
568
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
569
                            pattern.getRawValue()));
2✔
570
                }
571
            }).build());
1✔
572

573
    /**
574
     * String operator which takes the substring of the given string between the two integer indices.
575
     */
576
    public static final IOperator STRING_SUBSTRING = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract("substring")
7✔
577
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
15✔
578
        .inputTypes(ValueTypes.INTEGER, ValueTypes.INTEGER, ValueTypes.STRING)
2✔
579
        .output(ValueTypes.STRING)
2✔
580
        .function(variables -> {
1✔
581
            ValueTypeInteger.ValueInteger from = variables.getValue(0, ValueTypes.INTEGER);
6✔
582
            ValueTypeInteger.ValueInteger to = variables.getValue(1, ValueTypes.INTEGER);
6✔
583
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
584
            if (from.getRawValue() > to.getRawValue()) {
5✔
585
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_TOGREATERTHANFROM));
6✔
586
            }
587
            if (from.getRawValue() < 0 || to.getRawValue() < 0) {
6!
588
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_INDEXNEGATIVE));
6✔
589
            }
590
            int stringLength = str.getRawValue().length();
4✔
591
            if (from.getRawValue() > stringLength || to.getRawValue() > stringLength) {
8!
592
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_LONGERTHANSTRING));
6✔
593
            }
594
            return ValueTypeString.ValueString.of(str.getRawValue().substring(from.getRawValue(), to.getRawValue()));
9✔
595
        }).build());
1✔
596

597

598
    /**
599
     * String operator which matches against a regex and takes the group at the index of the integer given (including zero), in the input string. It is invalid for the pattern to not match.
600
     */
601
    public static final IOperator STRING_REGEX_GROUP = REGISTRY.register(OperatorBuilders.STRING.symbolOperator("regex_group").interactName("regexGroup")
9✔
602
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
15✔
603
        .inputTypes(ValueTypes.STRING, ValueTypes.INTEGER, ValueTypes.STRING)
2✔
604
        .output(ValueTypes.STRING)
2✔
605
        .function(variables -> {
1✔
606
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
607
            ValueTypeInteger.ValueInteger group = variables.getValue(1, ValueTypes.INTEGER);
6✔
608
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
609
            if (group.getRawValue() < 0) {
3✔
610
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_INDEXNEGATIVE));
6✔
611
            }
612
            try {
613
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
614
                if (m.find()) {
3!
615
                    String result = m.group(group.getRawValue());
5✔
616
                    return ValueTypeString.ValueString.of(result == null ? "" : result);
5!
617
                } else {
618
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCH,
×
619
                            str.getRawValue(), pattern.getRawValue()));
×
620
                }
621
            } catch (PatternSyntaxException e) {
1✔
622
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
623
                        pattern.getRawValue()));
2✔
624
            } catch (IndexOutOfBoundsException e) {
1✔
625
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCHGROUP,
11✔
626
                        str.getRawValue(), pattern.getRawValue(), group.getRawValue()));
13✔
627
            }
628
        }).build()
1✔
629
    );
630

631
    /**
632
     * String operator which matches against a regex the input string and returns a list containing all groups matched (including zero). An empty list is returned if the regex does not match.
633
     */
634
    public static final IOperator STRING_REGEX_GROUPS = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("regex_groups").interactName("regexGroups")
9✔
635
        .output(ValueTypes.LIST)
2✔
636
        .function(variables -> {
1✔
637
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
638
            ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
639
            try {
640
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
641
                if (m.find()) {
3✔
642
                    List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
643
                    for (int i = 0; i <= m.groupCount(); i++) {
8✔
644
                        String result = m.group(i);
4✔
645
                        values.add(ValueTypeString.ValueString.of(result == null ? "" : result));
7!
646
                    }
647
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
648
                } else {
649
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, Collections.<ValueTypeString.ValueString>emptyList());
4✔
650
                }
651
            } catch (PatternSyntaxException e) {
1✔
652
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
653
                        pattern.getRawValue()));
2✔
654
            }
655
        }).build()
1✔
656
    );
657

658
    /**
659
     * String operator which finds all matches of the regular expression in the given string and returns the given group for each match.
660
     */
661
    public static final IOperator STRING_REGEX_SCAN = REGISTRY.register(OperatorBuilders.STRING.symbolOperator("regex_scan").interactName("regexScan")
9✔
662
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
15✔
663
        .inputTypes(ValueTypes.STRING, ValueTypes.INTEGER, ValueTypes.STRING)
2✔
664
        .output(ValueTypes.LIST)
2✔
665
        .function(variables -> {
1✔
666
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
667
            ValueTypeInteger.ValueInteger group = variables.getValue(1, ValueTypes.INTEGER);
6✔
668
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
669
            if (group.getRawValue() < 0) {
3!
670
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEXSCAN_INDEXNEGATIVE));
×
671
            }
672
            try {
673
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
674
                List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
675
                while (m.find()) {
3✔
676
                    String match = m.group(group.getRawValue());
5✔
677
                    if (match != null) {
2✔
678
                        values.add(ValueTypeString.ValueString.of(match));
5✔
679
                    }
680
                }
1✔
681
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
682
            } catch (PatternSyntaxException e) {
1✔
683
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
684
                        pattern.getRawValue()));
2✔
685
            } catch (IndexOutOfBoundsException e) {
×
686
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEXSCAN_NOMATCHGROUP,
×
687
                        str.getRawValue(), pattern.getRawValue(), group.getRawValue()));
×
688
            }
689
        }).build()
1✔
690
    );
691

692
    /**
693
     * String operator which, finds all the matches of the (literal) search and replaces them with the given replacement, in the input string.
694
     */
695
    public static final IOperator STRING_REPLACE = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract("replace")
7✔
696
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
3✔
697
        .inputTypes(3, ValueTypes.STRING)
2✔
698
        .output(ValueTypes.STRING)
2✔
699
        .function(variables -> {
1✔
700
            ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
701
            ValueTypeString.ValueString replacement = variables.getValue(1, ValueTypes.STRING);
6✔
702
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
703
            return ValueTypeString.ValueString.of(str.getRawValue().replaceAll(java.util.regex.Pattern.quote(search.getRawValue()), java.util.regex.Matcher.quoteReplacement(replacement.getRawValue())));
11✔
704
        }).build()
1✔
705
    );
706

707
    /**
708
     * String operator which, finds all the matches of the regular expression pattern and replaces them with the given replacement, in the input string.
709
     */
710
    public static final IOperator STRING_REPLACE_REGEX = REGISTRY.register(OperatorBuilders.STRING.symbolOperator("replace_regex").interactName("replaceRegex")
9✔
711
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
3✔
712
        .inputTypes(3, ValueTypes.STRING)
2✔
713
        .output(ValueTypes.STRING)
2✔
714
        .function(variables -> {
1✔
715
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
716
            ValueTypeString.ValueString replacement = variables.getValue(1, ValueTypes.STRING);
6✔
717
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
718
            try {
719
                return ValueTypeString.ValueString.of(Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue()).replaceAll(replacement.getRawValue()));
11✔
720
            } catch (PatternSyntaxException e) {
×
721
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
×
722
                        pattern.getRawValue()));
×
723
            } catch (IndexOutOfBoundsException e) {
×
724
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REPLACEREGEX_INVALIDGROUP,
×
725
                        replacement.getRawValue(), e.getMessage()));
×
726
            }
727
        }).build()
1✔
728
    );
729

730
    /**
731
     * String operator to join a list using a string delimiter
732
     */
733
    public static final IOperator STRING_JOIN = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract("join")
7✔
734
            .renderPattern(IConfigRenderPattern.PREFIX_2)
11✔
735
            .inputTypes(ValueTypes.STRING, ValueTypes.LIST)
2✔
736
            .output(ValueTypes.STRING)
4✔
737
            .function(new OperatorBase.IFunction() {
4✔
738
                @Override
739
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
740
                    // Prepare values
741
                    ValueTypeString.ValueString delimiter = variables.getValue(0, ValueTypes.STRING);
6✔
742
                    ValueTypeList.ValueList<?, ?> elements = variables.getValue(1, ValueTypes.LIST);
6✔
743
                    if (!ValueHelpers.correspondsTo(elements.getRawValue().getValueType(), ValueTypes.STRING)) {
6✔
744
                        throw new EvaluationException(Component.translatable(
11✔
745
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
746
                                Component.translatable(elements.getRawValue().getValueType().getTranslationKey()),
8✔
747
                                Component.translatable(ValueTypes.STRING.getTranslationKey())));
3✔
748
                    }
749

750
                    // Don't allow joining on an infinite list
751
                    if (elements.getRawValue().isInfinite()) {
4✔
752
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
11✔
753
                                STRING_JOIN.getLocalizedNameFull()));
2✔
754
                    }
755

756
                    // Join in O(n), while type-checking each element, as the list may have been of ANY type.
757
                    StringBuilder sb = new StringBuilder();
4✔
758
                    for (IValue value : elements.getRawValue()) {
11✔
759
                        if (value.getType() != ValueTypes.STRING) {
4✔
760
                            throw new EvaluationException(Component.translatable(
11✔
761
                                    L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
762
                                    Component.translatable(value.getType().getTranslationKey()),
7✔
763
                                    Component.translatable(ValueTypes.STRING.getTranslationKey())));
3✔
764
                        }
765
                        if (sb.length() > 0) {
3✔
766
                            sb.append(delimiter.getRawValue());
5✔
767
                        }
768
                        sb.append(((ValueTypeString.ValueString) value).getRawValue());
6✔
769
                    }
1✔
770

771
                    return ValueTypeString.ValueString.of(sb.toString());
4✔
772
                }
773
            }).build()
1✔
774
    );
775

776
    /**
777
     * Get a name value type name.
778
     */
779
    public static final IOperator NAMED_NAME = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperatorInteract("name")
7✔
780
            .inputType(ValueTypes.CATEGORY_NAMED).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
781
            .function(
1✔
782
                variables -> ValueTypeString.ValueString.of(ValueTypes.CATEGORY_NAMED.getName(variables.getVariables()[0]))
8✔
783
            ).build());
1✔
784

785
    /**
786
     * Get a unique name value type name.
787
     */
788
    public static final IOperator UNIQUELYNAMED_UNIQUENAME = REGISTRY.register(OperatorBuilders.STRING_2.symbol("uname").operatorName("unique_name").interactName("uniqueName")
11✔
789
            .inputType(ValueTypes.CATEGORY_UNIQUELY_NAMED).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
790
            .function(
1✔
791
                variables -> ValueTypeString.ValueString.of(ValueTypes.CATEGORY_UNIQUELY_NAMED.getUniqueName(variables.getVariables()[0]))
×
792
            ).build());
1✔
793

794
    /**
795
     * Throw a custom error
796
     */
797
    public static final IOperator STRING_ERROR  = REGISTRY.register(OperatorBuilders.STRING_2.symbol("error").operatorName("string_error").interactName("stringError")
11✔
798
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
799
            .function(
1✔
800
                (variables) -> {
801
                    throw new EvaluationException(Component.translatable(variables.getValue(0, ValueTypes.STRING).getRawValue()));
11✔
802
                }
803
            ).build());
1✔
804

805
    /**
806
     * ----------------------------------- NUMBER OPERATORS -----------------------------------
807
     */
808

809
    /**
810
     * Number round operator with one input double and one output integers.
811
     */
812
    public static final IOperator NUMBER_ROUND = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
813
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol("|| ||").operatorInteract("round")
8✔
814
            .function(
1✔
815
                variables -> ValueTypes.CATEGORY_NUMBER.round(variables.getVariables()[0])
7✔
816
            ).build());
1✔
817

818
    /**
819
     * Number ceil operator with one input double and one output integers.
820
     */
821
    public static final IOperator NUMBER_CEIL = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
822
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol("⌈ ⌉").operatorInteract("ceil")
8✔
823
            .function(
1✔
824
                variables -> ValueTypes.CATEGORY_NUMBER.ceil(variables.getVariables()[0])
7✔
825
            ).build());
1✔
826

827
    /**
828
     * Number floor operator with one input double and one output integers.
829
     */
830
    public static final IOperator NUMBER_FLOOR = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
831
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol("⌊ ⌋").operatorInteract("floor")
8✔
832
            .function(
1✔
833
                variables -> ValueTypes.CATEGORY_NUMBER.floor(variables.getVariables()[0])
7✔
834
            ).build());
1✔
835

836
    /**
837
     * Accepts a number, and returns a string roughly representing that number
838
     */
839
    public static final IOperator NUMBER_COMPACT = REGISTRY.register(OperatorBuilders.NUMBER_1_LONG
5✔
840
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.STRING).symbolOperatorInteract("compact")
6✔
841
            .function(
1✔
842
                variables -> ValueTypes.CATEGORY_NUMBER.compact(variables.getVariables()[0])
7✔
843
            ).build());
1✔
844

845
    /**
846
     * ----------------------------------- NULLABLE OPERATORS -----------------------------------
847
     */
848

849
    /**
850
     * Check if something is null
851
     */
852
    public static final IOperator NULLABLE_ISNULL = REGISTRY.register(OperatorBuilders.NULLABLE_1_PREFIX.symbol("o").operatorName("isnull").interactName("isNull")
11✔
853
            .inputType(ValueTypes.CATEGORY_ANY).output(ValueTypes.BOOLEAN).function(variables -> {
5✔
854
                if(ValueHelpers.correspondsTo(variables.getVariables()[0].getType(), ValueTypes.CATEGORY_NULLABLE)) {
8!
855
                    return ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NULLABLE.isNull(variables.getVariables()[0]));
×
856
                }
857
                return ValueTypeBoolean.ValueBoolean.of(false);
3✔
858
            }).build());
1✔
859

860
    /**
861
     * Check if something is not null
862
     */
863
    public static final IOperator NULLABLE_ISNOTNULL = REGISTRY.register(new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT)
13✔
864
            .apply(NULLABLE_ISNULL).build("∅", "isnotnull", "isNotNull", IConfigRenderPattern.PREFIX_1, "general"));
7✔
865

866
    /**
867
     * ----------------------------------- LIST OPERATORS -----------------------------------
868
     */
869

870
    /**
871
     * List operator with one input list and one output integer
872
     */
873
    public static final IOperator LIST_LENGTH = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.INTEGER).symbol("| |").operatorInteract("length")
11✔
874
            .function(variables -> {
1✔
875
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
876
                IValueTypeListProxy a = valueList.getRawValue();
3✔
877
                return ValueTypeInteger.ValueInteger.of(a.getLength());
4✔
878
            }).build());
1✔
879

880
    /**
881
     * Check if a list is empty
882
     */
883
    public static final IOperator LIST_EMPTY = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.BOOLEAN).symbol("∅").operatorName("empty").interactName("isEmpty")
13✔
884
            .function(variables -> {
1✔
885
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
886
                IValueTypeListProxy a = valueList.getRawValue();
3✔
887
                return ValueTypeBoolean.ValueBoolean.of(a.getLength() == 0);
8✔
888
            }).build());
1✔
889

890
    /**
891
     * Check if a list is not empty
892
     */
893
    public static final IOperator LIST_NOT_EMPTY = REGISTRY.register(
13✔
894
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LIST_EMPTY).build(
7✔
895
                    "o", "notempty", "isNotEmpty", IConfigRenderPattern.PREFIX_1, "list"));
896

897
    /**
898
     * List operator with one input list and one output integer
899
     */
900
    public static final IOperator LIST_ELEMENT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
14✔
901
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER}).output(ValueTypes.CATEGORY_ANY)
4✔
902
            .renderPattern(IConfigRenderPattern.INFIX).symbolOperatorInteract("get")
4✔
903
            .function(variables -> {
2✔
904
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
905
                IValueTypeListProxy a = valueList.getRawValue();
3✔
906
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
907
                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {
8!
908
                    return a.get(b.getRawValue());
5✔
909
                } else {
910
                    throw new EvaluationException(Component.translatable(
11✔
911
                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, b.getRawValue(), a.getLength()));
9✔
912
                }
913
            }).conditionalOutputTypeDeriver((operator, input) -> {
1✔
914
                try {
915
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();
×
916
                    return a.getValueType();
×
917
                } catch (ClassCastException | EvaluationException e) {
×
918
                    return operator.getOutputType();
×
919
                }
920
            }).build());
1✔
921

922
    /**
923
     * List operator with one input list, one output integer, and one default value
924
     */
925
    public static final IOperator LIST_ELEMENT_DEFAULT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
18✔
926
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.CATEGORY_ANY}).output(ValueTypes.CATEGORY_ANY)
4✔
927
            .renderPattern(IConfigRenderPattern.INFIX_2_LONG).symbolOperator("get_or_default").interactName("getOrDefault")
6✔
928
            .function(variables -> {
2✔
929
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
930
                IValueTypeListProxy a = valueList.getRawValue();
3✔
931
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
932
                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {
8✔
933
                    return a.get(b.getRawValue());
5✔
934
                } else {
935
                    if (!ValueHelpers.correspondsTo(a.getValueType(), variables.getVariables()[2].getType())) {
9!
936
                        throw new EvaluationException(Component.translatable(
×
937
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
938
                                Component.translatable(a.getValueType().getTranslationKey()),
×
939
                                Component.translatable(variables.getVariables()[2].getType().getTranslationKey())));
×
940
                    }
941
                    return variables.getValue(2);
4✔
942
                }
943
            }).conditionalOutputTypeDeriver(
1✔
944
                (operator, input) -> input[2].getType()
×
945
            ).build());
1✔
946

947
    /**
948
     * List contains operator that takes a list, a list element to look for and returns a boolean.
949
     */
950
    public static final IOperator LIST_CONTAINS = REGISTRY.register(OperatorBuilders.LIST
14✔
951
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
952
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
953
            .output(ValueTypes.BOOLEAN).symbolOperatorInteract("contains")
4✔
954
            .function(variables -> {
1✔
955
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
956
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
957
                IValue input = variables.getValue(1);
4✔
958
                for (IValue value : list) {
10✔
959
                    if (value.equals(input)) {
4✔
960
                        return ValueTypeBoolean.ValueBoolean.of(true);
3✔
961
                    }
962
                }
1✔
963
                return ValueTypeBoolean.ValueBoolean.of(false);
3✔
964
            }).build());
1✔
965

966
    /**
967
     * List contains operator that takes a list, a predicate that maps a list element to a boolean, a list element and returns a boolean.
968
     */
969
    public static final IOperator LIST_CONTAINS_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
970
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
971
            .renderPattern(IConfigRenderPattern.INFIX)
2✔
972
            .output(ValueTypes.BOOLEAN).symbolOperator("contains_p").interactName("containsPredicate")
6✔
973
            .function(variables -> {
1✔
974
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
975
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
976
                IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
977
                for (IValue value : list) {
10✔
978
                    IValue result = ValueHelpers.evaluateOperator(operator, value);
9✔
979
                    ValueHelpers.validatePredicateOutput(operator, result);
3✔
980
                    if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
981
                        return ValueTypeBoolean.ValueBoolean.of(true);
3✔
982
                    }
983
                }
1✔
984
                return ValueTypeBoolean.ValueBoolean.of(false);
3✔
985
            }).build());
1✔
986

987
    /**
988
     * List operator with one input list, and element and one output integer
989
     */
990
    public static final IOperator LIST_COUNT = REGISTRY.register(OperatorBuilders.LIST
14✔
991
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
992
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.INTEGER)
4✔
993
            .symbolOperatorInteract("count")
4✔
994
            .function(new OperatorBase.IFunction() {
4✔
995
                @Override
996
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
997
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
998
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
999
                    if (list.isInfinite()) {
3✔
1000
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
11✔
1001
                                LIST_COUNT.getLocalizedNameFull()));
2✔
1002
                    }
1003
                    IValue value = variables.getValue(1);
4✔
1004
                    int count = 0;
2✔
1005
                    for (IValue listValue : list) {
10✔
1006
                        if (listValue.equals(value)) {
4✔
1007
                            count++;
1✔
1008
                        }
1009
                    }
1✔
1010
                    return ValueTypeInteger.ValueInteger.of(count);
3✔
1011
                }
1012
            }).build());
1✔
1013

1014
    /**
1015
     * List operator with one input list, a predicate and one output integer
1016
     */
1017
    public static final IOperator LIST_COUNT_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
1018
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
1019
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.INTEGER)
4✔
1020
            .symbolOperator("count_p").interactName("countPredicate")
6✔
1021
            .function(new OperatorBase.IFunction() {
4✔
1022
                @Override
1023
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1024
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1025
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1026
                    if (list.isInfinite()) {
3!
1027
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1028
                                LIST_COUNT_PREDICATE.getLocalizedNameFull()));
×
1029
                    }
1030
                    IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
1031
                    int count = 0;
2✔
1032
                    for (IValue listValue : list) {
10✔
1033
                        IValue result = ValueHelpers.evaluateOperator(operator, listValue);
9✔
1034
                        ValueHelpers.validatePredicateOutput(operator, result);
3✔
1035
                        if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
1036
                            count++;
1✔
1037
                        }
1038
                    }
1✔
1039
                    return ValueTypeInteger.ValueInteger.of(count);
3✔
1040
                }
1041
            }).build());
1✔
1042

1043
    /**
1044
     * Append an element to the given list
1045
     */
1046
    public static final IOperator LIST_APPEND = REGISTRY.register(OperatorBuilders.LIST
14✔
1047
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
1048
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1049
            .symbolOperatorInteract("append")
2✔
1050
            .function(variables -> {
1✔
1051
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1052
                IValueTypeListProxy a = valueList.getRawValue();
3✔
1053
                IValue value = variables.getValue(1);
4✔
1054
                if (!ValueHelpers.correspondsTo(a.getValueType(), value.getType())) {
6✔
1055
                    throw new EvaluationException(Component.translatable(
11✔
1056
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1057
                            Component.translatable(a.getValueType().getTranslationKey()),
7✔
1058
                            Component.translatable(value.getType().getTranslationKey())));
4✔
1059
                }
1060
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyAppend(a, value));
7✔
1061
            }).build());
1✔
1062

1063
    /**
1064
     * Concatenate two lists
1065
     */
1066
    public static final IOperator LIST_CONCAT = REGISTRY.register(OperatorBuilders.LIST
14✔
1067
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})
2✔
1068
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1069
            .symbolOperatorInteract("concat")
2✔
1070
            .function(variables -> {
1✔
1071
                ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);
6✔
1072
                IValueTypeListProxy a = valueList0.getRawValue();
3✔
1073
                ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);
6✔
1074
                IValueTypeListProxy b = valueList1.getRawValue();
3✔
1075
                if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {
6!
1076
                    throw new EvaluationException(Component.translatable(
×
1077
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1078
                            Component.translatable(a.getValueType().getTranslationKey()),
×
1079
                            Component.translatable(b.getValueType().getTranslationKey())));
×
1080
                }
1081
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyConcat(a, b));
15✔
1082
            }).build());
1✔
1083

1084
    /**
1085
     * Build a list lazily using a start value and an operator that is applied to the previous element to get a next element.
1086
     */
1087
    public static final IOperator LIST_LAZYBUILT = REGISTRY.register(OperatorBuilders.LIST
14✔
1088
            .inputTypes(new IValueType[]{ValueTypes.CATEGORY_ANY, ValueTypes.OPERATOR})
2✔
1089
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1090
            .symbolOperator("lazybuilt").interactName("lazyBuilt")
4✔
1091
            .function(variables -> {
1✔
1092
                IValue a = variables.getValue(0);
4✔
1093
                IOperator operator = OperatorBuilders.getSafeOperator(variables.getValue(1, ValueTypes.OPERATOR), a.getType());
9✔
1094
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyLazyBuilt<>(a, operator));
7✔
1095
            }).build());
1✔
1096

1097
    /**
1098
     * Get the first element of the given list.
1099
     */
1100
    public static final IOperator LIST_HEAD = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
10✔
1101
            .inputTypes(new IValueType[]{ValueTypes.LIST}).output(ValueTypes.CATEGORY_ANY)
4✔
1102
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).symbolOperatorInteract("head")
4✔
1103
            .function(variables -> {
2✔
1104
                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);
6✔
1105
                IValueTypeListProxy a = list.getRawValue();
3✔
1106
                if (a.getLength() > 0) {
3!
1107
                    return a.get(0);
4✔
1108
                } else {
1109
                    throw new EvaluationException(Component.translatable(
×
1110
                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, 0, a.getLength()));
×
1111
                }
1112
            }).conditionalOutputTypeDeriver((operator, input) -> {
1✔
1113
                try {
1114
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();
×
1115
                    return a.getValueType();
×
1116
                } catch (EvaluationException e) {
×
1117
                    return operator.getOutputType();
×
1118
                }
1119
            }).build());
1✔
1120

1121
    /**
1122
     * Append an element to the given list.
1123
     */
1124
    public static final IOperator LIST_TAIL = REGISTRY.register(OperatorBuilders.LIST
10✔
1125
            .inputTypes(new IValueType[]{ValueTypes.LIST})
2✔
1126
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)
4✔
1127
            .symbolOperatorInteract("tail")
2✔
1128
            .function(variables -> {
1✔
1129
                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);
6✔
1130
                IValueTypeListProxy a = list.getRawValue();
3✔
1131
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyTail(a));
6✔
1132
            }).build());
1✔
1133

1134
    /**
1135
     * Deduplicate the given list elements based on the given predicate.
1136
     */
1137
    public static final IOperator LIST_UNIQ_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
1138
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
1139
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1140
            .symbolOperator("uniq_p").interactName("uniquePredicate")
4✔
1141
            .function(variables -> {
1✔
1142
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1143
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1144
                final IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
1145
                List<IValue> values = new ArrayList<>();
4✔
1146
                outerLoop:
1147
                for(IValue value : list) {
10✔
1148
                    for(IValue existing : values) {
10✔
1149
                        IValue result;
1150
                        try {
1151
                            result = ValueHelpers.evaluateOperator(operator, value, existing);
13✔
1152
                            ValueHelpers.validatePredicateOutput(operator, result);
3✔
1153
                        } catch (EvaluationException e) {
×
1154
                            throw Lombok.sneakyThrow(e);
×
1155
                        }
1✔
1156
                        if(((ValueTypeBoolean.ValueBoolean) result).getRawValue()) continue outerLoop;
5✔
1157
                    }
1✔
1158
                    values.add(value);
4✔
1159
                }
1✔
1160
                return ValueTypeList.ValueList.ofList(list.getValueType(), values);
5✔
1161
            }).build());
1✔
1162

1163
    /**
1164
     * Deduplicate the given list elements.
1165
     */
1166
    public static final IOperator LIST_UNIQ = REGISTRY.register(OperatorBuilders.LIST
5✔
1167
            .inputType(ValueTypes.LIST)
2✔
1168
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)
4✔
1169
            .symbolOperator("uniq").interactName("unique")
4✔
1170
            .function(variables -> {
1✔
1171
                ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);
6✔
1172
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1173
                return ValueTypeList.ValueList.ofList(list.getValueType(), new ArrayList<>(Sets.newLinkedHashSet(list)));
9✔
1174
            }).build());
1✔
1175

1176
    /**
1177
     * Take a subset of the given list from the given index (inclusive) to the given index (exclusive).
1178
     */
1179
    public static final IOperator LIST_SLICE = REGISTRY.register(OperatorBuilders.LIST
18✔
1180
            .inputTypes(ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.INTEGER)
2✔
1181
            .renderPattern(IConfigRenderPattern.PREFIX_3).output(ValueTypes.LIST)
4✔
1182
            .symbolOperatorInteract("slice")
2✔
1183
            .function(variables -> {
1✔
1184
                ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);
6✔
1185
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1186
                ValueTypeInteger.ValueInteger from = variables.getValue(1, ValueTypes.INTEGER);
6✔
1187
                ValueTypeInteger.ValueInteger to = variables.getValue(2, ValueTypes.INTEGER);
6✔
1188
                if (from.getRawValue() >= to.getRawValue()) {
5✔
1189
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_TOGREATERTHANFROM));
6✔
1190
                }
1191
                if (from.getRawValue() < 0 || to.getRawValue() < 0){
6!
1192
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_INDEXNEGATIVE));
6✔
1193
                }
1194
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxySlice<>(list, from.getRawValue(), to.getRawValue()));
10✔
1195
            }).build());
1✔
1196

1197
    public static final IOperator LIST_INTERSECTION = REGISTRY.register(OperatorBuilders.LIST
14✔
1198
            .inputTypes(ValueTypes.LIST, ValueTypes.LIST)
2✔
1199
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1200
            .symbol("∩").operatorInteract("intersection")
6✔
1201
            .function(new OperatorBase.IFunction() {
4✔
1202
                @Override
1203
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1204
                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList1 = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
1205
                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList2 = variables.getValue(1, ValueTypes.LIST).getRawValue();
7✔
1206
                    if (rawList1.isInfinite() || rawList2.isInfinite()) {
6!
1207
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1208
                                LIST_INTERSECTION.getLocalizedNameFull()));
×
1209
                    }
1210
                    LinkedHashSet<IValue> result = Sets.newLinkedHashSet(rawList1);
3✔
1211
                    result.retainAll(Sets.newLinkedHashSet(rawList2));
5✔
1212

1213
                    return ValueTypeList.ValueList.ofList(rawList1.getValueType(), result.stream().toList());
7✔
1214
                }
1215
            }).build());
1✔
1216

1217
    /**
1218
     * Test list equality using set semantics.
1219
     */
1220
    public static final IOperator LIST_EQUALS_SET = REGISTRY.register(OperatorBuilders.LIST
14✔
1221
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})
2✔
1222
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.BOOLEAN)
4✔
1223
            .symbol("=set=").operatorInteract("equals_set")
4✔
1224
            .function(variables -> {
1✔
1225
                ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);
6✔
1226
                IValueTypeListProxy a = valueList0.getRawValue();
3✔
1227
                ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);
6✔
1228
                IValueTypeListProxy b = valueList1.getRawValue();
3✔
1229
                if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {
6!
1230
                    throw new EvaluationException(Component.translatable(
×
1231
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1232
                            Component.translatable(a.getValueType().getTranslationKey()),
×
1233
                            Component.translatable(b.getValueType().getTranslationKey())));
×
1234
                }
1235
                Set<Object> setA = Sets.newHashSet(a);
3✔
1236
                Set<Object> setB = Sets.newHashSet(b);
3✔
1237
                return ValueTypeBoolean.ValueBoolean.of(setA.equals(setB));
5✔
1238
            }).build());
1✔
1239

1240
    /**
1241
     * Test list equality using multiset semantics.
1242
     */
1243
    public static final IOperator LIST_EQUALS_MULTISET = REGISTRY.register(OperatorBuilders.LIST
14✔
1244
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})
2✔
1245
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.BOOLEAN)
4✔
1246
            .symbol("=multiset=").operatorInteract("equals_multiset")
4✔
1247
            .function(variables -> {
1✔
1248
                ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);
6✔
1249
                IValueTypeListProxy a = valueList0.getRawValue();
3✔
1250
                ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);
6✔
1251
                IValueTypeListProxy b = valueList1.getRawValue();
3✔
1252
                if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {
6!
1253
                    throw new EvaluationException(Component.translatable(
×
1254
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1255
                            Component.translatable(a.getValueType().getTranslationKey()),
×
1256
                            Component.translatable(b.getValueType().getTranslationKey())));
×
1257
                }
1258
                Multiset<Object> setA = HashMultiset.create(a);
3✔
1259
                Multiset<Object> setB = HashMultiset.create(b);
3✔
1260
                return ValueTypeBoolean.ValueBoolean.of(setA.equals(setB));
5✔
1261
            }).build());
1✔
1262

1263
    /**
1264
     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------
1265
     */
1266

1267
    /**
1268
     * Block isOpaque operator with one input block and one output boolean.
1269
     */
1270
    public static final IOperator OBJECT_BLOCK_OPAQUE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN).symbolOperator("opaque").interactName("isOpaque")
11✔
1271
            .function(variables -> {
1✔
1272
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1273
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent() && a.getRawValue().get().isSolidRender());
15!
1274
            }).build());
1✔
1275

1276
    /**
1277
     * The itemstack representation of the block
1278
     */
1279
    public static final IOperator OBJECT_BLOCK_ITEMSTACK = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK).symbolOperator("itemstack").interactName("itemStack")
11✔
1280
            .function(variables -> {
1✔
1281
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1282
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getRawValue().isPresent() ? IModHelpers.get().getBlockHelpers().getItemStackFromBlockState(a.getRawValue().get()) : ItemStack.EMPTY);
14!
1283
            }).build());
1✔
1284

1285
    /**
1286
     * The name of the mod owning this block
1287
     */
1288
    public static final IOperator OBJECT_BLOCK_MODNAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING).symbolOperatorInteract("mod")
20✔
1289
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1290
                    (OperatorBase.SafeVariablesGetter variables) -> {
1291
                        ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1292
                        return a.getRawValue().isPresent() ? BuiltInRegistries.BLOCK.getKey(a.getRawValue().get().getBlock()) : ResourceLocation.parse("");
13!
1293
                    },
1294
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
1295
            ))).build());
1✔
1296

1297
    /**
1298
     * The breaksound of the block
1299
     */
1300
    public static final IOperator OBJECT_BLOCK_BREAKSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1301
            .symbol("break_sound").operatorName("breaksound").interactName("breakSound")
21✔
1302
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1303
                    OperatorBuilders.BLOCK_SOUND,
1304
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getBreakSound().location().toString() : "",
11!
1305
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1306
            ))).build());
1✔
1307
    /**
1308
     * The placesound of the block
1309
     */
1310
    public static final IOperator OBJECT_BLOCK_PLACESOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1311
            .symbol("place_sound").operatorName("placesound").interactName("placeSound")
21✔
1312
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1313
                    OperatorBuilders.BLOCK_SOUND,
1314
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getPlaceSound().location().toString() : "",
11!
1315
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1316
            ))).build());
1✔
1317
    /**
1318
     * The stepsound of the block
1319
     */
1320
    public static final IOperator OBJECT_BLOCK_STEPSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1321
            .symbol("step_sound").operatorName("stepsound").interactName("stepSound")
21✔
1322
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1323
                    OperatorBuilders.BLOCK_SOUND,
1324
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getStepSound().location().toString() : "",
11!
1325
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1326
            ))).build());
1✔
1327

1328
    /**
1329
     * If the block is shearable
1330
     */
1331
    public static final IOperator OBJECT_BLOCK_ISSHEARABLE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
1332
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
1333
            .function(variables -> {
1✔
1334
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1335
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
1336
                        && a.getRawValue().get().getBlock() instanceof IShearable
7✔
1337
                        && ((IShearable) a.getRawValue().get().getBlock()).isShearable(null, ItemStack.EMPTY, null, null));
14!
1338
            }).build());
1✔
1339

1340
    /**
1341
     * The block when this block is planted
1342
     */
1343
    public static final IOperator OBJECT_BLOCK_PLANTAGE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1344
            .output(ValueTypes.INTEGER).symbol("plant_age").operatorName("plantage").interactName("plantAge")
8✔
1345
            .function(variables -> {
1✔
1346
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1347
                int age = 0;
2✔
1348
                if (a.getRawValue().isPresent()) {
4!
1349
                    for (Property<?> prop : a.getRawValue().get().getProperties()) {
14✔
1350
                        if (prop.getName().equals("age") && prop.getValueClass() == Integer.class) {
9!
1351
                            age = (Integer) a.getRawValue().get().getValue(prop);
9✔
1352
                        }
1353
                    }
1✔
1354
                }
1355
                return ValueTypeInteger.ValueInteger.of(age);
3✔
1356
            }).build());
1✔
1357

1358
    /**
1359
     * Get a block by name.
1360
     */
1361
    public static final IOperator OBJECT_BLOCK_BY_NAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1362
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_BLOCK)
4✔
1363
            .symbol("block_by_name").operatorName("blockbyname").interactName("blockByName")
7✔
1364
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
1365
                    .build(input -> {
1✔
1366
                        Block block = BuiltInRegistries.BLOCK.getValue(input);
5✔
1367
                        return ValueObjectTypeBlock.ValueBlock.of(block.defaultBlockState());
4✔
1368
                    })).build());
1✔
1369

1370
    /**
1371
     * Get the block properties as NBT compound tag.
1372
     */
1373
    public static final IOperator OBJECT_BLOCK_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1374
            .output(ValueTypes.NBT)
2✔
1375
            .symbol("block_props").operatorName("blockproperties").interactName("properties")
6✔
1376
            .function(variables -> {
1✔
1377
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1378
                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {
6✔
1379
                    CompoundTag tag = new CompoundTag();
4✔
1380
                    for (Property property : blockState.getProperties()) {
11✔
1381
                        Comparable<?> value = blockState.getValue(property);
4✔
1382
                        tag.putString(property.getName(), property.getName(value));
7✔
1383
                    }
1✔
1384
                    return tag;
2✔
1385
                }));
1386
            }).build());
1✔
1387

1388
    /**
1389
     * Get the given block applied with the given properties.
1390
     */
1391
    public static final IOperator OBJECT_BLOCK_WITH_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_INFIX_VERYLONG
14✔
1392
            .inputTypes(ValueTypes.OBJECT_BLOCK, ValueTypes.NBT).output(ValueTypes.OBJECT_BLOCK)
4✔
1393
            .symbol("block_with_props").operatorName("blockfromproperties").interactName("withProperties")
6✔
1394
            .function(variables -> {
1✔
1395
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1396
                ValueTypeNbt.ValueNbt b = variables.getValue(1, ValueTypes.NBT);
6✔
1397
                if (a.getRawValue().isPresent() && a.getRawValue().isPresent()) {
8!
1398
                    BlockState blockState = a.getRawValue().get();
5✔
1399
                    Tag tagRaw = b.getRawValue().get();
5✔
1400
                    if (tagRaw instanceof CompoundTag) {
3!
1401
                        CompoundTag tag = (CompoundTag) tagRaw;
3✔
1402
                        for (Property property : blockState.getProperties()) {
11✔
1403
                            if (tag.contains(property.getName())) {
5!
1404
                                Optional<Comparable> valueOptional = property.getValue(tag.getString(property.getName()).orElseThrow());
9✔
1405
                                if (valueOptional.isPresent()) {
3!
1406
                                    blockState = blockState.setValue(property, valueOptional.get());
8✔
1407
                                }
1408
                            }
1409
                        }
1✔
1410
                        return ValueObjectTypeBlock.ValueBlock.of(blockState);
3✔
1411
                    }
1412
                }
1413
                return a;
×
1414
            }).build());
1✔
1415

1416
    /**
1417
     * Get all possible block properties as NBT compound tag with list values.
1418
     */
1419
    public static final IOperator OBJECT_BLOCK_POSSIBLE_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1420
            .output(ValueTypes.NBT)
2✔
1421
            .symbol("block_all_props").operatorName("blockpossibleproperties").interactName("possibleProperties")
6✔
1422
            .function(variables -> {
1✔
1423
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1424
                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {
6✔
1425
                    CompoundTag tag = new CompoundTag();
4✔
1426
                    for (Property property : blockState.getProperties()) {
11✔
1427
                        ListTag list = new ListTag();
4✔
1428
                        for (Comparable value : (Collection<Comparable>) property.getPossibleValues()) {
11✔
1429
                            list.add(StringTag.valueOf(property.getName(value)));
7✔
1430
                        }
1✔
1431
                        tag.put(property.getName(), list);
6✔
1432
                    }
1✔
1433
                    return tag;
2✔
1434
                }));
1435
            }).build());
1✔
1436

1437
    /**
1438
     * The tag entries of the given block
1439
     */
1440
    public static final IOperator OBJECT_BLOCK_TAG = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1441
            .output(ValueTypes.LIST)
2✔
1442
            .symbol("block_tag_names").operatorName("tag").interactName("tags")
6✔
1443
            .function(variables -> {
1✔
1444
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1445
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
1446
                if(!a.getRawValue().isEmpty()) {
4!
1447
                    a.getRawValue().get().getBlock().builtInRegistryHolder().tags()
9✔
1448
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
1449
                                    .of(owningTag.location().toString())));
3✔
1450
                }
1451
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
1452
            }).build());
1✔
1453

1454
    /**
1455
     * Get a list of blocks that correspond to the given tag key.
1456
     */
1457
    public static final IOperator OBJECT_BLOCK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
1458
            .output(ValueTypes.LIST)
2✔
1459
            .symbol("block_tag_values").operatorName("blocktag").interactName("blocksByTag")
6✔
1460
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
1461
            .function(variables -> {
1✔
1462
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
1463
                ImmutableList.Builder<ValueObjectTypeBlock.ValueBlock> builder = ImmutableList.builder();
2✔
1464
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
1465
                    try {
1466
                        Helpers.getBlockTagValues(a.getRawValue())
4✔
1467
                                .map(ValueObjectTypeBlock.ValueBlock::of)
3✔
1468
                                .forEach(builder::add);
4✔
1469
                    } catch (ResourceLocationException e) {
×
1470
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
1471
                    }
1✔
1472
                }
1473
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_BLOCK, builder.build());
5✔
1474
            }).build());
1✔
1475

1476
    /**
1477
     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------
1478
     */
1479

1480
    /**
1481
     * Item Stack size operator with one input itemstack and one output integer.
1482
     */
1483
    public static final IOperator OBJECT_ITEMSTACK_SIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1484
            .output(ValueTypes.INTEGER).symbolOperator("size").interactName("size")
7✔
1485
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1486
                itemStack -> !itemStack.isEmpty() ? itemStack.getCount() : 0
8!
1487
            )).build());
1✔
1488

1489
    /**
1490
     * Item Stack maxsize operator with one input itemstack and one output integer.
1491
     */
1492
    public static final IOperator OBJECT_ITEMSTACK_MAXSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1493
            .output(ValueTypes.INTEGER).symbolOperator("maxsize").interactName("maxSize")
7✔
1494
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1495
                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxStackSize() : 0
8!
1496
            )).build());
1✔
1497

1498
    /**
1499
     * Item Stack isstackable operator with one input itemstack and one output boolean.
1500
     */
1501
    public static final IOperator OBJECT_ITEMSTACK_ISSTACKABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1502
            .output(ValueTypes.BOOLEAN).symbolOperator("stackable").interactName("isStackable")
7✔
1503
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1504
                itemStack -> !itemStack.isEmpty() && itemStack.isStackable()
11!
1505
            )).build());
1✔
1506

1507
    /**
1508
     * Item Stack isdamageable operator with one input itemstack and one output boolean.
1509
     */
1510
    public static final IOperator OBJECT_ITEMSTACK_ISDAMAGEABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1511
            .output(ValueTypes.BOOLEAN).symbolOperator("damageable").interactName("isDamageable")
7✔
1512
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1513
                itemStack -> !itemStack.isEmpty() && itemStack.isDamageableItem()
11!
1514
            )).build());
1✔
1515

1516
    /**
1517
     * Item Stack damage operator with one input itemstack and one output integer.
1518
     */
1519
    public static final IOperator OBJECT_ITEMSTACK_DAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1520
            .output(ValueTypes.INTEGER).symbolOperator("damage").interactName("damage")
7✔
1521
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1522
                itemStack -> !itemStack.isEmpty() ? itemStack.getDamageValue() : 0
8!
1523
            )).build());
1✔
1524

1525
    /**
1526
     * Item Stack maxdamage operator with one input itemstack and one output integer.
1527
     */
1528
    public static final IOperator OBJECT_ITEMSTACK_MAXDAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1529
            .output(ValueTypes.INTEGER).symbol("max_damage").operatorName("maxdamage").interactName("maxDamage")
9✔
1530
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1531
                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxDamage() : 0
8!
1532
            )).build());
1✔
1533

1534
    /**
1535
     * Item Stack isenchanted operator with one input itemstack and one output boolean.
1536
     */
1537
    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTED = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1538
            .output(ValueTypes.BOOLEAN).symbolOperator("enchanted").interactName("isEnchanted")
7✔
1539
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1540
                itemStack -> !itemStack.isEmpty() && itemStack.isEnchanted()
11!
1541
            )).build());
1✔
1542

1543
    /**
1544
     * Item Stack isenchantable operator with one input itemstack and one output boolean.
1545
     */
1546
    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1547
            .output(ValueTypes.BOOLEAN).symbolOperator("enchantable").interactName("isEnchantable")
7✔
1548
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1549
                itemStack -> !itemStack.isEmpty() && itemStack.isEnchantable()
11!
1550
            )).build());
1✔
1551

1552
    /**
1553
     * Item Stack repair cost with one input itemstack and one output integer.
1554
     */
1555
    public static final IOperator OBJECT_ITEMSTACK_REPAIRCOST = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1556
            .output(ValueTypes.INTEGER)
2✔
1557
            .symbol("repair_cost").operatorName("repaircost").interactName("repairCost")
7✔
1558
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1559
                itemStack -> !itemStack.isEmpty() ? itemStack.get(DataComponents.REPAIR_COST) : 0
9!
1560
            )).build());
1✔
1561

1562
    /**
1563
     * Get the rarity of an itemstack.
1564
     */
1565
    public static final IOperator OBJECT_ITEMSTACK_RARITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1566
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
1567
            .function(variables -> {
1✔
1568
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1569
                return ValueTypeString.ValueString.of(!a.getRawValue().isEmpty() ? a.getRawValue().getRarity().name() : "");
11!
1570
            }).build());
1✔
1571

1572
    /**
1573
     * Get the strength of an itemstack against a block as a double.
1574
     */
1575
    public static final IOperator OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
14✔
1576
            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.DOUBLE)
4✔
1577
            .symbolOperatorInteract("strength")
2✔
1578
            .function(variables -> {
1✔
1579
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1580
                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);
6✔
1581
                return ValueTypeDouble.ValueDouble.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() ? a.getRawValue().getDestroySpeed(b.getRawValue().get()) : 0);
19!
1582
            }).build());
1✔
1583

1584
    /**
1585
     * If the given itemstack can be used to harvest the given block.
1586
     */
1587
    public static final IOperator OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1588
            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.BOOLEAN)
4✔
1589
            .symbol("can_harvest").operatorName("canharvest").interactName("canHarvest")
6✔
1590
            .function(variables -> {
1✔
1591
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1592
                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);
6✔
1593
                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() && a.getRawValue().isCorrectToolForDrops(b.getRawValue().get()));
21!
1594
            }).build());
1✔
1595

1596
    /**
1597
     * The block from the stack
1598
     */
1599
    public static final IOperator OBJECT_ITEMSTACK_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1600
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
1601
            .function(variables -> {
1✔
1602
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1603
                return ValueObjectTypeBlock.ValueBlock.of((!a.getRawValue().isEmpty() && a.getRawValue().getItem() instanceof BlockItem) ? IModHelpers.get().getBlockHelpers().getBlockStateFromItemStack(a.getRawValue()) : null);
17!
1604
            }).build());
1✔
1605

1606
    /**
1607
     * If the given stack has a fluid.
1608
     */
1609
    public static final IOperator OBJECT_ITEMSTACK_ISFLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1610
            .output(ValueTypes.BOOLEAN)
2✔
1611
            .symbol("is_fluidstack").operatorName("isfluidstack").interactName("isFluidStack")
7✔
1612
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1613
                itemStack -> !itemStack.isEmpty() && !Helpers.getFluidStack(itemStack).isEmpty()
12!
1614
            )).build());
1✔
1615

1616
    /**
1617
     * The fluidstack from the stack
1618
     */
1619
    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1620
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("fluidstack").interactName("fluidStack")
6✔
1621
            .function(variables -> {
1✔
1622
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1623
                return ValueObjectTypeFluidStack.ValueFluidStack.of(!a.getRawValue().isEmpty() ? Helpers.getFluidStack(a.getRawValue()) : FluidStack.EMPTY);
11✔
1624
            }).build());
1✔
1625

1626
    /**
1627
     * The capacity of the fluidstack from the stack.
1628
     */
1629
    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1630
            .output(ValueTypes.INTEGER)
2✔
1631
            .symbol("fluidstack_capacity").operatorName("fluidstackcapacity").interactName("fluidCapacity")
7✔
1632
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1633
                itemStack -> !itemStack.isEmpty() ? Helpers.getFluidStackCapacity(itemStack) : 0
8!
1634
            )).build());
1✔
1635

1636
    /**
1637
     * If the data components of the given stacks are equal.
1638
     */
1639
    public static final IOperator OBJECT_ITEMSTACK_ISDATAEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1640
            .output(ValueTypes.BOOLEAN).symbol("=NBT=").operatorName("isnbtequal").interactName("isNbtEqual")
8✔
1641
            .function(variables -> {
1✔
1642
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1643
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1644
                ItemStack a = valueStack0.getRawValue();
3✔
1645
                ItemStack b = valueStack1.getRawValue();
3✔
1646
                boolean equal = false;
2✔
1647
                if(!a.isEmpty() && !b.isEmpty()) {
6!
1648
                    equal = ItemStack.isSameItem(a, b) && ItemMatch.areItemStacksEqual(a, b, ItemMatch.DATA);
14✔
1649
                } else if(a.isEmpty() && b.isEmpty()) {
×
1650
                    equal = true;
×
1651
                }
1652
                return ValueTypeBoolean.ValueBoolean.of(equal);
3✔
1653
            }).build());
1✔
1654

1655
    /**
1656
     * If the raw items of the given stacks are equal, ignoring data components but including damage value.
1657
     */
1658
    public static final IOperator OBJECT_ITEMSTACK_ISITEMEQUALNODATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1659
            .output(ValueTypes.BOOLEAN).symbol("=NoNBT=").operatorName("isitemequalnonbt").interactName("isEqualNonNbt")
8✔
1660
            .function(variables -> {
1✔
1661
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
×
1662
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
1663
                ItemStack a = valueStack0.getRawValue();
×
1664
                ItemStack b = valueStack1.getRawValue();
×
1665
                boolean equal = false;
×
1666
                if(!a.isEmpty() && !b.isEmpty()) {
×
1667
                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);
×
1668
                } else if(a.isEmpty() && b.isEmpty()) {
×
1669
                    equal = true;
×
1670
                }
1671
                return ValueTypeBoolean.ValueBoolean.of(equal);
×
1672
            }).build());
1✔
1673

1674
    /**
1675
     * If the raw items of the given stacks are equal, ignoring data components and damage value.
1676
     */
1677
    public static final IOperator OBJECT_ITEMSTACK_ISRAWITEMEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1678
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwitemequal").interactName("isEqualRaw")
8✔
1679
            .function(variables -> {
1✔
1680
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1681
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1682
                ItemStack a = valueStack0.getRawValue();
3✔
1683
                ItemStack b = valueStack1.getRawValue();
3✔
1684
                boolean equal = false;
2✔
1685
                if(!a.isEmpty() && !b.isEmpty()) {
6!
1686
                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);
6✔
1687
                } else if(a.isEmpty() && b.isEmpty()) {
×
1688
                    equal = true;
×
1689
                }
1690
                return ValueTypeBoolean.ValueBoolean.of(equal);
3✔
1691
            }).build());
1✔
1692

1693
    /**
1694
     * The name of the mod owning this item
1695
     */
1696
    public static final IOperator OBJECT_ITEMSTACK_MODNAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1697
            .symbolOperatorInteract("mod")
13✔
1698
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1699
                    (OperatorBase.SafeVariablesGetter variables) -> {
1700
                        ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1701
                        return !a.getRawValue().isEmpty() ? BuiltInRegistries.ITEM.getKey(a.getRawValue().getItem()) : ResourceLocation.parse("");
11!
1702
                    },
1703
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
1704
            ))).build());
1✔
1705

1706
    /**
1707
     * The fuel burn time of the given item
1708
     */
1709
    public static final IOperator OBJECT_ITEMSTACK_FUELBURNTIME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1710
            .output(ValueTypes.INTEGER)
2✔
1711
            .symbol("burn_time").operatorName("burntime").interactName("burnTime")
7✔
1712
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(itemStack -> {
2✔
1713
                if (!itemStack.isEmpty()) {
3!
1714
                    int burnTime = itemStack.getBurnTime(null, ServerLifecycleHooks.getCurrentServer().fuelValues());
6✔
1715
                    return EventHooks.getItemBurnTime(itemStack, burnTime == -1
7!
1716
                            ? itemStack.getBurnTime(RecipeType.SMELTING, ServerLifecycleHooks.getCurrentServer().fuelValues())
×
1717
                            : burnTime, null, ServerLifecycleHooks.getCurrentServer().fuelValues());
4✔
1718
                }
1719
                return 0;
×
1720
            })).build());
1✔
1721

1722
    /**
1723
     * If the given item can be used as fuel
1724
     */
1725
    public static final IOperator OBJECT_ITEMSTACK_CANBURN = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1726
            .output(ValueTypes.BOOLEAN)
2✔
1727
            .symbol("can_burn").operatorName("canburn").interactName("canBurn")
7✔
1728
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN
1✔
1729
                    .build(stack -> stack.getBurnTime(null, ServerLifecycleHooks.getCurrentServer().fuelValues()) > 0))
12✔
1730
            .build());
1✔
1731

1732
    /**
1733
     * The tag entries of the given item
1734
     */
1735
    public static final IOperator OBJECT_ITEMSTACK_TAG = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1736
            .output(ValueTypes.LIST)
2✔
1737
            .symbol("item_tag_names").operatorName("tag").interactName("tags")
6✔
1738
            .function(variables -> {
1✔
1739
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1740
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
1741
                if(!a.getRawValue().isEmpty()) {
4!
1742
                    a.getRawValue().getItem().builtInRegistryHolder().tags()
7✔
1743
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
1744
                                    .of(owningTag.location().toString())));
3✔
1745
                }
1746
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
1747
            }).build());
1✔
1748

1749
    /**
1750
     * Get a list of items that correspond to the given tag key.
1751
     */
1752
    public static final IOperator OBJECT_ITEMSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
1753
            .output(ValueTypes.LIST)
2✔
1754
            .symbol("item_tag_values").operatorName("tag").interactName("itemsByTag")
6✔
1755
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
1756
            .function(variables -> {
1✔
1757
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
1758
                ImmutableList.Builder<ValueObjectTypeItemStack.ValueItemStack> builder = ImmutableList.builder();
2✔
1759
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
1760
                    try {
1761
                        Helpers.getTagValues(a.getRawValue())
4✔
1762
                                .map(ValueObjectTypeItemStack.ValueItemStack::of)
3✔
1763
                                .forEach(builder::add);
4✔
1764
                    } catch (ResourceLocationException e) {
×
1765
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
1766
                    }
1✔
1767
                }
1768
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, builder.build());
5✔
1769
            }).build());
1✔
1770

1771
    /**
1772
     * ItemStack operator that applies the given stacksize to the given itemstack and creates a new ItemStack.
1773
     */
1774
    public static final IOperator OBJECT_ITEMSTACK_WITHSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_INTEGER_1
5✔
1775
            .output(ValueTypes.OBJECT_ITEMSTACK)
2✔
1776
            .symbol("with_size").operatorName("withsize").interactName("withSize")
6✔
1777
            .function(variables -> {
1✔
1778
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1779
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
1780
                if (!a.getRawValue().isEmpty()) {
4!
1781
                    ItemStack itemStack = a.getRawValue().copy();
4✔
1782
                    itemStack.setCount(b.getRawValue());
4✔
1783
                    return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
1784
                }
1785
                return a;
×
1786
            }).build());
1✔
1787

1788
    /**
1789
     * Check if the item is an RF container item
1790
     */
1791
    public static final IOperator OBJECT_ITEMSTACK_ISFECONTAINER = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1792
            .output(ValueTypes.BOOLEAN)
2✔
1793
            .symbol("is_fe_container").operatorName("isfecontainer").interactName("isFeContainer")
7✔
1794
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_BOOLEAN.build(
2✔
1795
                Objects::nonNull
1796
            )).build());
1✔
1797

1798
    /**
1799
     * Get the storage energy
1800
     */
1801
    public static final IOperator OBJECT_ITEMSTACK_STOREDFE = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1802
            .output(ValueTypes.INTEGER)
2✔
1803
            .symbol("stored_fe").operatorName("storedfe").interactName("feStored")
7✔
1804
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_INT.build(
2✔
1805
                input -> input != null ? input.getEnergyStored() : 0
8✔
1806
            )).build());
1✔
1807

1808
    /**
1809
     * Get the energy capacity
1810
     */
1811
    public static final IOperator OBJECT_ITEMSTACK_FECAPACITY = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1812
            .output(ValueTypes.INTEGER)
2✔
1813
            .symbol("capacity_fe").operatorName("fecapacity").interactName("feCapacity")
7✔
1814
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_INT.build(
2✔
1815
                input -> input != null ? input.getMaxEnergyStored() : 0
8✔
1816
            )).build());
1✔
1817

1818

1819
    /**
1820
     * If the given item has an inventory.
1821
     */
1822
    public static final IOperator OBJECT_ITEMSTACK_HASINVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1823
            .output(ValueTypes.BOOLEAN)
2✔
1824
            .symbol("has_inventory").operatorName("hasinventory").interactName("hasInventory")
6✔
1825
            .function(variables -> {
1✔
1826
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1827
                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM) != null);
14!
1828
            }).build());
1✔
1829

1830

1831

1832

1833
    /**
1834
     * Retrieve the inventory size of the given item handler contents.
1835
     */
1836
    public static final IOperator OBJECT_ITEMSTACK_INVENTORYSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1837
            .output(ValueTypes.INTEGER)
2✔
1838
            .symbol("inventory_size").operatorName("inventorysize").interactName("inventorySize")
6✔
1839
            .function(variables -> {
1✔
1840
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1841
                IItemHandler itemHandler = a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM);
6✔
1842
                return ValueTypeInteger.ValueInteger.of(itemHandler != null ? itemHandler.getSlots() : 0);
8✔
1843
            }).build());
1✔
1844

1845
    /**
1846
     * Retrieve the inventory of the given item handler contents.
1847
     */
1848
    public static final IOperator OBJECT_ITEMSTACK_INVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1849
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
1850
            .function(variables -> {
1✔
1851
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1852
                IItemHandler itemHandler = a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM);
6✔
1853
                if (itemHandler != null) {
2✔
1854
                    List<ValueObjectTypeItemStack.ValueItemStack> values = Lists.newArrayListWithCapacity(itemHandler.getSlots());
4✔
1855
                    for (int i = 0; i < itemHandler.getSlots(); i++) {
8✔
1856
                        values.add(ValueObjectTypeItemStack.ValueItemStack.of(itemHandler.getStackInSlot(i)));
7✔
1857
                    }
1858
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, values);
4✔
1859
                }
1860
                return ValueTypes.LIST.getDefault();
3✔
1861
            }).build());
1✔
1862

1863
    /**
1864
     * Get an item by name.
1865
     */
1866
    public static final IOperator OBJECT_ITEMSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
1867
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_ITEMSTACK)
4✔
1868
            .symbol("item_by_name").operatorName("itembyname").interactName("itemByName")
7✔
1869
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
1870
                    .build(input -> {
1✔
1871
                        Item item = BuiltInRegistries.ITEM.getValue(input);
5✔
1872
                        ItemStack itemStack = ItemStack.EMPTY;
2✔
1873
                        if (item != null) {
2!
1874
                            itemStack = new ItemStack(item);
5✔
1875
                        }
1876
                        return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
1877
                    })).build());
1✔
1878

1879
    /**
1880
     * Get the total item count of the given item in a list.
1881
     */
1882
    public static final IOperator OBJECT_ITEMSTACK_LIST_COUNT = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1883
            .inputTypes(ValueTypes.LIST, ValueTypes.OBJECT_ITEMSTACK)
2✔
1884
            .output(ValueTypes.INTEGER)
2✔
1885
            .symbol("item_list_count").operatorName("itemlistcount").interactName("itemListCount")
6✔
1886
            .function(variables -> {
1✔
1887
                ValueTypeList.ValueList<IValueType<IValue>, IValue> a = variables.getValue(0, ValueTypes.LIST);
6✔
1888
                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1889
                if (!ValueHelpers.correspondsTo(a.getRawValue().getValueType(), ValueTypes.OBJECT_ITEMSTACK)) {
6!
1890
                    MutableComponent error = Component.translatable(
×
1891
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1892
                            Component.translatable(a.getRawValue().getValueType().getTranslationKey()),
×
1893
                            Component.translatable(ValueTypes.OBJECT_ITEMSTACK.getTranslationKey()));
×
1894
                    throw new EvaluationException(error);
×
1895
                }
1896

1897
                ItemStack itemStack = b.getRawValue();
3✔
1898
                int count = 0;
2✔
1899
                for (IValue listValueRaw : a.getRawValue()) {
11✔
1900
                    if (listValueRaw.getType().correspondsTo(ValueTypes.OBJECT_ITEMSTACK)) {
5!
1901
                        ValueObjectTypeItemStack.ValueItemStack listValue = (ValueObjectTypeItemStack.ValueItemStack) listValueRaw;
3✔
1902
                        if (!listValue.getRawValue().isEmpty()) {
4!
1903
                            ItemStack listItem = listValue.getRawValue();
3✔
1904
                            if (!itemStack.isEmpty()) {
3!
1905
                                if (ItemStack.isSameItemSameComponents(itemStack, listItem)) {
4✔
1906
                                    count += listItem.getCount();
6✔
1907
                                }
1908
                            } else {
1909
                                count += listItem.getCount();
×
1910
                            }
1911
                        }
1912
                    }
1913
                }
1✔
1914

1915
                return ValueTypeInteger.ValueInteger.of(count);
3✔
1916
            }).build());
1✔
1917

1918
    /**
1919
     * Item Stack size operator with one input itemstack and one output NBT tag.
1920
     */
1921
    public static final IOperator OBJECT_ITEMSTACK_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1922
            .output(ValueTypes.NBT).symbol("NBT()").operatorName("nbt").interactName("nbt")
8✔
1923
            .function(input -> {
1✔
1924
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1925
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
1926
                if (itemStack.getRawValue().isEmpty() || itemStack.getRawValue().getComponents().isEmpty()) {
9!
1927
                    return ValueTypeNbt.ValueNbt.of();
×
1928
                }
1929
                CompoundTag tag = (CompoundTag) DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), itemStack.getRawValue().getComponentsPatch()).getOrThrow();
12✔
1930
                // Remove entries that have been marked as removed.
1931
                for (String key : Sets.newHashSet(tag.keySet())) {
12✔
1932
                    if (key.startsWith("!")) {
4✔
1933
                        tag.remove(key);
3✔
1934
                    }
1935
                }
1✔
1936
                if (tag.isEmpty()) {
3✔
1937
                    return ValueTypeNbt.ValueNbt.of();
2✔
1938
                }
1939
                return ValueTypeNbt.ValueNbt.of(Optional.of(tag));
4✔
1940
            }).build());
1✔
1941

1942
    /**
1943
     * Item Stack has_nbt operator with one input itemstack and one output boolean.
1944
     */
1945
    public static final IOperator OBJECT_ITEMSTACK_HASDATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
1946
            .output(ValueTypes.BOOLEAN).symbol("has_nbt").operatorName("hasnbt").interactName("hasNbt")
9✔
1947
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1948
                    itemStack -> !itemStack.isEmpty() && itemStack.getComponents().stream().anyMatch(t -> !t.type().isTransient())
21!
1949
            )).build());
1✔
1950

1951
    /**
1952
     * Get the data component keys of an itemstack.
1953
     */
1954
    public static final IOperator OBJECT_ITEMSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1955
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
1956
            .function(input -> {
1✔
1957
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1958
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
1959
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
1960
                        itemStack.getRawValue().isEmpty() ?
4✔
1961
                                Lists.newArrayList() :
2✔
1962
                                itemStack.getRawValue().getComponents().keySet().stream()
5✔
1963
                                        .filter(c -> !c.isTransient())
8!
1964
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
1965
                                        .sorted(Comparator.comparing(ValueTypeString.ValueString::getRawValue))
2✔
1966
                                        .toList());
2✔
1967
            }).build());
1✔
1968

1969
    /**
1970
     * Get the data component value by key
1971
     */
1972
    public static final IOperator OBJECT_ITEMSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1973
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING)
2✔
1974
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
1975
            .function(input -> {
1✔
1976
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1977

1978
                // Determine data component type
1979
                DataComponentType<?> dataComponentType;
1980
                try {
1981
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
1982
                } catch (ResourceLocationException e) {
×
1983
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
1984
                }
1✔
1985

1986
                // Fetch component value
1987
                TypedDataComponent<?> typedComponent = itemStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
1988
                if (typedComponent == null) {
2✔
1989
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
1990
                }
1991

1992
                // Encode component value
1993
                try {
1994
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
1995
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
1996
                } catch (IllegalStateException e) {
×
1997
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
1998
                }
1999
            }).build());
1✔
2000

2001
    /**
2002
     * Get the data component value by key
2003
     */
2004
    public static final IOperator OBJECT_ITEMSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_3
18✔
2005
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2006
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2007
            .function(input -> {
1✔
2008
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2009

2010
                // Skip further processing if input value is empty
2011
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2012
                if (!tagOptional.isPresent()) {
3!
2013
                    return itemStack;
×
2014
                }
2015

2016
                // Determine data component type
2017
                DataComponentType<?> dataComponentType;
2018
                try {
2019
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2020
                } catch (ResourceLocationException e) {
×
2021
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2022
                }
1✔
2023

2024
                // Encode component value
2025
                try {
2026
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2027
                    itemStack = ValueObjectTypeItemStack.ValueItemStack.of(itemStack.getRawValue().copy());
5✔
2028
                    itemStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2029
                    return itemStack;
2✔
2030
                } catch (IllegalStateException e) {
×
2031
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2032
                }
2033
            }).build());
1✔
2034

2035
    /**
2036
     * Get the tooltip of an itemstack in list form.
2037
     */
2038
    public static final IOperator OBJECT_ITEMSTACK_TOOLTIP = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
2039
            .output(ValueTypes.LIST).symbol("tooltip").operatorName("tooltip").interactName("tooltip")
8✔
2040
            .function(input -> {
1✔
2041
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2042
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
4✔
2043
                        itemStack.getRawValue().getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.Default.NORMAL).stream()
7✔
2044
                                .map(c -> ValueTypeString.ValueString.of(c.getString()))
5✔
2045
                                .toList());
1✔
2046
            }).build());
1✔
2047
    /**
2048
     * Get the tooltip of an itemstack in list form, using the provided player entity as the player context.
2049
     */
2050
    public static final IOperator OBJECT_ITEMSTACK_ENTITY_TOOLTIP = REGISTRY.register(OperatorBuilders.ENTITY_1_ITEMSTACK_1
14✔
2051
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2052
            .output(ValueTypes.LIST).symbol("entity_item_tooltip").operatorName("entityitemtooltip").interactName("entityItemTooltip")
8✔
2053
            .function(variables -> {
1✔
2054
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2055
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
2056
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2057
                    Player entity = (Player) a.getRawValue().get();
×
2058
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
×
2059
                            itemStack.getRawValue().getTooltipLines(Item.TooltipContext.of(entity.level()), entity, TooltipFlag.Default.NORMAL).stream()
×
2060
                                    .map(c -> ValueTypeString.ValueString.of(c.getString()))
×
2061
                                    .toList());
×
2062
                }
2063
                return ValueTypes.LIST.getDefault();
×
2064
            }).build());
1✔
2065

2066
    /**
2067
     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------
2068
     */
2069

2070
    /**
2071
     * If the entity is a mob
2072
     */
2073
    public static final IOperator OBJECT_ENTITY_ISMOB = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2074
            .output(ValueTypes.BOOLEAN).symbol("is_mob").operatorName("ismob").interactName("isMob")
9✔
2075
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2076
                entity -> entity instanceof Enemy
4✔
2077
            )).build());
1✔
2078

2079
    /**
2080
     * If the entity is an animal
2081
     */
2082
    public static final IOperator OBJECT_ENTITY_ISANIMAL = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2083
            .output(ValueTypes.BOOLEAN).symbol("is_animal").operatorName("isanimal").interactName("isAnimal")
9✔
2084
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2085
                entity -> entity instanceof Animal && !(entity instanceof Enemy)
11!
2086
            )).build());
1✔
2087

2088
    /**
2089
     * If the entity is an item
2090
     */
2091
    public static final IOperator OBJECT_ENTITY_ISITEM = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2092
            .output(ValueTypes.BOOLEAN).symbol("is_item").operatorName("isitem").interactName("isItem")
9✔
2093
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2094
                entity -> entity instanceof ItemEntity
4✔
2095
            )).build());
1✔
2096

2097
    /**
2098
     * If the entity is a player
2099
     */
2100
    public static final IOperator OBJECT_ENTITY_ISPLAYER = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2101
            .output(ValueTypes.BOOLEAN).symbol("is_player").operatorName("isplayer").interactName("isPlayer")
9✔
2102
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2103
                entity -> entity instanceof Player
4✔
2104
            )).build());
1✔
2105

2106
    /**
2107
     * If the entity is a minecart
2108
     */
2109
    public static final IOperator OBJECT_ENTITY_ISMINECART = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2110
            .output(ValueTypes.BOOLEAN).symbol("is_minecart").operatorName("isminecart").interactName("isMinecart")
9✔
2111
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2112
                entity -> entity instanceof AbstractMinecart
×
2113
            )).build());
1✔
2114

2115
    /**
2116
     * The itemstack from the entity
2117
     */
2118
    public static final IOperator OBJECT_ENTITY_ITEMSTACK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX
5✔
2119
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("item")
4✔
2120
            .function(variables -> {
1✔
2121
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2122
                Optional<Entity> a = valueEntity.getRawValue();
3✔
2123
                return ValueObjectTypeItemStack.ValueItemStack.of((a.isPresent() && a.get() instanceof ItemEntity) ? ((ItemEntity) a.get()).getItem() : ItemStack.EMPTY);
15!
2124
            }).build());
1✔
2125

2126
    /**
2127
     * The entity health
2128
     */
2129
    public static final IOperator OBJECT_ENTITY_HEALTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2130
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("health")
5✔
2131
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2132
                entity -> entity instanceof LivingEntity ? ((LivingEntity) entity).getHealth() : 0.0
11✔
2133
            )).build());
1✔
2134

2135
    /**
2136
     * The entity width
2137
     */
2138
    public static final IOperator OBJECT_ENTITY_WIDTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2139
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("width")
5✔
2140
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2141
                entity -> entity != null ? entity.getBbWidth() : 0.0
8!
2142
            )).build());
1✔
2143

2144
    /**
2145
     * The entity width
2146
     */
2147
    public static final IOperator OBJECT_ENTITY_HEIGHT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2148
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("height")
5✔
2149
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2150
                entity -> entity != null ? entity.getBbHeight() : 0.0
8!
2151
            )).build());
1✔
2152

2153
    /**
2154
     * If the entity is burning
2155
     */
2156
    public static final IOperator OBJECT_ENTITY_ISBURNING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2157
            .output(ValueTypes.BOOLEAN).symbol("is_burning").operatorName("isburning").interactName("entityIsBurning")
9✔
2158
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2159
                entity -> entity != null && entity.isOnFire()
10!
2160
            )).build());
1✔
2161

2162
    /**
2163
     * If the entity is wet
2164
     */
2165
    public static final IOperator OBJECT_ENTITY_ISWET = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2166
            .output(ValueTypes.BOOLEAN).symbol("is_wet").operatorName("iswet").interactName("isWet")
9✔
2167
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2168
                entity -> entity != null && entity.isInWaterOrRain()
10!
2169
            )).build());
1✔
2170

2171
    /**
2172
     * If the entity is crouching
2173
     */
2174
    public static final IOperator OBJECT_ENTITY_ISCROUCHING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2175
            .output(ValueTypes.BOOLEAN).symbol("is_crouching").operatorName("iscrouching").interactName("isCrouching")
9✔
2176
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2177
                entity -> entity != null && entity.isCrouching()
10!
2178
            )).build());
1✔
2179

2180
    /**
2181
     * If the entity is eating
2182
     */
2183
    public static final IOperator OBJECT_ENTITY_ISEATING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2184
            .output(ValueTypes.BOOLEAN).symbol("is_eating").operatorName("iseating").interactName("isEating")
9✔
2185
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2186
                entity -> entity instanceof LivingEntity && ((LivingEntity) entity).getUseItemRemainingTicks() > 0
12!
2187
            )).build());
1✔
2188

2189
    /**
2190
     * The list of armor itemstacks from an entity
2191
     */
2192
    public static final IOperator OBJECT_ENTITY_ARMORINVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2193
            .output(ValueTypes.LIST).symbol("armor_inventory").operatorName("armorinventory").interactName("armorInventory")
8✔
2194
            .function(variables -> {
1✔
2195
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2196
                Optional<Entity> a = valueEntity.getRawValue();
×
2197
                if(a.isPresent()) {
×
2198
                    Entity entity = a.get();
×
2199
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityArmorInventory(entity.level(), entity));
×
2200
                } else {
2201
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2202
                }
2203
            }).build());
1✔
2204

2205
    /**
2206
     * The list of itemstacks from an entity
2207
     */
2208
    public static final IOperator OBJECT_ENTITY_INVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2209
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
2210
            .function(variables -> {
1✔
2211
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2212
                Optional<Entity> a = valueEntity.getRawValue();
×
2213
                if(a.isPresent()) {
×
2214
                    Entity entity = a.get();
×
2215
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityInventory(entity.level(), entity));
×
2216
                } else {
2217
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2218
                }
2219
            }).build());
1✔
2220

2221
    /**
2222
     * The name of the mod owning this entity
2223
     */
2224
    public static final IOperator OBJECT_ENTITY_MODNAME = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2225
            .symbolOperatorInteract("mod")
13✔
2226
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2227
                    (OperatorBase.SafeVariablesGetter variables) -> {
2228
                        ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2229
                        if(a.getRawValue().isPresent()) {
4!
2230
                            Entity entity = a.getRawValue().get();
5✔
2231
                            return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());
5✔
2232
                        }
2233
                        return ResourceLocation.parse("");
×
2234
                    },
2235
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2236
            )))
2237
            .build());
1✔
2238

2239
    /**
2240
     * The block the given player is currently looking at.
2241
     */
2242
    public static final IOperator OBJECT_PLAYER_TARGETBLOCK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_BLOCK)
7✔
2243
            .symbol("target_block").operatorName("targetblock").interactName("targetBlock")
6✔
2244
            .function(variables -> {
1✔
2245
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2246
                BlockState blockState = null;
×
2247
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2248
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2249
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.BLOCK_INTERACTION_RANGE);
×
2250
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2251
                    double eyeHeight = entity.getEyeHeight();
×
2252
                    Vec3 lookVec = entity.getLookAngle();
×
2253
                    Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());
×
2254
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2255

2256
                    ClipContext rayTraceContext = new ClipContext(origin, direction, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity);
×
2257
                    BlockHitResult mop = entity.level().clip(rayTraceContext);
×
2258
                    if(mop != null && mop.getType() == HitResult.Type.BLOCK) {
×
2259
                        blockState = entity.level().getBlockState(mop.getBlockPos());
×
2260
                    }
2261
                }
2262
                return ValueObjectTypeBlock.ValueBlock.of(blockState);
×
2263
            }).build());
1✔
2264

2265
    /**
2266
     * The entity the given player is currently looking at.
2267
     */
2268
    public static final IOperator OBJECT_PLAYER_TARGETENTITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ENTITY)
7✔
2269
            .symbol("target_entity").operatorName("targetentity").interactName("targetEntity")
6✔
2270
            .function(variables -> {
1✔
2271
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2272
                Entity entityOut = null;
×
2273
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2274
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2275
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.ENTITY_INTERACTION_RANGE);
×
2276
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2277

2278
                    // Copied and modified from GameRenderer#getMouseOver
2279
                    Vec3 origin = entity.getEyePosition(1.0F);
×
2280
                    double reachDistanceSquared = reachDistance * reachDistance;
×
2281

2282
                    Vec3 lookVec = entity.getViewVector(1.0F);
×
2283
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2284
                    AABB boundingBox = entity.getBoundingBox()
×
2285
                            .expandTowards(lookVec.scale(reachDistance))
×
2286
                            .inflate(1.0D, 1.0D, 1.0D);
×
2287
                    EntityHitResult entityraytraceresult = ProjectileUtil.getEntityHitResult(entity, origin, direction,
×
2288
                            boundingBox, e -> !e.isSpectator() && e.isPickable(), reachDistanceSquared);
×
2289
                    if (entityraytraceresult != null) {
×
2290
                        Entity entity1 = entityraytraceresult.getEntity();
×
2291
                        Vec3 vec3d3 = entityraytraceresult.getLocation();
×
2292
                        double distanceSquared = origin.distanceToSqr(vec3d3);
×
2293
                        if (distanceSquared < reachDistanceSquared
×
2294
                                && (entity1 instanceof LivingEntity || entity1 instanceof ItemFrame)) {
2295
                            entityOut = entity1;
×
2296
                        }
2297
                    }
2298
                }
2299
                return ValueObjectTypeEntity.ValueEntity.of(entityOut);
×
2300
            }).build());
1✔
2301

2302
    /**
2303
     * If the given player has an external gui open.
2304
     */
2305
    public static final IOperator OBJECT_PLAYER_HASGUIOPEN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2306
            .symbol("has_gui_open").operatorName("hasguiopen").interactName("hasGuiOpen")
6✔
2307
            .function(variables -> {
1✔
2308
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2309
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2310
                    Player entity = (Player) a.getRawValue().get();
×
2311
                    return ValueTypeBoolean.ValueBoolean.of(entity.containerMenu != entity.inventoryMenu);
×
2312
                }
2313
                return ValueTypeBoolean.ValueBoolean.of(false);
×
2314
            }).build());
1✔
2315

2316
    /**
2317
     * The item the given entity is currently holding in its main hand.
2318
     */
2319
    public static final IOperator OBJECT_ENTITY_HELDITEM_MAIN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2320
            .symbol("held_item_1").operatorName("helditem").interactName("heldItem")
6✔
2321
            .function(variables -> {
1✔
2322
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2323
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2324
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2325
                    itemStack = ((LivingEntity) a.getRawValue().get()).getMainHandItem();
6✔
2326
                }
2327
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2328
            }).build());
1✔
2329

2330
    /**
2331
     * The item the given entity is currently holding in its off hand.
2332
     */
2333
    public static final IOperator OBJECT_ENTITY_HELDITEM_OFF = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2334
            .symbol("held_item_2").operatorName("helditemoffhand").interactName("heldItemOffHand")
6✔
2335
            .function(variables -> {
1✔
2336
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2337
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2338
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2339
                    itemStack = ((LivingEntity) a.getRawValue().get()).getOffhandItem();
6✔
2340
                }
2341
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2342
            }).build());
1✔
2343

2344
    /**
2345
     * The entity's mounted entity
2346
     */
2347
    public static final IOperator OBJECT_ENTITY_MOUNTED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.LIST)
7✔
2348
            .symbolOperator("mounted").interactName("mounted")
4✔
2349
            .function(variables -> {
1✔
2350
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2351
                List<ValueObjectTypeEntity.ValueEntity> passengers = Lists.newArrayList();
2✔
2352
                if(a.getRawValue().isPresent()) {
4!
2353
                    for (Entity passenger : a.getRawValue().get().getPassengers()) {
14✔
2354
                        passengers.add(ValueObjectTypeEntity.ValueEntity.of(passenger));
5✔
2355
                    }
1✔
2356
                }
2357
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, passengers);
4✔
2358
            }).build());
1✔
2359

2360
    /**
2361
     * The item frame's contents
2362
     */
2363
    public static final IOperator OBJECT_ITEMFRAME_CONTENTS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2364
            .symbol("itemframe_contents").operatorName("itemframecontents").interactName("itemFrameContents")
6✔
2365
            .function(variables -> {
1✔
2366
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2367
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2368
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2369
                    itemStack = ((ItemFrame) a.getRawValue().get()).getItem();
6✔
2370
                }
2371
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2372
            }).build());
1✔
2373

2374
    /**
2375
     * The item frame's rotation
2376
     */
2377
    public static final IOperator OBJECT_ITEMFRAME_ROTATION = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2378
            .symbol("itemframe_rotation").operatorName("itemframerotation").interactName("itemFrameRotation")
6✔
2379
            .function(variables -> {
1✔
2380
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2381
                Integer rotation = 0;
3✔
2382
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2383
                    rotation = ((ItemFrame) a.getRawValue().get()).getRotation();
7✔
2384
                }
2385
                return ValueTypeInteger.ValueInteger.of(rotation);
4✔
2386
            }).build());
1✔
2387

2388
    /**
2389
     * The hurtsound of this entity.
2390
     */
2391
    public static final IOperator OBJECT_ENTITY_HURTSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2392
            .symbolOperator("hurtsound").interactName("hurtSound")
4✔
2393
            .function(variables -> {
1✔
2394
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2395
                String hurtSound = "";
2✔
2396
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2397
                    String sound = ((LivingEntity) a.getRawValue().get()).getHurtSound(a.getRawValue().get().damageSources().generic()).location().toString();
14✔
2398
                    if (sound != null) {
2!
2399
                        hurtSound = sound;
2✔
2400
                    }
2401
                }
2402
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2403
            }).build());
1✔
2404

2405
    /**
2406
     * The deathsound of this entity.
2407
     */
2408
    public static final IOperator OBJECT_ENTITY_DEATHSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2409
            .symbolOperator("deathsound").interactName("deathSound")
4✔
2410
            .function(variables -> {
1✔
2411
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2412
                String hurtSound = "";
2✔
2413
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2414
                    String sound = ((LivingEntity) a.getRawValue().get()).getDeathSound().location().toString();
8✔
2415
                    if (sound != null) {
2!
2416
                        hurtSound = sound;
2✔
2417
                    }
2418
                }
2419
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2420
            }).build());
1✔
2421

2422
    /**
2423
     * The age of this entity.
2424
     */
2425
    public static final IOperator OBJECT_ENTITY_AGE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2426
            .symbolOperatorInteract("age")
2✔
2427
            .function(variables -> {
1✔
2428
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2429
                int age = 0;
2✔
2430
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2431
                    age = ((LivingEntity) a.getRawValue().get()).getNoActionTime();
6✔
2432
                }
2433
                return ValueTypeInteger.ValueInteger.of(age);
3✔
2434
            }).build());
1✔
2435

2436
    /**
2437
     * If the entity is a child.
2438
     */
2439
    public static final IOperator OBJECT_ENTITY_ISCHILD = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2440
            .symbol("is_child").operatorName("ischild").interactName("isChild")
6✔
2441
            .function(variables -> {
1✔
2442
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2443
                boolean child = false;
2✔
2444
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2445
                    child = ((LivingEntity) a.getRawValue().get()).isBaby();
6✔
2446
                }
2447
                return ValueTypeBoolean.ValueBoolean.of(child);
3✔
2448
            }).build());
1✔
2449

2450
    /**
2451
     * If the entity can be bred.
2452
     */
2453
    public static final IOperator OBJECT_ENTITY_CANBREED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2454
            .symbol("canbreed").operatorName("canbreed").interactName("canBreed")
6✔
2455
            .function(variables -> {
1✔
2456
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2457
                boolean canBreed = false;
2✔
2458
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof AgeableMob) {
9!
2459
                    canBreed = ((AgeableMob) a.getRawValue().get()).getAge() == 0;
10✔
2460
                }
2461
                return ValueTypeBoolean.ValueBoolean.of(canBreed);
3✔
2462
            }).build());
1✔
2463

2464
    /**
2465
     * If the entity is in love.
2466
     */
2467
    public static final IOperator OBJECT_ENTITY_ISINLOVE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2468
            .symbol("is_in_love").operatorName("isinlove").interactName("isInLove")
6✔
2469
            .function(variables -> {
1✔
2470
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2471
                boolean inLove = false;
2✔
2472
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof Animal) {
9!
2473
                    inLove = ((Animal) a.getRawValue().get()).isInLove();
6✔
2474
                }
2475
                return ValueTypeBoolean.ValueBoolean.of(inLove);
3✔
2476
            }).build());
1✔
2477

2478
    /**
2479
     * If the entity can be bred with the given item.
2480
     */
2481
    public static final IOperator OBJECT_ENTITY_CANBREEDWITH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
14✔
2482
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2483
            .output(ValueTypes.BOOLEAN)
2✔
2484
            .symbol("can_breed_with").operatorName("canbreedwith").interactName("canBreedWith")
6✔
2485
            .renderPattern(IConfigRenderPattern.INFIX_LONG)
2✔
2486
            .function(variables -> {
1✔
2487
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2488
                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
2489
                boolean canBreedWith = false;
2✔
2490
                if (a.getRawValue().isPresent() && !b.getRawValue().isEmpty() && a.getRawValue().get() instanceof Animal) {
13!
2491
                    canBreedWith = ((Animal) a.getRawValue().get()).isFood(b.getRawValue());
8✔
2492
                }
2493
                return ValueTypeBoolean.ValueBoolean.of(canBreedWith);
3✔
2494
            }).build());
1✔
2495

2496
    /**
2497
     * If the entity is shearable.
2498
     */
2499
    public static final IOperator OBJECT_ENTITY_ISSHEARABLE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2500
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
2501
            .function(variables -> {
1✔
2502
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2503
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
2504
                        && a.getRawValue().get() instanceof IShearable
5✔
2505
                        && ((IShearable) a.getRawValue().get()).isShearable(null, ItemStack.EMPTY, null, null));
12✔
2506
            }).build());
1✔
2507

2508
    /**
2509
     * The entity serialized to NBT.
2510
     */
2511
    public static final IOperator OBJECT_ENTITY_NBT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2512
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2513
            .function(input -> {
1✔
2514
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2515
                try {
2516
                    if (entity.getRawValue().isPresent()) {
4!
2517
                        Entity e = entity.getRawValue().get();
5✔
2518
                        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(e.problemPath(), LOGGER)) {
7✔
2519
                            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, e.level().registryAccess());
6✔
2520
                            e.save(valueOutput);
4✔
2521
                            return ValueTypeNbt.ValueNbt.of(valueOutput.buildResult());
6✔
2522
                        }
2523
                    }
2524
                } catch (Exception e) {
×
2525
                    // Catch possible errors during NBT writing
2526
                }
×
2527
                return ValueTypes.NBT.getDefault();
×
2528
            }).build());
1✔
2529

2530
    /**
2531
     * The entity type.
2532
     */
2533
    public static final IOperator OBJECT_ENTITY_TYPE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2534
            .output(ValueTypes.STRING).symbol("entity_type").operatorName("entitytype").interactName("type")
8✔
2535
            .function(input -> {
1✔
2536
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2537
                String entityType = "";
2✔
2538
                if (entity.getRawValue().isPresent()) {
4!
2539
                    Entity e = entity.getRawValue().get();
5✔
2540
                    entityType = BuiltInRegistries.ENTITY_TYPE.getKey(e.getType()).toString();
6✔
2541
                }
2542
                return ValueTypeString.ValueString.of(entityType);
3✔
2543
            }).build());
1✔
2544

2545
    /**
2546
     * The entity items.
2547
     */
2548
    public static final IOperator OBJECT_ENTITY_ITEMS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2549
            .output(ValueTypes.LIST).symbol("entity_items").operatorName("entityitems").interactName("items")
8✔
2550
            .function(input -> {
1✔
2551
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2552
                Optional<Entity> a = valueEntity.getRawValue();
×
2553
                if(a.isPresent()) {
×
2554
                    Entity entity = a.get();
×
2555
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityItems(entity.level(), entity, null));
×
2556
                } else {
2557
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.emptyList());
×
2558
                }
2559
            }).build());
1✔
2560

2561
    /**
2562
     * The entity fluids.
2563
     */
2564
    public static final IOperator OBJECT_ENTITY_FLUIDS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2565
            .output(ValueTypes.LIST).symbol("entity_fluids").operatorName("entityfluids").interactName("fluids")
8✔
2566
            .function(input -> {
1✔
2567
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2568
                Optional<Entity> a = valueEntity.getRawValue();
×
2569
                if(a.isPresent()) {
×
2570
                    Entity entity = a.get();
×
2571
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityFluids(entity.level(), entity, null));
×
2572
                } else {
2573
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, Collections.emptyList());
×
2574
                }
2575
            }).build());
1✔
2576

2577
    /**
2578
     * The entity energy stored.
2579
     */
2580
    public static final IOperator OBJECT_ENTITY_ENERGY_STORED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2581
            .output(ValueTypes.INTEGER).symbol("entity_stored_fe").operatorName("entityenergystored").interactName("energy")
8✔
2582
            .function(input -> {
1✔
2583
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2584
                Optional<Entity> a = valueEntity.getRawValue();
×
2585
                if(a.isPresent()) {
×
2586
                    Entity entity = a.get();
×
2587
                    IEnergyStorage energyStorage = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
×
2588
                    return ValueTypeInteger.ValueInteger.of(energyStorage != null ? energyStorage.getEnergyStored() : 0);
×
2589
                }
2590
                return ValueTypeInteger.ValueInteger.of(0);
×
2591
            }).build());
1✔
2592

2593
    /**
2594
     * The entity energy stored.
2595
     */
2596
    public static final IOperator OBJECT_ENTITY_ENERGY_CAPACITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2597
            .output(ValueTypes.INTEGER).symbol("entity_capacity_fe").operatorName("entityenergycapacity").interactName("energyCapacity")
8✔
2598
            .function(input -> {
1✔
2599
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2600
                Optional<Entity> a = valueEntity.getRawValue();
×
2601
                if(a.isPresent()) {
×
2602
                    Entity entity = a.get();
×
2603
                    IEnergyStorage energyStorage = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
×
2604
                    return ValueTypeInteger.ValueInteger.of(energyStorage != null ? energyStorage.getMaxEnergyStored() : 0);
×
2605
                }
2606
                return ValueTypeInteger.ValueInteger.of(0);
×
2607
            }).build());
1✔
2608

2609
    /**
2610
     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------
2611
     */
2612

2613
    /**
2614
     * The amount of fluid in the fluidstack
2615
     */
2616
    public static final IOperator OBJECT_FLUIDSTACK_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2617
            .output(ValueTypes.INTEGER).symbolOperatorInteract("amount")
5✔
2618
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2619
                    FluidStack::getAmount
2620
            )).build());
1✔
2621

2622
    /**
2623
     * The block from the fluidstack
2624
     */
2625
    public static final IOperator OBJECT_FLUIDSTACK_BLOCK = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2626
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
2627
            .function(variables -> {
1✔
2628
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2629
                FluidStack a = valueFluidStack.getRawValue();
3✔
2630
                return ValueObjectTypeBlock.ValueBlock.of(!a.isEmpty() ? a.getFluid().getFluidType().getStateForPlacement(null, null, a).createLegacyBlock() : null);
14!
2631
            }).build());
1✔
2632

2633
    /**
2634
     * The fluidstack luminosity
2635
     */
2636
    public static final IOperator OBJECT_FLUIDSTACK_LIGHT_LEVEL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2637
            .output(ValueTypes.INTEGER).symbolOperator("light_level").interactName("lightLevel")
7✔
2638
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2639
                fluidStack -> fluidStack.getFluid().getFluidType().getLightLevel(fluidStack)
7✔
2640
            )).build());
1✔
2641

2642
    /**
2643
     * The fluidstack density
2644
     */
2645
    public static final IOperator OBJECT_FLUIDSTACK_DENSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2646
            .output(ValueTypes.INTEGER).symbolOperatorInteract("density")
5✔
2647
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2648
                fluidStack -> fluidStack.getFluid().getFluidType().getDensity(fluidStack)
7✔
2649
            )).build());
1✔
2650

2651
    /**
2652
     * The fluidstack temperature
2653
     */
2654
    public static final IOperator OBJECT_FLUIDSTACK_TEMPERATURE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2655
            .output(ValueTypes.INTEGER).symbolOperatorInteract("temperature")
5✔
2656
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2657
                    fluidStack -> fluidStack.getFluid().getFluidType().getTemperature(fluidStack)
7✔
2658
            )).build());
1✔
2659

2660
    /**
2661
     * The fluidstack viscosity
2662
     */
2663
    public static final IOperator OBJECT_FLUIDSTACK_VISCOSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2664
            .output(ValueTypes.INTEGER).symbolOperatorInteract("viscosity")
5✔
2665
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2666
                fluidStack -> fluidStack.getFluid().getFluidType().getViscosity(fluidStack)
7✔
2667
            )).build());
1✔
2668

2669
    /**
2670
     * If the fluidstack is gaseous
2671
     */
2672
    public static final IOperator OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2673
            .output(ValueTypes.BOOLEAN).symbolOperator("lighter_than_air").interactName("isLighterThanAir")
7✔
2674
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_BOOLEAN.build(
2✔
2675
                fluidStack -> fluidStack.getFluid().getFluidType().isLighterThanAir()
6✔
2676
            )).build());
1✔
2677

2678
    /**
2679
     * The rarity of the fluidstack
2680
     */
2681
    public static final IOperator OBJECT_FLUIDSTACK_RARITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2682
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
2683
            .function(variables -> {
1✔
2684
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2685
                FluidStack a = valueFluidStack.getRawValue();
3✔
2686
                return ValueTypeString.ValueString.of(a.getFluid().getFluidType().getRarity(a).name());
8✔
2687
            }).build());
1✔
2688

2689
    /**
2690
     * The bucket empty sound of the fluidstack
2691
     */
2692
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2693
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_empty").interactName("bucketEmptySound")
6✔
2694
            .function(variables -> {
1✔
2695
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2696
                FluidStack a = valueFluidStack.getRawValue();
3✔
2697
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_EMPTY))
10✔
2698
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2699
                        .orElse(""));
2✔
2700
            }).build());
1✔
2701

2702
    /**
2703
     * The fluid vaporize sound of the fluidstack
2704
     */
2705
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2706
            .output(ValueTypes.STRING).symbolOperator("sound_fluid_vaporize").interactName("fluidVaporizeSound")
6✔
2707
            .function(variables -> {
1✔
2708
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2709
                FluidStack a = valueFluidStack.getRawValue();
3✔
2710
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.FLUID_VAPORIZE))
10✔
2711
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2712
                        .orElse(""));
2✔
2713
            }).build());
1✔
2714

2715
    /**
2716
     * The bucket fill sound of the fluidstack
2717
     */
2718
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2719
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_fill").interactName("bucketFillSound")
6✔
2720
            .function(variables -> {
1✔
2721
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2722
                FluidStack a = valueFluidStack.getRawValue();
3✔
2723
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_FILL))
10✔
2724
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2725
                        .orElse(""));
2✔
2726
            }).build());
1✔
2727

2728
    /**
2729
     * The bucket of the fluidstack
2730
     */
2731
    public static final IOperator OBJECT_FLUIDSTACK_BUCKET = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2732
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("bucket")
4✔
2733
            .function(variables -> {
1✔
2734
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2735
                FluidStack a = valueFluidStack.getRawValue();
3✔
2736
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getFluid().getFluidType().getBucket(a));
7✔
2737
            }).build());
1✔
2738

2739
    /**
2740
     * If the fluid types of the two given fluidstacks are equal
2741
     */
2742
    public static final IOperator OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
5✔
2743
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwfluidequal").interactName("isRawEqual")
8✔
2744
            .function(variables -> {
1✔
2745
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack0 = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2746
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack1 = variables.getValue(1, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2747
                return ValueTypeBoolean.ValueBoolean.of(FluidStack.isSameFluid(valueFluidStack0.getRawValue(), valueFluidStack1.getRawValue()));
7✔
2748
            }).build());
1✔
2749

2750
    /**
2751
     * The name of the mod owning this fluid
2752
     */
2753
    public static final IOperator OBJECT_FLUIDSTACK_MODNAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2754
            .symbolOperatorInteract("mod")
13✔
2755
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2756
                    (OperatorBase.SafeVariablesGetter variables) -> {
2757
                        ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2758
                        return BuiltInRegistries.FLUID.getKey(a.getRawValue().getFluid());
6✔
2759
                    },
2760
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2761
            ))).build());
1✔
2762

2763
    /**
2764
     * The tag of the given fluidstack.
2765
     */
2766
    public static final IOperator OBJECT_FLUIDSTACK_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2767
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2768
            .function(input -> {
1✔
2769
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2770
                if (fluidStack.getRawValue().getComponentsPatch().isEmpty()) {
5✔
2771
                    return ValueTypeNbt.ValueNbt.of(Optional.empty());
3✔
2772
                }
2773
                return ValueTypeNbt.ValueNbt.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), fluidStack.getRawValue().getComponentsPatch()).getOrThrow());
13✔
2774
            }).build());
1✔
2775

2776
    /**
2777
     * Create a new fluidstack with the given amount.
2778
     */
2779
    public static final IOperator OBJECT_FLUIDSTACK_WITH_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
14✔
2780
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.INTEGER)
2✔
2781
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("with_amount").interactName("withAmount")
6✔
2782
            .function(variables -> {
1✔
2783
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2784
                ValueTypeInteger.ValueInteger valueInteger = variables.getValue(1, ValueTypes.INTEGER);
6✔
2785
                FluidStack fluidStack = valueFluidStack.getRawValue().copy();
4✔
2786
                fluidStack.setAmount(valueInteger.getRawValue());
4✔
2787
                return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
2788
            }).build());
1✔
2789

2790
    /**
2791
     * Get the data component keys of an fluidstack.
2792
     */
2793
    public static final IOperator OBJECT_FLUIDSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2794
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
2795
            .function(input -> {
1✔
2796
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2797
                // Explicitly check for fluid emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2798
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
2799
                        fluidStack.getRawValue().isEmpty() ?
4!
2800
                                Lists.newArrayList() :
×
2801
                                fluidStack.getRawValue().getComponents().keySet().stream()
5✔
2802
                                        .filter(c -> !c.isTransient())
8!
2803
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
2804
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
1✔
2805
                                        .toList());
2✔
2806
            }).build());
1✔
2807

2808
    /**
2809
     * Get the data component value by key
2810
     */
2811
    public static final IOperator OBJECT_FLUIDSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2_LONG
14✔
2812
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING)
2✔
2813
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
2814
            .function(input -> {
1✔
2815
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2816

2817
                // Determine data component type
2818
                DataComponentType<?> dataComponentType;
2819
                try {
2820
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2821
                } catch (ResourceLocationException e) {
×
2822
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2823
                }
1✔
2824

2825
                // Fetch component value
2826
                TypedDataComponent<?> typedComponent = fluidStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
2827
                if (typedComponent == null) {
2✔
2828
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
2829
                }
2830

2831
                // Encode component value
2832
                try {
2833
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
2834
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
2835
                } catch (IllegalStateException e) {
×
2836
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
2837
                }
2838
            }).build());
1✔
2839

2840
    /**
2841
     * Set the data component value by key
2842
     */
2843
    public static final IOperator OBJECT_FLUIDSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_3
18✔
2844
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2845
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2846
            .function(input -> {
1✔
2847
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2848

2849
                // Skip further processing if input value is empty
2850
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2851
                if (!tagOptional.isPresent()) {
3!
2852
                    return fluidStack;
×
2853
                }
2854

2855
                // Determine data component type
2856
                DataComponentType<?> dataComponentType;
2857
                try {
2858
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2859
                } catch (ResourceLocationException e) {
×
2860
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2861
                }
1✔
2862

2863
                // Encode component value
2864
                try {
2865
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2866
                    fluidStack = ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack.getRawValue().copy());
5✔
2867
                    fluidStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2868
                    return fluidStack;
2✔
2869
                } catch (IllegalStateException e) {
×
2870
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2871
                }
2872
            }).build());
1✔
2873

2874
    /**
2875
     * The tag entries of the given fluidstack
2876
     */
2877
    public static final IOperator OBJECT_FLUIDSTACK_TAG = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2878
            .output(ValueTypes.LIST)
2✔
2879
            .symbol("fluid_tag_names").operatorName("tag").interactName("tags")
6✔
2880
            .function(variables -> {
1✔
2881
                ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2882
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
2883
                if(!a.getRawValue().isEmpty()) {
4✔
2884
                    a.getRawValue().getFluid().builtInRegistryHolder().tags()
7✔
2885
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
2886
                                    .of(owningTag.location().toString())));
3✔
2887
                }
2888
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
2889
            }).build());
1✔
2890

2891
    /**
2892
     * Get a list of fluidstacks that correspond to the given tag key.
2893
     */
2894
    public static final IOperator OBJECT_FLUIDSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
2895
            .output(ValueTypes.LIST)
2✔
2896
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2897
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2898
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
2899
            .function(variables -> {
1✔
2900
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
2901
                ImmutableList.Builder<ValueObjectTypeFluidStack.ValueFluidStack> builder = ImmutableList.builder();
2✔
2902
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
2903
                    try {
2904
                        Helpers.getFluidTagValues(a.getRawValue())
4✔
2905
                                .map(ValueObjectTypeFluidStack.ValueFluidStack::of)
3✔
2906
                                .forEach(builder::add);
4✔
2907
                    } catch (ResourceLocationException e) {
×
2908
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
2909
                    }
1✔
2910
                }
2911
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, builder.build());
5✔
2912
            }).build());
1✔
2913

2914
    /**
2915
     * Get an fluid by name.
2916
     */
2917
    public static final IOperator OBJECT_FLUIDSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_PREFIX_LONG
5✔
2918
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_FLUIDSTACK)
4✔
2919
            .symbol("fluid_by_name").operatorName("fluidbyname").interactName("fluidByName")
7✔
2920
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
2921
                    .build(input -> {
1✔
2922
                        Fluid fluid = BuiltInRegistries.FLUID.getValue(input);
5✔
2923
                        FluidStack fluidStack = FluidStack.EMPTY;
2✔
2924
                        if (fluid != null) {
2!
2925
                            fluidStack = new FluidStack(fluid, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());
8✔
2926
                        }
2927
                        return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
2928
                    })).build());
1✔
2929

2930
    /**
2931
     * ----------------------------------- OPERATOR OPERATORS -----------------------------------
2932
     */
2933

2934
    /**
2935
     * Apply for a given operator a given value.
2936
     */
2937
    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2938
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2939
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply")
9✔
2940
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY))
4✔
2941
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2942
                    input -> {
2943
                        IOperator innerOperator = input.getLeft();
4✔
2944
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2945
                        IVariable variable = variables.getVariables()[0];
5✔
2946
                        return ValueHelpers.evaluateOperator(innerOperator, variable);
9✔
2947
                    })).build());
1✔
2948
    static {
2949
        REGISTRY.registerSerializer(new CurriedOperator.Serializer());
5✔
2950
    }
2951

2952
    /**
2953
     * Apply for a given operator the given 2 values.
2954
     */
2955
    public static final IOperator OPERATOR_APPLY_2 = REGISTRY.register(OperatorBuilders.OPERATOR
5✔
2956
            .renderPattern(IConfigRenderPattern.INFIX_2)
2✔
2957
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
15✔
2958
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
2959
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply2")
13✔
2960
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
2961
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2962
                    input -> {
2963
                        IOperator innerOperator = input.getLeft();
4✔
2964
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2965
                        IVariable variable0 = variables.getVariables()[0];
5✔
2966
                        IVariable variable1 = variables.getVariables()[1];
5✔
2967
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1);
13✔
2968
                    })).build());
1✔
2969

2970
    /**
2971
     * Apply for a given operator the given 3 values.
2972
     */
2973
    public static final IOperator OPERATOR_APPLY_3 = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2974
            .renderPattern(IConfigRenderPattern.INFIX_3)
2✔
2975
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
19✔
2976
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
2977
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply3")
17✔
2978
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
2979
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2980
                    input -> {
2981
                        IOperator innerOperator = input.getLeft();
4✔
2982
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2983
                        IVariable variable0 = variables.getVariables()[0];
5✔
2984
                        IVariable variable1 = variables.getVariables()[1];
5✔
2985
                        IVariable variable2 = variables.getVariables()[2];
5✔
2986
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1, variable2);
17✔
2987
                    })).build());
1✔
2988

2989
    /**
2990
     * Apply for a given operator the given list of values.
2991
     */
2992
    public static final IOperator OPERATOR_APPLY_N = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2993
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST)
11✔
2994
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.LIST)
2✔
2995
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply_n")
9✔
2996
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.LIST))
4✔
2997
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
2998
                    input -> {
2999
                        IOperator innerOperator = input.getLeft();
4✔
3000
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3001
                        IValueTypeListProxy<IValueType<IValue>, IValue> list = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
3002
                        return ValueHelpers.evaluateOperator(innerOperator, Iterables.toArray(list, IValue.class));
7✔
3003
                    })).build());
1✔
3004

3005
    /**
3006
     * Apply for a given operator with zero arguments.
3007
     */
3008
    public static final IOperator OPERATOR_APPLY_0 = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3009
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
3010
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply0")
5✔
3011
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(new IValueType[0]))
4✔
3012
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3013
                    input -> {
3014
                        IOperator innerOperator = input.getLeft();
4✔
3015
                        return ValueHelpers.evaluateOperator(innerOperator, new IVariable[0]);
5✔
3016
                    })).build());
1✔
3017

3018
    /**
3019
     * Apply the given operator on all elements of a list, resulting in a new list of mapped values.
3020
     */
3021
    public static final IOperator OPERATOR_MAP = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3022
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3023
            .output(ValueTypes.LIST).symbolOperatorInteract("map")
5✔
3024
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3025
                    input -> {
3026
                        final IOperator innerOperator = input.getLeft();
4✔
3027
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3028
                        ValueTypeList.ValueList inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3029
                        return ValueTypeList.ValueList.ofFactory(
6✔
3030
                                new ValueTypeListProxyOperatorMapped(innerOperator, inputList.getRawValue()));
2✔
3031
                    })).build());
1✔
3032

3033
    /**
3034
     * Filter a list of elements by matching them all with the given predicate.
3035
     */
3036
    public static final IOperator OPERATOR_FILTER = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3037
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3038
            .output(ValueTypes.LIST).symbolOperatorInteract("filter")
7✔
3039
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3040
                    new IOperatorValuePropagator<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue>() {
3✔
3041
                        @Override
3042
                        public IValue getOutput(Pair<IOperator, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3043
                            final IOperator innerOperator = input.getLeft();
4✔
3044
                            OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3045
                            ValueTypeList.ValueList<?, ?> inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3046
                            List<IValue> filtered = Lists.newArrayList();
2✔
3047
                            for (IValue value : inputList.getRawValue()) {
11✔
3048
                                IValue result = ValueHelpers.evaluateOperator(innerOperator, value);
9✔
3049
                                ValueHelpers.validatePredicateOutput(innerOperator, result);
3✔
3050
                                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
3051
                                    filtered.add(value);
4✔
3052
                                }
3053
                            }
1✔
3054
                            IValueType valueType = inputList.getRawValue().getValueType();
4✔
3055
                            return ValueTypeList.ValueList.ofList(valueType, filtered);
4✔
3056
                        }
3057
                    })).build());
1✔
3058

3059
    /**
3060
     * Takes the conjunction of two predicates.
3061
     */
3062
    public static final IOperator OPERATOR_CONJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3063
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3064
            .output(ValueTypes.OPERATOR).symbol(".&&.").operatorInteract("conjunction")
7✔
3065
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3066
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Conjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3067
            )).build());
1✔
3068
    static {
3069
        REGISTRY.registerSerializer(new CombinedOperator.Conjunction.Serializer());
5✔
3070
    }
3071

3072
    /**
3073
     * Takes the disjunction of two predicates.
3074
     */
3075
    public static final IOperator OPERATOR_DISJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3076
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3077
            .output(ValueTypes.OPERATOR).symbol(".||.").operatorInteract("disjunction")
7✔
3078
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3079
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Disjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3080
            )).build());
1✔
3081
    static {
3082
        REGISTRY.registerSerializer(new CombinedOperator.Disjunction.Serializer());
5✔
3083
    }
3084

3085
    /**
3086
     * Takes the negation of a predicate.
3087
     */
3088
    public static final IOperator OPERATOR_NEGATION = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3089
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3090
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3091
            .output(ValueTypes.OPERATOR).symbol("!.").operatorInteract("negation")
7✔
3092
            .function(OperatorBuilders.FUNCTION_ONE_PREDICATE.build(
2✔
3093
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Negation.asOperator(input))
4✔
3094
            )).build());
1✔
3095
    static {
3096
        REGISTRY.registerSerializer(new CombinedOperator.Negation.Serializer());
5✔
3097
    }
3098

3099
    /**
3100
     * Create a new operator that pipes the output from the first operator to the second operator.
3101
     */
3102
    public static final IOperator OPERATOR_PIPE = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3103
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3104
            .output(ValueTypes.OPERATOR).symbol(".").operatorInteract("pipe")
7✔
3105
            .function(OperatorBuilders.FUNCTION_TWO_OPERATORS.build(
2✔
3106
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe.asOperator(input.getLeft(), input.getRight()))
17✔
3107
            )).build());
1✔
3108
    static {
3109
        REGISTRY.registerSerializer(new CombinedOperator.Pipe.Serializer());
5✔
3110
    }
3111

3112
    /**
3113
     * Create a new operator that gives its input to the first and second operators, and pipes the outputs from both of them to the third operator.
3114
     */
3115
    public static final IOperator OPERATOR_PIPE2 = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3116
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3117
            .renderPattern(IConfigRenderPattern.INFIX_2_LATE)
2✔
3118
            .output(ValueTypes.OPERATOR).symbol(".2").operatorInteract("pipe2")
7✔
3119
            .function(OperatorBuilders.FUNCTION_THREE_OPERATORS.build(
2✔
3120
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe2.asOperator(input.getLeft(), input.getMiddle(), input.getRight()))
23✔
3121
            )).build());
1✔
3122
    static {
3123
        REGISTRY.registerSerializer(new CombinedOperator.Pipe2.Serializer());
5✔
3124
    }
3125

3126
    /**
3127
     * Flip the input parameters of an operator with two inputs.
3128
     */
3129
    public static final IOperator OPERATOR_FLIP = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3130
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3131
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3132
            .output(ValueTypes.OPERATOR).symbolOperatorInteract("flip")
5✔
3133
            .function(OperatorBuilders.FUNCTION_ONE_OPERATOR.build(
2✔
3134
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Flip.asOperator(input))
4✔
3135
            )).build());
1✔
3136
    static {
3137
        REGISTRY.registerSerializer(new CombinedOperator.Flip.Serializer());
5✔
3138
    }
3139

3140
    /**
3141
     * Apply the given operator on all elements of a list to reduce the list to one value.
3142
     */
3143
    public static final IOperator OPERATOR_REDUCE = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3144
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
3145
            .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
2✔
3146
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce")
4✔
3147
            .conditionalOutputTypeDeriver((operator, input) -> input[2].getType())
7✔
3148
            .function(variables -> {
1✔
3149
                IValue accumulator = variables.getValue(2);
4✔
3150
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3151
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3152
                ValueTypeList.ValueList<IValueType<IValue>, IValue> inputList = variables.getValue(1, ValueTypes.LIST);
6✔
3153
                for (IValue listValue : inputList.getRawValue()) {
11✔
3154
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3155
                }
1✔
3156
                return accumulator;
2✔
3157
            }).build());
1✔
3158

3159
    /**
3160
     * Apply the given operator on all elements of a list to reduce the list to one value.
3161
     */
3162
    public static final IOperator OPERATOR_REDUCE1 = REGISTRY.register(OperatorBuilders.OPERATOR
14✔
3163
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3164
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
3165
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce1")
4✔
3166
            .conditionalOutputTypeDeriver((operator, input) -> {
2✔
3167
                try {
3168
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[1].getValue()).getRawValue();
7✔
3169
                    return a.getValueType();
3✔
3170
                } catch (EvaluationException e) {
×
3171
                    return operator.getOutputType();
×
3172
                }
3173
            })
3174
            .function(variables -> {
1✔
3175
                ValueTypeList.ValueList valueList = variables.getValue(1, ValueTypes.LIST);
6✔
3176
                Iterator<IValue> iter = valueList.getRawValue().iterator();
4✔
3177
                if (!iter.hasNext()) {
3✔
3178
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REDUCE_EMPTY));
6✔
3179
                }
3180

3181
                IValue accumulator = iter.next();
4✔
3182
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3183
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3184

3185
                while (iter.hasNext()) {
3✔
3186
                    IValue listValue = iter.next();
4✔
3187
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3188
                }
1✔
3189
                return accumulator;
2✔
3190
            }).build());
1✔
3191

3192
    /**
3193
     * Apply for a given operator a given value.
3194
     */
3195
    public static final IOperator OPERATOR_BY_NAME = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3196
            .inputType(ValueTypes.STRING).output(ValueTypes.OPERATOR)
4✔
3197
            .symbol("op_by_name").operatorName("by_name").interactName("operatorByName")
6✔
3198
            .function(input -> {
1✔
3199
                ValueTypeString.ValueString name = input.getValue(0, ValueTypes.STRING);
6✔
3200
                try {
3201
                    ResourceLocation id = ResourceLocation.parse(name.getRawValue());
4✔
3202
                    IOperator operator = Operators.REGISTRY.getOperator(id);
4✔
3203
                    if (operator == null) {
2✔
3204
                        throw new EvaluationException(Component.translatable(
11✔
3205
                                L10NValues.OPERATOR_ERROR_OPERATORNOTFOUND, name.getRawValue()));
2✔
3206
                    }
3207
                    return ValueTypeOperator.ValueOperator.of(operator);
3✔
3208
                } catch (ResourceLocationException e) {
×
3209
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
3210
                }
3211
            }).build());
1✔
3212

3213
    /**
3214
     * ----------------------------------- NBT OPERATORS -----------------------------------
3215
     */
3216

3217
    /**
3218
     * The number of entries in an NBT tag
3219
     */
3220
    public static final IOperator NBT_COMPOUND_SIZE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3221
            .output(ValueTypes.INTEGER).operatorName("compound_size").symbol("NBT{}.size").interactName("size")
9✔
3222
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_TO_INT.build(
2✔
3223
                opt -> opt.map(CompoundTag::size).orElse(0)
8✔
3224
            )).build());
1✔
3225

3226
    /**
3227
     * The list of keys in an NBT tag
3228
     */
3229
    public static final IOperator NBT_COMPOUND_KEYS = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3230
            .output(ValueTypes.LIST).operatorName("compound_keys").symbol("NBT{}.keys").interactName("keys")
8✔
3231
            .function(variables -> {
1✔
3232
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3233
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtKeys(value.getRawValue()));
7✔
3234
            }).build());
1✔
3235

3236
    /**
3237
     * If an NBT tag has the given key
3238
     */
3239
    public static final IOperator NBT_COMPOUND_HASKEY = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3240
            .output(ValueTypes.BOOLEAN).operatorName("compound_haskey").symbol("NBT{}.has_key").interactName("hasKey")
9✔
3241
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3242
                    Optional::isPresent
3243
            )).build());
1✔
3244

3245
    /**
3246
     * The NBT value type of an entry
3247
     */
3248
    public static final IOperator NBT_COMPOUND_VALUE_TYPE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3249
            .output(ValueTypes.STRING).operatorName("compound_type").symbol("NBT{}.type").interactName("type")
9✔
3250
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(tag -> {
2✔
3251
                if (tag.isPresent()) {
3✔
3252
                    try {
3253
                        return TagTypes.getType(tag.get().getId()).getName();
7✔
3254
                    } catch (IndexOutOfBoundsException e) {
×
3255

3256
                    }
3257
                }
3258
                return "null";
2✔
3259
            })).build());
1✔
3260

3261
    /**
3262
     * The NBT tag value
3263
     */
3264
    public static final IOperator NBT_COMPOUND_VALUE_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3265
            .output(ValueTypes.NBT).operatorName("compound_value_tag").symbol("NBT{}.get_tag").interactName("getTag")
9✔
3266
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(o -> o)).build());
5✔
3267

3268
    /**
3269
     * The NBT boolean value
3270
     */
3271
    public static final IOperator NBT_COMPOUND_VALUE_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3272
            .output(ValueTypes.BOOLEAN).operatorName("compound_value_boolean").symbol("NBT{}.get_boolean").interactName("getBoolean")
9✔
3273
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3274
                    o -> o.flatMap(Tag::asBoolean).orElse(false)
8✔
3275
            )).build());
1✔
3276

3277
    /**
3278
     * The NBT integer value
3279
     */
3280
    public static final IOperator NBT_COMPOUND_VALUE_INTEGER = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3281
            .output(ValueTypes.INTEGER).operatorName("compound_value_integer").symbol("NBT{}.get_integer").interactName("getInteger")
9✔
3282
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_INT.build(
2✔
3283
                    o -> o.flatMap(Tag::asInt).orElse(0)
8✔
3284
            )).build());
1✔
3285

3286
    /**
3287
     * The NBT long value
3288
     */
3289
    public static final IOperator NBT_COMPOUND_VALUE_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3290
            .output(ValueTypes.LONG).operatorName("compound_value_long").symbol("NBT{}.get_long").interactName("getLong")
9✔
3291
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG.build(
2✔
3292
                    o -> o.flatMap(Tag::asLong).orElse(0L)
8✔
3293
            )).build());
1✔
3294

3295
    /**
3296
     * The NBT double value
3297
     */
3298
    public static final IOperator NBT_COMPOUND_VALUE_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3299
            .output(ValueTypes.DOUBLE).operatorName("compound_value_double").symbol("NBT{}.get_double").interactName("getDouble")
9✔
3300
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE.build(
2✔
3301
                    o -> o.flatMap(Tag::asDouble).orElse(0D)
8✔
3302
            )).build());
1✔
3303

3304
    /**
3305
     * The NBT string value
3306
     */
3307
    public static final IOperator NBT_COMPOUND_VALUE_STRING = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3308
            .output(ValueTypes.STRING).operatorName("compound_value_string").symbol("NBT{}.get_string").interactName("getString")
9✔
3309
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(
2✔
3310
                    o -> o.flatMap(Tag::asString).orElse("")
7✔
3311
            )).build());
1✔
3312

3313
    /**
3314
     * The NBT compound value
3315
     */
3316
    public static final IOperator NBT_COMPOUND_VALUE_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3317
            .output(ValueTypes.NBT).operatorName("compound_value_compound").symbol("NBT{}.get_compound").interactName("getCompound")
9✔
3318
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(
2✔
3319
                    o -> o.map(tag -> tag instanceof CompoundTag ? (CompoundTag) tag : new CompoundTag())
11!
3320
            )).build());
1✔
3321

3322
    /**
3323
     * The NBT tag list value
3324
     */
3325
    public static final IOperator NBT_COMPOUND_VALUE_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3326
            .output(ValueTypes.LIST).operatorName("compound_value_list_tag").symbol("NBT{}.get_list_tag").interactName("getListTag")
8✔
3327
            .function(variables -> {
1✔
3328
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3329
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3330
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListTag(key.getRawValue(), value.getRawValue()));
9✔
3331
            }).build());
1✔
3332

3333
    /**
3334
     * The NBT boolean list value
3335
     */
3336
    public static final IOperator NBT_COMPOUND_VALUE_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3337
            .output(ValueTypes.LIST).operatorName("compound_value_list_byte").symbol("NBT{}.get_list_byte").interactName("getListByte")
8✔
3338
            .function(variables -> {
1✔
3339
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3340
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3341
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListByte(key.getRawValue(), value.getRawValue()));
9✔
3342
            }).build());
1✔
3343

3344
    /**
3345
     * The NBT int list value
3346
     */
3347
    public static final IOperator NBT_COMPOUND_VALUE_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3348
            .output(ValueTypes.LIST).operatorName("compound_value_list_int").symbol("NBT{}.get_list_int").interactName("getListInt")
8✔
3349
            .function(variables -> {
1✔
3350
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3351
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3352
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListInt(key.getRawValue(), value.getRawValue()));
9✔
3353
            }).build());
1✔
3354

3355
    /**
3356
     * The NBT long list value
3357
     */
3358
    public static final IOperator NBT_COMPOUND_VALUE_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3359
            .output(ValueTypes.LIST).operatorName("compound_value_list_long").symbol("NBT{}.get_list_long").interactName("getListLong")
8✔
3360
            .function(variables -> {
1✔
3361
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3362
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3363
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListLong(key.getRawValue(), value.getRawValue()));
9✔
3364
            }).build());
1✔
3365

3366
    /**
3367
     * Remove an entry from an NBT compound
3368
     */
3369
    public static final IOperator NBT_COMPOUND_WITHOUT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3370
            .output(ValueTypes.NBT).operatorName("compound_without").symbol("NBT{}.without").interactName("without")
8✔
3371
            .function(variables -> {
1✔
3372
                ValueTypeNbt.ValueNbt valueNbt = variables.getValue(0, ValueTypes.NBT);
6✔
3373
                Optional<Tag> tag = valueNbt.getRawValue();
3✔
3374
                if (tag.isPresent()) {
3!
3375
                    if (!(tag.get() instanceof CompoundTag)) {
4!
3376
                        return ValueTypeNbt.ValueNbt.of();
×
3377
                    }
3378
                    ValueTypeString.ValueString valueString = variables.getValue(1, ValueTypes.STRING);
6✔
3379
                    String key = valueString.getRawValue();
3✔
3380
                    CompoundTag tagCompound = (CompoundTag) tag.get();
4✔
3381
                    if (tagCompound.contains(key)) {
4!
3382
                        // Copy the tag to ensure immutability
3383
                        tagCompound = tagCompound.copy();
3✔
3384
                        tagCompound.remove(key);
3✔
3385
                    }
3386
                    return ValueTypeNbt.ValueNbt.of(tagCompound);
3✔
3387
                }
3388
                return valueNbt;
×
3389
            }).build());
1✔
3390

3391

3392

3393
    /**
3394
     * Set an NBT compound boolean value
3395
     */
3396
    public static final IOperator NBT_COMPOUND_WITH_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3397
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3398
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.BOOLEAN)
2✔
3399
            .operatorName("compound_with_boolean").symbol("NBT{}.with_boolean").interactName("withBoolean")
7✔
3400
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3401
                ValueTypeBoolean.ValueBoolean value = input.getRight().getValue(0, ValueTypes.BOOLEAN);
8✔
3402
                input.getLeft().ifPresent(tag -> tag.putBoolean(input.getMiddle(), value.getRawValue()));
15✔
3403
                return input.getLeft();
4✔
3404
            })).build());
1✔
3405

3406
    /**
3407
     * Set an NBT compound short value
3408
     */
3409
    public static final IOperator NBT_COMPOUND_WITH_SHORT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3410
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3411
            .operatorName("compound_with_short").symbol("NBT{}.with_short").interactName("withShort")
7✔
3412
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3413
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3414
                input.getLeft().ifPresent(tag -> tag.putShort(input.getMiddle(), (short) value.getRawValue()));
16✔
3415
                return input.getLeft();
4✔
3416
            })).build());
1✔
3417

3418
    /**
3419
     * Set an NBT compound integer value
3420
     */
3421
    public static final IOperator NBT_COMPOUND_WITH_INTEGER = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3422
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3423
            .operatorName("compound_with_integer").symbol("NBT{}.with_integer").interactName("withInteger")
7✔
3424
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3425
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3426
                input.getLeft().ifPresent(tag -> tag.putInt(input.getMiddle(), value.getRawValue()));
15✔
3427
                return input.getLeft();
4✔
3428
            })).build());
1✔
3429

3430
    /**
3431
     * Set an NBT compound long value
3432
     */
3433
    public static final IOperator NBT_COMPOUND_WITH_LONG = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3434
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LONG)
2✔
3435
            .operatorName("compound_with_long").symbol("NBT{}.with_long").interactName("withLong")
7✔
3436
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3437
                ValueTypeLong.ValueLong value = input.getRight().getValue(0, ValueTypes.LONG);
8✔
3438
                input.getLeft().ifPresent(tag -> tag.putLong(input.getMiddle(), value.getRawValue()));
15✔
3439
                return input.getLeft();
4✔
3440
            })).build());
1✔
3441

3442
    /**
3443
     * Set an NBT compound double value
3444
     */
3445
    public static final IOperator NBT_COMPOUND_WITH_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3446
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3447
            .operatorName("compound_with_double").symbol("NBT{}.with_double").interactName("withDouble")
7✔
3448
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3449
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3450
                input.getLeft().ifPresent(tag -> tag.putDouble(input.getMiddle(), value.getRawValue()));
15✔
3451
                return input.getLeft();
4✔
3452
            })).build());
1✔
3453

3454
    /**
3455
     * Set an NBT compound float value
3456
     */
3457
    public static final IOperator NBT_COMPOUND_WITH_FLOAT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3458
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3459
            .operatorName("compound_with_float").symbol("NBT{}.with_float").interactName("withFloat")
7✔
3460
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3461
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3462
                input.getLeft().ifPresent(tag -> tag.putFloat(input.getMiddle(), (float) value.getRawValue()));
16✔
3463
                return input.getLeft();
4✔
3464
            })).build());
1✔
3465

3466
    /**
3467
     * Set an NBT compound string value
3468
     */
3469
    public static final IOperator NBT_COMPOUND_WITH_STRING = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3470
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING)
2✔
3471
            .operatorName("compound_with_string").symbol("NBT{}.with_string").interactName("withString")
7✔
3472
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3473
                ValueTypeString.ValueString value = input.getRight().getValue(0, ValueTypes.STRING);
8✔
3474
                input.getLeft().ifPresent(tag -> tag.putString(input.getMiddle(), value.getRawValue()));
15✔
3475
                return input.getLeft();
4✔
3476
            })).build());
1✔
3477

3478
    /**
3479
     * Set an NBT compound compound value
3480
     */
3481
    public static final IOperator NBT_COMPOUND_WITH_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3482
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.NBT)
2✔
3483
            .operatorName("compound_with_tag").symbol("NBT{}.with_tag").interactName("withTag")
7✔
3484
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3485
                ValueTypeNbt.ValueNbt value = input.getRight().getValue(0, ValueTypes.NBT);
8✔
3486
                input.getLeft()
6✔
3487
                        .ifPresent(tag -> value.getRawValue()
7✔
3488
                                .ifPresent(v -> tag.put(input.getMiddle(), v)));
9✔
3489
                return input.getLeft();
4✔
3490
            })).build());
1✔
3491

3492
    /**
3493
     * Set an NBT compound tag list value
3494
     */
3495
    public static final IOperator NBT_COMPOUND_WITH_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3496
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3497
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3498
            .operatorName("compound_with_list_tag").symbol("NBT{}.with_tag_list").interactName("withTagList")
9✔
3499
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3500
                @Override
3501
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3502
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3503
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3504
                            NbtHelpers.getListNbtTag(value, NBT_COMPOUND_WITH_LIST_TAG.getLocalizedNameFull())));
2✔
3505
                    return input.getLeft();
4✔
3506
                }
3507
            })).build());
1✔
3508

3509
    /**
3510
     * Set an NBT compound byte list value
3511
     */
3512
    public static final IOperator NBT_COMPOUND_WITH_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3513
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3514
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3515
            .operatorName("compound_with_list_byte").symbol("NBT{}.with_byte_list").interactName("withByteList")
9✔
3516
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3517
                @Override
3518
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3519
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3520
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3521
                            NbtHelpers.getListNbtByte(value, NBT_COMPOUND_WITH_LIST_BYTE.getLocalizedNameFull())));
2✔
3522
                    return input.getLeft();
4✔
3523
                }
3524
            })).build());
1✔
3525

3526
    /**
3527
     * Set an NBT compound int list value
3528
     */
3529
    public static final IOperator NBT_COMPOUND_WITH_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3530
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3531
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3532
            .operatorName("compound_with_list_int").symbol("NBT{}.with_int_list").interactName("withIntList")
9✔
3533
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3534
                @Override
3535
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3536
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3537
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3538
                            NbtHelpers.getListNbtInt(value, NBT_COMPOUND_WITH_LIST_INT.getLocalizedNameFull())));
2✔
3539
                    return input.getLeft();
4✔
3540
                }
3541
            })).build());
1✔
3542

3543
    /**
3544
     * Set an NBT compound long list value
3545
     */
3546
    public static final IOperator NBT_COMPOUND_WITH_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3547
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3548
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3549
            .operatorName("compound_with_list_long").symbol("NBT{}.with_list_long").interactName("withListLong")
9✔
3550
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3551
                @Override
3552
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3553
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3554
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3555
                            NbtHelpers.getListNbtLong(value, NBT_COMPOUND_WITH_LIST_LONG.getLocalizedNameFull())));
2✔
3556
                    return input.getLeft();
4✔
3557
                }
3558
            })).build());
1✔
3559

3560
    /**
3561
     * Check if the first NBT compound tag is a subset of the second NBT compound tag.
3562
     */
3563
    public static final IOperator NBT_COMPOUND_SUBSET = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3564
            .output(ValueTypes.BOOLEAN).operatorName("compound_subset").symbol("NBT{}.⊆").interactName("isSubset")
8✔
3565
            .function(variables -> {
1✔
3566
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3567
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3568
                if (valueNbt0.getRawValue().isPresent()
5!
3569
                        && valueNbt1.getRawValue().isPresent()
4!
3570
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3571
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3572
                    return ValueTypeBoolean.ValueBoolean.of(NbtHelpers.nbtMatchesSubset((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get(), true));
12✔
3573
                }
3574
                return ValueTypeBoolean.ValueBoolean.of(false);
×
3575
            }).build());
1✔
3576

3577
    /**
3578
     * The union of the given NBT compound tags. Nested tags will be joined recusively.
3579
     */
3580
    public static final IOperator NBT_COMPOUND_UNION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3581
            .output(ValueTypes.NBT).operatorName("compound_union").symbol("NBT{}.∪").interactName("union")
8✔
3582
            .function(variables -> {
1✔
3583
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3584
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3585
                if (valueNbt0.getRawValue().isPresent()
5!
3586
                        && valueNbt1.getRawValue().isPresent()
4!
3587
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3588
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3589
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.union((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3590
                }
3591
                return ValueTypeNbt.ValueNbt.of();
×
3592
            }).build());
1✔
3593

3594
    /**
3595
     * The intersection of the given NBT compound tags. Nested tags will be intersected recusively.
3596
     */
3597
    public static final IOperator NBT_COMPOUND_INTERSECTION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3598
            .output(ValueTypes.NBT).operatorName("compound_intersection").symbol("NBT{}.∩").interactName("intersection")
8✔
3599
            .function(variables -> {
1✔
3600
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3601
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3602
                if (valueNbt0.getRawValue().isPresent()
5!
3603
                        && valueNbt1.getRawValue().isPresent()
4!
3604
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3605
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3606
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.intersection((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3607
                }
3608
                return ValueTypeNbt.ValueNbt.of();
×
3609
            }).build());
1✔
3610

3611
    /**
3612
     * The difference of the given NBT compound tags. Nested tags will be subtracted recusively.
3613
     */
3614
    public static final IOperator NBT_COMPOUND_MINUS = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3615
            .output(ValueTypes.NBT).operatorName("compound_minus").symbol("NBT{}.∖").interactName("minus")
8✔
3616
            .function(variables -> {
1✔
3617
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3618
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3619
                if (valueNbt0.getRawValue().isPresent()
5!
3620
                        && valueNbt1.getRawValue().isPresent()
4!
3621
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3622
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3623
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.minus((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
11✔
3624
                }
3625
                return ValueTypeNbt.ValueNbt.of();
×
3626
            }).build());
1✔
3627

3628
    /**
3629
     * The boolean value of an NBT value
3630
     */
3631
    public static final IOperator NBT_AS_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3632
            .output(ValueTypes.BOOLEAN).operatorName("as_boolean").symbol("NBT.as_boolean").interactName("asBoolean")
9✔
3633
            .function(OperatorBuilders.FUNCTION_NBT_TO_BOOLEAN.build(
2✔
3634
                    o -> o.flatMap(Tag::asBoolean).orElse(false)
8✔
3635
            )).build());
1✔
3636

3637
    /**
3638
     * The byte value of an NBT value
3639
     */
3640
    public static final IOperator NBT_AS_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3641
            .output(ValueTypes.INTEGER).operatorName("as_byte").symbol("NBT.as_byte").interactName("asByte")
9✔
3642
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3643
                    o -> o.flatMap(Tag::asByte).map(s -> (int) s).orElse(0)
14✔
3644
            )).build());
1✔
3645

3646
    /**
3647
     * The short value of an NBT value
3648
     */
3649
    public static final IOperator NBT_AS_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3650
            .output(ValueTypes.INTEGER).operatorName("as_short").symbol("NBT.as_short").interactName("asShort")
9✔
3651
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3652
                    o -> o.flatMap(Tag::asShort).map(s -> (int) s).orElse(0)
14✔
3653
            )).build());
1✔
3654

3655
    /**
3656
     * The int value of an NBT value
3657
     */
3658
    public static final IOperator NBT_AS_INT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3659
            .output(ValueTypes.INTEGER).operatorName("as_int").symbol("NBT.as_int").interactName("asInt")
9✔
3660
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3661
                    o -> o.flatMap(Tag::asInt).orElse(0)
8✔
3662
            )).build());
1✔
3663

3664
    /**
3665
     * The long value of an NBT value
3666
     */
3667
    public static final IOperator NBT_AS_LONG = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3668
            .output(ValueTypes.LONG).operatorName("as_long").symbol("NBT.as_long").interactName("asLong")
9✔
3669
            .function(OperatorBuilders.FUNCTION_NBT_TO_LONG.build(
2✔
3670
                    o -> o.flatMap(Tag::asLong).orElse(0L)
8✔
3671
            )).build());
1✔
3672

3673
    /**
3674
     * The double value of an NBT value
3675
     */
3676
    public static final IOperator NBT_AS_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3677
            .output(ValueTypes.DOUBLE).operatorName("as_double").symbol("NBT.as_double").interactName("asDouble")
9✔
3678
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3679
                    o -> o.flatMap(Tag::asDouble).orElse(0D)
8✔
3680
            )).build());
1✔
3681

3682
    /**
3683
     * The float value of an NBT value
3684
     */
3685
    public static final IOperator NBT_AS_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3686
            .output(ValueTypes.DOUBLE).operatorName("as_float").symbol("NBT.as_float").interactName("asFloat")
9✔
3687
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3688
                    o -> o.flatMap(Tag::asFloat).map(f -> (double) f).orElse(0D)
15✔
3689
            )).build());
1✔
3690

3691
    /**
3692
     * The string value of an NBT value
3693
     */
3694
    public static final IOperator NBT_AS_STRING = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3695
            .output(ValueTypes.STRING).operatorName("as_string").symbol("NBT.as_string").interactName("asString")
9✔
3696
            .function(OperatorBuilders.FUNCTION_NBT_TO_STRING.build(
2✔
3697
                    o -> o.flatMap(Tag::asString).orElse("")
7✔
3698
            )).build());
1✔
3699

3700
    /**
3701
     * The tag list value of an NBT value
3702
     */
3703
    public static final IOperator NBT_AS_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3704
            .output(ValueTypes.LIST).operatorName("as_tag_list").symbol("NBT.as_tag_list").interactName("asTagList")
8✔
3705
            .function(variables -> {
1✔
3706
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3707
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListTag(value.getRawValue()));
7✔
3708
            }).build());
1✔
3709

3710
    /**
3711
     * The byte list value of an NBT value
3712
     */
3713
    public static final IOperator NBT_AS_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3714
            .output(ValueTypes.LIST).operatorName("as_byte_list").symbol("NBT.as_byte_list").interactName("asByteList")
8✔
3715
            .function(variables -> {
1✔
3716
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3717
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListByte(value.getRawValue()));
7✔
3718
            }).build());
1✔
3719

3720
    /**
3721
     * The int list value of an NBT value
3722
     */
3723
    public static final IOperator NBT_AS_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3724
            .output(ValueTypes.LIST).operatorName("as_int_list").symbol("NBT.as_int_list").interactName("asIntList")
8✔
3725
            .function(variables -> {
1✔
3726
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3727
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListInt(value.getRawValue()));
7✔
3728
            }).build());
1✔
3729

3730
    /**
3731
     * The long list value of an NBT value
3732
     */
3733
    public static final IOperator NBT_AS_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3734
            .output(ValueTypes.LIST).operatorName("as_long_list").symbol("NBT.as_long_list").interactName("asLongList")
8✔
3735
            .function(variables -> {
1✔
3736
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3737
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListLong(value.getRawValue()));
7✔
3738
            }).build());
1✔
3739

3740
    /**
3741
     * The NBT value of a boolean value
3742
     */
3743
    public static final IOperator NBT_FROM_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3744
            .inputType(ValueTypes.BOOLEAN).output(ValueTypes.NBT)
4✔
3745
            .operatorName("from_boolean").symbol("NBT.from_boolean").interactName("asNbt")
6✔
3746
            .function(variables -> {
1✔
3747
                ValueTypeBoolean.ValueBoolean value = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
3748
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf(value.getRawValue()));
5✔
3749
            }).build());
1✔
3750

3751
    /**
3752
     * The NBT value of a short value
3753
     */
3754
    public static final IOperator NBT_FROM_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3755
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3756
            .operatorName("from_short").symbol("NBT.from_short").interactName("asNbt", "short", true)
8✔
3757
            .function(variables -> {
1✔
3758
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3759
                return ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) value.getRawValue()));
6✔
3760
            }).build());
1✔
3761

3762
    /**
3763
     * The NBT value of a byte value
3764
     */
3765
    public static final IOperator NBT_FROM_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3766
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3767
            .operatorName("from_byte").symbol("NBT.from_byte").interactName("asNbt", "byte", true)
8✔
3768
            .function(variables -> {
1✔
3769
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3770
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf((byte) value.getRawValue()));
6✔
3771
            }).build());
1✔
3772

3773
    /**
3774
     * The NBT value of an int value
3775
     */
3776
    public static final IOperator NBT_FROM_INT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3777
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3778
            .operatorName("from_int").symbol("NBT.from_int").interactName("asNbt")
6✔
3779
            .function(variables -> {
1✔
3780
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3781
                return ValueTypeNbt.ValueNbt.of(IntTag.valueOf(value.getRawValue()));
5✔
3782
            }).build());
1✔
3783

3784
    /**
3785
     * The NBT value of a long value
3786
     */
3787
    public static final IOperator NBT_FROM_LONG = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3788
            .inputType(ValueTypes.LONG).output(ValueTypes.NBT)
4✔
3789
            .operatorName("from_long").symbol("NBT.from_long").interactName("asNbt")
6✔
3790
            .function(variables -> {
1✔
3791
                ValueTypeLong.ValueLong value = variables.getValue(0, ValueTypes.LONG);
6✔
3792
                return ValueTypeNbt.ValueNbt.of(LongTag.valueOf(value.getRawValue()));
5✔
3793
            }).build());
1✔
3794

3795
    /**
3796
     * The NBT value of a double value
3797
     */
3798
    public static final IOperator NBT_FROM_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3799
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3800
            .operatorName("from_double").symbol("NBT.from_double").interactName("asNbt")
6✔
3801
            .function(variables -> {
1✔
3802
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3803
                return ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(value.getRawValue()));
5✔
3804
            }).build());
1✔
3805

3806
    /**
3807
     * The NBT value of a float value
3808
     */
3809
    public static final IOperator NBT_FROM_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3810
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3811
            .operatorName("from_float").symbol("NBT.from_float").interactName("asNbt", "float", true)
8✔
3812
            .function(variables -> {
1✔
3813
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3814
                return ValueTypeNbt.ValueNbt.of(FloatTag.valueOf((float) value.getRawValue()));
6✔
3815
            }).build());
1✔
3816

3817
    /**
3818
     * The NBT value of a string value
3819
     */
3820
    public static final IOperator NBT_FROM_STRING = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3821
            .inputType(ValueTypes.STRING).output(ValueTypes.NBT)
4✔
3822
            .operatorName("from_string").symbol("NBT.from_string").interactName("asNbt")
6✔
3823
            .function(variables -> {
1✔
3824
                ValueTypeString.ValueString value = variables.getValue(0, ValueTypes.STRING);
6✔
3825
                return ValueTypeNbt.ValueNbt.of(StringTag.valueOf(value.getRawValue()));
5✔
3826
            }).build());
1✔
3827

3828
    /**
3829
     * The NBT value of a tag list value
3830
     */
3831
    public static final IOperator NBT_FROM_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3832
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3833
            .operatorName("from_tag_list").symbol("NBT.from_tag_list").interactName("asNbt", "tagList", true)
10✔
3834
            .function(new OperatorBase.IFunction() {
4✔
3835
                @Override
3836
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3837
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3838
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtTag(value, NBT_FROM_TAG_LIST.getLocalizedNameFull()));
6✔
3839
                }
3840
            }).build());
1✔
3841

3842
    /**
3843
     * The NBT value of a byte list value
3844
     */
3845
    public static final IOperator NBT_FROM_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3846
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3847
            .operatorName("from_byte_list").symbol("NBT.from_byte_list").interactName("asNbt", "byteList", true)
10✔
3848
            .function(new OperatorBase.IFunction() {
4✔
3849
                @Override
3850
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3851
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3852
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtByte(value, NBT_FROM_BYTE_LIST.getLocalizedNameFull()));
6✔
3853
                }
3854
            }).build());
1✔
3855

3856
    /**
3857
     * The NBT value of a int list value
3858
     */
3859
    public static final IOperator NBT_FROM_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3860
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3861
            .operatorName("from_int_list").symbol("NBT.from_int_list").interactName("asNbt", "intList", true)
10✔
3862
            .function(new OperatorBase.IFunction() {
4✔
3863
                @Override
3864
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3865
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3866
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtInt(value, NBT_FROM_INT_LIST.getLocalizedNameFull()));
6✔
3867
                }
3868
            }).build());
1✔
3869

3870
    /**
3871
     * The NBT value of a long list value
3872
     */
3873
    public static final IOperator NBT_FROM_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3874
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3875
            .operatorName("from_long_list").symbol("NBT.from_long_list").interactName("asNbt", "longList", true)
10✔
3876
            .function(new OperatorBase.IFunction() {
4✔
3877
                @Override
3878
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3879
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3880
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtLong(value, NBT_FROM_LONG_LIST.getLocalizedNameFull()));
6✔
3881
                }
3882
            }).build());
1✔
3883

3884
    /**
3885
     * Apply the given NBT path expression on the given NBT value and get the first result.
3886
     */
3887
    public static final IOperator NBT_PATH_MATCH_FIRST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3888
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.NBT)
4✔
3889
            .operatorName("path_match_first").symbol("NBT.path_match_first").interactName("nbtPathMatchFirst")
6✔
3890
            .function(variables -> {
1✔
3891
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3892
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3893
                INbtPathExpression expression = null;
2✔
3894
                try {
3895
                    expression = NbtPath.parse(string.getRawValue());
4✔
3896
                } catch (NbtParseException e) {
×
3897
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3898
                            string.getRawValue(),
×
3899
                            e.getMessage()));
×
3900
                }
1✔
3901
                if (!nbt.getRawValue().isPresent()) {
4!
3902
                    return ValueTypeNbt.ValueNbt.of();
×
3903
                }
3904
                return ValueTypeNbt.ValueNbt.of(expression.match(nbt.getRawValue().get()).getMatches().findAny());
10✔
3905
            }).build());
1✔
3906

3907
    /**
3908
     * Apply the given NBT path expression on the given NBT value and get all results.
3909
     */
3910
    public static final IOperator NBT_PATH_MATCH_ALL = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3911
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.LIST)
4✔
3912
            .operatorName("path_match_all").symbol("NBT.path_match_all").interactName("nbtPathMatchAll")
6✔
3913
            .function(variables -> {
1✔
3914
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3915
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3916
                INbtPathExpression expression = null;
2✔
3917
                try {
3918
                    expression = NbtPath.parse(string.getRawValue());
4✔
3919
                } catch (NbtParseException e) {
×
3920
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3921
                            string.getRawValue(),
×
3922
                            e.getMessage()));
×
3923
                }
1✔
3924
                if (!nbt.getRawValue().isPresent()) {
4!
3925
                    return ValueTypeList.ValueList.ofAll(ValueTypes.NBT);
×
3926
                }
3927
                List<ValueTypeNbt.ValueNbt> matches = expression.match(nbt.getRawValue().get()).getMatches()
8✔
3928
                        .map(ValueTypeNbt.ValueNbt::of)
1✔
3929
                        .collect(Collectors.toList());
4✔
3930
                return ValueTypeList.ValueList.ofList(ValueTypes.NBT, matches);
4✔
3931
            }).build());
1✔
3932

3933
    /**
3934
     * Test the given NBT path expression on the given NBT value.
3935
     */
3936
    public static final IOperator NBT_PATH_TEST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3937
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.BOOLEAN)
4✔
3938
            .operatorName("path_test").symbol("NBT.path_test").interactName("nbtPathTest")
6✔
3939
            .function(variables -> {
1✔
3940
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3941
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3942
                INbtPathExpression expression = null;
2✔
3943
                try {
3944
                    expression = NbtPath.parse(string.getRawValue());
4✔
3945
                } catch (NbtParseException e) {
×
3946
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3947
                            string.getRawValue(),
×
3948
                            e.getMessage()));
×
3949
                }
1✔
3950
                if (!nbt.getRawValue().isPresent()) {
4!
3951
                    return ValueTypeBoolean.ValueBoolean.of(false);
×
3952
                }
3953
                return ValueTypeBoolean.ValueBoolean.of(expression.test(nbt.getRawValue().get()));
8✔
3954
            }).build());
1✔
3955

3956
    /**
3957
     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------
3958
     */
3959

3960
    /**
3961
     * The list of items.
3962
     */
3963
    public static final IOperator INGREDIENTS_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3964
            .output(ValueTypes.LIST).operatorInteract("items").symbol("Ingr.items")
6✔
3965
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ITEMSTACK))
4✔
3966
            .build());
1✔
3967

3968
    /**
3969
     * The list of fluids
3970
     */
3971
    public static final IOperator INGREDIENTS_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3972
            .output(ValueTypes.LIST).operatorInteract("fluids").symbol("Ingr.fluids")
6✔
3973
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.FLUIDSTACK))
4✔
3974
            .build());
1✔
3975

3976
    /**
3977
     * The list of fluids
3978
     */
3979
    public static final IOperator INGREDIENTS_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3980
            .output(ValueTypes.LIST).operatorInteract("energies").symbol("Ingr.energies")
6✔
3981
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ENERGY))
4✔
3982
            .build());
1✔
3983

3984
    /**
3985
     * Set an ingredient item
3986
     */
3987
    public static final IOperator INGREDIENTS_WITH_ITEM = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_ITEMSTACK
5✔
3988
            .operatorName("with_item").symbol("Ingr.with_item").interactName("withItem")
6✔
3989
            .function(variables -> {
1✔
3990
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3991
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3992
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(2, ValueTypes.OBJECT_ITEMSTACK);
6✔
3993
                if (value.getRawValue().isEmpty()) {
4!
3994
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3995
                }
3996
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3997
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3998
                        index.getRawValue(), IngredientComponent.ITEMSTACK, itemStack.getRawValue()));
5✔
3999
            }).build());
1✔
4000

4001
    /**
4002
     * Set an ingredient fluid
4003
     */
4004
    public static final IOperator INGREDIENTS_WITH_FLUID = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_FLUIDSTACK
5✔
4005
            .operatorName("with_fluid").symbol("Ingr.with_fluid").interactName("withFluid")
6✔
4006
            .function(variables -> {
1✔
4007
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4008
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4009
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = variables.getValue(2, ValueTypes.OBJECT_FLUIDSTACK);
6✔
4010
                if (value.getRawValue().isEmpty()) {
4!
4011
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4012
                }
4013
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4014
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4015
                        index.getRawValue(), IngredientComponent.FLUIDSTACK, fluidStack.getRawValue()));
5✔
4016
            }).build());
1✔
4017

4018
    /**
4019
     * Set an ingredient energy
4020
     */
4021
    public static final IOperator INGREDIENTS_WITH_ENERGY = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_LONG
5✔
4022
            .operatorName("with_energy").symbol("Ingr.with_energy").interactName("withEnergy")
6✔
4023
            .function(variables -> {
1✔
4024
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4025
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4026
                ValueTypeLong.ValueLong energy = variables.getValue(2, ValueTypes.LONG);
6✔
4027
                if (value.getRawValue().isEmpty()) {
4!
4028
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4029
                }
4030
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4031
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4032
                        index.getRawValue(), IngredientComponent.ENERGY, energy.getRawValue()));
6✔
4033
            }).build());
1✔
4034

4035
    /**
4036
     * Set the list of items
4037
     */
4038
    public static final IOperator INGREDIENTS_WITH_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4039
            .operatorName("with_items").symbol("Ingr.with_items").interactName("withItems")
6✔
4040
            .function(variables -> {
1✔
4041
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4042
                ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4043
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4044
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4045
                }
4046
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4047
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4048
                        IngredientComponent.ITEMSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ITEMSTACK, list)));
2✔
4049
            }).build());
1✔
4050

4051
    /**
4052
     * Set the list of fluids
4053
     */
4054
    public static final IOperator INGREDIENTS_WITH_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4055
            .operatorName("with_fluids").symbol("Ingr.with_fluids").interactName("withFluids")
6✔
4056
            .function(variables -> {
1✔
4057
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4058
                ValueTypeList.ValueList<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4059
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4060
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4061
                }
4062
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4063
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4064
                        IngredientComponent.FLUIDSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.FLUIDSTACK, list)));
2✔
4065
            }).build());
1✔
4066

4067
    /**
4068
     * Set the list of energies
4069
     */
4070
    public static final IOperator INGREDIENTS_WITH_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4071
            .renderPattern(IConfigRenderPattern.INFIX_VERYLONG)
2✔
4072
            .operatorName("with_energies").symbol("Ingr.with_energies").interactName("withEnergies")
6✔
4073
            .function(variables -> {
1✔
4074
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4075
                ValueTypeList.ValueList<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = variables.getValue(1, ValueTypes.LIST);
6✔
4076
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4077
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4078
                }
4079
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4080
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4081
                        IngredientComponent.ENERGY, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ENERGY, list)));
2✔
4082
            }).build());
1✔
4083

4084
    /**
4085
     * ----------------------------------- RECIPE OPERATORS -----------------------------------
4086
     */
4087

4088
    /**
4089
     * The input ingredients of a recipe
4090
     */
4091
    public static final IOperator RECIPE_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4092
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4093
            .operatorInteract("input").symbol("recipe_in")
4✔
4094
            .function(variables -> {
1✔
4095
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4096
                if (value.getRawValue().isPresent()) {
4!
4097
                    return ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(value.getRawValue().get()));
7✔
4098
                }
4099
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4100
            }).build());
1✔
4101

4102
    /**
4103
     * The output ingredients of a recipe
4104
     */
4105
    public static final IOperator RECIPE_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4106
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4107
            .operatorInteract("output").symbol("recipe_out")
4✔
4108
            .function(variables -> {
1✔
4109
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4110
                if (value.getRawValue().isPresent()) {
4!
4111
                    return ValueObjectTypeIngredients.ValueIngredients.of(value.getRawValue().get().getOutput());
7✔
4112
                }
4113
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4114
            }).build());
1✔
4115

4116
    /**
4117
     * Set the input ingredients of a recipe
4118
     */
4119
    public static final IOperator RECIPE_WITH_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4120
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4121
            .operatorName("with_input").symbol("Recipe.with_in").interactName("withInput")
6✔
4122
            .function(variables -> {
1✔
4123
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4124
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4125
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4126
                    IMixedIngredients ingredients = valueIngredients.getRawValue().get();
5✔
4127
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4128
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4129
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4130
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4131
                                .stream()
4✔
4132
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4133
                                .collect(Collectors.toList()));
3✔
4134
                    }
1✔
4135
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4136
                            inputs,
4137
                            valueRecipe.getRawValue().get().getOutput()
5✔
4138
                    ));
4139
                }
4140
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4141
            }).build());
1✔
4142

4143
    /**
4144
     * Set the output ingredients of a recipe
4145
     */
4146
    public static final IOperator RECIPE_WITH_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4147
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4148
            .operatorName("with_output").symbol("Recipe.with_out").interactName("withOutput")
6✔
4149
            .function(variables -> {
1✔
4150
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4151
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4152
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4153
                    IRecipeDefinition recipe = valueRecipe.getRawValue().get();
5✔
4154
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4155
                    for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {
11✔
4156
                        inputs.put(component, (List) recipe.getInputs(component));
7✔
4157
                    }
1✔
4158
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4159
                            inputs,
4160
                            valueIngredients.getRawValue().get()
4✔
4161
                    ));
4162
                }
4163
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4164
            }).build());
1✔
4165

4166
    /**
4167
     * Create a recipe from two the given I/O ingredients
4168
     */
4169
    public static final IOperator RECIPE_WITH_INPUT_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_PREFIX
5✔
4170
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4171
            .operatorName("with_input_output").symbol("Recipe.with_io").interactName("withInputOutput")
6✔
4172
            .function(variables -> {
1✔
4173
                ValueObjectTypeIngredients.ValueIngredients valueIn = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4174
                ValueObjectTypeIngredients.ValueIngredients valueOut = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4175
                if (valueIn.getRawValue().isPresent() && valueOut.getRawValue().isPresent()) {
8!
4176
                    IMixedIngredients ingredients = valueIn.getRawValue().get();
5✔
4177
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4178
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4179
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4180
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4181
                                .stream()
4✔
4182
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4183
                                .collect(Collectors.toList()));
3✔
4184
                    }
1✔
4185
                    try {
4186
                        return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4187
                                inputs,
4188
                                valueOut.getRawValue().get()
4✔
4189
                        ));
4190
                    } catch (IllegalArgumentException e) {
×
4191
                        throw new EvaluationException(Component.literal(e.getMessage()));
×
4192
                    }
4193
                }
4194
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4195
            }).build());
1✔
4196

4197
    /**
4198
     * ------------------------------------ PARSE OPERATORS ------------------------------------
4199
     */
4200

4201
    /**
4202
     * Boolean Parse operator which takes a string of form `/(F(alse)?|[+-]?(0x|#)?0+|)/i`.
4203
     */
4204
    public static final IOperator PARSE_BOOLEAN = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.BOOLEAN, v -> {
8✔
4205
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4206
      Pattern p = Pattern.compile("\\A(F(alse)?|[+-]?(0x|#)?0+|)\\z", Pattern.CASE_INSENSITIVE);
4✔
4207
      return ValueTypeBoolean.ValueBoolean.of(!p.matcher(value.getRawValue().trim()).matches());
12✔
4208
    }));
4209

4210
    /**
4211
     * Double Parse operator which takes a string of a form Double.parseDouble(),
4212
     * `/([+-]?)(Inf(inity)?|\u221E)/i`, or Long.decode() can consume.
4213
     */
4214
    public static final IOperator PARSE_DOUBLE = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.DOUBLE, v -> {
8✔
4215
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4216
      try {
4217
        return ValueTypeDouble.ValueDouble.of(Double.parseDouble(value.getRawValue()));
5✔
4218
      } catch (NumberFormatException e) {
1✔
4219
        try {
4220
          // \u221E = infinity symbol
4221
          Pattern p = Pattern.compile("\\A([+-]?)(Inf(inity)?|\u221E)\\z", Pattern.CASE_INSENSITIVE);
4✔
4222
          Matcher m = p.matcher(value.getRawValue().trim());
6✔
4223
          if (m.matches()){
3✔
4224
            if (m.group(1).equals("-")){
6✔
4225
              return ValueTypeDouble.ValueDouble.of(Double.NEGATIVE_INFINITY);
3✔
4226
            }
4227
            return ValueTypeDouble.ValueDouble.of(Double.POSITIVE_INFINITY);
3✔
4228
          }
4229
          // Try as a long
4230
          return ValueTypeDouble.ValueDouble.of((double) Long.decode(value.getRawValue()));
7✔
4231
        } catch (NumberFormatException e2) {
1✔
4232
            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4233
                    Component.translatable(ValueTypes.DOUBLE.getTranslationKey())));
3✔
4234
        }
4235
      }
4236
    }));
4237

4238
    /**
4239
     * Integer Parse operator which takes a string of a form Integer.decode() can consume.
4240
     */
4241
    public static final IOperator PARSE_INTEGER = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.INTEGER, v -> {
8✔
4242
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4243
      try{
4244
        return ValueTypeInteger.ValueInteger.of(Integer.decode(value.getRawValue()));
6✔
4245
      } catch (NumberFormatException e) {
1✔
4246
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4247
                  Component.translatable(ValueTypes.INTEGER.getTranslationKey())));
3✔
4248
      }
4249
    }));
4250

4251
    /**
4252
     * Long Parse operator which takes a string of a form Long.decode() can consume.
4253
     */
4254
    public static final IOperator PARSE_LONG = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.LONG, v -> {
8✔
4255
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4256
      try {
4257
        return ValueTypeLong.ValueLong.of(Long.decode(value.getRawValue()));
6✔
4258
      } catch (NumberFormatException e) {
1✔
4259
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4260
                  Component.translatable(ValueTypes.LONG.getTranslationKey())));
3✔
4261
      }
4262
    }));
4263

4264
    /**
4265
     * NBT Parse operator which takes a string of a form ValueTypeNbt().deserialize() can consume.
4266
     */
4267
    public static final IOperator PARSE_NBT = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.NBT, v -> {
8✔
4268
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4269
      try {
4270
        return ValueTypeNbt.ValueNbt.of(Helpers.TAG_PARSER.parseFully(value.getRawValue()));
7✔
4271
      } catch (CommandSyntaxException e) {
1✔
4272
        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4273
                Component.translatable(ValueTypes.NBT.getTranslationKey())));
3✔
4274
      }
4275
    }));
4276

4277
    /**
4278
     * ----------------------------------- GENERAL OPERATORS -----------------------------------
4279
     */
4280

4281
    /**
4282
     * Choice operator with one boolean input, two any inputs and one output any.
4283
     */
4284
    public static final GeneralOperator GENERAL_CHOICE = REGISTRY.register(new GeneralChoiceOperator("?", "choice", "choice"));
10✔
4285

4286
    /**
4287
     * Identity operator with one any input and one any output
4288
     */
4289
    public static final GeneralOperator GENERAL_IDENTITY = REGISTRY.register(new GeneralIdentityOperator("id", "identity", "identity"));
10✔
4290

4291
    /**
4292
     * Constant operator with two any inputs and one any output
4293
     */
4294
    public static final GeneralOperator GENERAL_CONSTANT = REGISTRY.register(new GeneralConstantOperator("K", "constant", "constant"));
11✔
4295

4296
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc