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

CyclopsMC / IntegratedDynamics / 21094800655

17 Jan 2026 01:10PM UTC coverage: 45.184% (+0.1%) from 45.089%
21094800655

push

github

rubensworks
Bump mod version

2632 of 8600 branches covered (30.6%)

Branch coverage included in aggregate %.

11924 of 23615 relevant lines covered (50.49%)

2.4 hits per line

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

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

82
import java.util.*;
83
import java.util.stream.Collectors;
84

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

92
    public static final IOperatorRegistry REGISTRY = constructRegistry();
2✔
93

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

103
    public static void load() {}
1✔
104

105
    /**
106
     * ----------------------------------- LOGICAL OPERATORS -----------------------------------
107
     */
108

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

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

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

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

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

159
    /**
160
     * ----------------------------------- ARITHMETIC OPERATORS -----------------------------------
161
     */
162

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

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

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

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

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

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

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

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

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

235
    /**
236
     * ----------------------------------- INTEGER OPERATORS -----------------------------------
237
     */
238

239
     private static final ValueTypeInteger.ValueInteger ZERO = ValueTypeInteger.ValueInteger.of(0);
3✔
240

241
    /**
242
     * ----------------------------------- DOUBLE OPERATORS -----------------------------------
243
     */
244

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

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

264
    /**
265
     * ----------------------------------- RELATIONAL OPERATORS -----------------------------------
266
     */
267

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

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

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

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

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

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

347
    /**
348
     * ----------------------------------- BINARY OPERATORS -----------------------------------
349
     */
350

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

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

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

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

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

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

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

420
    /**
421
     * ----------------------------------- STRING OPERATORS -----------------------------------
422
     */
423

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

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

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

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

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

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

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

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

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

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

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

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

594

595
    /**
596
     * 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.
597
     */
598
    public static final IOperator STRING_REGEX_GROUP = REGISTRY.register(OperatorBuilders.STRING.symbolOperator("regex_group").interactName("regexGroup")
9✔
599
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
15✔
600
        .inputTypes(ValueTypes.STRING, ValueTypes.INTEGER, ValueTypes.STRING)
2✔
601
        .output(ValueTypes.STRING)
2✔
602
        .function(variables -> {
1✔
603
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
604
            ValueTypeInteger.ValueInteger group = variables.getValue(1, ValueTypes.INTEGER);
6✔
605
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
606
            if (group.getRawValue() < 0) {
3✔
607
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_INDEXNEGATIVE));
6✔
608
            }
609
            try {
610
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
611
                if (m.find()) {
3!
612
                    String result = m.group(group.getRawValue());
5✔
613
                    return ValueTypeString.ValueString.of(result == null ? "" : result);
5!
614
                } else {
615
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCH,
×
616
                            str.getRawValue(), pattern.getRawValue()));
×
617
                }
618
            } catch (PatternSyntaxException e) {
1✔
619
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
620
                        pattern.getRawValue()));
2✔
621
            } catch (IndexOutOfBoundsException e) {
1✔
622
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCHGROUP,
11✔
623
                        str.getRawValue(), pattern.getRawValue(), group.getRawValue()));
13✔
624
            }
625
        }).build()
1✔
626
    );
627

628
    /**
629
     * 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.
630
     */
631
    public static final IOperator STRING_REGEX_GROUPS = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("regex_groups").interactName("regexGroups")
9✔
632
        .output(ValueTypes.LIST)
2✔
633
        .function(variables -> {
1✔
634
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
635
            ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
636
            try {
637
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
638
                if (m.find()) {
3✔
639
                    List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
640
                    for (int i = 0; i <= m.groupCount(); i++) {
8✔
641
                        String result = m.group(i);
4✔
642
                        values.add(ValueTypeString.ValueString.of(result == null ? "" : result));
7!
643
                    }
644
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
645
                } else {
646
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, Collections.<ValueTypeString.ValueString>emptyList());
4✔
647
                }
648
            } catch (PatternSyntaxException e) {
1✔
649
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
650
                        pattern.getRawValue()));
2✔
651
            }
652
        }).build()
1✔
653
    );
654

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

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

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

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

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

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

768
                    return ValueTypeString.ValueString.of(sb.toString());
4✔
769
                }
770
            }).build()
1✔
771
    );
772

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

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

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

802
    /**
803
     * ----------------------------------- NUMBER OPERATORS -----------------------------------
804
     */
805

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

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

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

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

842
    /**
843
     * Cast any number to a double
844
     */
845
    public static final IOperator NUMBER_CAST_DOUBLE = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
846
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.DOUBLE)
4✔
847
            .symbol("(double)").operatorName("cast_double").interactName("toDouble")
6✔
848
            .function(variables -> variables.getVariables()[0].getValue().cast(ValueTypes.DOUBLE)).build());
10✔
849

850
    /**
851
     * Cast any number to a double
852
     */
853
    public static final IOperator NUMBER_CAST_LONG = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
854
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.LONG)
4✔
855
            .symbol("(long)").operatorName("cast_long").interactName("toLong")
6✔
856
            .function(variables -> variables.getVariables()[0].getValue().cast(ValueTypes.LONG)).build());
10✔
857

858
    /**
859
     * Cast any number to a double
860
     */
861
    public static final IOperator NUMBER_CAST_INTEGER = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
862
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER)
4✔
863
            .symbol("(integer)").operatorName("cast_integer").interactName("toInteger")
6✔
864
            .function(variables -> variables.getVariables()[0].getValue().cast(ValueTypes.INTEGER)).build());
10✔
865

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

870
    /**
871
     * Check if something is null
872
     */
873
    public static final IOperator NULLABLE_ISNULL = REGISTRY.register(OperatorBuilders.NULLABLE_1_PREFIX.symbol("o").operatorName("isnull").interactName("isNull")
11✔
874
            .inputType(ValueTypes.CATEGORY_ANY).output(ValueTypes.BOOLEAN).function(variables -> {
5✔
875
                if(ValueHelpers.correspondsTo(variables.getVariables()[0].getType(), ValueTypes.CATEGORY_NULLABLE)) {
8!
876
                    return ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NULLABLE.isNull(variables.getVariables()[0]));
×
877
                }
878
                return ValueTypeBoolean.ValueBoolean.of(false);
3✔
879
            }).build());
1✔
880

881
    /**
882
     * Check if something is not null
883
     */
884
    public static final IOperator NULLABLE_ISNOTNULL = REGISTRY.register(new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT)
13✔
885
            .apply(NULLABLE_ISNULL).build("∅", "isnotnull", "isNotNull", IConfigRenderPattern.PREFIX_1, "general"));
7✔
886

887
    /**
888
     * ----------------------------------- LIST OPERATORS -----------------------------------
889
     */
890

891
    /**
892
     * List operator with one input list and one output integer
893
     */
894
    public static final IOperator LIST_LENGTH = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.INTEGER).symbol("| |").operatorInteract("length")
11✔
895
            .function(variables -> {
1✔
896
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
897
                IValueTypeListProxy a = valueList.getRawValue();
3✔
898
                return ValueTypeInteger.ValueInteger.of(a.getLength());
4✔
899
            }).build());
1✔
900

901
    /**
902
     * Check if a list is empty
903
     */
904
    public static final IOperator LIST_EMPTY = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.BOOLEAN).symbol("∅").operatorName("empty").interactName("isEmpty")
13✔
905
            .function(variables -> {
1✔
906
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
907
                IValueTypeListProxy a = valueList.getRawValue();
3✔
908
                return ValueTypeBoolean.ValueBoolean.of(a.getLength() == 0);
8✔
909
            }).build());
1✔
910

911
    /**
912
     * Check if a list is not empty
913
     */
914
    public static final IOperator LIST_NOT_EMPTY = REGISTRY.register(
13✔
915
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LIST_EMPTY).build(
7✔
916
                    "o", "notempty", "isNotEmpty", IConfigRenderPattern.PREFIX_1, "list"));
917

918
    /**
919
     * List operator with one input list and one output integer
920
     */
921
    public static final IOperator LIST_ELEMENT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
14✔
922
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER}).output(ValueTypes.CATEGORY_ANY)
4✔
923
            .renderPattern(IConfigRenderPattern.INFIX).symbolOperatorInteract("get")
4✔
924
            .function(variables -> {
2✔
925
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
926
                IValueTypeListProxy a = valueList.getRawValue();
3✔
927
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
928
                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {
8!
929
                    return a.get(b.getRawValue());
5✔
930
                } else {
931
                    throw new EvaluationException(Component.translatable(
11✔
932
                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, b.getRawValue(), a.getLength()));
9✔
933
                }
934
            }).conditionalOutputTypeDeriver((operator, input) -> {
1✔
935
                try {
936
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();
×
937
                    return a.getValueType();
×
938
                } catch (ClassCastException | EvaluationException e) {
×
939
                    return operator.getOutputType();
×
940
                }
941
            }).build());
1✔
942

943
    /**
944
     * List operator with one input list, one output integer, and one default value
945
     */
946
    public static final IOperator LIST_ELEMENT_DEFAULT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
18✔
947
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.CATEGORY_ANY}).output(ValueTypes.CATEGORY_ANY)
4✔
948
            .renderPattern(IConfigRenderPattern.INFIX_2_LONG).symbolOperator("get_or_default").interactName("getOrDefault")
6✔
949
            .function(variables -> {
2✔
950
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
951
                IValueTypeListProxy a = valueList.getRawValue();
3✔
952
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
953
                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {
8✔
954
                    return a.get(b.getRawValue());
5✔
955
                } else {
956
                    if (!ValueHelpers.correspondsTo(a.getValueType(), variables.getVariables()[2].getType())) {
9!
957
                        throw new EvaluationException(Component.translatable(
×
958
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
959
                                Component.translatable(a.getValueType().getTranslationKey()),
×
960
                                Component.translatable(variables.getVariables()[2].getType().getTranslationKey())));
×
961
                    }
962
                    return variables.getValue(2);
4✔
963
                }
964
            }).conditionalOutputTypeDeriver(
1✔
965
                (operator, input) -> input[2].getType()
×
966
            ).build());
1✔
967

968
    /**
969
     * List contains operator that takes a list, a list element to look for and returns a boolean.
970
     */
971
    public static final IOperator LIST_CONTAINS = REGISTRY.register(OperatorBuilders.LIST
14✔
972
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
973
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
974
            .output(ValueTypes.BOOLEAN).symbolOperatorInteract("contains")
6✔
975
            .function(new OperatorBase.IFunction() {
4✔
976
                @Override
977
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
978
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
979
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
980
                    if (list.isInfinite()) {
3!
981
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
982
                                LIST_CONTAINS.getLocalizedNameFull()));
×
983
                    }
984
                    IValue input = variables.getValue(1);
4✔
985
                    for (IValue value : list) {
10✔
986
                        if (value.equals(input)) {
4✔
987
                            return ValueTypeBoolean.ValueBoolean.of(true);
3✔
988
                        }
989
                    }
1✔
990
                    return ValueTypeBoolean.ValueBoolean.of(false);
3✔
991
                }
992
            }).build());
1✔
993

994
    /**
995
     * List contains operator that takes a list, a predicate that maps a list element to a boolean, a list element and returns a boolean.
996
     */
997
    public static final IOperator LIST_CONTAINS_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
998
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
999
            .renderPattern(IConfigRenderPattern.INFIX)
2✔
1000
            .output(ValueTypes.BOOLEAN).symbolOperator("contains_p").interactName("containsPredicate")
8✔
1001
            .function(new OperatorBase.IFunction() {
4✔
1002
                @Override
1003
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1004
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1005
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1006
                    if (list.isInfinite()) {
3!
1007
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1008
                                LIST_CONTAINS_PREDICATE.getLocalizedNameFull()));
×
1009
                    }
1010
                    IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
1011
                    for (IValue value : list) {
10✔
1012
                        IValue result = ValueHelpers.evaluateOperator(operator, value);
9✔
1013
                        ValueHelpers.validatePredicateOutput(operator, result);
3✔
1014
                        if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
1015
                            return ValueTypeBoolean.ValueBoolean.of(true);
3✔
1016
                        }
1017
                    }
1✔
1018
                    return ValueTypeBoolean.ValueBoolean.of(false);
3✔
1019
                }
1020
            }).build());
1✔
1021

1022
    /**
1023
     * List operator with one input list, and element and one output integer
1024
     */
1025
    public static final IOperator LIST_COUNT = REGISTRY.register(OperatorBuilders.LIST
14✔
1026
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
1027
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.INTEGER)
4✔
1028
            .symbolOperatorInteract("count")
4✔
1029
            .function(new OperatorBase.IFunction() {
4✔
1030
                @Override
1031
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1032
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1033
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1034
                    if (list.isInfinite()) {
3✔
1035
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
11✔
1036
                                LIST_COUNT.getLocalizedNameFull()));
2✔
1037
                    }
1038
                    IValue value = variables.getValue(1);
4✔
1039
                    int count = 0;
2✔
1040
                    for (IValue listValue : list) {
10✔
1041
                        if (listValue.equals(value)) {
4✔
1042
                            count++;
1✔
1043
                        }
1044
                    }
1✔
1045
                    return ValueTypeInteger.ValueInteger.of(count);
3✔
1046
                }
1047
            }).build());
1✔
1048

1049
    /**
1050
     * List operator with one input list, a predicate and one output integer
1051
     */
1052
    public static final IOperator LIST_COUNT_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
1053
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
1054
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.INTEGER)
4✔
1055
            .symbolOperator("count_p").interactName("countPredicate")
6✔
1056
            .function(new OperatorBase.IFunction() {
4✔
1057
                @Override
1058
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1059
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1060
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1061
                    if (list.isInfinite()) {
3!
1062
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1063
                                LIST_COUNT_PREDICATE.getLocalizedNameFull()));
×
1064
                    }
1065
                    IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
1066
                    int count = 0;
2✔
1067
                    for (IValue listValue : list) {
10✔
1068
                        IValue result = ValueHelpers.evaluateOperator(operator, listValue);
9✔
1069
                        ValueHelpers.validatePredicateOutput(operator, result);
3✔
1070
                        if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
1071
                            count++;
1✔
1072
                        }
1073
                    }
1✔
1074
                    return ValueTypeInteger.ValueInteger.of(count);
3✔
1075
                }
1076
            }).build());
1✔
1077

1078
    /**
1079
     * Append an element to the given list
1080
     */
1081
    public static final IOperator LIST_APPEND = REGISTRY.register(OperatorBuilders.LIST
14✔
1082
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
1083
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1084
            .symbolOperatorInteract("append")
2✔
1085
            .function(variables -> {
1✔
1086
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1087
                IValueTypeListProxy a = valueList.getRawValue();
3✔
1088
                IValue value = variables.getValue(1);
4✔
1089
                if (!ValueHelpers.correspondsTo(a.getValueType(), value.getType())) {
6✔
1090
                    throw new EvaluationException(Component.translatable(
11✔
1091
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1092
                            Component.translatable(a.getValueType().getTranslationKey()),
7✔
1093
                            Component.translatable(value.getType().getTranslationKey())));
4✔
1094
                }
1095
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyAppend(a, value));
7✔
1096
            }).build());
1✔
1097

1098
    /**
1099
     * Concatenate two lists
1100
     */
1101
    public static final IOperator LIST_CONCAT = REGISTRY.register(OperatorBuilders.LIST
14✔
1102
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})
2✔
1103
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1104
            .symbolOperatorInteract("concat")
2✔
1105
            .function(variables -> {
1✔
1106
                ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);
6✔
1107
                IValueTypeListProxy a = valueList0.getRawValue();
3✔
1108
                ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);
6✔
1109
                IValueTypeListProxy b = valueList1.getRawValue();
3✔
1110
                if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {
6!
1111
                    throw new EvaluationException(Component.translatable(
×
1112
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1113
                            Component.translatable(a.getValueType().getTranslationKey()),
×
1114
                            Component.translatable(b.getValueType().getTranslationKey())));
×
1115
                }
1116
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyConcat(a, b));
15✔
1117
            }).build());
1✔
1118

1119
    /**
1120
     * Build a list lazily using a start value and an operator that is applied to the previous element to get a next element.
1121
     */
1122
    public static final IOperator LIST_LAZYBUILT = REGISTRY.register(OperatorBuilders.LIST
14✔
1123
            .inputTypes(new IValueType[]{ValueTypes.CATEGORY_ANY, ValueTypes.OPERATOR})
2✔
1124
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1125
            .symbolOperator("lazybuilt").interactName("lazyBuilt")
4✔
1126
            .function(variables -> {
1✔
1127
                IValue a = variables.getValue(0);
4✔
1128
                IOperator operator = OperatorBuilders.getSafeOperator(variables.getValue(1, ValueTypes.OPERATOR), a.getType());
9✔
1129
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyLazyBuilt<>(a, operator));
7✔
1130
            }).build());
1✔
1131

1132
    /**
1133
     * Get the first element of the given list.
1134
     */
1135
    public static final IOperator LIST_HEAD = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
10✔
1136
            .inputTypes(new IValueType[]{ValueTypes.LIST}).output(ValueTypes.CATEGORY_ANY)
4✔
1137
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).symbolOperatorInteract("head")
4✔
1138
            .function(variables -> {
2✔
1139
                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);
6✔
1140
                IValueTypeListProxy a = list.getRawValue();
3✔
1141
                if (a.getLength() > 0) {
3!
1142
                    return a.get(0);
4✔
1143
                } else {
1144
                    throw new EvaluationException(Component.translatable(
×
1145
                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, 0, a.getLength()));
×
1146
                }
1147
            }).conditionalOutputTypeDeriver((operator, input) -> {
1✔
1148
                try {
1149
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();
×
1150
                    return a.getValueType();
×
1151
                } catch (EvaluationException e) {
×
1152
                    return operator.getOutputType();
×
1153
                }
1154
            }).build());
1✔
1155

1156
    /**
1157
     * Append an element to the given list.
1158
     */
1159
    public static final IOperator LIST_TAIL = REGISTRY.register(OperatorBuilders.LIST
10✔
1160
            .inputTypes(new IValueType[]{ValueTypes.LIST})
2✔
1161
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)
4✔
1162
            .symbolOperatorInteract("tail")
2✔
1163
            .function(variables -> {
1✔
1164
                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);
6✔
1165
                IValueTypeListProxy a = list.getRawValue();
3✔
1166
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyTail(a));
6✔
1167
            }).build());
1✔
1168

1169
    /**
1170
     * Deduplicate the given list elements based on the given predicate.
1171
     */
1172
    public static final IOperator LIST_UNIQ_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
1173
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
1174
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1175
            .symbolOperator("uniq_p").interactName("uniquePredicate")
6✔
1176
            .function(new OperatorBase.IFunction() {
4✔
1177
                @Override
1178
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1179
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1180
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1181
                    if (list.isInfinite()) {
3!
1182
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1183
                                LIST_UNIQ_PREDICATE.getLocalizedNameFull()));
×
1184
                    }
1185
                    final IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
1186
                    List<IValue> values = new ArrayList<>();
4✔
1187
                    outerLoop:
1188
                    for(IValue value : list) {
10✔
1189
                        for(IValue existing : values) {
10✔
1190
                            IValue result;
1191
                            try {
1192
                                result = ValueHelpers.evaluateOperator(operator, value, existing);
13✔
1193
                                ValueHelpers.validatePredicateOutput(operator, result);
3✔
1194
                            } catch (EvaluationException e) {
×
1195
                                throw Lombok.sneakyThrow(e);
×
1196
                            }
1✔
1197
                            if(((ValueTypeBoolean.ValueBoolean) result).getRawValue()) continue outerLoop;
5✔
1198
                        }
1✔
1199
                        values.add(value);
4✔
1200
                    }
1✔
1201
                    return ValueTypeList.ValueList.ofList(list.getValueType(), values);
5✔
1202
                }
1203
            }).build());
1✔
1204

1205
    /**
1206
     * Deduplicate the given list elements.
1207
     */
1208
    public static final IOperator LIST_UNIQ = REGISTRY.register(OperatorBuilders.LIST
5✔
1209
            .inputType(ValueTypes.LIST)
2✔
1210
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)
4✔
1211
            .symbolOperator("uniq").interactName("unique")
6✔
1212
            .function(new OperatorBase.IFunction() {
4✔
1213
                @Override
1214
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1215
                    ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);
6✔
1216
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1217
                    if (list.isInfinite()) {
3!
1218
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1219
                                LIST_UNIQ.getLocalizedNameFull()));
×
1220
                    }
1221
                    return ValueTypeList.ValueList.ofList(list.getValueType(), new ArrayList<>(Sets.newLinkedHashSet(list)));
9✔
1222
                }
1223
            }).build());
1✔
1224

1225
    /**
1226
     * Take a subset of the given list from the given index (inclusive) to the given index (exclusive).
1227
     */
1228
    public static final IOperator LIST_SLICE = REGISTRY.register(OperatorBuilders.LIST
18✔
1229
            .inputTypes(ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.INTEGER)
2✔
1230
            .renderPattern(IConfigRenderPattern.PREFIX_3).output(ValueTypes.LIST)
4✔
1231
            .symbolOperatorInteract("slice")
2✔
1232
            .function(variables -> {
1✔
1233
                ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);
6✔
1234
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1235
                ValueTypeInteger.ValueInteger from = variables.getValue(1, ValueTypes.INTEGER);
6✔
1236
                ValueTypeInteger.ValueInteger to = variables.getValue(2, ValueTypes.INTEGER);
6✔
1237
                if (from.getRawValue() >= to.getRawValue()) {
5✔
1238
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_TOGREATERTHANFROM));
6✔
1239
                }
1240
                if (from.getRawValue() < 0 || to.getRawValue() < 0){
6!
1241
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_INDEXNEGATIVE));
6✔
1242
                }
1243
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxySlice<>(list, from.getRawValue(), to.getRawValue()));
10✔
1244
            }).build());
1✔
1245

1246
    public static final IOperator LIST_INTERSECTION = REGISTRY.register(OperatorBuilders.LIST
14✔
1247
            .inputTypes(ValueTypes.LIST, ValueTypes.LIST)
2✔
1248
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1249
            .symbol("∩").operatorInteract("intersection")
6✔
1250
            .function(new OperatorBase.IFunction() {
4✔
1251
                @Override
1252
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1253
                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList1 = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
1254
                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList2 = variables.getValue(1, ValueTypes.LIST).getRawValue();
7✔
1255
                    if (rawList1.isInfinite() || rawList2.isInfinite()) {
6!
1256
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1257
                                LIST_INTERSECTION.getLocalizedNameFull()));
×
1258
                    }
1259
                    LinkedHashSet<IValue> result = Sets.newLinkedHashSet(rawList1);
3✔
1260
                    result.retainAll(Sets.newLinkedHashSet(rawList2));
5✔
1261

1262
                    return ValueTypeList.ValueList.ofList(rawList1.getValueType(), result.stream().toList());
7✔
1263
                }
1264
            }).build());
1✔
1265

1266
    /**
1267
     * Test list equality using set semantics.
1268
     */
1269
    public static final IOperator LIST_EQUALS_SET = REGISTRY.register(OperatorBuilders.LIST
14✔
1270
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})
2✔
1271
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.BOOLEAN)
4✔
1272
            .symbol("=set=").operatorInteract("equals_set")
6✔
1273
            .function(new OperatorBase.IFunction() {
4✔
1274
                @Override
1275
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1276
                    ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);
6✔
1277
                    IValueTypeListProxy a = valueList0.getRawValue();
3✔
1278
                    if (a.isInfinite()) {
3!
1279
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1280
                                LIST_EQUALS_SET.getLocalizedNameFull()));
×
1281
                    }
1282
                    ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);
6✔
1283
                    IValueTypeListProxy b = valueList1.getRawValue();
3✔
1284
                    if (b.isInfinite()) {
3!
1285
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1286
                                LIST_EQUALS_SET.getLocalizedNameFull()));
×
1287
                    }
1288
                    if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {
6!
1289
                        throw new EvaluationException(Component.translatable(
×
1290
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1291
                                Component.translatable(a.getValueType().getTranslationKey()),
×
1292
                                Component.translatable(b.getValueType().getTranslationKey())));
×
1293
                    }
1294
                    Set<Object> setA = Sets.newHashSet(a);
3✔
1295
                    Set<Object> setB = Sets.newHashSet(b);
3✔
1296
                    return ValueTypeBoolean.ValueBoolean.of(setA.equals(setB));
5✔
1297
                }
1298
            }).build());
1✔
1299

1300
    /**
1301
     * Test list equality using multiset semantics.
1302
     */
1303
    public static final IOperator LIST_EQUALS_MULTISET = REGISTRY.register(OperatorBuilders.LIST
14✔
1304
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})
2✔
1305
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.BOOLEAN)
4✔
1306
            .symbol("=multiset=").operatorInteract("equals_multiset")
6✔
1307
            .function(new OperatorBase.IFunction() {
4✔
1308
                @Override
1309
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1310
                    ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);
6✔
1311
                    IValueTypeListProxy a = valueList0.getRawValue();
3✔
1312
                    if (a.isInfinite()) {
3!
1313
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1314
                                LIST_EQUALS_MULTISET.getLocalizedNameFull()));
×
1315
                    }
1316
                    ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);
6✔
1317
                    IValueTypeListProxy b = valueList1.getRawValue();
3✔
1318
                    if (b.isInfinite()) {
3!
1319
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1320
                                LIST_EQUALS_MULTISET.getLocalizedNameFull()));
×
1321
                    }
1322
                    if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {
6!
1323
                        throw new EvaluationException(Component.translatable(
×
1324
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1325
                                Component.translatable(a.getValueType().getTranslationKey()),
×
1326
                                Component.translatable(b.getValueType().getTranslationKey())));
×
1327
                    }
1328
                    Multiset<Object> setA = HashMultiset.create(a);
3✔
1329
                    Multiset<Object> setB = HashMultiset.create(b);
3✔
1330
                    return ValueTypeBoolean.ValueBoolean.of(setA.equals(setB));
5✔
1331
                }
1332
            }).build());
1✔
1333

1334
    /**
1335
     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------
1336
     */
1337

1338
    /**
1339
     * Block isOpaque operator with one input block and one output boolean.
1340
     */
1341
    public static final IOperator OBJECT_BLOCK_OPAQUE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN).symbolOperator("opaque").interactName("isOpaque")
11✔
1342
            .function(variables -> {
1✔
1343
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1344
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent() && a.getRawValue().get().isSolidRender(null, null));
17!
1345
            }).build());
1✔
1346

1347
    /**
1348
     * The itemstack representation of the block
1349
     */
1350
    public static final IOperator OBJECT_BLOCK_ITEMSTACK = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK).symbolOperator("itemstack").interactName("itemStack")
11✔
1351
            .function(variables -> {
1✔
1352
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1353
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getRawValue().isPresent() ? BlockHelpers.getItemStackFromBlockState(a.getRawValue().get()) : ItemStack.EMPTY);
12!
1354
            }).build());
1✔
1355

1356
    /**
1357
     * The name of the mod owning this block
1358
     */
1359
    public static final IOperator OBJECT_BLOCK_MODNAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING).symbolOperatorInteract("mod")
20✔
1360
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1361
                    (OperatorBase.SafeVariablesGetter variables) -> {
1362
                        ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1363
                        return a.getRawValue().isPresent() ? BuiltInRegistries.BLOCK.getKey(a.getRawValue().get().getBlock()) : ResourceLocation.parse("");
13!
1364
                    },
1365
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
1366
            ))).build());
1✔
1367

1368
    /**
1369
     * The breaksound of the block
1370
     */
1371
    public static final IOperator OBJECT_BLOCK_BREAKSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1372
            .symbol("break_sound").operatorName("breaksound").interactName("breakSound")
21✔
1373
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1374
                    OperatorBuilders.BLOCK_SOUND,
1375
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getBreakSound().getLocation().toString() : "",
11!
1376
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1377
            ))).build());
1✔
1378
    /**
1379
     * The placesound of the block
1380
     */
1381
    public static final IOperator OBJECT_BLOCK_PLACESOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1382
            .symbol("place_sound").operatorName("placesound").interactName("placeSound")
21✔
1383
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1384
                    OperatorBuilders.BLOCK_SOUND,
1385
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getPlaceSound().getLocation().toString() : "",
11!
1386
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1387
            ))).build());
1✔
1388
    /**
1389
     * The stepsound of the block
1390
     */
1391
    public static final IOperator OBJECT_BLOCK_STEPSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1392
            .symbol("step_sound").operatorName("stepsound").interactName("stepSound")
21✔
1393
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1394
                    OperatorBuilders.BLOCK_SOUND,
1395
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getStepSound().getLocation().toString() : "",
11!
1396
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1397
            ))).build());
1✔
1398

1399
    /**
1400
     * If the block is shearable
1401
     */
1402
    public static final IOperator OBJECT_BLOCK_ISSHEARABLE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
1403
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
1404
            .function(variables -> {
1✔
1405
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1406
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
1407
                        && a.getRawValue().get().getBlock() instanceof IShearable
7✔
1408
                        && ((IShearable) a.getRawValue().get().getBlock()).isShearable(null, ItemStack.EMPTY, null, null));
14!
1409
            }).build());
1✔
1410

1411
    /**
1412
     * The block when this block is planted
1413
     */
1414
    public static final IOperator OBJECT_BLOCK_PLANTAGE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1415
            .output(ValueTypes.INTEGER).symbol("plant_age").operatorName("plantage").interactName("plantAge")
8✔
1416
            .function(variables -> {
1✔
1417
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1418
                int age = 0;
2✔
1419
                if (a.getRawValue().isPresent()) {
4!
1420
                    for (Property<?> prop : a.getRawValue().get().getProperties()) {
14✔
1421
                        if (prop.getName().equals("age") && prop.getValueClass() == Integer.class) {
9!
1422
                            age = (Integer) a.getRawValue().get().getValue(prop);
9✔
1423
                        }
1424
                    }
1✔
1425
                }
1426
                return ValueTypeInteger.ValueInteger.of(age);
3✔
1427
            }).build());
1✔
1428

1429
    /**
1430
     * Get a block by name.
1431
     */
1432
    public static final IOperator OBJECT_BLOCK_BY_NAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1433
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_BLOCK)
4✔
1434
            .symbol("block_by_name").operatorName("blockbyname").interactName("blockByName")
7✔
1435
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
1436
                    .build(input -> {
1✔
1437
                        Block block = BuiltInRegistries.BLOCK.get(input);
5✔
1438
                        return ValueObjectTypeBlock.ValueBlock.of(block.defaultBlockState());
4✔
1439
                    })).build());
1✔
1440

1441
    /**
1442
     * Get the block properties as NBT compound tag.
1443
     */
1444
    public static final IOperator OBJECT_BLOCK_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1445
            .output(ValueTypes.NBT)
2✔
1446
            .symbol("block_props").operatorName("blockproperties").interactName("properties")
6✔
1447
            .function(variables -> {
1✔
1448
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1449
                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {
6✔
1450
                    CompoundTag tag = new CompoundTag();
4✔
1451
                    for (Property property : blockState.getProperties()) {
11✔
1452
                        Comparable<?> value = blockState.getValue(property);
4✔
1453
                        tag.putString(property.getName(), property.getName(value));
7✔
1454
                    }
1✔
1455
                    return tag;
2✔
1456
                }));
1457
            }).build());
1✔
1458

1459
    /**
1460
     * Get the given block applied with the given properties.
1461
     */
1462
    public static final IOperator OBJECT_BLOCK_WITH_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_INFIX_VERYLONG
14✔
1463
            .inputTypes(ValueTypes.OBJECT_BLOCK, ValueTypes.NBT).output(ValueTypes.OBJECT_BLOCK)
4✔
1464
            .symbol("block_with_props").operatorName("blockfromproperties").interactName("withProperties")
6✔
1465
            .function(variables -> {
1✔
1466
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1467
                ValueTypeNbt.ValueNbt b = variables.getValue(1, ValueTypes.NBT);
6✔
1468
                if (a.getRawValue().isPresent() && a.getRawValue().isPresent()) {
8!
1469
                    BlockState blockState = a.getRawValue().get();
5✔
1470
                    Tag tagRaw = b.getRawValue().get();
5✔
1471
                    if (tagRaw instanceof CompoundTag) {
3!
1472
                        CompoundTag tag = (CompoundTag) tagRaw;
3✔
1473
                        for (Property property : blockState.getProperties()) {
11✔
1474
                            if (tag.contains(property.getName(), Tag.TAG_STRING)) {
6!
1475
                                Optional<Comparable> valueOptional = property.getValue(tag.getString(property.getName()));
7✔
1476
                                if (valueOptional.isPresent()) {
3!
1477
                                    blockState = blockState.setValue(property, valueOptional.get());
8✔
1478
                                }
1479
                            }
1480
                        }
1✔
1481
                        return ValueObjectTypeBlock.ValueBlock.of(blockState);
3✔
1482
                    }
1483
                }
1484
                return a;
×
1485
            }).build());
1✔
1486

1487
    /**
1488
     * Get all possible block properties as NBT compound tag with list values.
1489
     */
1490
    public static final IOperator OBJECT_BLOCK_POSSIBLE_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1491
            .output(ValueTypes.NBT)
2✔
1492
            .symbol("block_all_props").operatorName("blockpossibleproperties").interactName("possibleProperties")
6✔
1493
            .function(variables -> {
1✔
1494
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1495
                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {
6✔
1496
                    CompoundTag tag = new CompoundTag();
4✔
1497
                    for (Property property : blockState.getProperties()) {
11✔
1498
                        ListTag list = new ListTag();
4✔
1499
                        for (Comparable value : (Collection<Comparable>) property.getPossibleValues()) {
11✔
1500
                            list.add(StringTag.valueOf(property.getName(value)));
7✔
1501
                        }
1✔
1502
                        tag.put(property.getName(), list);
6✔
1503
                    }
1✔
1504
                    return tag;
2✔
1505
                }));
1506
            }).build());
1✔
1507

1508
    /**
1509
     * The tag entries of the given block
1510
     */
1511
    public static final IOperator OBJECT_BLOCK_TAG = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1512
            .output(ValueTypes.LIST)
2✔
1513
            .symbol("block_tag_names").operatorName("tag").interactName("tags")
6✔
1514
            .function(variables -> {
1✔
1515
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1516
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
1517
                if(!a.getRawValue().isEmpty()) {
4!
1518
                    a.getRawValue().get().getBlock().builtInRegistryHolder().tags()
9✔
1519
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
1520
                                    .of(owningTag.location().toString())));
3✔
1521
                }
1522
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
1523
            }).build());
1✔
1524

1525
    /**
1526
     * Get a list of blocks that correspond to the given tag key.
1527
     */
1528
    public static final IOperator OBJECT_BLOCK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
1529
            .output(ValueTypes.LIST)
2✔
1530
            .symbol("block_tag_values").operatorName("blocktag").interactName("blocksByTag")
6✔
1531
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
1532
            .function(variables -> {
1✔
1533
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
1534
                ImmutableList.Builder<ValueObjectTypeBlock.ValueBlock> builder = ImmutableList.builder();
2✔
1535
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
1536
                    try {
1537
                        Helpers.getBlockTagValues(a.getRawValue())
4✔
1538
                                .map(ValueObjectTypeBlock.ValueBlock::of)
3✔
1539
                                .forEach(builder::add);
4✔
1540
                    } catch (ResourceLocationException e) {
×
1541
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
1542
                    }
1✔
1543
                }
1544
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_BLOCK, builder.build());
5✔
1545
            }).build());
1✔
1546

1547
    /**
1548
     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------
1549
     */
1550

1551
    /**
1552
     * Item Stack size operator with one input itemstack and one output integer.
1553
     */
1554
    public static final IOperator OBJECT_ITEMSTACK_SIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1555
            .output(ValueTypes.INTEGER).symbolOperator("size").interactName("size")
7✔
1556
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1557
                itemStack -> !itemStack.isEmpty() ? itemStack.getCount() : 0
8!
1558
            )).build());
1✔
1559

1560
    /**
1561
     * Item Stack maxsize operator with one input itemstack and one output integer.
1562
     */
1563
    public static final IOperator OBJECT_ITEMSTACK_MAXSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1564
            .output(ValueTypes.INTEGER).symbolOperator("maxsize").interactName("maxSize")
7✔
1565
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1566
                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxStackSize() : 0
8!
1567
            )).build());
1✔
1568

1569
    /**
1570
     * Item Stack isstackable operator with one input itemstack and one output boolean.
1571
     */
1572
    public static final IOperator OBJECT_ITEMSTACK_ISSTACKABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1573
            .output(ValueTypes.BOOLEAN).symbolOperator("stackable").interactName("isStackable")
7✔
1574
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1575
                itemStack -> !itemStack.isEmpty() && itemStack.isStackable()
11!
1576
            )).build());
1✔
1577

1578
    /**
1579
     * Item Stack isdamageable operator with one input itemstack and one output boolean.
1580
     */
1581
    public static final IOperator OBJECT_ITEMSTACK_ISDAMAGEABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1582
            .output(ValueTypes.BOOLEAN).symbolOperator("damageable").interactName("isDamageable")
7✔
1583
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1584
                itemStack -> !itemStack.isEmpty() && itemStack.isDamageableItem()
11!
1585
            )).build());
1✔
1586

1587
    /**
1588
     * Item Stack damage operator with one input itemstack and one output integer.
1589
     */
1590
    public static final IOperator OBJECT_ITEMSTACK_DAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1591
            .output(ValueTypes.INTEGER).symbolOperator("damage").interactName("damage")
7✔
1592
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1593
                itemStack -> !itemStack.isEmpty() ? itemStack.getDamageValue() : 0
8!
1594
            )).build());
1✔
1595

1596
    /**
1597
     * Item Stack maxdamage operator with one input itemstack and one output integer.
1598
     */
1599
    public static final IOperator OBJECT_ITEMSTACK_MAXDAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1600
            .output(ValueTypes.INTEGER).symbol("max_damage").operatorName("maxdamage").interactName("maxDamage")
9✔
1601
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1602
                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxDamage() : 0
8!
1603
            )).build());
1✔
1604

1605
    /**
1606
     * Item Stack isenchanted operator with one input itemstack and one output boolean.
1607
     */
1608
    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTED = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1609
            .output(ValueTypes.BOOLEAN).symbolOperator("enchanted").interactName("isEnchanted")
7✔
1610
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1611
                itemStack -> !itemStack.isEmpty() && itemStack.isEnchanted()
11!
1612
            )).build());
1✔
1613

1614
    /**
1615
     * Item Stack isenchantable operator with one input itemstack and one output boolean.
1616
     */
1617
    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1618
            .output(ValueTypes.BOOLEAN).symbolOperator("enchantable").interactName("isEnchantable")
7✔
1619
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1620
                itemStack -> !itemStack.isEmpty() && itemStack.isEnchantable()
11!
1621
            )).build());
1✔
1622

1623
    /**
1624
     * Item Stack repair cost with one input itemstack and one output integer.
1625
     */
1626
    public static final IOperator OBJECT_ITEMSTACK_REPAIRCOST = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1627
            .output(ValueTypes.INTEGER)
2✔
1628
            .symbol("repair_cost").operatorName("repaircost").interactName("repairCost")
7✔
1629
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1630
                itemStack -> !itemStack.isEmpty() ? itemStack.get(DataComponents.REPAIR_COST) : 0
9!
1631
            )).build());
1✔
1632

1633
    /**
1634
     * Get the rarity of an itemstack.
1635
     */
1636
    public static final IOperator OBJECT_ITEMSTACK_RARITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1637
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
1638
            .function(variables -> {
1✔
1639
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1640
                return ValueTypeString.ValueString.of(!a.getRawValue().isEmpty() ? a.getRawValue().getRarity().name() : "");
11!
1641
            }).build());
1✔
1642

1643
    /**
1644
     * Get the strength of an itemstack against a block as a double.
1645
     */
1646
    public static final IOperator OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
14✔
1647
            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.DOUBLE)
4✔
1648
            .symbolOperatorInteract("strength")
2✔
1649
            .function(variables -> {
1✔
1650
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1651
                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);
6✔
1652
                return ValueTypeDouble.ValueDouble.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() ? a.getRawValue().getDestroySpeed(b.getRawValue().get()) : 0);
19!
1653
            }).build());
1✔
1654

1655
    /**
1656
     * If the given itemstack can be used to harvest the given block.
1657
     */
1658
    public static final IOperator OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1659
            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.BOOLEAN)
4✔
1660
            .symbol("can_harvest").operatorName("canharvest").interactName("canHarvest")
6✔
1661
            .function(variables -> {
1✔
1662
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1663
                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);
6✔
1664
                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() && a.getRawValue().isCorrectToolForDrops(b.getRawValue().get()));
21!
1665
            }).build());
1✔
1666

1667
    /**
1668
     * The block from the stack
1669
     */
1670
    public static final IOperator OBJECT_ITEMSTACK_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1671
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
1672
            .function(variables -> {
1✔
1673
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1674
                return ValueObjectTypeBlock.ValueBlock.of((!a.getRawValue().isEmpty() && a.getRawValue().getItem() instanceof BlockItem) ? BlockHelpers.getBlockStateFromItemStack(a.getRawValue()) : null);
15!
1675
            }).build());
1✔
1676

1677
    /**
1678
     * If the given stack has a fluid.
1679
     */
1680
    public static final IOperator OBJECT_ITEMSTACK_ISFLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1681
            .output(ValueTypes.BOOLEAN)
2✔
1682
            .symbol("is_fluidstack").operatorName("isfluidstack").interactName("isFluidStack")
7✔
1683
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1684
                itemStack -> !itemStack.isEmpty() && !Helpers.getFluidStack(itemStack).isEmpty()
12!
1685
            )).build());
1✔
1686

1687
    /**
1688
     * The fluidstack from the stack
1689
     */
1690
    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1691
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("fluidstack").interactName("fluidStack")
6✔
1692
            .function(variables -> {
1✔
1693
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1694
                return ValueObjectTypeFluidStack.ValueFluidStack.of(!a.getRawValue().isEmpty() ? Helpers.getFluidStack(a.getRawValue()) : FluidStack.EMPTY);
11✔
1695
            }).build());
1✔
1696

1697
    /**
1698
     * The capacity of the fluidstack from the stack.
1699
     */
1700
    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1701
            .output(ValueTypes.INTEGER)
2✔
1702
            .symbol("fluidstack_capacity").operatorName("fluidstackcapacity").interactName("fluidCapacity")
7✔
1703
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1704
                itemStack -> !itemStack.isEmpty() ? Helpers.getFluidStackCapacity(itemStack) : 0
8!
1705
            )).build());
1✔
1706

1707
    /**
1708
     * If the data components of the given stacks are equal.
1709
     */
1710
    public static final IOperator OBJECT_ITEMSTACK_ISDATAEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1711
            .output(ValueTypes.BOOLEAN).symbol("=NBT=").operatorName("isnbtequal").interactName("isNbtEqual")
8✔
1712
            .function(variables -> {
1✔
1713
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1714
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1715
                ItemStack a = valueStack0.getRawValue();
3✔
1716
                ItemStack b = valueStack1.getRawValue();
3✔
1717
                boolean equal = false;
2✔
1718
                if(!a.isEmpty() && !b.isEmpty()) {
6!
1719
                    equal = ItemStack.isSameItem(a, b) && ItemMatch.areItemStacksEqual(a, b, ItemMatch.DATA);
14✔
1720
                } else if(a.isEmpty() && b.isEmpty()) {
×
1721
                    equal = true;
×
1722
                }
1723
                return ValueTypeBoolean.ValueBoolean.of(equal);
3✔
1724
            }).build());
1✔
1725

1726
    /**
1727
     * If the raw items of the given stacks are equal, ignoring data components but including damage value.
1728
     */
1729
    public static final IOperator OBJECT_ITEMSTACK_ISITEMEQUALNODATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1730
            .output(ValueTypes.BOOLEAN).symbol("=NoNBT=").operatorName("isitemequalnonbt").interactName("isEqualNonNbt")
8✔
1731
            .function(variables -> {
1✔
1732
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
×
1733
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
1734
                ItemStack a = valueStack0.getRawValue();
×
1735
                ItemStack b = valueStack1.getRawValue();
×
1736
                boolean equal = false;
×
1737
                if(!a.isEmpty() && !b.isEmpty()) {
×
1738
                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);
×
1739
                } else if(a.isEmpty() && b.isEmpty()) {
×
1740
                    equal = true;
×
1741
                }
1742
                return ValueTypeBoolean.ValueBoolean.of(equal);
×
1743
            }).build());
1✔
1744

1745
    /**
1746
     * If the raw items of the given stacks are equal, ignoring data components and damage value.
1747
     */
1748
    public static final IOperator OBJECT_ITEMSTACK_ISRAWITEMEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1749
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwitemequal").interactName("isEqualRaw")
8✔
1750
            .function(variables -> {
1✔
1751
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1752
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1753
                ItemStack a = valueStack0.getRawValue();
3✔
1754
                ItemStack b = valueStack1.getRawValue();
3✔
1755
                boolean equal = false;
2✔
1756
                if(!a.isEmpty() && !b.isEmpty()) {
6!
1757
                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);
6✔
1758
                } else if(a.isEmpty() && b.isEmpty()) {
×
1759
                    equal = true;
×
1760
                }
1761
                return ValueTypeBoolean.ValueBoolean.of(equal);
3✔
1762
            }).build());
1✔
1763

1764
    /**
1765
     * The name of the mod owning this item
1766
     */
1767
    public static final IOperator OBJECT_ITEMSTACK_MODNAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1768
            .symbolOperatorInteract("mod")
13✔
1769
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1770
                    (OperatorBase.SafeVariablesGetter variables) -> {
1771
                        ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1772
                        return !a.getRawValue().isEmpty() ? BuiltInRegistries.ITEM.getKey(a.getRawValue().getItem()) : ResourceLocation.parse("");
11!
1773
                    },
1774
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
1775
            ))).build());
1✔
1776

1777
    /**
1778
     * The fuel burn time of the given item
1779
     */
1780
    public static final IOperator OBJECT_ITEMSTACK_FUELBURNTIME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1781
            .output(ValueTypes.INTEGER)
2✔
1782
            .symbol("burn_time").operatorName("burntime").interactName("burnTime")
7✔
1783
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(itemStack -> {
2✔
1784
                if (!itemStack.isEmpty()) {
3!
1785
                    int burnTime = itemStack.getBurnTime(null);
4✔
1786
                    return EventHooks.getItemBurnTime(itemStack, burnTime == -1
7!
1787
                            ? itemStack.getBurnTime(RecipeType.SMELTING)
×
1788
                            : burnTime, null);
2✔
1789
                }
1790
                return 0;
×
1791
            })).build());
1✔
1792

1793
    /**
1794
     * If the given item can be used as fuel
1795
     */
1796
    public static final IOperator OBJECT_ITEMSTACK_CANBURN = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1797
            .output(ValueTypes.BOOLEAN)
2✔
1798
            .symbol("can_burn").operatorName("canburn").interactName("canBurn")
7✔
1799
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN
1✔
1800
                    .build(AbstractFurnaceBlockEntity::isFuel))
1✔
1801
            .build());
1✔
1802

1803
    /**
1804
     * The tag entries of the given item
1805
     */
1806
    public static final IOperator OBJECT_ITEMSTACK_TAG = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1807
            .output(ValueTypes.LIST)
2✔
1808
            .symbol("item_tag_names").operatorName("tag").interactName("tags")
6✔
1809
            .function(variables -> {
1✔
1810
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1811
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
1812
                if(!a.getRawValue().isEmpty()) {
4!
1813
                    a.getRawValue().getItem().builtInRegistryHolder().tags()
7✔
1814
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
1815
                                    .of(owningTag.location().toString())));
3✔
1816
                }
1817
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
1818
            }).build());
1✔
1819

1820
    /**
1821
     * Get a list of items that correspond to the given tag key.
1822
     */
1823
    public static final IOperator OBJECT_ITEMSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
1824
            .output(ValueTypes.LIST)
2✔
1825
            .symbol("item_tag_values").operatorName("tag").interactName("itemsByTag")
6✔
1826
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
1827
            .function(variables -> {
1✔
1828
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
1829
                ImmutableList.Builder<ValueObjectTypeItemStack.ValueItemStack> builder = ImmutableList.builder();
2✔
1830
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
1831
                    try {
1832
                        Helpers.getTagValues(a.getRawValue())
4✔
1833
                                .map(ValueObjectTypeItemStack.ValueItemStack::of)
3✔
1834
                                .forEach(builder::add);
4✔
1835
                    } catch (ResourceLocationException e) {
×
1836
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
1837
                    }
1✔
1838
                }
1839
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, builder.build());
5✔
1840
            }).build());
1✔
1841

1842
    /**
1843
     * ItemStack operator that applies the given stacksize to the given itemstack and creates a new ItemStack.
1844
     */
1845
    public static final IOperator OBJECT_ITEMSTACK_WITHSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_INTEGER_1
5✔
1846
            .output(ValueTypes.OBJECT_ITEMSTACK)
2✔
1847
            .symbol("with_size").operatorName("withsize").interactName("withSize")
6✔
1848
            .function(variables -> {
1✔
1849
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1850
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
1851
                if (!a.getRawValue().isEmpty()) {
4!
1852
                    ItemStack itemStack = a.getRawValue().copy();
4✔
1853
                    itemStack.setCount(b.getRawValue());
4✔
1854
                    return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
1855
                }
1856
                return a;
×
1857
            }).build());
1✔
1858

1859
    /**
1860
     * Check if the item is an RF container item
1861
     */
1862
    public static final IOperator OBJECT_ITEMSTACK_ISFECONTAINER = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1863
            .output(ValueTypes.BOOLEAN)
2✔
1864
            .symbol("is_fe_container").operatorName("isfecontainer").interactName("isFeContainer")
7✔
1865
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_BOOLEAN.build(
2✔
1866
                Objects::nonNull
1867
            )).build());
1✔
1868

1869
    /**
1870
     * Get the storage energy
1871
     */
1872
    public static final IOperator OBJECT_ITEMSTACK_STOREDFE = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1873
            .output(ValueTypes.INTEGER)
2✔
1874
            .symbol("stored_fe").operatorName("storedfe").interactName("feStored")
7✔
1875
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_INT.build(
2✔
1876
                input -> input != null ? input.getEnergyStored() : 0
8✔
1877
            )).build());
1✔
1878

1879
    /**
1880
     * Get the energy capacity
1881
     */
1882
    public static final IOperator OBJECT_ITEMSTACK_FECAPACITY = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1883
            .output(ValueTypes.INTEGER)
2✔
1884
            .symbol("capacity_fe").operatorName("fecapacity").interactName("feCapacity")
7✔
1885
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_INT.build(
2✔
1886
                input -> input != null ? input.getMaxEnergyStored() : 0
8✔
1887
            )).build());
1✔
1888

1889

1890
    /**
1891
     * If the given item has an inventory.
1892
     */
1893
    public static final IOperator OBJECT_ITEMSTACK_HASINVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1894
            .output(ValueTypes.BOOLEAN)
2✔
1895
            .symbol("has_inventory").operatorName("hasinventory").interactName("hasInventory")
6✔
1896
            .function(variables -> {
1✔
1897
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1898
                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM) != null);
14!
1899
            }).build());
1✔
1900

1901

1902

1903

1904
    /**
1905
     * Retrieve the inventory size of the given item handler contents.
1906
     */
1907
    public static final IOperator OBJECT_ITEMSTACK_INVENTORYSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1908
            .output(ValueTypes.INTEGER)
2✔
1909
            .symbol("inventory_size").operatorName("inventorysize").interactName("inventorySize")
6✔
1910
            .function(variables -> {
1✔
1911
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1912
                IItemHandler itemHandler = a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM);
6✔
1913
                return ValueTypeInteger.ValueInteger.of(itemHandler != null ? itemHandler.getSlots() : 0);
8✔
1914
            }).build());
1✔
1915

1916
    /**
1917
     * Retrieve the inventory of the given item handler contents.
1918
     */
1919
    public static final IOperator OBJECT_ITEMSTACK_INVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1920
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
1921
            .function(variables -> {
1✔
1922
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1923
                IItemHandler itemHandler = a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM);
6✔
1924
                if (itemHandler != null) {
2✔
1925
                    List<ValueObjectTypeItemStack.ValueItemStack> values = Lists.newArrayListWithCapacity(itemHandler.getSlots());
4✔
1926
                    for (int i = 0; i < itemHandler.getSlots(); i++) {
8✔
1927
                        values.add(ValueObjectTypeItemStack.ValueItemStack.of(itemHandler.getStackInSlot(i)));
7✔
1928
                    }
1929
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, values);
4✔
1930
                }
1931
                return ValueTypes.LIST.getDefault();
3✔
1932
            }).build());
1✔
1933

1934
    /**
1935
     * Get an item by name.
1936
     */
1937
    public static final IOperator OBJECT_ITEMSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
1938
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_ITEMSTACK)
4✔
1939
            .symbol("item_by_name").operatorName("itembyname").interactName("itemByName")
7✔
1940
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
1941
                    .build(input -> {
1✔
1942
                        Item item = BuiltInRegistries.ITEM.get(input);
5✔
1943
                        ItemStack itemStack = ItemStack.EMPTY;
2✔
1944
                        if (item != null) {
2!
1945
                            itemStack = new ItemStack(item);
5✔
1946
                        }
1947
                        return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
1948
                    })).build());
1✔
1949

1950
    /**
1951
     * Get the total item count of the given item in a list.
1952
     */
1953
    public static final IOperator OBJECT_ITEMSTACK_LIST_COUNT = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1954
            .inputTypes(ValueTypes.LIST, ValueTypes.OBJECT_ITEMSTACK)
2✔
1955
            .output(ValueTypes.INTEGER)
2✔
1956
            .symbol("item_list_count").operatorName("itemlistcount").interactName("itemListCount")
8✔
1957
            .function(new OperatorBase.IFunction() {
4✔
1958
                @Override
1959
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1960
                    ValueTypeList.ValueList<IValueType<IValue>, IValue> a = variables.getValue(0, ValueTypes.LIST);
6✔
1961
                    ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1962
                    if (!ValueHelpers.correspondsTo(a.getRawValue().getValueType(), ValueTypes.OBJECT_ITEMSTACK)) {
6!
1963
                        MutableComponent error = Component.translatable(
×
1964
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1965
                                Component.translatable(a.getRawValue().getValueType().getTranslationKey()),
×
1966
                                Component.translatable(ValueTypes.OBJECT_ITEMSTACK.getTranslationKey()));
×
1967
                        throw new EvaluationException(error);
×
1968
                    }
1969

1970
                    ItemStack itemStack = b.getRawValue();
3✔
1971
                    int count = 0;
2✔
1972
                    if (a.getRawValue().isInfinite()) {
4!
1973
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1974
                                OBJECT_ITEMSTACK_LIST_COUNT.getLocalizedNameFull()));
×
1975
                    }
1976
                    for (IValue listValueRaw : a.getRawValue()) {
11✔
1977
                        if (listValueRaw.getType().correspondsTo(ValueTypes.OBJECT_ITEMSTACK)) {
5!
1978
                            ValueObjectTypeItemStack.ValueItemStack listValue = (ValueObjectTypeItemStack.ValueItemStack) listValueRaw;
3✔
1979
                            if (!listValue.getRawValue().isEmpty()) {
4!
1980
                                ItemStack listItem = listValue.getRawValue();
3✔
1981
                                if (!itemStack.isEmpty()) {
3!
1982
                                    if (ItemStack.isSameItemSameComponents(itemStack, listItem)) {
4✔
1983
                                        count += listItem.getCount();
5✔
1984
                                    }
1985
                                }
1986
                            }
1987
                        }
1988
                    }
1✔
1989
                    return ValueTypeInteger.ValueInteger.of(count);
3✔
1990
                }
1991
            }).build());
1✔
1992

1993
    /**
1994
     * Item Stack size operator with one input itemstack and one output NBT tag.
1995
     */
1996
    public static final IOperator OBJECT_ITEMSTACK_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1997
            .output(ValueTypes.NBT).symbol("NBT()").operatorName("nbt").interactName("nbt")
8✔
1998
            .function(input -> {
1✔
1999
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2000
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2001
                return ValueTypeNbt.ValueNbt.of(itemStack.getRawValue().isEmpty() || itemStack.getRawValue().getComponents().isEmpty() ?
11!
2002
                        Optional.empty() :
2✔
2003
                        Optional.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), itemStack.getRawValue().getComponentsPatch()).getOrThrow()));
12✔
2004
            }).build());
1✔
2005

2006
    /**
2007
     * Item Stack has_nbt operator with one input itemstack and one output boolean.
2008
     */
2009
    public static final IOperator OBJECT_ITEMSTACK_HASDATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
2010
            .output(ValueTypes.BOOLEAN).symbol("has_nbt").operatorName("hasnbt").interactName("hasNbt")
9✔
2011
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
2012
                    itemStack -> !itemStack.isEmpty() && itemStack.getComponents().stream().anyMatch(t -> !t.type().isTransient())
21!
2013
            )).build());
1✔
2014

2015
    /**
2016
     * Get the data component keys of an itemstack.
2017
     */
2018
    public static final IOperator OBJECT_ITEMSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
2019
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
2020
            .function(input -> {
1✔
2021
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2022
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2023
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
2024
                        itemStack.getRawValue().isEmpty() ?
4!
2025
                                Lists.newArrayList() :
×
2026
                                itemStack.getRawValue().getComponents().keySet().stream()
5✔
2027
                                        .filter(c -> !c.isTransient())
8!
2028
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
2029
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
7✔
2030
                                        .toList());
2✔
2031
            }).build());
1✔
2032

2033
    /**
2034
     * Get the data component value by key
2035
     */
2036
    public static final IOperator OBJECT_ITEMSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
2037
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING)
2✔
2038
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
2039
            .function(input -> {
1✔
2040
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2041

2042
                // Determine data component type
2043
                DataComponentType<?> dataComponentType;
2044
                try {
2045
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.get(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2046
                } catch (ResourceLocationException e) {
×
2047
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2048
                }
1✔
2049

2050
                // Fetch component value
2051
                TypedDataComponent<?> typedComponent = itemStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
2052
                if (typedComponent == null) {
2✔
2053
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
2054
                }
2055

2056
                // Encode component value
2057
                try {
2058
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
2059
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
2060
                } catch (IllegalStateException e) {
×
2061
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
2062
                }
2063
            }).build());
1✔
2064

2065
    /**
2066
     * Get the data component value by key
2067
     */
2068
    public static final IOperator OBJECT_ITEMSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_3
18✔
2069
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2070
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2071
            .function(input -> {
1✔
2072
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2073

2074
                // Skip further processing if input value is empty
2075
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2076
                if (!tagOptional.isPresent()) {
3!
2077
                    return itemStack;
×
2078
                }
2079

2080
                // Determine data component type
2081
                DataComponentType<?> dataComponentType;
2082
                try {
2083
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.get(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2084
                } catch (ResourceLocationException e) {
×
2085
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2086
                }
1✔
2087

2088
                // Encode component value
2089
                try {
2090
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2091
                    itemStack = ValueObjectTypeItemStack.ValueItemStack.of(itemStack.getRawValue().copy());
5✔
2092
                    itemStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2093
                    return itemStack;
2✔
2094
                } catch (IllegalStateException e) {
×
2095
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2096
                }
2097
            }).build());
1✔
2098

2099
    /**
2100
     * Get the tooltip of an itemstack in list form.
2101
     */
2102
    public static final IOperator OBJECT_ITEMSTACK_TOOLTIP = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
2103
            .output(ValueTypes.LIST).symbol("tooltip").operatorName("tooltip").interactName("tooltip")
8✔
2104
            .function(input -> {
1✔
2105
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2106
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
4✔
2107
                        itemStack.getRawValue().getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.Default.NORMAL).stream()
7✔
2108
                                .map(c -> ValueTypeString.ValueString.of(c.getString()))
5✔
2109
                                .toList());
1✔
2110
            }).build());
1✔
2111
    /**
2112
     * Get the tooltip of an itemstack in list form, using the provided player entity as the player context.
2113
     */
2114
    public static final IOperator OBJECT_ITEMSTACK_ENTITY_TOOLTIP = REGISTRY.register(OperatorBuilders.ENTITY_1_ITEMSTACK_1
14✔
2115
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2116
            .output(ValueTypes.LIST).symbol("entity_item_tooltip").operatorName("entityitemtooltip").interactName("entityItemTooltip")
8✔
2117
            .function(variables -> {
1✔
2118
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2119
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
2120
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2121
                    Player entity = (Player) a.getRawValue().get();
×
2122
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
×
2123
                            itemStack.getRawValue().getTooltipLines(Item.TooltipContext.of(entity.level()), entity, TooltipFlag.Default.NORMAL).stream()
×
2124
                                    .map(c -> ValueTypeString.ValueString.of(c.getString()))
×
2125
                                    .toList());
×
2126
                }
2127
                return ValueTypes.LIST.getDefault();
×
2128
            }).build());
1✔
2129

2130
    /**
2131
     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------
2132
     */
2133

2134
    /**
2135
     * If the entity is a mob
2136
     */
2137
    public static final IOperator OBJECT_ENTITY_ISMOB = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2138
            .output(ValueTypes.BOOLEAN).symbol("is_mob").operatorName("ismob").interactName("isMob")
9✔
2139
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2140
                entity -> entity instanceof Enemy
4✔
2141
            )).build());
1✔
2142

2143
    /**
2144
     * If the entity is an animal
2145
     */
2146
    public static final IOperator OBJECT_ENTITY_ISANIMAL = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2147
            .output(ValueTypes.BOOLEAN).symbol("is_animal").operatorName("isanimal").interactName("isAnimal")
9✔
2148
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2149
                entity -> entity instanceof Animal && !(entity instanceof Enemy)
11!
2150
            )).build());
1✔
2151

2152
    /**
2153
     * If the entity is an item
2154
     */
2155
    public static final IOperator OBJECT_ENTITY_ISITEM = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2156
            .output(ValueTypes.BOOLEAN).symbol("is_item").operatorName("isitem").interactName("isItem")
9✔
2157
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2158
                entity -> entity instanceof ItemEntity
4✔
2159
            )).build());
1✔
2160

2161
    /**
2162
     * If the entity is a player
2163
     */
2164
    public static final IOperator OBJECT_ENTITY_ISPLAYER = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2165
            .output(ValueTypes.BOOLEAN).symbol("is_player").operatorName("isplayer").interactName("isPlayer")
9✔
2166
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2167
                entity -> entity instanceof Player
4✔
2168
            )).build());
1✔
2169

2170
    /**
2171
     * If the entity is a minecart
2172
     */
2173
    public static final IOperator OBJECT_ENTITY_ISMINECART = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2174
            .output(ValueTypes.BOOLEAN).symbol("is_minecart").operatorName("isminecart").interactName("isMinecart")
9✔
2175
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2176
                entity -> entity instanceof AbstractMinecart
×
2177
            )).build());
1✔
2178

2179
    /**
2180
     * The itemstack from the entity
2181
     */
2182
    public static final IOperator OBJECT_ENTITY_ITEMSTACK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX
5✔
2183
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("item")
4✔
2184
            .function(variables -> {
1✔
2185
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2186
                Optional<Entity> a = valueEntity.getRawValue();
3✔
2187
                return ValueObjectTypeItemStack.ValueItemStack.of((a.isPresent() && a.get() instanceof ItemEntity) ? ((ItemEntity) a.get()).getItem() : ItemStack.EMPTY);
15!
2188
            }).build());
1✔
2189

2190
    /**
2191
     * The entity health
2192
     */
2193
    public static final IOperator OBJECT_ENTITY_HEALTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2194
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("health")
5✔
2195
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2196
                entity -> entity instanceof LivingEntity ? ((LivingEntity) entity).getHealth() : 0.0
11✔
2197
            )).build());
1✔
2198

2199
    /**
2200
     * The entity width
2201
     */
2202
    public static final IOperator OBJECT_ENTITY_WIDTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2203
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("width")
5✔
2204
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2205
                entity -> entity != null ? entity.getBbWidth() : 0.0
8!
2206
            )).build());
1✔
2207

2208
    /**
2209
     * The entity width
2210
     */
2211
    public static final IOperator OBJECT_ENTITY_HEIGHT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2212
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("height")
5✔
2213
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2214
                entity -> entity != null ? entity.getBbHeight() : 0.0
8!
2215
            )).build());
1✔
2216

2217
    /**
2218
     * If the entity is burning
2219
     */
2220
    public static final IOperator OBJECT_ENTITY_ISBURNING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2221
            .output(ValueTypes.BOOLEAN).symbol("is_burning").operatorName("isburning").interactName("entityIsBurning")
9✔
2222
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2223
                entity -> entity != null && entity.isOnFire()
10!
2224
            )).build());
1✔
2225

2226
    /**
2227
     * If the entity is wet
2228
     */
2229
    public static final IOperator OBJECT_ENTITY_ISWET = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2230
            .output(ValueTypes.BOOLEAN).symbol("is_wet").operatorName("iswet").interactName("isWet")
9✔
2231
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2232
                entity -> entity != null && entity.isInWaterOrRain()
10!
2233
            )).build());
1✔
2234

2235
    /**
2236
     * If the entity is crouching
2237
     */
2238
    public static final IOperator OBJECT_ENTITY_ISCROUCHING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2239
            .output(ValueTypes.BOOLEAN).symbol("is_crouching").operatorName("iscrouching").interactName("isCrouching")
9✔
2240
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2241
                entity -> entity != null && entity.isCrouching()
10!
2242
            )).build());
1✔
2243

2244
    /**
2245
     * If the entity is eating
2246
     */
2247
    public static final IOperator OBJECT_ENTITY_ISEATING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2248
            .output(ValueTypes.BOOLEAN).symbol("is_eating").operatorName("iseating").interactName("isEating")
9✔
2249
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2250
                entity -> entity instanceof LivingEntity && ((LivingEntity) entity).getUseItemRemainingTicks() > 0
12!
2251
            )).build());
1✔
2252

2253
    /**
2254
     * The list of armor itemstacks from an entity
2255
     */
2256
    public static final IOperator OBJECT_ENTITY_ARMORINVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2257
            .output(ValueTypes.LIST).symbol("armor_inventory").operatorName("armorinventory").interactName("armorInventory")
8✔
2258
            .function(variables -> {
1✔
2259
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2260
                Optional<Entity> a = valueEntity.getRawValue();
×
2261
                if(a.isPresent()) {
×
2262
                    Entity entity = a.get();
×
2263
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityArmorInventory(entity.level(), entity));
×
2264
                } else {
2265
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2266
                }
2267
            }).build());
1✔
2268

2269
    /**
2270
     * The list of itemstacks from an entity
2271
     */
2272
    public static final IOperator OBJECT_ENTITY_INVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2273
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
2274
            .function(variables -> {
1✔
2275
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2276
                Optional<Entity> a = valueEntity.getRawValue();
×
2277
                if(a.isPresent()) {
×
2278
                    Entity entity = a.get();
×
2279
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityInventory(entity.level(), entity));
×
2280
                } else {
2281
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2282
                }
2283
            }).build());
1✔
2284

2285
    /**
2286
     * The name of the mod owning this entity
2287
     */
2288
    public static final IOperator OBJECT_ENTITY_MODNAME = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2289
            .symbolOperatorInteract("mod")
13✔
2290
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2291
                    (OperatorBase.SafeVariablesGetter variables) -> {
2292
                        ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2293
                        if(a.getRawValue().isPresent()) {
4!
2294
                            Entity entity = a.getRawValue().get();
5✔
2295
                            return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());
5✔
2296
                        }
2297
                        return ResourceLocation.parse("");
×
2298
                    },
2299
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2300
            )))
2301
            .build());
1✔
2302

2303
    /**
2304
     * The block the given player is currently looking at.
2305
     */
2306
    public static final IOperator OBJECT_PLAYER_TARGETBLOCK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_BLOCK)
7✔
2307
            .symbol("target_block").operatorName("targetblock").interactName("targetBlock")
6✔
2308
            .function(variables -> {
1✔
2309
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2310
                BlockState blockState = null;
×
2311
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2312
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2313
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.BLOCK_INTERACTION_RANGE);
×
2314
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2315
                    double eyeHeight = entity.getEyeHeight();
×
2316
                    Vec3 lookVec = entity.getLookAngle();
×
2317
                    Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());
×
2318
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2319

2320
                    ClipContext rayTraceContext = new ClipContext(origin, direction, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity);
×
2321
                    BlockHitResult mop = entity.level().clip(rayTraceContext);
×
2322
                    if(mop != null && mop.getType() == HitResult.Type.BLOCK) {
×
2323
                        blockState = entity.level().getBlockState(mop.getBlockPos());
×
2324
                    }
2325
                }
2326
                return ValueObjectTypeBlock.ValueBlock.of(blockState);
×
2327
            }).build());
1✔
2328

2329
    /**
2330
     * The entity the given player is currently looking at.
2331
     */
2332
    public static final IOperator OBJECT_PLAYER_TARGETENTITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ENTITY)
7✔
2333
            .symbol("target_entity").operatorName("targetentity").interactName("targetEntity")
6✔
2334
            .function(variables -> {
1✔
2335
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2336
                Entity entityOut = null;
×
2337
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2338
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2339
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.ENTITY_INTERACTION_RANGE);
×
2340
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2341

2342
                    // Copied and modified from GameRenderer#getMouseOver
2343
                    Vec3 origin = entity.getEyePosition(1.0F);
×
2344
                    double reachDistanceSquared = reachDistance * reachDistance;
×
2345

2346
                    Vec3 lookVec = entity.getViewVector(1.0F);
×
2347
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2348
                    AABB boundingBox = entity.getBoundingBox()
×
2349
                            .expandTowards(lookVec.scale(reachDistance))
×
2350
                            .inflate(1.0D, 1.0D, 1.0D);
×
2351
                    EntityHitResult entityraytraceresult = ProjectileUtil.getEntityHitResult(entity, origin, direction,
×
2352
                            boundingBox, e -> !e.isSpectator() && e.isPickable(), reachDistanceSquared);
×
2353
                    if (entityraytraceresult != null) {
×
2354
                        Entity entity1 = entityraytraceresult.getEntity();
×
2355
                        Vec3 vec3d3 = entityraytraceresult.getLocation();
×
2356
                        double distanceSquared = origin.distanceToSqr(vec3d3);
×
2357
                        if (distanceSquared < reachDistanceSquared
×
2358
                                && (entity1 instanceof LivingEntity || entity1 instanceof ItemFrame)) {
2359
                            entityOut = entity1;
×
2360
                        }
2361
                    }
2362
                }
2363
                return ValueObjectTypeEntity.ValueEntity.of(entityOut);
×
2364
            }).build());
1✔
2365

2366
    /**
2367
     * If the given player has an external gui open.
2368
     */
2369
    public static final IOperator OBJECT_PLAYER_HASGUIOPEN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2370
            .symbol("has_gui_open").operatorName("hasguiopen").interactName("hasGuiOpen")
6✔
2371
            .function(variables -> {
1✔
2372
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2373
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2374
                    Player entity = (Player) a.getRawValue().get();
×
2375
                    return ValueTypeBoolean.ValueBoolean.of(entity.containerMenu != entity.inventoryMenu);
×
2376
                }
2377
                return ValueTypeBoolean.ValueBoolean.of(false);
×
2378
            }).build());
1✔
2379

2380
    /**
2381
     * The item the given entity is currently holding in its main hand.
2382
     */
2383
    public static final IOperator OBJECT_ENTITY_HELDITEM_MAIN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2384
            .symbol("held_item_1").operatorName("helditem").interactName("heldItem")
6✔
2385
            .function(variables -> {
1✔
2386
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2387
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2388
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2389
                    itemStack = ((LivingEntity) a.getRawValue().get()).getMainHandItem();
6✔
2390
                }
2391
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2392
            }).build());
1✔
2393

2394
    /**
2395
     * The item the given entity is currently holding in its off hand.
2396
     */
2397
    public static final IOperator OBJECT_ENTITY_HELDITEM_OFF = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2398
            .symbol("held_item_2").operatorName("helditemoffhand").interactName("heldItemOffHand")
6✔
2399
            .function(variables -> {
1✔
2400
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2401
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2402
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2403
                    itemStack = ((LivingEntity) a.getRawValue().get()).getOffhandItem();
6✔
2404
                }
2405
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2406
            }).build());
1✔
2407

2408
    /**
2409
     * The entity's mounted entity
2410
     */
2411
    public static final IOperator OBJECT_ENTITY_MOUNTED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.LIST)
7✔
2412
            .symbolOperator("mounted").interactName("mounted")
4✔
2413
            .function(variables -> {
1✔
2414
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2415
                List<ValueObjectTypeEntity.ValueEntity> passengers = Lists.newArrayList();
2✔
2416
                if(a.getRawValue().isPresent()) {
4!
2417
                    for (Entity passenger : a.getRawValue().get().getPassengers()) {
14✔
2418
                        passengers.add(ValueObjectTypeEntity.ValueEntity.of(passenger));
5✔
2419
                    }
1✔
2420
                }
2421
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, passengers);
4✔
2422
            }).build());
1✔
2423

2424
    /**
2425
     * The item frame's contents
2426
     */
2427
    public static final IOperator OBJECT_ITEMFRAME_CONTENTS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2428
            .symbol("itemframe_contents").operatorName("itemframecontents").interactName("itemFrameContents")
6✔
2429
            .function(variables -> {
1✔
2430
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2431
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2432
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2433
                    itemStack = ((ItemFrame) a.getRawValue().get()).getItem();
6✔
2434
                }
2435
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2436
            }).build());
1✔
2437

2438
    /**
2439
     * The item frame's rotation
2440
     */
2441
    public static final IOperator OBJECT_ITEMFRAME_ROTATION = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2442
            .symbol("itemframe_rotation").operatorName("itemframerotation").interactName("itemFrameRotation")
6✔
2443
            .function(variables -> {
1✔
2444
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2445
                Integer rotation = 0;
3✔
2446
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2447
                    rotation = ((ItemFrame) a.getRawValue().get()).getRotation();
7✔
2448
                }
2449
                return ValueTypeInteger.ValueInteger.of(rotation);
4✔
2450
            }).build());
1✔
2451

2452
    /**
2453
     * The hurtsound of this entity.
2454
     */
2455
    public static final IOperator OBJECT_ENTITY_HURTSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2456
            .symbolOperator("hurtsound").interactName("hurtSound")
4✔
2457
            .function(variables -> {
1✔
2458
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2459
                String hurtSound = "";
2✔
2460
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2461
                    String sound = ((LivingEntity) a.getRawValue().get()).getHurtSound(a.getRawValue().get().damageSources().generic()).getLocation().toString();
14✔
2462
                    if (sound != null) {
2!
2463
                        hurtSound = sound;
2✔
2464
                    }
2465
                }
2466
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2467
            }).build());
1✔
2468

2469
    /**
2470
     * The deathsound of this entity.
2471
     */
2472
    public static final IOperator OBJECT_ENTITY_DEATHSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2473
            .symbolOperator("deathsound").interactName("deathSound")
4✔
2474
            .function(variables -> {
1✔
2475
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2476
                String hurtSound = "";
2✔
2477
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2478
                    String sound = ((LivingEntity) a.getRawValue().get()).getDeathSound().getLocation().toString();
8✔
2479
                    if (sound != null) {
2!
2480
                        hurtSound = sound;
2✔
2481
                    }
2482
                }
2483
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2484
            }).build());
1✔
2485

2486
    /**
2487
     * The age of this entity.
2488
     */
2489
    public static final IOperator OBJECT_ENTITY_AGE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2490
            .symbolOperatorInteract("age")
2✔
2491
            .function(variables -> {
1✔
2492
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2493
                int age = 0;
2✔
2494
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2495
                    age = ((LivingEntity) a.getRawValue().get()).getNoActionTime();
6✔
2496
                }
2497
                return ValueTypeInteger.ValueInteger.of(age);
3✔
2498
            }).build());
1✔
2499

2500
    /**
2501
     * If the entity is a child.
2502
     */
2503
    public static final IOperator OBJECT_ENTITY_ISCHILD = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2504
            .symbol("is_child").operatorName("ischild").interactName("isChild")
6✔
2505
            .function(variables -> {
1✔
2506
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2507
                boolean child = false;
2✔
2508
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2509
                    child = ((LivingEntity) a.getRawValue().get()).isBaby();
6✔
2510
                }
2511
                return ValueTypeBoolean.ValueBoolean.of(child);
3✔
2512
            }).build());
1✔
2513

2514
    /**
2515
     * If the entity can be bred.
2516
     */
2517
    public static final IOperator OBJECT_ENTITY_CANBREED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2518
            .symbol("canbreed").operatorName("canbreed").interactName("canBreed")
6✔
2519
            .function(variables -> {
1✔
2520
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2521
                boolean canBreed = false;
2✔
2522
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof AgeableMob) {
9!
2523
                    canBreed = ((AgeableMob) a.getRawValue().get()).getAge() == 0;
10✔
2524
                }
2525
                return ValueTypeBoolean.ValueBoolean.of(canBreed);
3✔
2526
            }).build());
1✔
2527

2528
    /**
2529
     * If the entity is in love.
2530
     */
2531
    public static final IOperator OBJECT_ENTITY_ISINLOVE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2532
            .symbol("is_in_love").operatorName("isinlove").interactName("isInLove")
6✔
2533
            .function(variables -> {
1✔
2534
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2535
                boolean inLove = false;
2✔
2536
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof Animal) {
9!
2537
                    inLove = ((Animal) a.getRawValue().get()).isInLove();
6✔
2538
                }
2539
                return ValueTypeBoolean.ValueBoolean.of(inLove);
3✔
2540
            }).build());
1✔
2541

2542
    /**
2543
     * If the entity can be bred with the given item.
2544
     */
2545
    public static final IOperator OBJECT_ENTITY_CANBREEDWITH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
14✔
2546
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2547
            .output(ValueTypes.BOOLEAN)
2✔
2548
            .symbol("can_breed_with").operatorName("canbreedwith").interactName("canBreedWith")
6✔
2549
            .renderPattern(IConfigRenderPattern.INFIX_LONG)
2✔
2550
            .function(variables -> {
1✔
2551
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2552
                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
2553
                boolean canBreedWith = false;
2✔
2554
                if (a.getRawValue().isPresent() && !b.getRawValue().isEmpty() && a.getRawValue().get() instanceof Animal) {
13!
2555
                    canBreedWith = ((Animal) a.getRawValue().get()).isFood(b.getRawValue());
8✔
2556
                }
2557
                return ValueTypeBoolean.ValueBoolean.of(canBreedWith);
3✔
2558
            }).build());
1✔
2559

2560
    /**
2561
     * If the entity is shearable.
2562
     */
2563
    public static final IOperator OBJECT_ENTITY_ISSHEARABLE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2564
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
2565
            .function(variables -> {
1✔
2566
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2567
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
2568
                        && a.getRawValue().get() instanceof IShearable
5✔
2569
                        && ((IShearable) a.getRawValue().get()).isShearable(null, ItemStack.EMPTY, null, null));
12✔
2570
            }).build());
1✔
2571

2572
    /**
2573
     * The entity serialized to NBT.
2574
     */
2575
    public static final IOperator OBJECT_ENTITY_NBT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2576
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2577
            .function(input -> {
1✔
2578
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2579
                try {
2580
                    if (entity.getRawValue().isPresent()) {
4!
2581
                        return ValueTypeNbt.ValueNbt.of(entity.getRawValue().get().serializeNBT(ServerLifecycleHooks.getCurrentServer().registryAccess()));
9✔
2582
                    }
2583
                } catch (Exception e) {
×
2584
                    // Catch possible errors during NBT writing
2585
                }
×
2586
                return ValueTypes.NBT.getDefault();
×
2587
            }).build());
1✔
2588

2589
    /**
2590
     * The entity type.
2591
     */
2592
    public static final IOperator OBJECT_ENTITY_TYPE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2593
            .output(ValueTypes.STRING).symbol("entity_type").operatorName("entitytype").interactName("type")
8✔
2594
            .function(input -> {
1✔
2595
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2596
                String entityType = "";
2✔
2597
                if (entity.getRawValue().isPresent()) {
4!
2598
                    Entity e = entity.getRawValue().get();
5✔
2599
                    entityType = BuiltInRegistries.ENTITY_TYPE.getKey(e.getType()).toString();
6✔
2600
                }
2601
                return ValueTypeString.ValueString.of(entityType);
3✔
2602
            }).build());
1✔
2603

2604
    /**
2605
     * The entity items.
2606
     */
2607
    public static final IOperator OBJECT_ENTITY_ITEMS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2608
            .output(ValueTypes.LIST).symbol("entity_items").operatorName("entityitems").interactName("items")
8✔
2609
            .function(input -> {
1✔
2610
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2611
                Optional<Entity> a = valueEntity.getRawValue();
×
2612
                if(a.isPresent()) {
×
2613
                    Entity entity = a.get();
×
2614
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityItems(entity.level(), entity, null));
×
2615
                } else {
2616
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.emptyList());
×
2617
                }
2618
            }).build());
1✔
2619

2620
    /**
2621
     * The entity fluids.
2622
     */
2623
    public static final IOperator OBJECT_ENTITY_FLUIDS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2624
            .output(ValueTypes.LIST).symbol("entity_fluids").operatorName("entityfluids").interactName("fluids")
8✔
2625
            .function(input -> {
1✔
2626
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2627
                Optional<Entity> a = valueEntity.getRawValue();
×
2628
                if(a.isPresent()) {
×
2629
                    Entity entity = a.get();
×
2630
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityFluids(entity.level(), entity, null));
×
2631
                } else {
2632
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, Collections.emptyList());
×
2633
                }
2634
            }).build());
1✔
2635

2636
    /**
2637
     * The entity energy stored.
2638
     */
2639
    public static final IOperator OBJECT_ENTITY_ENERGY_STORED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2640
            .output(ValueTypes.INTEGER).symbol("entity_stored_fe").operatorName("entityenergystored").interactName("energy")
8✔
2641
            .function(input -> {
1✔
2642
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2643
                Optional<Entity> a = valueEntity.getRawValue();
×
2644
                if(a.isPresent()) {
×
2645
                    Entity entity = a.get();
×
2646
                    IEnergyStorage energyStorage = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
×
2647
                    return ValueTypeInteger.ValueInteger.of(energyStorage != null ? energyStorage.getEnergyStored() : 0);
×
2648
                }
2649
                return ValueTypeInteger.ValueInteger.of(0);
×
2650
            }).build());
1✔
2651

2652
    /**
2653
     * The entity energy stored.
2654
     */
2655
    public static final IOperator OBJECT_ENTITY_ENERGY_CAPACITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2656
            .output(ValueTypes.INTEGER).symbol("entity_capacity_fe").operatorName("entityenergycapacity").interactName("energyCapacity")
8✔
2657
            .function(input -> {
1✔
2658
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2659
                Optional<Entity> a = valueEntity.getRawValue();
×
2660
                if(a.isPresent()) {
×
2661
                    Entity entity = a.get();
×
2662
                    IEnergyStorage energyStorage = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
×
2663
                    return ValueTypeInteger.ValueInteger.of(energyStorage != null ? energyStorage.getMaxEnergyStored() : 0);
×
2664
                }
2665
                return ValueTypeInteger.ValueInteger.of(0);
×
2666
            }).build());
1✔
2667

2668
    /**
2669
     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------
2670
     */
2671

2672
    /**
2673
     * The amount of fluid in the fluidstack
2674
     */
2675
    public static final IOperator OBJECT_FLUIDSTACK_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2676
            .output(ValueTypes.INTEGER).symbolOperatorInteract("amount")
5✔
2677
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2678
                    FluidStack::getAmount
2679
            )).build());
1✔
2680

2681
    /**
2682
     * The block from the fluidstack
2683
     */
2684
    public static final IOperator OBJECT_FLUIDSTACK_BLOCK = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2685
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
2686
            .function(variables -> {
1✔
2687
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2688
                FluidStack a = valueFluidStack.getRawValue();
3✔
2689
                return ValueObjectTypeBlock.ValueBlock.of(!a.isEmpty() ? a.getFluid().getFluidType().getStateForPlacement(null, null, a).createLegacyBlock() : null);
14!
2690
            }).build());
1✔
2691

2692
    /**
2693
     * The fluidstack luminosity
2694
     */
2695
    public static final IOperator OBJECT_FLUIDSTACK_LIGHT_LEVEL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2696
            .output(ValueTypes.INTEGER).symbolOperator("light_level").interactName("lightLevel")
7✔
2697
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2698
                fluidStack -> fluidStack.getFluid().getFluidType().getLightLevel(fluidStack)
7✔
2699
            )).build());
1✔
2700

2701
    /**
2702
     * The fluidstack density
2703
     */
2704
    public static final IOperator OBJECT_FLUIDSTACK_DENSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2705
            .output(ValueTypes.INTEGER).symbolOperatorInteract("density")
5✔
2706
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2707
                fluidStack -> fluidStack.getFluid().getFluidType().getDensity(fluidStack)
7✔
2708
            )).build());
1✔
2709

2710
    /**
2711
     * The fluidstack temperature
2712
     */
2713
    public static final IOperator OBJECT_FLUIDSTACK_TEMPERATURE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2714
            .output(ValueTypes.INTEGER).symbolOperatorInteract("temperature")
5✔
2715
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2716
                    fluidStack -> fluidStack.getFluid().getFluidType().getTemperature(fluidStack)
7✔
2717
            )).build());
1✔
2718

2719
    /**
2720
     * The fluidstack viscosity
2721
     */
2722
    public static final IOperator OBJECT_FLUIDSTACK_VISCOSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2723
            .output(ValueTypes.INTEGER).symbolOperatorInteract("viscosity")
5✔
2724
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2725
                fluidStack -> fluidStack.getFluid().getFluidType().getViscosity(fluidStack)
7✔
2726
            )).build());
1✔
2727

2728
    /**
2729
     * If the fluidstack is gaseous
2730
     */
2731
    public static final IOperator OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2732
            .output(ValueTypes.BOOLEAN).symbolOperator("lighter_than_air").interactName("isLighterThanAir")
7✔
2733
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_BOOLEAN.build(
2✔
2734
                fluidStack -> fluidStack.getFluid().getFluidType().isLighterThanAir()
6✔
2735
            )).build());
1✔
2736

2737
    /**
2738
     * The rarity of the fluidstack
2739
     */
2740
    public static final IOperator OBJECT_FLUIDSTACK_RARITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2741
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
2742
            .function(variables -> {
1✔
2743
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2744
                FluidStack a = valueFluidStack.getRawValue();
3✔
2745
                return ValueTypeString.ValueString.of(a.getFluid().getFluidType().getRarity(a).name());
8✔
2746
            }).build());
1✔
2747

2748
    /**
2749
     * The bucket empty sound of the fluidstack
2750
     */
2751
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2752
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_empty").interactName("bucketEmptySound")
6✔
2753
            .function(variables -> {
1✔
2754
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2755
                FluidStack a = valueFluidStack.getRawValue();
3✔
2756
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_EMPTY))
10✔
2757
                        .map(soundEvent -> soundEvent.getLocation().toString())
6✔
2758
                        .orElse(""));
2✔
2759
            }).build());
1✔
2760

2761
    /**
2762
     * The fluid vaporize sound of the fluidstack
2763
     */
2764
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2765
            .output(ValueTypes.STRING).symbolOperator("sound_fluid_vaporize").interactName("fluidVaporizeSound")
6✔
2766
            .function(variables -> {
1✔
2767
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2768
                FluidStack a = valueFluidStack.getRawValue();
3✔
2769
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.FLUID_VAPORIZE))
10✔
2770
                        .map(soundEvent -> soundEvent.getLocation().toString())
6✔
2771
                        .orElse(""));
2✔
2772
            }).build());
1✔
2773

2774
    /**
2775
     * The bucket fill sound of the fluidstack
2776
     */
2777
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2778
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_fill").interactName("bucketFillSound")
6✔
2779
            .function(variables -> {
1✔
2780
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2781
                FluidStack a = valueFluidStack.getRawValue();
3✔
2782
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_FILL))
10✔
2783
                        .map(soundEvent -> soundEvent.getLocation().toString())
6✔
2784
                        .orElse(""));
2✔
2785
            }).build());
1✔
2786

2787
    /**
2788
     * The bucket of the fluidstack
2789
     */
2790
    public static final IOperator OBJECT_FLUIDSTACK_BUCKET = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2791
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("bucket")
4✔
2792
            .function(variables -> {
1✔
2793
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2794
                FluidStack a = valueFluidStack.getRawValue();
3✔
2795
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getFluid().getFluidType().getBucket(a));
7✔
2796
            }).build());
1✔
2797

2798
    /**
2799
     * If the fluid types of the two given fluidstacks are equal
2800
     */
2801
    public static final IOperator OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
5✔
2802
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwfluidequal").interactName("isRawEqual")
8✔
2803
            .function(variables -> {
1✔
2804
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack0 = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2805
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack1 = variables.getValue(1, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2806
                return ValueTypeBoolean.ValueBoolean.of(FluidStack.isSameFluid(valueFluidStack0.getRawValue(), valueFluidStack1.getRawValue()));
7✔
2807
            }).build());
1✔
2808

2809
    /**
2810
     * The name of the mod owning this fluid
2811
     */
2812
    public static final IOperator OBJECT_FLUIDSTACK_MODNAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2813
            .symbolOperatorInteract("mod")
13✔
2814
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2815
                    (OperatorBase.SafeVariablesGetter variables) -> {
2816
                        ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2817
                        return BuiltInRegistries.FLUID.getKey(a.getRawValue().getFluid());
6✔
2818
                    },
2819
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2820
            ))).build());
1✔
2821

2822
    /**
2823
     * The tag of the given fluidstack.
2824
     */
2825
    public static final IOperator OBJECT_FLUIDSTACK_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2826
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2827
            .function(input -> {
1✔
2828
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2829
                if (fluidStack.getRawValue().getComponentsPatch().isEmpty()) {
5✔
2830
                    return ValueTypeNbt.ValueNbt.of(Optional.empty());
3✔
2831
                }
2832
                return ValueTypeNbt.ValueNbt.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), fluidStack.getRawValue().getComponentsPatch()).getOrThrow());
13✔
2833
            }).build());
1✔
2834

2835
    /**
2836
     * Create a new fluidstack with the given amount.
2837
     */
2838
    public static final IOperator OBJECT_FLUIDSTACK_WITH_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
14✔
2839
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.INTEGER)
2✔
2840
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("with_amount").interactName("withAmount")
6✔
2841
            .function(variables -> {
1✔
2842
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2843
                ValueTypeInteger.ValueInteger valueInteger = variables.getValue(1, ValueTypes.INTEGER);
6✔
2844
                FluidStack fluidStack = valueFluidStack.getRawValue().copy();
4✔
2845
                fluidStack.setAmount(valueInteger.getRawValue());
4✔
2846
                return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
2847
            }).build());
1✔
2848

2849
    /**
2850
     * Get the data component keys of an fluidstack.
2851
     */
2852
    public static final IOperator OBJECT_FLUIDSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2853
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
2854
            .function(input -> {
1✔
2855
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2856
                // Explicitly check for fluid emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2857
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
2858
                        fluidStack.getRawValue().isEmpty() ?
4!
2859
                                Lists.newArrayList() :
×
2860
                                fluidStack.getRawValue().getComponents().keySet().stream()
5✔
2861
                                        .filter(c -> !c.isTransient())
8!
2862
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
2863
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
1✔
2864
                                        .toList());
2✔
2865
            }).build());
1✔
2866

2867
    /**
2868
     * Get the data component value by key
2869
     */
2870
    public static final IOperator OBJECT_FLUIDSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2_LONG
14✔
2871
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING)
2✔
2872
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
2873
            .function(input -> {
1✔
2874
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2875

2876
                // Determine data component type
2877
                DataComponentType<?> dataComponentType;
2878
                try {
2879
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.get(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2880
                } catch (ResourceLocationException e) {
×
2881
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2882
                }
1✔
2883

2884
                // Fetch component value
2885
                TypedDataComponent<?> typedComponent = fluidStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
2886
                if (typedComponent == null) {
2✔
2887
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
2888
                }
2889

2890
                // Encode component value
2891
                try {
2892
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
2893
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
2894
                } catch (IllegalStateException e) {
×
2895
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
2896
                }
2897
            }).build());
1✔
2898

2899
    /**
2900
     * Set the data component value by key
2901
     */
2902
    public static final IOperator OBJECT_FLUIDSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_3
18✔
2903
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2904
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2905
            .function(input -> {
1✔
2906
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2907

2908
                // Skip further processing if input value is empty
2909
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2910
                if (!tagOptional.isPresent()) {
3!
2911
                    return fluidStack;
×
2912
                }
2913

2914
                // Determine data component type
2915
                DataComponentType<?> dataComponentType;
2916
                try {
2917
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.get(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2918
                } catch (ResourceLocationException e) {
×
2919
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2920
                }
1✔
2921

2922
                // Encode component value
2923
                try {
2924
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2925
                    fluidStack = ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack.getRawValue().copy());
5✔
2926
                    fluidStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2927
                    return fluidStack;
2✔
2928
                } catch (IllegalStateException e) {
×
2929
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2930
                }
2931
            }).build());
1✔
2932

2933
    /**
2934
     * The tag entries of the given fluidstack
2935
     */
2936
    public static final IOperator OBJECT_FLUIDSTACK_TAG = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2937
            .output(ValueTypes.LIST)
2✔
2938
            .symbol("fluid_tag_names").operatorName("tag").interactName("tags")
6✔
2939
            .function(variables -> {
1✔
2940
                ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2941
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
2942
                if(!a.getRawValue().isEmpty()) {
4✔
2943
                    a.getRawValue().getFluid().builtInRegistryHolder().tags()
7✔
2944
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
2945
                                    .of(owningTag.location().toString())));
3✔
2946
                }
2947
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
2948
            }).build());
1✔
2949

2950
    /**
2951
     * Get a list of fluidstacks that correspond to the given tag key.
2952
     */
2953
    public static final IOperator OBJECT_FLUIDSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
2954
            .output(ValueTypes.LIST)
2✔
2955
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2956
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2957
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
2958
            .function(variables -> {
1✔
2959
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
2960
                ImmutableList.Builder<ValueObjectTypeFluidStack.ValueFluidStack> builder = ImmutableList.builder();
2✔
2961
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
2962
                    try {
2963
                        Helpers.getFluidTagValues(a.getRawValue())
4✔
2964
                                .map(ValueObjectTypeFluidStack.ValueFluidStack::of)
3✔
2965
                                .forEach(builder::add);
4✔
2966
                    } catch (ResourceLocationException e) {
×
2967
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
2968
                    }
1✔
2969
                }
2970
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, builder.build());
5✔
2971
            }).build());
1✔
2972

2973
    /**
2974
     * Get an fluid by name.
2975
     */
2976
    public static final IOperator OBJECT_FLUIDSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_PREFIX_LONG
5✔
2977
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_FLUIDSTACK)
4✔
2978
            .symbol("fluid_by_name").operatorName("fluidbyname").interactName("fluidByName")
7✔
2979
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
2980
                    .build(input -> {
1✔
2981
                        Fluid fluid = BuiltInRegistries.FLUID.get(input);
5✔
2982
                        FluidStack fluidStack = FluidStack.EMPTY;
2✔
2983
                        if (fluid != null) {
2!
2984
                            fluidStack = new FluidStack(fluid, FluidHelpers.BUCKET_VOLUME);
6✔
2985
                        }
2986
                        return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
2987
                    })).build());
1✔
2988

2989
    /**
2990
     * ----------------------------------- OPERATOR OPERATORS -----------------------------------
2991
     */
2992

2993
    /**
2994
     * Apply for a given operator a given value.
2995
     */
2996
    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2997
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2998
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply")
9✔
2999
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY))
4✔
3000
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3001
                    input -> {
3002
                        IOperator innerOperator = input.getLeft();
4✔
3003
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3004
                        IVariable variable = variables.getVariables()[0];
5✔
3005
                        return ValueHelpers.evaluateOperator(innerOperator, variable);
9✔
3006
                    })).build());
1✔
3007
    static {
3008
        REGISTRY.registerSerializer(new CurriedOperator.Serializer());
5✔
3009
    }
3010

3011
    /**
3012
     * Apply for a given operator the given 2 values.
3013
     */
3014
    public static final IOperator OPERATOR_APPLY_2 = REGISTRY.register(OperatorBuilders.OPERATOR
5✔
3015
            .renderPattern(IConfigRenderPattern.INFIX_2)
2✔
3016
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
15✔
3017
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
3018
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply2")
13✔
3019
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
3020
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3021
                    input -> {
3022
                        IOperator innerOperator = input.getLeft();
4✔
3023
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3024
                        IVariable variable0 = variables.getVariables()[0];
5✔
3025
                        IVariable variable1 = variables.getVariables()[1];
5✔
3026
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1);
13✔
3027
                    })).build());
1✔
3028

3029
    /**
3030
     * Apply for a given operator the given 3 values.
3031
     */
3032
    public static final IOperator OPERATOR_APPLY_3 = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
3033
            .renderPattern(IConfigRenderPattern.INFIX_3)
2✔
3034
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
19✔
3035
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
3036
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply3")
17✔
3037
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
3038
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3039
                    input -> {
3040
                        IOperator innerOperator = input.getLeft();
4✔
3041
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3042
                        IVariable variable0 = variables.getVariables()[0];
5✔
3043
                        IVariable variable1 = variables.getVariables()[1];
5✔
3044
                        IVariable variable2 = variables.getVariables()[2];
5✔
3045
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1, variable2);
17✔
3046
                    })).build());
1✔
3047

3048
    /**
3049
     * Apply for a given operator the given list of values.
3050
     */
3051
    public static final IOperator OPERATOR_APPLY_N = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
3052
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST)
11✔
3053
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.LIST)
2✔
3054
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply_n")
9✔
3055
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.LIST))
4✔
3056
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3057
                    input -> {
3058
                        IOperator innerOperator = input.getLeft();
4✔
3059
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3060
                        IValueTypeListProxy<IValueType<IValue>, IValue> list = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
3061
                        return ValueHelpers.evaluateOperator(innerOperator, Iterables.toArray(list, IValue.class));
7✔
3062
                    })).build());
1✔
3063

3064
    /**
3065
     * Apply for a given operator with zero arguments.
3066
     */
3067
    public static final IOperator OPERATOR_APPLY_0 = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3068
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
3069
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply0")
5✔
3070
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(new IValueType[0]))
4✔
3071
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3072
                    input -> {
3073
                        IOperator innerOperator = input.getLeft();
4✔
3074
                        return ValueHelpers.evaluateOperator(innerOperator, new IVariable[0]);
5✔
3075
                    })).build());
1✔
3076

3077
    /**
3078
     * Apply the given operator on all elements of a list, resulting in a new list of mapped values.
3079
     */
3080
    public static final IOperator OPERATOR_MAP = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3081
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3082
            .output(ValueTypes.LIST).symbolOperatorInteract("map")
5✔
3083
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3084
                    input -> {
3085
                        final IOperator innerOperator = input.getLeft();
4✔
3086
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3087
                        ValueTypeList.ValueList inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3088
                        return ValueTypeList.ValueList.ofFactory(
6✔
3089
                                new ValueTypeListProxyOperatorMapped(innerOperator, inputList.getRawValue()));
2✔
3090
                    })).build());
1✔
3091

3092
    /**
3093
     * Filter a list of elements by matching them all with the given predicate.
3094
     */
3095
    public static final IOperator OPERATOR_FILTER = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3096
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3097
            .output(ValueTypes.LIST).symbolOperatorInteract("filter")
7✔
3098
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3099
                    new IOperatorValuePropagator<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue>() {
3✔
3100
                        @Override
3101
                        public IValue getOutput(Pair<IOperator, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3102
                            final IOperator innerOperator = input.getLeft();
4✔
3103
                            OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3104
                            ValueTypeList.ValueList<?, ?> inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3105
                            if (inputList.getRawValue().isInfinite()) {
4!
3106
                                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
3107
                                        OPERATOR_FILTER.getLocalizedNameFull()));
×
3108
                            }
3109
                            List<IValue> filtered = Lists.newArrayList();
2✔
3110
                            for (IValue value : inputList.getRawValue()) {
11✔
3111
                                IValue result = ValueHelpers.evaluateOperator(innerOperator, value);
9✔
3112
                                ValueHelpers.validatePredicateOutput(innerOperator, result);
3✔
3113
                                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
3114
                                    filtered.add(value);
4✔
3115
                                }
3116
                            }
1✔
3117
                            IValueType valueType = inputList.getRawValue().getValueType();
4✔
3118
                            return ValueTypeList.ValueList.ofList(valueType, filtered);
4✔
3119
                        }
3120
                    })).build());
1✔
3121

3122
    /**
3123
     * Takes the conjunction of two predicates.
3124
     */
3125
    public static final IOperator OPERATOR_CONJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3126
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3127
            .output(ValueTypes.OPERATOR).symbol(".&&.").operatorInteract("conjunction")
7✔
3128
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3129
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Conjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3130
            )).build());
1✔
3131
    static {
3132
        REGISTRY.registerSerializer(new CombinedOperator.Conjunction.Serializer());
5✔
3133
    }
3134

3135
    /**
3136
     * Takes the disjunction of two predicates.
3137
     */
3138
    public static final IOperator OPERATOR_DISJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3139
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3140
            .output(ValueTypes.OPERATOR).symbol(".||.").operatorInteract("disjunction")
7✔
3141
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3142
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Disjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3143
            )).build());
1✔
3144
    static {
3145
        REGISTRY.registerSerializer(new CombinedOperator.Disjunction.Serializer());
5✔
3146
    }
3147

3148
    /**
3149
     * Takes the negation of a predicate.
3150
     */
3151
    public static final IOperator OPERATOR_NEGATION = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3152
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3153
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3154
            .output(ValueTypes.OPERATOR).symbol("!.").operatorInteract("negation")
7✔
3155
            .function(OperatorBuilders.FUNCTION_ONE_PREDICATE.build(
2✔
3156
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Negation.asOperator(input))
4✔
3157
            )).build());
1✔
3158
    static {
3159
        REGISTRY.registerSerializer(new CombinedOperator.Negation.Serializer());
5✔
3160
    }
3161

3162
    /**
3163
     * Create a new operator that pipes the output from the first operator to the second operator.
3164
     */
3165
    public static final IOperator OPERATOR_PIPE = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3166
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3167
            .output(ValueTypes.OPERATOR).symbol(".").operatorInteract("pipe")
7✔
3168
            .function(OperatorBuilders.FUNCTION_TWO_OPERATORS.build(
2✔
3169
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe.asOperator(input.getLeft(), input.getRight()))
17✔
3170
            )).build());
1✔
3171
    static {
3172
        REGISTRY.registerSerializer(new CombinedOperator.Pipe.Serializer());
5✔
3173
    }
3174

3175
    /**
3176
     * 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.
3177
     */
3178
    public static final IOperator OPERATOR_PIPE2 = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3179
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3180
            .renderPattern(IConfigRenderPattern.INFIX_2_LATE)
2✔
3181
            .output(ValueTypes.OPERATOR).symbol(".2").operatorInteract("pipe2")
7✔
3182
            .function(OperatorBuilders.FUNCTION_THREE_OPERATORS.build(
2✔
3183
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe2.asOperator(input.getLeft(), input.getMiddle(), input.getRight()))
23✔
3184
            )).build());
1✔
3185
    static {
3186
        REGISTRY.registerSerializer(new CombinedOperator.Pipe2.Serializer());
5✔
3187
    }
3188

3189
    /**
3190
     * Flip the input parameters of an operator with two inputs.
3191
     */
3192
    public static final IOperator OPERATOR_FLIP = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3193
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3194
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3195
            .output(ValueTypes.OPERATOR).symbolOperatorInteract("flip")
5✔
3196
            .function(OperatorBuilders.FUNCTION_ONE_OPERATOR.build(
2✔
3197
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Flip.asOperator(input))
4✔
3198
            )).build());
1✔
3199
    static {
3200
        REGISTRY.registerSerializer(new CombinedOperator.Flip.Serializer());
5✔
3201
    }
3202

3203
    /**
3204
     * Apply the given operator on all elements of a list to reduce the list to one value.
3205
     */
3206
    public static final IOperator OPERATOR_REDUCE = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3207
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
3208
            .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
2✔
3209
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce")
4✔
3210
            .conditionalOutputTypeDeriver((operator, input) -> input[2].getType())
9✔
3211
            .function(new OperatorBase.IFunction() {
4✔
3212
                @Override
3213
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3214
                    IValue accumulator = variables.getValue(2);
4✔
3215
                    final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3216
                            variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3217
                    ValueTypeList.ValueList<IValueType<IValue>, IValue> inputList = variables.getValue(1, ValueTypes.LIST);
6✔
3218
                    if (inputList.getRawValue().isInfinite()) {
4!
3219
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
3220
                                OPERATOR_REDUCE.getLocalizedNameFull()));
×
3221
                    }
3222
                    for (IValue listValue : inputList.getRawValue()) {
11✔
3223
                        accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3224
                    }
1✔
3225
                    return accumulator;
2✔
3226
                }
3227
            }).build());
1✔
3228

3229
    /**
3230
     * Apply the given operator on all elements of a list to reduce the list to one value.
3231
     */
3232
    public static final IOperator OPERATOR_REDUCE1 = REGISTRY.register(OperatorBuilders.OPERATOR
14✔
3233
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3234
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
3235
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce1")
4✔
3236
            .conditionalOutputTypeDeriver((operator, input) -> {
4✔
3237
                try {
3238
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[1].getValue()).getRawValue();
7✔
3239
                    return a.getValueType();
3✔
3240
                } catch (EvaluationException e) {
×
3241
                    return operator.getOutputType();
×
3242
                }
3243
            })
3244
            .function(new OperatorBase.IFunction() {
4✔
3245
                @Override
3246
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3247
                    ValueTypeList.ValueList valueList = variables.getValue(1, ValueTypes.LIST);
6✔
3248
                    if (valueList.getRawValue().isInfinite()) {
4!
3249
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
3250
                                OPERATOR_REDUCE1.getLocalizedNameFull()));
×
3251
                    }
3252
                    Iterator<IValue> iter = valueList.getRawValue().iterator();
4✔
3253
                    if (!iter.hasNext()) {
3✔
3254
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REDUCE_EMPTY));
6✔
3255
                    }
3256

3257
                    IValue accumulator = iter.next();
4✔
3258
                    final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3259
                            variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3260

3261
                    while (iter.hasNext()) {
3✔
3262
                        IValue listValue = iter.next();
4✔
3263
                        accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3264
                    }
1✔
3265
                    return accumulator;
2✔
3266
                }
3267
            }).build());
1✔
3268

3269
    /**
3270
     * Apply for a given operator a given value.
3271
     */
3272
    public static final IOperator OPERATOR_BY_NAME = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3273
            .inputType(ValueTypes.STRING).output(ValueTypes.OPERATOR)
4✔
3274
            .symbol("op_by_name").operatorName("by_name").interactName("operatorByName")
6✔
3275
            .function(input -> {
1✔
3276
                ValueTypeString.ValueString name = input.getValue(0, ValueTypes.STRING);
6✔
3277
                try {
3278
                    ResourceLocation id = ResourceLocation.parse(name.getRawValue());
4✔
3279
                    IOperator operator = Operators.REGISTRY.getOperator(id);
4✔
3280
                    if (operator == null) {
2✔
3281
                        throw new EvaluationException(Component.translatable(
11✔
3282
                                L10NValues.OPERATOR_ERROR_OPERATORNOTFOUND, name.getRawValue()));
2✔
3283
                    }
3284
                    return ValueTypeOperator.ValueOperator.of(operator);
3✔
3285
                } catch (ResourceLocationException e) {
×
3286
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
3287
                }
3288
            }).build());
1✔
3289

3290
    /**
3291
     * ----------------------------------- NBT OPERATORS -----------------------------------
3292
     */
3293

3294
    /**
3295
     * The number of entries in an NBT tag
3296
     */
3297
    public static final IOperator NBT_COMPOUND_SIZE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3298
            .output(ValueTypes.INTEGER).operatorName("compound_size").symbol("NBT{}.size").interactName("size")
9✔
3299
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_TO_INT.build(
2✔
3300
                opt -> opt.map(CompoundTag::size).orElse(0)
8✔
3301
            )).build());
1✔
3302

3303
    /**
3304
     * The list of keys in an NBT tag
3305
     */
3306
    public static final IOperator NBT_COMPOUND_KEYS = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3307
            .output(ValueTypes.LIST).operatorName("compound_keys").symbol("NBT{}.keys").interactName("keys")
8✔
3308
            .function(variables -> {
1✔
3309
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3310
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtKeys(value.getRawValue()));
7✔
3311
            }).build());
1✔
3312

3313
    /**
3314
     * If an NBT tag has the given key
3315
     */
3316
    public static final IOperator NBT_COMPOUND_HASKEY = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3317
            .output(ValueTypes.BOOLEAN).operatorName("compound_haskey").symbol("NBT{}.has_key").interactName("hasKey")
9✔
3318
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3319
                    Optional::isPresent
3320
            )).build());
1✔
3321

3322
    /**
3323
     * The NBT value type of an entry
3324
     */
3325
    public static final IOperator NBT_COMPOUND_VALUE_TYPE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3326
            .output(ValueTypes.STRING).operatorName("compound_type").symbol("NBT{}.type").interactName("type")
9✔
3327
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(tag -> {
2✔
3328
                if (tag.isPresent()) {
3✔
3329
                    try {
3330
                        return TagTypes.getType(tag.get().getId()).getName();
7✔
3331
                    } catch (IndexOutOfBoundsException e) {
×
3332

3333
                    }
3334
                }
3335
                return "null";
2✔
3336
            })).build());
1✔
3337

3338
    /**
3339
     * The NBT tag value
3340
     */
3341
    public static final IOperator NBT_COMPOUND_VALUE_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3342
            .output(ValueTypes.NBT).operatorName("compound_value_tag").symbol("NBT{}.get_tag").interactName("getTag")
9✔
3343
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(o -> o)).build());
5✔
3344

3345
    /**
3346
     * The NBT boolean value
3347
     */
3348
    public static final IOperator NBT_COMPOUND_VALUE_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3349
            .output(ValueTypes.BOOLEAN).operatorName("compound_value_boolean").symbol("NBT{}.get_boolean").interactName("getBoolean")
9✔
3350
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3351
                    o -> o.map(tag -> tag instanceof NumericTag && ((NumericTag) tag).getAsByte() != 0).orElse(false)
19!
3352
            )).build());
1✔
3353

3354
    /**
3355
     * The NBT integer value
3356
     */
3357
    public static final IOperator NBT_COMPOUND_VALUE_INTEGER = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3358
            .output(ValueTypes.INTEGER).operatorName("compound_value_integer").symbol("NBT{}.get_integer").interactName("getInteger")
9✔
3359
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_INT.build(
2✔
3360
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
17!
3361
            )).build());
1✔
3362

3363
    /**
3364
     * The NBT long value
3365
     */
3366
    public static final IOperator NBT_COMPOUND_VALUE_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3367
            .output(ValueTypes.LONG).operatorName("compound_value_long").symbol("NBT{}.get_long").interactName("getLong")
9✔
3368
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG.build(
2✔
3369
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsLong() : 0).orElse(0L)
17!
3370
            )).build());
1✔
3371

3372
    /**
3373
     * The NBT double value
3374
     */
3375
    public static final IOperator NBT_COMPOUND_VALUE_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3376
            .output(ValueTypes.DOUBLE).operatorName("compound_value_double").symbol("NBT{}.get_double").interactName("getDouble")
9✔
3377
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE.build(
2✔
3378
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsDouble() : 0).orElse(0D)
17!
3379
            )).build());
1✔
3380

3381
    /**
3382
     * The NBT string value
3383
     */
3384
    public static final IOperator NBT_COMPOUND_VALUE_STRING = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3385
            .output(ValueTypes.STRING).operatorName("compound_value_string").symbol("NBT{}.get_string").interactName("getString")
9✔
3386
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(
2✔
3387
                    o -> o.map(tag -> tag instanceof StringTag ? tag.getAsString() : "").orElse("")
14!
3388
            )).build());
1✔
3389

3390
    /**
3391
     * The NBT compound value
3392
     */
3393
    public static final IOperator NBT_COMPOUND_VALUE_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3394
            .output(ValueTypes.NBT).operatorName("compound_value_compound").symbol("NBT{}.get_compound").interactName("getCompound")
9✔
3395
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(
2✔
3396
                    o -> o.map(tag -> tag instanceof CompoundTag ? (CompoundTag) tag : new CompoundTag())
11!
3397
            )).build());
1✔
3398

3399
    /**
3400
     * The NBT tag list value
3401
     */
3402
    public static final IOperator NBT_COMPOUND_VALUE_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3403
            .output(ValueTypes.LIST).operatorName("compound_value_list_tag").symbol("NBT{}.get_list_tag").interactName("getListTag")
8✔
3404
            .function(variables -> {
1✔
3405
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3406
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3407
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListTag(key.getRawValue(), value.getRawValue()));
9✔
3408
            }).build());
1✔
3409

3410
    /**
3411
     * The NBT boolean list value
3412
     */
3413
    public static final IOperator NBT_COMPOUND_VALUE_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3414
            .output(ValueTypes.LIST).operatorName("compound_value_list_byte").symbol("NBT{}.get_list_byte").interactName("getListByte")
8✔
3415
            .function(variables -> {
1✔
3416
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3417
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3418
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListByte(key.getRawValue(), value.getRawValue()));
9✔
3419
            }).build());
1✔
3420

3421
    /**
3422
     * The NBT int list value
3423
     */
3424
    public static final IOperator NBT_COMPOUND_VALUE_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3425
            .output(ValueTypes.LIST).operatorName("compound_value_list_int").symbol("NBT{}.get_list_int").interactName("getListInt")
8✔
3426
            .function(variables -> {
1✔
3427
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3428
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3429
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListInt(key.getRawValue(), value.getRawValue()));
9✔
3430
            }).build());
1✔
3431

3432
    /**
3433
     * The NBT long list value
3434
     */
3435
    public static final IOperator NBT_COMPOUND_VALUE_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3436
            .output(ValueTypes.LIST).operatorName("compound_value_list_long").symbol("NBT{}.get_list_long").interactName("getListLong")
8✔
3437
            .function(variables -> {
1✔
3438
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3439
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3440
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListLong(key.getRawValue(), value.getRawValue()));
9✔
3441
            }).build());
1✔
3442

3443
    /**
3444
     * Remove an entry from an NBT compound
3445
     */
3446
    public static final IOperator NBT_COMPOUND_WITHOUT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3447
            .output(ValueTypes.NBT).operatorName("compound_without").symbol("NBT{}.without").interactName("without")
8✔
3448
            .function(variables -> {
1✔
3449
                ValueTypeNbt.ValueNbt valueNbt = variables.getValue(0, ValueTypes.NBT);
6✔
3450
                Optional<Tag> tag = valueNbt.getRawValue();
3✔
3451
                if (tag.isPresent()) {
3!
3452
                    if (!(tag.get() instanceof CompoundTag)) {
4!
3453
                        return ValueTypeNbt.ValueNbt.of();
×
3454
                    }
3455
                    ValueTypeString.ValueString valueString = variables.getValue(1, ValueTypes.STRING);
6✔
3456
                    String key = valueString.getRawValue();
3✔
3457
                    CompoundTag tagCompound = (CompoundTag) tag.get();
4✔
3458
                    if (tagCompound.contains(key)) {
4!
3459
                        // Copy the tag to ensure immutability
3460
                        tagCompound = tagCompound.copy();
3✔
3461
                        tagCompound.remove(key);
3✔
3462
                    }
3463
                    return ValueTypeNbt.ValueNbt.of(tagCompound);
3✔
3464
                }
3465
                return valueNbt;
×
3466
            }).build());
1✔
3467

3468

3469

3470
    /**
3471
     * Set an NBT compound boolean value
3472
     */
3473
    public static final IOperator NBT_COMPOUND_WITH_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3474
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3475
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.BOOLEAN)
2✔
3476
            .operatorName("compound_with_boolean").symbol("NBT{}.with_boolean").interactName("withBoolean")
7✔
3477
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3478
                ValueTypeBoolean.ValueBoolean value = input.getRight().getValue(0, ValueTypes.BOOLEAN);
8✔
3479
                input.getLeft().ifPresent(tag -> tag.putBoolean(input.getMiddle(), value.getRawValue()));
15✔
3480
                return input.getLeft();
4✔
3481
            })).build());
1✔
3482

3483
    /**
3484
     * Set an NBT compound short value
3485
     */
3486
    public static final IOperator NBT_COMPOUND_WITH_SHORT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3487
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3488
            .operatorName("compound_with_short").symbol("NBT{}.with_short").interactName("withShort")
7✔
3489
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3490
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3491
                input.getLeft().ifPresent(tag -> tag.putShort(input.getMiddle(), (short) value.getRawValue()));
16✔
3492
                return input.getLeft();
4✔
3493
            })).build());
1✔
3494

3495
    /**
3496
     * Set an NBT compound integer value
3497
     */
3498
    public static final IOperator NBT_COMPOUND_WITH_INTEGER = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3499
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3500
            .operatorName("compound_with_integer").symbol("NBT{}.with_integer").interactName("withInteger")
7✔
3501
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3502
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3503
                input.getLeft().ifPresent(tag -> tag.putInt(input.getMiddle(), value.getRawValue()));
15✔
3504
                return input.getLeft();
4✔
3505
            })).build());
1✔
3506

3507
    /**
3508
     * Set an NBT compound long value
3509
     */
3510
    public static final IOperator NBT_COMPOUND_WITH_LONG = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3511
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LONG)
2✔
3512
            .operatorName("compound_with_long").symbol("NBT{}.with_long").interactName("withLong")
7✔
3513
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3514
                ValueTypeLong.ValueLong value = input.getRight().getValue(0, ValueTypes.LONG);
8✔
3515
                input.getLeft().ifPresent(tag -> tag.putLong(input.getMiddle(), value.getRawValue()));
15✔
3516
                return input.getLeft();
4✔
3517
            })).build());
1✔
3518

3519
    /**
3520
     * Set an NBT compound double value
3521
     */
3522
    public static final IOperator NBT_COMPOUND_WITH_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3523
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3524
            .operatorName("compound_with_double").symbol("NBT{}.with_double").interactName("withDouble")
7✔
3525
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3526
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3527
                input.getLeft().ifPresent(tag -> tag.putDouble(input.getMiddle(), value.getRawValue()));
15✔
3528
                return input.getLeft();
4✔
3529
            })).build());
1✔
3530

3531
    /**
3532
     * Set an NBT compound float value
3533
     */
3534
    public static final IOperator NBT_COMPOUND_WITH_FLOAT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3535
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3536
            .operatorName("compound_with_float").symbol("NBT{}.with_float").interactName("withFloat")
7✔
3537
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3538
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3539
                input.getLeft().ifPresent(tag -> tag.putFloat(input.getMiddle(), (float) value.getRawValue()));
16✔
3540
                return input.getLeft();
4✔
3541
            })).build());
1✔
3542

3543
    /**
3544
     * Set an NBT compound string value
3545
     */
3546
    public static final IOperator NBT_COMPOUND_WITH_STRING = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3547
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING)
2✔
3548
            .operatorName("compound_with_string").symbol("NBT{}.with_string").interactName("withString")
7✔
3549
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3550
                ValueTypeString.ValueString value = input.getRight().getValue(0, ValueTypes.STRING);
8✔
3551
                input.getLeft().ifPresent(tag -> tag.putString(input.getMiddle(), value.getRawValue()));
15✔
3552
                return input.getLeft();
4✔
3553
            })).build());
1✔
3554

3555
    /**
3556
     * Set an NBT compound compound value
3557
     */
3558
    public static final IOperator NBT_COMPOUND_WITH_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3559
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.NBT)
2✔
3560
            .operatorName("compound_with_tag").symbol("NBT{}.with_tag").interactName("withTag")
7✔
3561
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3562
                ValueTypeNbt.ValueNbt value = input.getRight().getValue(0, ValueTypes.NBT);
8✔
3563
                input.getLeft()
6✔
3564
                        .ifPresent(tag -> value.getRawValue()
7✔
3565
                                .ifPresent(v -> tag.put(input.getMiddle(), v)));
9✔
3566
                return input.getLeft();
4✔
3567
            })).build());
1✔
3568

3569
    /**
3570
     * Set an NBT compound tag list value
3571
     */
3572
    public static final IOperator NBT_COMPOUND_WITH_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3573
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3574
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3575
            .operatorName("compound_with_list_tag").symbol("NBT{}.with_tag_list").interactName("withTagList")
9✔
3576
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3577
                @Override
3578
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3579
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3580
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3581
                            NbtHelpers.getListNbtTag(value, NBT_COMPOUND_WITH_LIST_TAG.getLocalizedNameFull())));
2✔
3582
                    return input.getLeft();
4✔
3583
                }
3584
            })).build());
1✔
3585

3586
    /**
3587
     * Set an NBT compound byte list value
3588
     */
3589
    public static final IOperator NBT_COMPOUND_WITH_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3590
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3591
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3592
            .operatorName("compound_with_list_byte").symbol("NBT{}.with_byte_list").interactName("withByteList")
9✔
3593
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3594
                @Override
3595
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3596
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3597
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3598
                            NbtHelpers.getListNbtByte(value, NBT_COMPOUND_WITH_LIST_BYTE.getLocalizedNameFull())));
2✔
3599
                    return input.getLeft();
4✔
3600
                }
3601
            })).build());
1✔
3602

3603
    /**
3604
     * Set an NBT compound int list value
3605
     */
3606
    public static final IOperator NBT_COMPOUND_WITH_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3607
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3608
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3609
            .operatorName("compound_with_list_int").symbol("NBT{}.with_int_list").interactName("withIntList")
9✔
3610
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3611
                @Override
3612
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3613
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3614
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3615
                            NbtHelpers.getListNbtInt(value, NBT_COMPOUND_WITH_LIST_INT.getLocalizedNameFull())));
2✔
3616
                    return input.getLeft();
4✔
3617
                }
3618
            })).build());
1✔
3619

3620
    /**
3621
     * Set an NBT compound long list value
3622
     */
3623
    public static final IOperator NBT_COMPOUND_WITH_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3624
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3625
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3626
            .operatorName("compound_with_list_long").symbol("NBT{}.with_list_long").interactName("withListLong")
9✔
3627
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3628
                @Override
3629
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3630
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3631
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3632
                            NbtHelpers.getListNbtLong(value, NBT_COMPOUND_WITH_LIST_LONG.getLocalizedNameFull())));
2✔
3633
                    return input.getLeft();
4✔
3634
                }
3635
            })).build());
1✔
3636

3637
    /**
3638
     * Check if the first NBT compound tag is a subset of the second NBT compound tag.
3639
     */
3640
    public static final IOperator NBT_COMPOUND_SUBSET = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3641
            .output(ValueTypes.BOOLEAN).operatorName("compound_subset").symbol("NBT{}.⊆").interactName("isSubset")
8✔
3642
            .function(variables -> {
1✔
3643
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3644
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3645
                if (valueNbt0.getRawValue().isPresent()
5!
3646
                        && valueNbt1.getRawValue().isPresent()
4!
3647
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3648
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3649
                    return ValueTypeBoolean.ValueBoolean.of(NbtHelpers.nbtMatchesSubset((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get(), true));
12✔
3650
                }
3651
                return ValueTypeBoolean.ValueBoolean.of(false);
×
3652
            }).build());
1✔
3653

3654
    /**
3655
     * The union of the given NBT compound tags. Nested tags will be joined recusively.
3656
     */
3657
    public static final IOperator NBT_COMPOUND_UNION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3658
            .output(ValueTypes.NBT).operatorName("compound_union").symbol("NBT{}.∪").interactName("union")
8✔
3659
            .function(variables -> {
1✔
3660
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3661
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3662
                if (valueNbt0.getRawValue().isPresent()
5!
3663
                        && valueNbt1.getRawValue().isPresent()
4!
3664
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3665
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3666
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.union((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3667
                }
3668
                return ValueTypeNbt.ValueNbt.of();
×
3669
            }).build());
1✔
3670

3671
    /**
3672
     * The intersection of the given NBT compound tags. Nested tags will be intersected recusively.
3673
     */
3674
    public static final IOperator NBT_COMPOUND_INTERSECTION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3675
            .output(ValueTypes.NBT).operatorName("compound_intersection").symbol("NBT{}.∩").interactName("intersection")
8✔
3676
            .function(variables -> {
1✔
3677
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3678
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3679
                if (valueNbt0.getRawValue().isPresent()
5!
3680
                        && valueNbt1.getRawValue().isPresent()
4!
3681
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3682
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3683
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.intersection((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3684
                }
3685
                return ValueTypeNbt.ValueNbt.of();
×
3686
            }).build());
1✔
3687

3688
    /**
3689
     * The difference of the given NBT compound tags. Nested tags will be subtracted recusively.
3690
     */
3691
    public static final IOperator NBT_COMPOUND_MINUS = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3692
            .output(ValueTypes.NBT).operatorName("compound_minus").symbol("NBT{}.∖").interactName("minus")
8✔
3693
            .function(variables -> {
1✔
3694
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3695
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3696
                if (valueNbt0.getRawValue().isPresent()
5!
3697
                        && valueNbt1.getRawValue().isPresent()
4!
3698
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3699
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3700
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.minus((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
11✔
3701
                }
3702
                return ValueTypeNbt.ValueNbt.of();
×
3703
            }).build());
1✔
3704

3705
    /**
3706
     * The boolean value of an NBT value
3707
     */
3708
    public static final IOperator NBT_AS_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3709
            .output(ValueTypes.BOOLEAN).operatorName("as_boolean").symbol("NBT.as_boolean").interactName("asBoolean")
9✔
3710
            .function(OperatorBuilders.FUNCTION_NBT_TO_BOOLEAN.build(
2✔
3711
                    o -> o.map(tag -> tag instanceof ByteTag && ((ByteTag) tag).getAsByte() != 0).orElse(false)
20!
3712
            )).build());
1✔
3713

3714
    /**
3715
     * The byte value of an NBT value
3716
     */
3717
    public static final IOperator NBT_AS_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3718
            .output(ValueTypes.INTEGER).operatorName("as_byte").symbol("NBT.as_byte").interactName("asByte")
9✔
3719
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3720
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3721
            )).build());
1✔
3722

3723
    /**
3724
     * The short value of an NBT value
3725
     */
3726
    public static final IOperator NBT_AS_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3727
            .output(ValueTypes.INTEGER).operatorName("as_short").symbol("NBT.as_short").interactName("asShort")
9✔
3728
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3729
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3730
            )).build());
1✔
3731

3732
    /**
3733
     * The int value of an NBT value
3734
     */
3735
    public static final IOperator NBT_AS_INT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3736
            .output(ValueTypes.INTEGER).operatorName("as_int").symbol("NBT.as_int").interactName("asInt")
9✔
3737
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3738
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3739
            )).build());
1✔
3740

3741
    /**
3742
     * The long value of an NBT value
3743
     */
3744
    public static final IOperator NBT_AS_LONG = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3745
            .output(ValueTypes.LONG).operatorName("as_long").symbol("NBT.as_long").interactName("asLong")
9✔
3746
            .function(OperatorBuilders.FUNCTION_NBT_TO_LONG.build(
2✔
3747
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsLong() : 0L).orElse(0L)
18✔
3748
            )).build());
1✔
3749

3750
    /**
3751
     * The double value of an NBT value
3752
     */
3753
    public static final IOperator NBT_AS_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3754
            .output(ValueTypes.DOUBLE).operatorName("as_double").symbol("NBT.as_double").interactName("asDouble")
9✔
3755
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3756
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsDouble() : 0D).orElse(0D)
18✔
3757
            )).build());
1✔
3758

3759
    /**
3760
     * The float value of an NBT value
3761
     */
3762
    public static final IOperator NBT_AS_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3763
            .output(ValueTypes.DOUBLE).operatorName("as_float").symbol("NBT.as_float").interactName("asFloat")
9✔
3764
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3765
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsFloat() : 0D).orElse(0D)
19✔
3766
            )).build());
1✔
3767

3768
    /**
3769
     * The string value of an NBT value
3770
     */
3771
    public static final IOperator NBT_AS_STRING = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3772
            .output(ValueTypes.STRING).operatorName("as_string").symbol("NBT.as_string").interactName("asString")
9✔
3773
            .function(OperatorBuilders.FUNCTION_NBT_TO_STRING.build(
2✔
3774
                    o -> o.map(tag -> tag instanceof StringTag ? ((StringTag) tag).getAsString() : "").orElse("")
16✔
3775
            )).build());
1✔
3776

3777
    /**
3778
     * The tag list value of an NBT value
3779
     */
3780
    public static final IOperator NBT_AS_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3781
            .output(ValueTypes.LIST).operatorName("as_tag_list").symbol("NBT.as_tag_list").interactName("asTagList")
8✔
3782
            .function(variables -> {
1✔
3783
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3784
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListTag(value.getRawValue()));
7✔
3785
            }).build());
1✔
3786

3787
    /**
3788
     * The byte list value of an NBT value
3789
     */
3790
    public static final IOperator NBT_AS_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3791
            .output(ValueTypes.LIST).operatorName("as_byte_list").symbol("NBT.as_byte_list").interactName("asByteList")
8✔
3792
            .function(variables -> {
1✔
3793
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3794
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListByte(value.getRawValue()));
7✔
3795
            }).build());
1✔
3796

3797
    /**
3798
     * The int list value of an NBT value
3799
     */
3800
    public static final IOperator NBT_AS_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3801
            .output(ValueTypes.LIST).operatorName("as_int_list").symbol("NBT.as_int_list").interactName("asIntList")
8✔
3802
            .function(variables -> {
1✔
3803
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3804
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListInt(value.getRawValue()));
7✔
3805
            }).build());
1✔
3806

3807
    /**
3808
     * The long list value of an NBT value
3809
     */
3810
    public static final IOperator NBT_AS_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3811
            .output(ValueTypes.LIST).operatorName("as_long_list").symbol("NBT.as_long_list").interactName("asLongList")
8✔
3812
            .function(variables -> {
1✔
3813
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3814
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListLong(value.getRawValue()));
7✔
3815
            }).build());
1✔
3816

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

3828
    /**
3829
     * The NBT value of a short value
3830
     */
3831
    public static final IOperator NBT_FROM_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3832
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3833
            .operatorName("from_short").symbol("NBT.from_short").interactName("asNbt", "short", true)
8✔
3834
            .function(variables -> {
1✔
3835
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3836
                return ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) value.getRawValue()));
6✔
3837
            }).build());
1✔
3838

3839
    /**
3840
     * The NBT value of a byte value
3841
     */
3842
    public static final IOperator NBT_FROM_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3843
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3844
            .operatorName("from_byte").symbol("NBT.from_byte").interactName("asNbt", "byte", true)
8✔
3845
            .function(variables -> {
1✔
3846
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3847
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf((byte) value.getRawValue()));
6✔
3848
            }).build());
1✔
3849

3850
    /**
3851
     * The NBT value of an int value
3852
     */
3853
    public static final IOperator NBT_FROM_INT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3854
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3855
            .operatorName("from_int").symbol("NBT.from_int").interactName("asNbt")
6✔
3856
            .function(variables -> {
1✔
3857
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3858
                return ValueTypeNbt.ValueNbt.of(IntTag.valueOf(value.getRawValue()));
5✔
3859
            }).build());
1✔
3860

3861
    /**
3862
     * The NBT value of a long value
3863
     */
3864
    public static final IOperator NBT_FROM_LONG = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3865
            .inputType(ValueTypes.LONG).output(ValueTypes.NBT)
4✔
3866
            .operatorName("from_long").symbol("NBT.from_long").interactName("asNbt")
6✔
3867
            .function(variables -> {
1✔
3868
                ValueTypeLong.ValueLong value = variables.getValue(0, ValueTypes.LONG);
6✔
3869
                return ValueTypeNbt.ValueNbt.of(LongTag.valueOf(value.getRawValue()));
5✔
3870
            }).build());
1✔
3871

3872
    /**
3873
     * The NBT value of a double value
3874
     */
3875
    public static final IOperator NBT_FROM_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3876
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3877
            .operatorName("from_double").symbol("NBT.from_double").interactName("asNbt")
6✔
3878
            .function(variables -> {
1✔
3879
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3880
                return ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(value.getRawValue()));
5✔
3881
            }).build());
1✔
3882

3883
    /**
3884
     * The NBT value of a float value
3885
     */
3886
    public static final IOperator NBT_FROM_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3887
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3888
            .operatorName("from_float").symbol("NBT.from_float").interactName("asNbt", "float", true)
8✔
3889
            .function(variables -> {
1✔
3890
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3891
                return ValueTypeNbt.ValueNbt.of(FloatTag.valueOf((float) value.getRawValue()));
6✔
3892
            }).build());
1✔
3893

3894
    /**
3895
     * The NBT value of a string value
3896
     */
3897
    public static final IOperator NBT_FROM_STRING = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3898
            .inputType(ValueTypes.STRING).output(ValueTypes.NBT)
4✔
3899
            .operatorName("from_string").symbol("NBT.from_string").interactName("asNbt")
6✔
3900
            .function(variables -> {
1✔
3901
                ValueTypeString.ValueString value = variables.getValue(0, ValueTypes.STRING);
6✔
3902
                return ValueTypeNbt.ValueNbt.of(StringTag.valueOf(value.getRawValue()));
5✔
3903
            }).build());
1✔
3904

3905
    /**
3906
     * The NBT value of a tag list value
3907
     */
3908
    public static final IOperator NBT_FROM_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3909
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3910
            .operatorName("from_tag_list").symbol("NBT.from_tag_list").interactName("asNbt", "tagList", true)
10✔
3911
            .function(new OperatorBase.IFunction() {
4✔
3912
                @Override
3913
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3914
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3915
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtTag(value, NBT_FROM_TAG_LIST.getLocalizedNameFull()));
6✔
3916
                }
3917
            }).build());
1✔
3918

3919
    /**
3920
     * The NBT value of a byte list value
3921
     */
3922
    public static final IOperator NBT_FROM_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3923
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3924
            .operatorName("from_byte_list").symbol("NBT.from_byte_list").interactName("asNbt", "byteList", true)
10✔
3925
            .function(new OperatorBase.IFunction() {
4✔
3926
                @Override
3927
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3928
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3929
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtByte(value, NBT_FROM_BYTE_LIST.getLocalizedNameFull()));
6✔
3930
                }
3931
            }).build());
1✔
3932

3933
    /**
3934
     * The NBT value of a int list value
3935
     */
3936
    public static final IOperator NBT_FROM_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3937
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3938
            .operatorName("from_int_list").symbol("NBT.from_int_list").interactName("asNbt", "intList", true)
10✔
3939
            .function(new OperatorBase.IFunction() {
4✔
3940
                @Override
3941
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3942
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3943
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtInt(value, NBT_FROM_INT_LIST.getLocalizedNameFull()));
6✔
3944
                }
3945
            }).build());
1✔
3946

3947
    /**
3948
     * The NBT value of a long list value
3949
     */
3950
    public static final IOperator NBT_FROM_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3951
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3952
            .operatorName("from_long_list").symbol("NBT.from_long_list").interactName("asNbt", "longList", true)
10✔
3953
            .function(new OperatorBase.IFunction() {
4✔
3954
                @Override
3955
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3956
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3957
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtLong(value, NBT_FROM_LONG_LIST.getLocalizedNameFull()));
6✔
3958
                }
3959
            }).build());
1✔
3960

3961
    /**
3962
     * Apply the given NBT path expression on the given NBT value and get the first result.
3963
     */
3964
    public static final IOperator NBT_PATH_MATCH_FIRST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3965
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.NBT)
4✔
3966
            .operatorName("path_match_first").symbol("NBT.path_match_first").interactName("nbtPathMatchFirst")
6✔
3967
            .function(variables -> {
1✔
3968
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3969
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3970
                INbtPathExpression expression = null;
2✔
3971
                try {
3972
                    expression = NbtPath.parse(string.getRawValue());
4✔
3973
                } catch (NbtParseException e) {
×
3974
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3975
                            string.getRawValue(),
×
3976
                            e.getMessage()));
×
3977
                }
1✔
3978
                if (!nbt.getRawValue().isPresent()) {
4!
3979
                    return ValueTypeNbt.ValueNbt.of();
×
3980
                }
3981
                return ValueTypeNbt.ValueNbt.of(expression.match(nbt.getRawValue().get()).getMatches().findAny());
10✔
3982
            }).build());
1✔
3983

3984
    /**
3985
     * Apply the given NBT path expression on the given NBT value and get all results.
3986
     */
3987
    public static final IOperator NBT_PATH_MATCH_ALL = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3988
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.LIST)
4✔
3989
            .operatorName("path_match_all").symbol("NBT.path_match_all").interactName("nbtPathMatchAll")
6✔
3990
            .function(variables -> {
1✔
3991
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3992
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3993
                INbtPathExpression expression = null;
2✔
3994
                try {
3995
                    expression = NbtPath.parse(string.getRawValue());
4✔
3996
                } catch (NbtParseException e) {
×
3997
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3998
                            string.getRawValue(),
×
3999
                            e.getMessage()));
×
4000
                }
1✔
4001
                if (!nbt.getRawValue().isPresent()) {
4!
4002
                    return ValueTypeList.ValueList.ofAll(ValueTypes.NBT);
×
4003
                }
4004
                List<ValueTypeNbt.ValueNbt> matches = expression.match(nbt.getRawValue().get()).getMatches()
8✔
4005
                        .map(ValueTypeNbt.ValueNbt::of)
1✔
4006
                        .collect(Collectors.toList());
4✔
4007
                return ValueTypeList.ValueList.ofList(ValueTypes.NBT, matches);
4✔
4008
            }).build());
1✔
4009

4010
    /**
4011
     * Test the given NBT path expression on the given NBT value.
4012
     */
4013
    public static final IOperator NBT_PATH_TEST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
4014
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.BOOLEAN)
4✔
4015
            .operatorName("path_test").symbol("NBT.path_test").interactName("nbtPathTest")
6✔
4016
            .function(variables -> {
1✔
4017
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
4018
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
4019
                INbtPathExpression expression = null;
2✔
4020
                try {
4021
                    expression = NbtPath.parse(string.getRawValue());
4✔
4022
                } catch (NbtParseException e) {
×
4023
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
4024
                            string.getRawValue(),
×
4025
                            e.getMessage()));
×
4026
                }
1✔
4027
                if (!nbt.getRawValue().isPresent()) {
4!
4028
                    return ValueTypeBoolean.ValueBoolean.of(false);
×
4029
                }
4030
                return ValueTypeBoolean.ValueBoolean.of(expression.test(nbt.getRawValue().get()));
8✔
4031
            }).build());
1✔
4032

4033
    /**
4034
     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------
4035
     */
4036

4037
    /**
4038
     * The list of items.
4039
     */
4040
    public static final IOperator INGREDIENTS_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
4041
            .output(ValueTypes.LIST).operatorInteract("items").symbol("Ingr.items")
6✔
4042
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ITEMSTACK))
4✔
4043
            .build());
1✔
4044

4045
    /**
4046
     * The list of fluids
4047
     */
4048
    public static final IOperator INGREDIENTS_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
4049
            .output(ValueTypes.LIST).operatorInteract("fluids").symbol("Ingr.fluids")
6✔
4050
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.FLUIDSTACK))
4✔
4051
            .build());
1✔
4052

4053
    /**
4054
     * The list of fluids
4055
     */
4056
    public static final IOperator INGREDIENTS_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
4057
            .output(ValueTypes.LIST).operatorInteract("energies").symbol("Ingr.energies")
6✔
4058
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ENERGY))
4✔
4059
            .build());
1✔
4060

4061
    /**
4062
     * Set an ingredient item
4063
     */
4064
    public static final IOperator INGREDIENTS_WITH_ITEM = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_ITEMSTACK
5✔
4065
            .operatorName("with_item").symbol("Ingr.with_item").interactName("withItem")
6✔
4066
            .function(variables -> {
1✔
4067
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4068
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4069
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(2, ValueTypes.OBJECT_ITEMSTACK);
6✔
4070
                if (value.getRawValue().isEmpty()) {
4!
4071
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4072
                }
4073
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4074
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4075
                        index.getRawValue(), IngredientComponent.ITEMSTACK, itemStack.getRawValue()));
5✔
4076
            }).build());
1✔
4077

4078
    /**
4079
     * Set an ingredient fluid
4080
     */
4081
    public static final IOperator INGREDIENTS_WITH_FLUID = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_FLUIDSTACK
5✔
4082
            .operatorName("with_fluid").symbol("Ingr.with_fluid").interactName("withFluid")
6✔
4083
            .function(variables -> {
1✔
4084
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4085
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4086
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = variables.getValue(2, ValueTypes.OBJECT_FLUIDSTACK);
6✔
4087
                if (value.getRawValue().isEmpty()) {
4!
4088
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4089
                }
4090
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4091
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4092
                        index.getRawValue(), IngredientComponent.FLUIDSTACK, fluidStack.getRawValue()));
5✔
4093
            }).build());
1✔
4094

4095
    /**
4096
     * Set an ingredient energy
4097
     */
4098
    public static final IOperator INGREDIENTS_WITH_ENERGY = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_LONG
5✔
4099
            .operatorName("with_energy").symbol("Ingr.with_energy").interactName("withEnergy")
6✔
4100
            .function(variables -> {
1✔
4101
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4102
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4103
                ValueTypeLong.ValueLong energy = variables.getValue(2, ValueTypes.LONG);
6✔
4104
                if (value.getRawValue().isEmpty()) {
4!
4105
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4106
                }
4107
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4108
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4109
                        index.getRawValue(), IngredientComponent.ENERGY, energy.getRawValue()));
6✔
4110
            }).build());
1✔
4111

4112
    /**
4113
     * Set the list of items
4114
     */
4115
    public static final IOperator INGREDIENTS_WITH_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4116
            .operatorName("with_items").symbol("Ingr.with_items").interactName("withItems")
6✔
4117
            .function(variables -> {
1✔
4118
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4119
                ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4120
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4121
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4122
                }
4123
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4124
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4125
                        IngredientComponent.ITEMSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ITEMSTACK, list)));
2✔
4126
            }).build());
1✔
4127

4128
    /**
4129
     * Set the list of fluids
4130
     */
4131
    public static final IOperator INGREDIENTS_WITH_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4132
            .operatorName("with_fluids").symbol("Ingr.with_fluids").interactName("withFluids")
6✔
4133
            .function(variables -> {
1✔
4134
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4135
                ValueTypeList.ValueList<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4136
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4137
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4138
                }
4139
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4140
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4141
                        IngredientComponent.FLUIDSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.FLUIDSTACK, list)));
2✔
4142
            }).build());
1✔
4143

4144
    /**
4145
     * Set the list of energies
4146
     */
4147
    public static final IOperator INGREDIENTS_WITH_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4148
            .renderPattern(IConfigRenderPattern.INFIX_VERYLONG)
2✔
4149
            .operatorName("with_energies").symbol("Ingr.with_energies").interactName("withEnergies")
6✔
4150
            .function(variables -> {
1✔
4151
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4152
                ValueTypeList.ValueList<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = variables.getValue(1, ValueTypes.LIST);
6✔
4153
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4154
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4155
                }
4156
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4157
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4158
                        IngredientComponent.ENERGY, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ENERGY, list)));
2✔
4159
            }).build());
1✔
4160

4161
    /**
4162
     * ----------------------------------- RECIPE OPERATORS -----------------------------------
4163
     */
4164

4165
    /**
4166
     * The input ingredients of a recipe
4167
     */
4168
    public static final IOperator RECIPE_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4169
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4170
            .operatorInteract("input").symbol("recipe_in")
4✔
4171
            .function(variables -> {
1✔
4172
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4173
                if (value.getRawValue().isPresent()) {
4!
4174
                    return ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(value.getRawValue().get()));
7✔
4175
                }
4176
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4177
            }).build());
1✔
4178

4179
    /**
4180
     * The output ingredients of a recipe
4181
     */
4182
    public static final IOperator RECIPE_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4183
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4184
            .operatorInteract("output").symbol("recipe_out")
4✔
4185
            .function(variables -> {
1✔
4186
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4187
                if (value.getRawValue().isPresent()) {
4!
4188
                    return ValueObjectTypeIngredients.ValueIngredients.of(value.getRawValue().get().getOutput());
7✔
4189
                }
4190
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4191
            }).build());
1✔
4192

4193
    /**
4194
     * Set the input ingredients of a recipe
4195
     */
4196
    public static final IOperator RECIPE_WITH_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4197
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4198
            .operatorName("with_input").symbol("Recipe.with_in").interactName("withInput")
6✔
4199
            .function(variables -> {
1✔
4200
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4201
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4202
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4203
                    IMixedIngredients ingredients = valueIngredients.getRawValue().get();
5✔
4204
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4205
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4206
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4207
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4208
                                .stream()
4✔
4209
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4210
                                .collect(Collectors.toList()));
3✔
4211
                    }
1✔
4212
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4213
                            inputs,
4214
                            valueRecipe.getRawValue().get().getOutput()
5✔
4215
                    ));
4216
                }
4217
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4218
            }).build());
1✔
4219

4220
    /**
4221
     * Set the output ingredients of a recipe
4222
     */
4223
    public static final IOperator RECIPE_WITH_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4224
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4225
            .operatorName("with_output").symbol("Recipe.with_out").interactName("withOutput")
6✔
4226
            .function(variables -> {
1✔
4227
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4228
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4229
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4230
                    IRecipeDefinition recipe = valueRecipe.getRawValue().get();
5✔
4231
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4232
                    for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {
11✔
4233
                        inputs.put(component, (List) recipe.getInputs(component));
7✔
4234
                    }
1✔
4235
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4236
                            inputs,
4237
                            valueIngredients.getRawValue().get()
4✔
4238
                    ));
4239
                }
4240
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4241
            }).build());
1✔
4242

4243
    /**
4244
     * Create a recipe from two the given I/O ingredients
4245
     */
4246
    public static final IOperator RECIPE_WITH_INPUT_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_PREFIX
5✔
4247
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4248
            .operatorName("with_input_output").symbol("Recipe.with_io").interactName("withInputOutput")
6✔
4249
            .function(variables -> {
1✔
4250
                ValueObjectTypeIngredients.ValueIngredients valueIn = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4251
                ValueObjectTypeIngredients.ValueIngredients valueOut = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4252
                if (valueIn.getRawValue().isPresent() && valueOut.getRawValue().isPresent()) {
8!
4253
                    IMixedIngredients ingredients = valueIn.getRawValue().get();
5✔
4254
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4255
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4256
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4257
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4258
                                .stream()
4✔
4259
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4260
                                .collect(Collectors.toList()));
3✔
4261
                    }
1✔
4262
                    try {
4263
                        return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4264
                                inputs,
4265
                                valueOut.getRawValue().get()
4✔
4266
                        ));
4267
                    } catch (IllegalArgumentException e) {
×
4268
                        throw new EvaluationException(Component.literal(e.getMessage()));
×
4269
                    }
4270
                }
4271
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4272
            }).build());
1✔
4273

4274
    /**
4275
     * ------------------------------------ PARSE OPERATORS ------------------------------------
4276
     */
4277

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

4287
    /**
4288
     * Double Parse operator which takes a string of a form Double.parseDouble(),
4289
     * `/([+-]?)(Inf(inity)?|\u221E)/i`, or Long.decode() can consume.
4290
     */
4291
    public static final IOperator PARSE_DOUBLE = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.DOUBLE, v -> {
8✔
4292
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4293
      try {
4294
        return ValueTypeDouble.ValueDouble.of(Double.parseDouble(value.getRawValue()));
5✔
4295
      } catch (NumberFormatException e) {
1✔
4296
        try {
4297
          // \u221E = infinity symbol
4298
          Pattern p = Pattern.compile("\\A([+-]?)(Inf(inity)?|\u221E)\\z", Pattern.CASE_INSENSITIVE);
4✔
4299
          Matcher m = p.matcher(value.getRawValue().trim());
6✔
4300
          if (m.matches()){
3✔
4301
            if (m.group(1).equals("-")){
6✔
4302
              return ValueTypeDouble.ValueDouble.of(Double.NEGATIVE_INFINITY);
3✔
4303
            }
4304
            return ValueTypeDouble.ValueDouble.of(Double.POSITIVE_INFINITY);
3✔
4305
          }
4306
          // Try as a long
4307
          return ValueTypeDouble.ValueDouble.of((double) Long.decode(value.getRawValue()));
7✔
4308
        } catch (NumberFormatException e2) {
1✔
4309
            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4310
                    Component.translatable(ValueTypes.DOUBLE.getTranslationKey())));
3✔
4311
        }
4312
      }
4313
    }));
4314

4315
    /**
4316
     * Integer Parse operator which takes a string of a form Integer.decode() can consume.
4317
     */
4318
    public static final IOperator PARSE_INTEGER = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.INTEGER, v -> {
8✔
4319
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4320
      try{
4321
        return ValueTypeInteger.ValueInteger.of(Integer.decode(value.getRawValue()));
6✔
4322
      } catch (NumberFormatException e) {
1✔
4323
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4324
                  Component.translatable(ValueTypes.INTEGER.getTranslationKey())));
3✔
4325
      }
4326
    }));
4327

4328
    /**
4329
     * Long Parse operator which takes a string of a form Long.decode() can consume.
4330
     */
4331
    public static final IOperator PARSE_LONG = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.LONG, v -> {
8✔
4332
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4333
      try {
4334
        return ValueTypeLong.ValueLong.of(Long.decode(value.getRawValue()));
6✔
4335
      } catch (NumberFormatException e) {
1✔
4336
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4337
                  Component.translatable(ValueTypes.LONG.getTranslationKey())));
3✔
4338
      }
4339
    }));
4340

4341
    /**
4342
     * NBT Parse operator which takes a string of a form ValueTypeNbt().deserialize() can consume.
4343
     */
4344
    public static final IOperator PARSE_NBT = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.NBT, v -> {
8✔
4345
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4346
      try {
4347
        return ValueTypeNbt.ValueNbt.of(TagParser.parseTag(value.getRawValue()));
5✔
4348
      } catch (CommandSyntaxException e) {
1✔
4349
        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4350
                Component.translatable(ValueTypes.NBT.getTranslationKey())));
3✔
4351
      }
4352
    }));
4353

4354
    /**
4355
     * ----------------------------------- GENERAL OPERATORS -----------------------------------
4356
     */
4357

4358
    /**
4359
     * Choice operator with one boolean input, two any inputs and one output any.
4360
     */
4361
    public static final GeneralOperator GENERAL_CHOICE = REGISTRY.register(new GeneralChoiceOperator("?", "choice", "choice"));
10✔
4362

4363
    /**
4364
     * Identity operator with one any input and one any output
4365
     */
4366
    public static final GeneralOperator GENERAL_IDENTITY = REGISTRY.register(new GeneralIdentityOperator("id", "identity", "identity"));
10✔
4367

4368
    /**
4369
     * Constant operator with two any inputs and one any output
4370
     */
4371
    public static final GeneralOperator GENERAL_CONSTANT = REGISTRY.register(new GeneralConstantOperator("K", "constant", "constant"));
11✔
4372

4373
}
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