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

CyclopsMC / IntegratedDynamics / 22186773560

19 Feb 2026 02:52PM UTC coverage: 52.603% (+0.2%) from 52.363%
22186773560

push

github

web-flow
Remove Lombok dependency (#1604)

2911 of 8664 branches covered (33.6%)

Branch coverage included in aggregate %.

17683 of 30486 relevant lines covered (58.0%)

3.01 hits per line

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

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

3
import com.google.common.collect.*;
4
import com.google.re2j.Matcher;
5
import com.google.re2j.Pattern;
6
import com.google.re2j.PatternSyntaxException;
7
import com.mojang.brigadier.exceptions.CommandSyntaxException;
8
import com.mojang.logging.LogUtils;
9
import net.minecraft.IdentifierException;
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.Identifier;
19
import net.minecraft.util.ProblemReporter;
20
import net.minecraft.util.StringUtil;
21
import net.minecraft.world.entity.AgeableMob;
22
import net.minecraft.world.entity.Entity;
23
import net.minecraft.world.entity.LivingEntity;
24
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
25
import net.minecraft.world.entity.ai.attributes.Attributes;
26
import net.minecraft.world.entity.animal.Animal;
27
import net.minecraft.world.entity.decoration.ItemFrame;
28
import net.minecraft.world.entity.item.ItemEntity;
29
import net.minecraft.world.entity.monster.Enemy;
30
import net.minecraft.world.entity.player.Player;
31
import net.minecraft.world.entity.projectile.ProjectileUtil;
32
import net.minecraft.world.entity.vehicle.minecart.AbstractMinecart;
33
import net.minecraft.world.item.BlockItem;
34
import net.minecraft.world.item.Item;
35
import net.minecraft.world.item.ItemStack;
36
import net.minecraft.world.item.TooltipFlag;
37
import net.minecraft.world.item.crafting.RecipeType;
38
import net.minecraft.world.level.ClipContext;
39
import net.minecraft.world.level.block.Block;
40
import net.minecraft.world.level.block.SoundType;
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.level.storage.TagValueOutput;
45
import net.minecraft.world.phys.*;
46
import net.neoforged.neoforge.capabilities.Capabilities;
47
import net.neoforged.neoforge.common.IShearable;
48
import net.neoforged.neoforge.common.SoundActions;
49
import net.neoforged.neoforge.event.EventHooks;
50
import net.neoforged.neoforge.fluids.FluidStack;
51
import net.neoforged.neoforge.server.ServerLifecycleHooks;
52
import net.neoforged.neoforge.transfer.ResourceHandler;
53
import net.neoforged.neoforge.transfer.access.ItemAccess;
54
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
55
import net.neoforged.neoforge.transfer.item.ItemResource;
56
import org.apache.commons.lang3.tuple.Pair;
57
import org.apache.commons.lang3.tuple.Triple;
58
import org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;
59
import org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;
60
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
61
import org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;
62
import org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;
63
import org.cyclops.commoncapabilities.api.ingredient.*;
64
import org.cyclops.cyclopscore.helper.IModHelpers;
65
import org.cyclops.cyclopscore.helper.IModHelpersNeoForge;
66
import org.cyclops.cyclopscore.nbt.path.INbtPathExpression;
67
import org.cyclops.cyclopscore.nbt.path.NbtParseException;
68
import org.cyclops.cyclopscore.nbt.path.NbtPath;
69
import org.cyclops.integrateddynamics.IntegratedDynamics;
70
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
71
import org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;
72
import org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorRegistry;
73
import org.cyclops.integrateddynamics.api.evaluate.variable.*;
74
import org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;
75
import org.cyclops.integrateddynamics.core.evaluate.IOperatorValuePropagator;
76
import org.cyclops.integrateddynamics.core.evaluate.OperatorBuilders;
77
import org.cyclops.integrateddynamics.core.evaluate.variable.*;
78
import org.cyclops.integrateddynamics.core.helper.Helpers;
79
import org.cyclops.integrateddynamics.core.helper.L10NValues;
80
import org.cyclops.integrateddynamics.core.helper.NbtHelpers;
81
import org.cyclops.integrateddynamics.core.ingredient.ExtendedIngredientsList;
82
import org.cyclops.integrateddynamics.core.ingredient.ExtendedIngredientsSingle;
83
import org.slf4j.Logger;
84

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

598

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

870
    /**
871
     * ----------------------------------- NULLABLE OPERATORS -----------------------------------
872
     */
873

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

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

891
    /**
892
     * ----------------------------------- LIST OPERATORS -----------------------------------
893
     */
894

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1266
                    return ValueTypeList.ValueList.ofList(rawList1.getValueType(), result.stream().toList());
7✔
1267
                }
1268
            }).build());
1✔
1269

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

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

1338
    /**
1339
     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------
1340
     */
1341

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

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

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

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

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

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

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

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

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

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

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

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

1551
    /**
1552
     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------
1553
     */
1554

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1797
    /**
1798
     * If the given item can be used as fuel
1799
     */
1800
    public static final IOperator OBJECT_ITEMSTACK_CANBURN = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1801
            .output(ValueTypes.BOOLEAN)
2✔
1802
            .symbol("can_burn").operatorName("canburn").interactName("canBurn")
7✔
1803
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN
1✔
1804
                    .build(stack -> stack.getBurnTime(null, ServerLifecycleHooks.getCurrentServer().fuelValues()) > 0))
12✔
1805
            .build());
1✔
1806

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

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

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

1863
    /**
1864
     * Check if the item is an energy container item
1865
     */
1866
    public static final IOperator OBJECT_ITEMSTACK_ISENERGYCONTAINER = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1867
            .output(ValueTypes.BOOLEAN)
2✔
1868
            .symbol("is_energy_container").operatorName("isenergycontainer").interactName("isEnergyContainer")
7✔
1869
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_BOOLEAN.build(
2✔
1870
                Objects::nonNull
1871
            )).build());
1✔
1872

1873
    /**
1874
     * Get the storage energy
1875
     */
1876
    public static final IOperator OBJECT_ITEMSTACK_STOREDENERGY = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1877
            .output(ValueTypes.LONG)
2✔
1878
            .symbol("stored_energy").operatorName("storedenergy").interactName("energyStored")
7✔
1879
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_LONG.build(
2✔
1880
                input -> input != null ? input.getAmountAsLong() : 0
8✔
1881
            )).build());
1✔
1882

1883
    /**
1884
     * Get the energy capacity
1885
     */
1886
    public static final IOperator OBJECT_ITEMSTACK_ENERGYCAPACITY = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1887
            .output(ValueTypes.LONG)
2✔
1888
            .symbol("capacity_energy").operatorName("energycapacity").interactName("energyCapacity")
7✔
1889
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_LONG.build(
2✔
1890
                input -> input != null ? input.getCapacityAsLong() : 0
8✔
1891
            )).build());
1✔
1892

1893

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

1905

1906

1907

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

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

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

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

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

1997
    /**
1998
     * Item Stack size operator with one input itemstack and one output NBT tag.
1999
     */
2000
    public static final IOperator OBJECT_ITEMSTACK_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
2001
            .output(ValueTypes.NBT).symbol("NBT()").operatorName("nbt").interactName("nbt")
8✔
2002
            .function(input -> {
1✔
2003
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2004
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2005
                if (itemStack.getRawValue().isEmpty() || itemStack.getRawValue().getComponents().isEmpty()) {
9!
2006
                    return ValueTypeNbt.ValueNbt.of();
×
2007
                }
2008
                CompoundTag tag = (CompoundTag) DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), itemStack.getRawValue().getComponentsPatch()).getOrThrow();
12✔
2009
                // Remove entries that have been marked as removed.
2010
                for (String key : Sets.newHashSet(tag.keySet())) {
12✔
2011
                    if (key.startsWith("!")) {
4✔
2012
                        tag.remove(key);
4✔
2013
                    }
2014
                }
1✔
2015
                if (tag.isEmpty()) {
3✔
2016
                    return ValueTypeNbt.ValueNbt.of();
2✔
2017
                }
2018
                return ValueTypeNbt.ValueNbt.of(Optional.of(tag));
4✔
2019
            }).build());
1✔
2020

2021
    /**
2022
     * Item Stack has_nbt operator with one input itemstack and one output boolean.
2023
     */
2024
    public static final IOperator OBJECT_ITEMSTACK_HASDATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
2025
            .output(ValueTypes.BOOLEAN).symbol("has_nbt").operatorName("hasnbt").interactName("hasNbt")
9✔
2026
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
2027
                    itemStack -> !itemStack.isEmpty() && itemStack.getComponents().stream().anyMatch(t -> !t.type().isTransient())
21!
2028
            )).build());
1✔
2029

2030
    /**
2031
     * Get the data component keys of an itemstack.
2032
     */
2033
    public static final IOperator OBJECT_ITEMSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
2034
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
2035
            .function(input -> {
1✔
2036
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2037
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2038
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
2039
                        itemStack.getRawValue().isEmpty() ?
4✔
2040
                                Lists.newArrayList() :
2✔
2041
                                itemStack.getRawValue().getComponents().keySet().stream()
5✔
2042
                                        .filter(c -> !c.isTransient())
8!
2043
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
2044
                                        .sorted(Comparator.comparing(ValueTypeString.ValueString::getRawValue))
2✔
2045
                                        .toList());
2✔
2046
            }).build());
1✔
2047

2048
    /**
2049
     * Get the data component value by key
2050
     */
2051
    public static final IOperator OBJECT_ITEMSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
2052
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING)
2✔
2053
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
2054
            .function(input -> {
1✔
2055
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2056

2057
                // Determine data component type
2058
                DataComponentType<?> dataComponentType;
2059
                try {
2060
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2061
                } catch (IdentifierException e) {
×
2062
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2063
                }
1✔
2064

2065
                // Fetch component value
2066
                TypedDataComponent<?> typedComponent = itemStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
2067
                if (typedComponent == null) {
2✔
2068
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
2069
                }
2070

2071
                // Encode component value
2072
                try {
2073
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
2074
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
2075
                } catch (IllegalStateException e) {
×
2076
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
2077
                }
2078
            }).build());
1✔
2079

2080
    /**
2081
     * Get the data component value by key
2082
     */
2083
    public static final IOperator OBJECT_ITEMSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_3
18✔
2084
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2085
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2086
            .function(input -> {
1✔
2087
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2088

2089
                // Skip further processing if input value is empty
2090
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2091
                if (!tagOptional.isPresent()) {
3!
2092
                    return itemStack;
×
2093
                }
2094

2095
                // Determine data component type
2096
                DataComponentType<?> dataComponentType;
2097
                try {
2098
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2099
                } catch (IdentifierException e) {
×
2100
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2101
                }
1✔
2102

2103
                // Encode component value
2104
                try {
2105
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2106
                    itemStack = ValueObjectTypeItemStack.ValueItemStack.of(itemStack.getRawValue().copy());
5✔
2107
                    itemStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2108
                    return itemStack;
2✔
2109
                } catch (IllegalStateException e) {
×
2110
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2111
                }
2112
            }).build());
1✔
2113

2114
    /**
2115
     * Get the tooltip of an itemstack in list form.
2116
     */
2117
    public static final IOperator OBJECT_ITEMSTACK_TOOLTIP = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
2118
            .output(ValueTypes.LIST).symbol("tooltip").operatorName("tooltip").interactName("tooltip")
8✔
2119
            .function(input -> {
1✔
2120
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2121
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
4✔
2122
                        itemStack.getRawValue().getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.Default.ADVANCED).stream()
7✔
2123
                                .map(c -> ValueTypeString.ValueString.of(c.getString()))
5✔
2124
                                .toList());
1✔
2125
            }).build());
1✔
2126
    /**
2127
     * Get the tooltip of an itemstack in list form, using the provided player entity as the player context.
2128
     */
2129
    public static final IOperator OBJECT_ITEMSTACK_ENTITY_TOOLTIP = REGISTRY.register(OperatorBuilders.ENTITY_1_ITEMSTACK_1
14✔
2130
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2131
            .output(ValueTypes.LIST).symbol("entity_item_tooltip").operatorName("entityitemtooltip").interactName("entityItemTooltip")
8✔
2132
            .function(variables -> {
1✔
2133
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2134
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
2135
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2136
                    Player entity = (Player) a.getRawValue().get();
×
2137
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
×
2138
                            itemStack.getRawValue().getTooltipLines(Item.TooltipContext.of(entity.level()), entity, TooltipFlag.Default.ADVANCED).stream()
×
2139
                                    .map(c -> ValueTypeString.ValueString.of(c.getString()))
×
2140
                                    .toList());
×
2141
                }
2142
                return ValueTypes.LIST.getDefault();
×
2143
            }).build());
1✔
2144

2145
    /**
2146
     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------
2147
     */
2148

2149
    /**
2150
     * If the entity is a mob
2151
     */
2152
    public static final IOperator OBJECT_ENTITY_ISMOB = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2153
            .output(ValueTypes.BOOLEAN).symbol("is_mob").operatorName("ismob").interactName("isMob")
9✔
2154
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2155
                entity -> entity instanceof Enemy
4✔
2156
            )).build());
1✔
2157

2158
    /**
2159
     * If the entity is an animal
2160
     */
2161
    public static final IOperator OBJECT_ENTITY_ISANIMAL = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2162
            .output(ValueTypes.BOOLEAN).symbol("is_animal").operatorName("isanimal").interactName("isAnimal")
9✔
2163
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2164
                entity -> entity instanceof Animal && !(entity instanceof Enemy)
11!
2165
            )).build());
1✔
2166

2167
    /**
2168
     * If the entity is an item
2169
     */
2170
    public static final IOperator OBJECT_ENTITY_ISITEM = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2171
            .output(ValueTypes.BOOLEAN).symbol("is_item").operatorName("isitem").interactName("isItem")
9✔
2172
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2173
                entity -> entity instanceof ItemEntity
4✔
2174
            )).build());
1✔
2175

2176
    /**
2177
     * If the entity is a player
2178
     */
2179
    public static final IOperator OBJECT_ENTITY_ISPLAYER = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2180
            .output(ValueTypes.BOOLEAN).symbol("is_player").operatorName("isplayer").interactName("isPlayer")
9✔
2181
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2182
                entity -> entity instanceof Player
4✔
2183
            )).build());
1✔
2184

2185
    /**
2186
     * If the entity is a minecart
2187
     */
2188
    public static final IOperator OBJECT_ENTITY_ISMINECART = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2189
            .output(ValueTypes.BOOLEAN).symbol("is_minecart").operatorName("isminecart").interactName("isMinecart")
9✔
2190
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2191
                entity -> entity instanceof AbstractMinecart
×
2192
            )).build());
1✔
2193

2194
    /**
2195
     * The itemstack from the entity
2196
     */
2197
    public static final IOperator OBJECT_ENTITY_ITEMSTACK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX
5✔
2198
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("item")
4✔
2199
            .function(variables -> {
1✔
2200
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2201
                Optional<Entity> a = valueEntity.getRawValue();
3✔
2202
                return ValueObjectTypeItemStack.ValueItemStack.of((a.isPresent() && a.get() instanceof ItemEntity) ? ((ItemEntity) a.get()).getItem() : ItemStack.EMPTY);
15!
2203
            }).build());
1✔
2204

2205
    /**
2206
     * The entity health
2207
     */
2208
    public static final IOperator OBJECT_ENTITY_HEALTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2209
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("health")
5✔
2210
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2211
                entity -> entity instanceof LivingEntity ? ((LivingEntity) entity).getHealth() : 0.0
11✔
2212
            )).build());
1✔
2213

2214
    /**
2215
     * The entity width
2216
     */
2217
    public static final IOperator OBJECT_ENTITY_WIDTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2218
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("width")
5✔
2219
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2220
                entity -> entity != null ? entity.getBbWidth() : 0.0
8!
2221
            )).build());
1✔
2222

2223
    /**
2224
     * The entity width
2225
     */
2226
    public static final IOperator OBJECT_ENTITY_HEIGHT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2227
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("height")
5✔
2228
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2229
                entity -> entity != null ? entity.getBbHeight() : 0.0
8!
2230
            )).build());
1✔
2231

2232
    /**
2233
     * If the entity is burning
2234
     */
2235
    public static final IOperator OBJECT_ENTITY_ISBURNING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2236
            .output(ValueTypes.BOOLEAN).symbol("is_burning").operatorName("isburning").interactName("entityIsBurning")
9✔
2237
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2238
                entity -> entity != null && entity.isOnFire()
10!
2239
            )).build());
1✔
2240

2241
    /**
2242
     * If the entity is wet
2243
     */
2244
    public static final IOperator OBJECT_ENTITY_ISWET = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2245
            .output(ValueTypes.BOOLEAN).symbol("is_wet").operatorName("iswet").interactName("isWet")
9✔
2246
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2247
                entity -> entity != null && entity.isInWaterOrRain()
10!
2248
            )).build());
1✔
2249

2250
    /**
2251
     * If the entity is crouching
2252
     */
2253
    public static final IOperator OBJECT_ENTITY_ISCROUCHING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2254
            .output(ValueTypes.BOOLEAN).symbol("is_crouching").operatorName("iscrouching").interactName("isCrouching")
9✔
2255
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2256
                entity -> entity != null && entity.isCrouching()
10!
2257
            )).build());
1✔
2258

2259
    /**
2260
     * If the entity is eating
2261
     */
2262
    public static final IOperator OBJECT_ENTITY_ISEATING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2263
            .output(ValueTypes.BOOLEAN).symbol("is_eating").operatorName("iseating").interactName("isEating")
9✔
2264
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2265
                entity -> entity instanceof LivingEntity && ((LivingEntity) entity).getUseItemRemainingTicks() > 0
12!
2266
            )).build());
1✔
2267

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

2284
    /**
2285
     * The list of itemstacks from an entity
2286
     */
2287
    public static final IOperator OBJECT_ENTITY_INVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2288
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
2289
            .function(variables -> {
1✔
2290
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2291
                Optional<Entity> a = valueEntity.getRawValue();
×
2292
                if(a.isPresent()) {
×
2293
                    Entity entity = a.get();
×
2294
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityInventory(entity.level(), entity));
×
2295
                } else {
2296
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2297
                }
2298
            }).build());
1✔
2299

2300
    /**
2301
     * The name of the mod owning this entity
2302
     */
2303
    public static final IOperator OBJECT_ENTITY_MODNAME = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2304
            .symbolOperatorInteract("mod")
13✔
2305
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2306
                    (OperatorBase.SafeVariablesGetter variables) -> {
2307
                        ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2308
                        if(a.getRawValue().isPresent()) {
4!
2309
                            Entity entity = a.getRawValue().get();
5✔
2310
                            return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());
5✔
2311
                        }
2312
                        return Identifier.parse("");
×
2313
                    },
2314
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2315
            )))
2316
            .build());
1✔
2317

2318
    /**
2319
     * The block the given player is currently looking at.
2320
     */
2321
    public static final IOperator OBJECT_PLAYER_TARGETBLOCK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_BLOCK)
7✔
2322
            .symbol("target_block").operatorName("targetblock").interactName("targetBlock")
6✔
2323
            .function(variables -> {
1✔
2324
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2325
                BlockState blockState = null;
×
2326
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2327
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2328
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.BLOCK_INTERACTION_RANGE);
×
2329
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2330
                    double eyeHeight = entity.getEyeHeight();
×
2331
                    Vec3 lookVec = entity.getLookAngle();
×
2332
                    Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());
×
2333
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2334

2335
                    ClipContext rayTraceContext = new ClipContext(origin, direction, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity);
×
2336
                    BlockHitResult mop = entity.level().clip(rayTraceContext);
×
2337
                    if(mop != null && mop.getType() == HitResult.Type.BLOCK) {
×
2338
                        blockState = entity.level().getBlockState(mop.getBlockPos());
×
2339
                    }
2340
                }
2341
                return ValueObjectTypeBlock.ValueBlock.of(blockState);
×
2342
            }).build());
1✔
2343

2344
    /**
2345
     * The entity the given player is currently looking at.
2346
     */
2347
    public static final IOperator OBJECT_PLAYER_TARGETENTITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ENTITY)
7✔
2348
            .symbol("target_entity").operatorName("targetentity").interactName("targetEntity")
6✔
2349
            .function(variables -> {
1✔
2350
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2351
                Entity entityOut = null;
×
2352
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2353
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2354
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.ENTITY_INTERACTION_RANGE);
×
2355
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2356

2357
                    // Copied and modified from GameRenderer#getMouseOver
2358
                    Vec3 origin = entity.getEyePosition(1.0F);
×
2359
                    double reachDistanceSquared = reachDistance * reachDistance;
×
2360

2361
                    Vec3 lookVec = entity.getViewVector(1.0F);
×
2362
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2363
                    AABB boundingBox = entity.getBoundingBox()
×
2364
                            .expandTowards(lookVec.scale(reachDistance))
×
2365
                            .inflate(1.0D, 1.0D, 1.0D);
×
2366
                    EntityHitResult entityraytraceresult = ProjectileUtil.getEntityHitResult(entity, origin, direction,
×
2367
                            boundingBox, e -> !e.isSpectator() && e.isPickable(), reachDistanceSquared);
×
2368
                    if (entityraytraceresult != null) {
×
2369
                        Entity entity1 = entityraytraceresult.getEntity();
×
2370
                        Vec3 vec3d3 = entityraytraceresult.getLocation();
×
2371
                        double distanceSquared = origin.distanceToSqr(vec3d3);
×
2372
                        if (distanceSquared < reachDistanceSquared
×
2373
                                && (entity1 instanceof LivingEntity || entity1 instanceof ItemFrame)) {
2374
                            entityOut = entity1;
×
2375
                        }
2376
                    }
2377
                }
2378
                return ValueObjectTypeEntity.ValueEntity.of(entityOut);
×
2379
            }).build());
1✔
2380

2381
    /**
2382
     * If the given player has an external gui open.
2383
     */
2384
    public static final IOperator OBJECT_PLAYER_HASGUIOPEN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2385
            .symbol("has_gui_open").operatorName("hasguiopen").interactName("hasGuiOpen")
6✔
2386
            .function(variables -> {
1✔
2387
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2388
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2389
                    Player entity = (Player) a.getRawValue().get();
×
2390
                    return ValueTypeBoolean.ValueBoolean.of(entity.containerMenu != entity.inventoryMenu);
×
2391
                }
2392
                return ValueTypeBoolean.ValueBoolean.of(false);
×
2393
            }).build());
1✔
2394

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

2409
    /**
2410
     * The item the given entity is currently holding in its off hand.
2411
     */
2412
    public static final IOperator OBJECT_ENTITY_HELDITEM_OFF = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2413
            .symbol("held_item_2").operatorName("helditemoffhand").interactName("heldItemOffHand")
6✔
2414
            .function(variables -> {
1✔
2415
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2416
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2417
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2418
                    itemStack = ((LivingEntity) a.getRawValue().get()).getOffhandItem();
6✔
2419
                }
2420
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2421
            }).build());
1✔
2422

2423
    /**
2424
     * The entity's mounted entity
2425
     */
2426
    public static final IOperator OBJECT_ENTITY_MOUNTED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.LIST)
7✔
2427
            .symbolOperator("mounted").interactName("mounted")
4✔
2428
            .function(variables -> {
1✔
2429
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2430
                List<ValueObjectTypeEntity.ValueEntity> passengers = Lists.newArrayList();
2✔
2431
                if(a.getRawValue().isPresent()) {
4!
2432
                    for (Entity passenger : a.getRawValue().get().getPassengers()) {
14✔
2433
                        passengers.add(ValueObjectTypeEntity.ValueEntity.of(passenger));
5✔
2434
                    }
1✔
2435
                }
2436
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, passengers);
4✔
2437
            }).build());
1✔
2438

2439
    /**
2440
     * The item frame's contents
2441
     */
2442
    public static final IOperator OBJECT_ITEMFRAME_CONTENTS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2443
            .symbol("itemframe_contents").operatorName("itemframecontents").interactName("itemFrameContents")
6✔
2444
            .function(variables -> {
1✔
2445
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2446
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2447
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2448
                    itemStack = ((ItemFrame) a.getRawValue().get()).getItem();
6✔
2449
                }
2450
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2451
            }).build());
1✔
2452

2453
    /**
2454
     * The item frame's rotation
2455
     */
2456
    public static final IOperator OBJECT_ITEMFRAME_ROTATION = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2457
            .symbol("itemframe_rotation").operatorName("itemframerotation").interactName("itemFrameRotation")
6✔
2458
            .function(variables -> {
1✔
2459
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2460
                Integer rotation = 0;
3✔
2461
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2462
                    rotation = ((ItemFrame) a.getRawValue().get()).getRotation();
7✔
2463
                }
2464
                return ValueTypeInteger.ValueInteger.of(rotation);
4✔
2465
            }).build());
1✔
2466

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

2484
    /**
2485
     * The deathsound of this entity.
2486
     */
2487
    public static final IOperator OBJECT_ENTITY_DEATHSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2488
            .symbolOperator("deathsound").interactName("deathSound")
4✔
2489
            .function(variables -> {
1✔
2490
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2491
                String hurtSound = "";
2✔
2492
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2493
                    String sound = ((LivingEntity) a.getRawValue().get()).getDeathSound().location().toString();
8✔
2494
                    if (sound != null) {
2!
2495
                        hurtSound = sound;
2✔
2496
                    }
2497
                }
2498
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2499
            }).build());
1✔
2500

2501
    /**
2502
     * The age of this entity.
2503
     */
2504
    public static final IOperator OBJECT_ENTITY_AGE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2505
            .symbolOperatorInteract("age")
2✔
2506
            .function(variables -> {
1✔
2507
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2508
                int age = 0;
2✔
2509
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2510
                    age = ((LivingEntity) a.getRawValue().get()).getNoActionTime();
6✔
2511
                }
2512
                return ValueTypeInteger.ValueInteger.of(age);
3✔
2513
            }).build());
1✔
2514

2515
    /**
2516
     * If the entity is a child.
2517
     */
2518
    public static final IOperator OBJECT_ENTITY_ISCHILD = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2519
            .symbol("is_child").operatorName("ischild").interactName("isChild")
6✔
2520
            .function(variables -> {
1✔
2521
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2522
                boolean child = false;
2✔
2523
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2524
                    child = ((LivingEntity) a.getRawValue().get()).isBaby();
6✔
2525
                }
2526
                return ValueTypeBoolean.ValueBoolean.of(child);
3✔
2527
            }).build());
1✔
2528

2529
    /**
2530
     * If the entity can be bred.
2531
     */
2532
    public static final IOperator OBJECT_ENTITY_CANBREED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2533
            .symbol("canbreed").operatorName("canbreed").interactName("canBreed")
6✔
2534
            .function(variables -> {
1✔
2535
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2536
                boolean canBreed = false;
2✔
2537
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof AgeableMob) {
9!
2538
                    canBreed = ((AgeableMob) a.getRawValue().get()).getAge() == 0;
10✔
2539
                }
2540
                return ValueTypeBoolean.ValueBoolean.of(canBreed);
3✔
2541
            }).build());
1✔
2542

2543
    /**
2544
     * If the entity is in love.
2545
     */
2546
    public static final IOperator OBJECT_ENTITY_ISINLOVE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2547
            .symbol("is_in_love").operatorName("isinlove").interactName("isInLove")
6✔
2548
            .function(variables -> {
1✔
2549
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2550
                boolean inLove = false;
2✔
2551
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof Animal) {
9!
2552
                    inLove = ((Animal) a.getRawValue().get()).isInLove();
6✔
2553
                }
2554
                return ValueTypeBoolean.ValueBoolean.of(inLove);
3✔
2555
            }).build());
1✔
2556

2557
    /**
2558
     * If the entity can be bred with the given item.
2559
     */
2560
    public static final IOperator OBJECT_ENTITY_CANBREEDWITH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
14✔
2561
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2562
            .output(ValueTypes.BOOLEAN)
2✔
2563
            .symbol("can_breed_with").operatorName("canbreedwith").interactName("canBreedWith")
6✔
2564
            .renderPattern(IConfigRenderPattern.INFIX_LONG)
2✔
2565
            .function(variables -> {
1✔
2566
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2567
                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
2568
                boolean canBreedWith = false;
2✔
2569
                if (a.getRawValue().isPresent() && !b.getRawValue().isEmpty() && a.getRawValue().get() instanceof Animal) {
13!
2570
                    canBreedWith = ((Animal) a.getRawValue().get()).isFood(b.getRawValue());
8✔
2571
                }
2572
                return ValueTypeBoolean.ValueBoolean.of(canBreedWith);
3✔
2573
            }).build());
1✔
2574

2575
    /**
2576
     * If the entity is shearable.
2577
     */
2578
    public static final IOperator OBJECT_ENTITY_ISSHEARABLE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2579
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
2580
            .function(variables -> {
1✔
2581
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2582
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
2583
                        && a.getRawValue().get() instanceof IShearable
5✔
2584
                        && ((IShearable) a.getRawValue().get()).isShearable(null, ItemStack.EMPTY, null, null));
12✔
2585
            }).build());
1✔
2586

2587
    /**
2588
     * The entity serialized to NBT.
2589
     */
2590
    public static final IOperator OBJECT_ENTITY_NBT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2591
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2592
            .function(input -> {
1✔
2593
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2594
                try {
2595
                    if (entity.getRawValue().isPresent()) {
4!
2596
                        Entity e = entity.getRawValue().get();
5✔
2597
                        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(e.problemPath(), LOGGER)) {
7✔
2598
                            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, e.level().registryAccess());
6✔
2599
                            e.save(valueOutput);
4✔
2600
                            return ValueTypeNbt.ValueNbt.of(valueOutput.buildResult());
6✔
2601
                        }
2602
                    }
2603
                } catch (Exception e) {
×
2604
                    // Catch possible errors during NBT writing
2605
                }
×
2606
                return ValueTypes.NBT.getDefault();
×
2607
            }).build());
1✔
2608

2609
    /**
2610
     * The entity type.
2611
     */
2612
    public static final IOperator OBJECT_ENTITY_TYPE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2613
            .output(ValueTypes.STRING).symbol("entity_type").operatorName("entitytype").interactName("type")
8✔
2614
            .function(input -> {
1✔
2615
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2616
                String entityType = "";
2✔
2617
                if (entity.getRawValue().isPresent()) {
4!
2618
                    Entity e = entity.getRawValue().get();
5✔
2619
                    entityType = BuiltInRegistries.ENTITY_TYPE.getKey(e.getType()).toString();
6✔
2620
                }
2621
                return ValueTypeString.ValueString.of(entityType);
3✔
2622
            }).build());
1✔
2623

2624
    /**
2625
     * The entity items.
2626
     */
2627
    public static final IOperator OBJECT_ENTITY_ITEMS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2628
            .output(ValueTypes.LIST).symbol("entity_items").operatorName("entityitems").interactName("items")
8✔
2629
            .function(input -> {
1✔
2630
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2631
                Optional<Entity> a = valueEntity.getRawValue();
×
2632
                if(a.isPresent()) {
×
2633
                    Entity entity = a.get();
×
2634
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityItems(entity.level(), entity, null));
×
2635
                } else {
2636
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.emptyList());
×
2637
                }
2638
            }).build());
1✔
2639

2640
    /**
2641
     * The entity fluids.
2642
     */
2643
    public static final IOperator OBJECT_ENTITY_FLUIDS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2644
            .output(ValueTypes.LIST).symbol("entity_fluids").operatorName("entityfluids").interactName("fluids")
8✔
2645
            .function(input -> {
1✔
2646
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2647
                Optional<Entity> a = valueEntity.getRawValue();
×
2648
                if(a.isPresent()) {
×
2649
                    Entity entity = a.get();
×
2650
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityFluids(entity.level(), entity, null));
×
2651
                } else {
2652
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, Collections.emptyList());
×
2653
                }
2654
            }).build());
1✔
2655

2656
    /**
2657
     * The entity energy stored.
2658
     */
2659
    public static final IOperator OBJECT_ENTITY_ENERGY_STORED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2660
            .output(ValueTypes.LONG).symbol("entity_stored_energy").operatorName("entityenergystored").interactName("energy")
8✔
2661
            .function(input -> {
1✔
2662
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2663
                Optional<Entity> a = valueEntity.getRawValue();
×
2664
                if(a.isPresent()) {
×
2665
                    Entity entity = a.get();
×
2666
                    EnergyHandler energyStorage = entity.getCapability(Capabilities.Energy.ENTITY, null);
×
2667
                    return ValueTypeLong.ValueLong.of(energyStorage != null ? energyStorage.getAmountAsLong() : 0);
×
2668
                }
2669
                return ValueTypeLong.ValueLong.of(0);
×
2670
            }).build());
1✔
2671

2672
    /**
2673
     * The entity energy stored.
2674
     */
2675
    public static final IOperator OBJECT_ENTITY_ENERGY_CAPACITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2676
            .output(ValueTypes.LONG).symbol("entity_capacity_energy").operatorName("entityenergycapacity").interactName("energyCapacity")
8✔
2677
            .function(input -> {
1✔
2678
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2679
                Optional<Entity> a = valueEntity.getRawValue();
×
2680
                if(a.isPresent()) {
×
2681
                    Entity entity = a.get();
×
2682
                    EnergyHandler energyStorage = entity.getCapability(Capabilities.Energy.ENTITY, null);
×
2683
                    return ValueTypeLong.ValueLong.of(energyStorage != null ? energyStorage.getCapacityAsLong() : 0);
×
2684
                }
2685
                return ValueTypeLong.ValueLong.of(0);
×
2686
            }).build());
1✔
2687

2688
    /**
2689
     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------
2690
     */
2691

2692
    /**
2693
     * The amount of fluid in the fluidstack
2694
     */
2695
    public static final IOperator OBJECT_FLUIDSTACK_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2696
            .output(ValueTypes.INTEGER).symbolOperatorInteract("amount")
5✔
2697
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2698
                    FluidStack::getAmount
2699
            )).build());
1✔
2700

2701
    /**
2702
     * The block from the fluidstack
2703
     */
2704
    public static final IOperator OBJECT_FLUIDSTACK_BLOCK = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2705
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
2706
            .function(variables -> {
1✔
2707
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2708
                FluidStack a = valueFluidStack.getRawValue();
3✔
2709
                return ValueObjectTypeBlock.ValueBlock.of(!a.isEmpty() ? a.getFluid().getFluidType().getStateForPlacement(null, null, a).createLegacyBlock() : null);
14!
2710
            }).build());
1✔
2711

2712
    /**
2713
     * The fluidstack luminosity
2714
     */
2715
    public static final IOperator OBJECT_FLUIDSTACK_LIGHT_LEVEL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2716
            .output(ValueTypes.INTEGER).symbolOperator("light_level").interactName("lightLevel")
7✔
2717
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2718
                fluidStack -> fluidStack.getFluid().getFluidType().getLightLevel(fluidStack)
7✔
2719
            )).build());
1✔
2720

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

2730
    /**
2731
     * The fluidstack temperature
2732
     */
2733
    public static final IOperator OBJECT_FLUIDSTACK_TEMPERATURE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2734
            .output(ValueTypes.INTEGER).symbolOperatorInteract("temperature")
5✔
2735
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2736
                    fluidStack -> fluidStack.getFluid().getFluidType().getTemperature(fluidStack)
7✔
2737
            )).build());
1✔
2738

2739
    /**
2740
     * The fluidstack viscosity
2741
     */
2742
    public static final IOperator OBJECT_FLUIDSTACK_VISCOSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2743
            .output(ValueTypes.INTEGER).symbolOperatorInteract("viscosity")
5✔
2744
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2745
                fluidStack -> fluidStack.getFluid().getFluidType().getViscosity(fluidStack)
7✔
2746
            )).build());
1✔
2747

2748
    /**
2749
     * If the fluidstack is gaseous
2750
     */
2751
    public static final IOperator OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2752
            .output(ValueTypes.BOOLEAN).symbolOperator("lighter_than_air").interactName("isLighterThanAir")
7✔
2753
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_BOOLEAN.build(
2✔
2754
                fluidStack -> fluidStack.getFluid().getFluidType().isLighterThanAir()
6✔
2755
            )).build());
1✔
2756

2757
    /**
2758
     * The rarity of the fluidstack
2759
     */
2760
    public static final IOperator OBJECT_FLUIDSTACK_RARITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2761
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
2762
            .function(variables -> {
1✔
2763
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2764
                FluidStack a = valueFluidStack.getRawValue();
3✔
2765
                return ValueTypeString.ValueString.of(a.getFluid().getFluidType().getRarity(a).name());
8✔
2766
            }).build());
1✔
2767

2768
    /**
2769
     * The bucket empty sound of the fluidstack
2770
     */
2771
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2772
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_empty").interactName("bucketEmptySound")
6✔
2773
            .function(variables -> {
1✔
2774
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2775
                FluidStack a = valueFluidStack.getRawValue();
3✔
2776
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_EMPTY))
10✔
2777
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2778
                        .orElse(""));
2✔
2779
            }).build());
1✔
2780

2781
    /**
2782
     * The fluid vaporize sound of the fluidstack
2783
     */
2784
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2785
            .output(ValueTypes.STRING).symbolOperator("sound_fluid_vaporize").interactName("fluidVaporizeSound")
6✔
2786
            .function(variables -> {
1✔
2787
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2788
                FluidStack a = valueFluidStack.getRawValue();
3✔
2789
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.FLUID_VAPORIZE))
10✔
2790
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2791
                        .orElse(""));
2✔
2792
            }).build());
1✔
2793

2794
    /**
2795
     * The bucket fill sound of the fluidstack
2796
     */
2797
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2798
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_fill").interactName("bucketFillSound")
6✔
2799
            .function(variables -> {
1✔
2800
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2801
                FluidStack a = valueFluidStack.getRawValue();
3✔
2802
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_FILL))
10✔
2803
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2804
                        .orElse(""));
2✔
2805
            }).build());
1✔
2806

2807
    /**
2808
     * The bucket of the fluidstack
2809
     */
2810
    public static final IOperator OBJECT_FLUIDSTACK_BUCKET = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2811
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("bucket")
4✔
2812
            .function(variables -> {
1✔
2813
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2814
                FluidStack a = valueFluidStack.getRawValue();
3✔
2815
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getFluid().getFluidType().getBucket(a));
7✔
2816
            }).build());
1✔
2817

2818
    /**
2819
     * If the fluid types of the two given fluidstacks are equal
2820
     */
2821
    public static final IOperator OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
5✔
2822
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwfluidequal").interactName("isRawEqual")
8✔
2823
            .function(variables -> {
1✔
2824
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack0 = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2825
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack1 = variables.getValue(1, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2826
                return ValueTypeBoolean.ValueBoolean.of(FluidStack.isSameFluid(valueFluidStack0.getRawValue(), valueFluidStack1.getRawValue()));
7✔
2827
            }).build());
1✔
2828

2829
    /**
2830
     * The name of the mod owning this fluid
2831
     */
2832
    public static final IOperator OBJECT_FLUIDSTACK_MODNAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2833
            .symbolOperatorInteract("mod")
13✔
2834
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2835
                    (OperatorBase.SafeVariablesGetter variables) -> {
2836
                        ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2837
                        return BuiltInRegistries.FLUID.getKey(a.getRawValue().getFluid());
6✔
2838
                    },
2839
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2840
            ))).build());
1✔
2841

2842
    /**
2843
     * The tag of the given fluidstack.
2844
     */
2845
    public static final IOperator OBJECT_FLUIDSTACK_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2846
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2847
            .function(input -> {
1✔
2848
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2849
                if (fluidStack.getRawValue().getComponentsPatch().isEmpty()) {
5✔
2850
                    return ValueTypeNbt.ValueNbt.of(Optional.empty());
3✔
2851
                }
2852
                return ValueTypeNbt.ValueNbt.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), fluidStack.getRawValue().getComponentsPatch()).getOrThrow());
13✔
2853
            }).build());
1✔
2854

2855
    /**
2856
     * Create a new fluidstack with the given amount.
2857
     */
2858
    public static final IOperator OBJECT_FLUIDSTACK_WITH_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
14✔
2859
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.INTEGER)
2✔
2860
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("with_amount").interactName("withAmount")
6✔
2861
            .function(variables -> {
1✔
2862
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2863
                ValueTypeInteger.ValueInteger valueInteger = variables.getValue(1, ValueTypes.INTEGER);
6✔
2864
                FluidStack fluidStack = valueFluidStack.getRawValue().copy();
4✔
2865
                fluidStack.setAmount(valueInteger.getRawValue());
4✔
2866
                return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
2867
            }).build());
1✔
2868

2869
    /**
2870
     * Get the data component keys of an fluidstack.
2871
     */
2872
    public static final IOperator OBJECT_FLUIDSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2873
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
2874
            .function(input -> {
1✔
2875
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2876
                // Explicitly check for fluid emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2877
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
2878
                        fluidStack.getRawValue().isEmpty() ?
4!
2879
                                Lists.newArrayList() :
×
2880
                                fluidStack.getRawValue().getComponents().keySet().stream()
5✔
2881
                                        .filter(c -> !c.isTransient())
8!
2882
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
2883
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
1✔
2884
                                        .toList());
2✔
2885
            }).build());
1✔
2886

2887
    /**
2888
     * Get the data component value by key
2889
     */
2890
    public static final IOperator OBJECT_FLUIDSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2_LONG
14✔
2891
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING)
2✔
2892
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
2893
            .function(input -> {
1✔
2894
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2895

2896
                // Determine data component type
2897
                DataComponentType<?> dataComponentType;
2898
                try {
2899
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2900
                } catch (IdentifierException e) {
×
2901
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2902
                }
1✔
2903

2904
                // Fetch component value
2905
                TypedDataComponent<?> typedComponent = fluidStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
2906
                if (typedComponent == null) {
2✔
2907
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
2908
                }
2909

2910
                // Encode component value
2911
                try {
2912
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
2913
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
2914
                } catch (IllegalStateException e) {
×
2915
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
2916
                }
2917
            }).build());
1✔
2918

2919
    /**
2920
     * Set the data component value by key
2921
     */
2922
    public static final IOperator OBJECT_FLUIDSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_3
18✔
2923
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2924
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2925
            .function(input -> {
1✔
2926
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2927

2928
                // Skip further processing if input value is empty
2929
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2930
                if (!tagOptional.isPresent()) {
3!
2931
                    return fluidStack;
×
2932
                }
2933

2934
                // Determine data component type
2935
                DataComponentType<?> dataComponentType;
2936
                try {
2937
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2938
                } catch (IdentifierException e) {
×
2939
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2940
                }
1✔
2941

2942
                // Encode component value
2943
                try {
2944
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2945
                    fluidStack = ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack.getRawValue().copy());
5✔
2946
                    fluidStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2947
                    return fluidStack;
2✔
2948
                } catch (IllegalStateException e) {
×
2949
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2950
                }
2951
            }).build());
1✔
2952

2953
    /**
2954
     * The tag entries of the given fluidstack
2955
     */
2956
    public static final IOperator OBJECT_FLUIDSTACK_TAG = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2957
            .output(ValueTypes.LIST)
2✔
2958
            .symbol("fluid_tag_names").operatorName("tag").interactName("tags")
6✔
2959
            .function(variables -> {
1✔
2960
                ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2961
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
2962
                if(!a.getRawValue().isEmpty()) {
4✔
2963
                    a.getRawValue().getFluid().builtInRegistryHolder().tags()
7✔
2964
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
2965
                                    .of(owningTag.location().toString())));
3✔
2966
                }
2967
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
2968
            }).build());
1✔
2969

2970
    /**
2971
     * Get a list of fluidstacks that correspond to the given tag key.
2972
     */
2973
    public static final IOperator OBJECT_FLUIDSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
2974
            .output(ValueTypes.LIST)
2✔
2975
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2976
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2977
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
2978
            .function(variables -> {
1✔
2979
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
2980
                ImmutableList.Builder<ValueObjectTypeFluidStack.ValueFluidStack> builder = ImmutableList.builder();
2✔
2981
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
2982
                    try {
2983
                        Helpers.getFluidTagValues(a.getRawValue())
4✔
2984
                                .map(ValueObjectTypeFluidStack.ValueFluidStack::of)
3✔
2985
                                .forEach(builder::add);
4✔
2986
                    } catch (IdentifierException e) {
×
2987
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
2988
                    }
1✔
2989
                }
2990
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, builder.build());
5✔
2991
            }).build());
1✔
2992

2993
    /**
2994
     * Get an fluid by name.
2995
     */
2996
    public static final IOperator OBJECT_FLUIDSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_PREFIX_LONG
5✔
2997
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_FLUIDSTACK)
4✔
2998
            .symbol("fluid_by_name").operatorName("fluidbyname").interactName("fluidByName")
7✔
2999
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
3000
                    .build(input -> {
1✔
3001
                        Fluid fluid = BuiltInRegistries.FLUID.getValue(input);
5✔
3002
                        FluidStack fluidStack = FluidStack.EMPTY;
2✔
3003
                        if (fluid != null) {
2!
3004
                            fluidStack = new FluidStack(fluid, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());
8✔
3005
                        }
3006
                        return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
3007
                    })).build());
1✔
3008

3009
    /**
3010
     * ----------------------------------- OPERATOR OPERATORS -----------------------------------
3011
     */
3012

3013
    /**
3014
     * Apply for a given operator a given value.
3015
     */
3016
    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
3017
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
3018
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply")
9✔
3019
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(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 variable = variables.getVariables()[0];
5✔
3025
                        return ValueHelpers.evaluateOperator(innerOperator, variable);
9✔
3026
                    })).build());
1✔
3027
    static {
3028
        REGISTRY.registerSerializer(new CurriedOperator.Serializer());
5✔
3029
    }
3030

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

3049
    /**
3050
     * Apply for a given operator the given 3 values.
3051
     */
3052
    public static final IOperator OPERATOR_APPLY_3 = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
3053
            .renderPattern(IConfigRenderPattern.INFIX_3)
2✔
3054
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
19✔
3055
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
3056
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply3")
17✔
3057
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
3058
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3059
                    input -> {
3060
                        IOperator innerOperator = input.getLeft();
4✔
3061
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3062
                        IVariable variable0 = variables.getVariables()[0];
5✔
3063
                        IVariable variable1 = variables.getVariables()[1];
5✔
3064
                        IVariable variable2 = variables.getVariables()[2];
5✔
3065
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1, variable2);
17✔
3066
                    })).build());
1✔
3067

3068
    /**
3069
     * Apply for a given operator the given list of values.
3070
     */
3071
    public static final IOperator OPERATOR_APPLY_N = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
3072
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST)
11✔
3073
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.LIST)
2✔
3074
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply_n")
9✔
3075
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.LIST))
4✔
3076
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3077
                    input -> {
3078
                        IOperator innerOperator = input.getLeft();
4✔
3079
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3080
                        IValueTypeListProxy<IValueType<IValue>, IValue> list = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
3081
                        return ValueHelpers.evaluateOperator(innerOperator, Iterables.toArray(list, IValue.class));
7✔
3082
                    })).build());
1✔
3083

3084
    /**
3085
     * Apply for a given operator with zero arguments.
3086
     */
3087
    public static final IOperator OPERATOR_APPLY_0 = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3088
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
3089
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply0")
5✔
3090
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(new IValueType[0]))
4✔
3091
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3092
                    input -> {
3093
                        IOperator innerOperator = input.getLeft();
4✔
3094
                        return ValueHelpers.evaluateOperator(innerOperator, new IVariable[0]);
5✔
3095
                    })).build());
1✔
3096

3097
    /**
3098
     * Apply the given operator on all elements of a list, resulting in a new list of mapped values.
3099
     */
3100
    public static final IOperator OPERATOR_MAP = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3101
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3102
            .output(ValueTypes.LIST).symbolOperatorInteract("map")
5✔
3103
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3104
                    input -> {
3105
                        final IOperator innerOperator = input.getLeft();
4✔
3106
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3107
                        ValueTypeList.ValueList inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3108
                        return ValueTypeList.ValueList.ofFactory(
6✔
3109
                                new ValueTypeListProxyOperatorMapped(innerOperator, inputList.getRawValue()));
2✔
3110
                    })).build());
1✔
3111

3112
    /**
3113
     * Filter a list of elements by matching them all with the given predicate.
3114
     */
3115
    public static final IOperator OPERATOR_FILTER = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3116
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3117
            .output(ValueTypes.LIST).symbolOperatorInteract("filter")
7✔
3118
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3119
                    new IOperatorValuePropagator<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue>() {
3✔
3120
                        @Override
3121
                        public IValue getOutput(Pair<IOperator, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3122
                            final IOperator innerOperator = input.getLeft();
4✔
3123
                            OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3124
                            ValueTypeList.ValueList<?, ?> inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3125
                            if (inputList.getRawValue().isInfinite()) {
4!
3126
                                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
3127
                                        OPERATOR_FILTER.getLocalizedNameFull()));
×
3128
                            }
3129
                            List<IValue> filtered = Lists.newArrayList();
2✔
3130
                            for (IValue value : inputList.getRawValue()) {
11✔
3131
                                IValue result = ValueHelpers.evaluateOperator(innerOperator, value);
9✔
3132
                                ValueHelpers.validatePredicateOutput(innerOperator, result);
3✔
3133
                                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
3134
                                    filtered.add(value);
4✔
3135
                                }
3136
                            }
1✔
3137
                            IValueType valueType = inputList.getRawValue().getValueType();
4✔
3138
                            return ValueTypeList.ValueList.ofList(valueType, filtered);
4✔
3139
                        }
3140
                    })).build());
1✔
3141

3142
    /**
3143
     * Takes the conjunction of two predicates.
3144
     */
3145
    public static final IOperator OPERATOR_CONJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3146
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3147
            .output(ValueTypes.OPERATOR).symbol(".&&.").operatorInteract("conjunction")
7✔
3148
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3149
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Conjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3150
            )).build());
1✔
3151
    static {
3152
        REGISTRY.registerSerializer(new CombinedOperator.Conjunction.Serializer());
5✔
3153
    }
3154

3155
    /**
3156
     * Takes the disjunction of two predicates.
3157
     */
3158
    public static final IOperator OPERATOR_DISJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3159
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3160
            .output(ValueTypes.OPERATOR).symbol(".||.").operatorInteract("disjunction")
7✔
3161
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3162
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Disjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3163
            )).build());
1✔
3164
    static {
3165
        REGISTRY.registerSerializer(new CombinedOperator.Disjunction.Serializer());
5✔
3166
    }
3167

3168
    /**
3169
     * Takes the negation of a predicate.
3170
     */
3171
    public static final IOperator OPERATOR_NEGATION = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3172
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3173
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3174
            .output(ValueTypes.OPERATOR).symbol("!.").operatorInteract("negation")
7✔
3175
            .function(OperatorBuilders.FUNCTION_ONE_PREDICATE.build(
2✔
3176
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Negation.asOperator(input))
4✔
3177
            )).build());
1✔
3178
    static {
3179
        REGISTRY.registerSerializer(new CombinedOperator.Negation.Serializer());
5✔
3180
    }
3181

3182
    /**
3183
     * Create a new operator that pipes the output from the first operator to the second operator.
3184
     */
3185
    public static final IOperator OPERATOR_PIPE = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3186
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3187
            .output(ValueTypes.OPERATOR).symbol(".").operatorInteract("pipe")
7✔
3188
            .function(OperatorBuilders.FUNCTION_TWO_OPERATORS.build(
2✔
3189
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe.asOperator(input.getLeft(), input.getRight()))
17✔
3190
            )).build());
1✔
3191
    static {
3192
        REGISTRY.registerSerializer(new CombinedOperator.Pipe.Serializer());
5✔
3193
    }
3194

3195
    /**
3196
     * 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.
3197
     */
3198
    public static final IOperator OPERATOR_PIPE2 = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3199
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3200
            .renderPattern(IConfigRenderPattern.INFIX_2_LATE)
2✔
3201
            .output(ValueTypes.OPERATOR).symbol(".2").operatorInteract("pipe2")
7✔
3202
            .function(OperatorBuilders.FUNCTION_THREE_OPERATORS.build(
2✔
3203
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe2.asOperator(input.getLeft(), input.getMiddle(), input.getRight()))
23✔
3204
            )).build());
1✔
3205
    static {
3206
        REGISTRY.registerSerializer(new CombinedOperator.Pipe2.Serializer());
5✔
3207
    }
3208

3209
    /**
3210
     * Flip the input parameters of an operator with two inputs.
3211
     */
3212
    public static final IOperator OPERATOR_FLIP = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3213
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3214
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3215
            .output(ValueTypes.OPERATOR).symbolOperatorInteract("flip")
5✔
3216
            .function(OperatorBuilders.FUNCTION_ONE_OPERATOR.build(
2✔
3217
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Flip.asOperator(input))
4✔
3218
            )).build());
1✔
3219
    static {
3220
        REGISTRY.registerSerializer(new CombinedOperator.Flip.Serializer());
5✔
3221
    }
3222

3223
    /**
3224
     * Apply the given operator on all elements of a list to reduce the list to one value.
3225
     */
3226
    public static final IOperator OPERATOR_REDUCE = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3227
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
3228
            .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
2✔
3229
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce")
4✔
3230
            .conditionalOutputTypeDeriver((operator, input) -> input[2].getType())
9✔
3231
            .function(new OperatorBase.IFunction() {
4✔
3232
                @Override
3233
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3234
                    IValue accumulator = variables.getValue(2);
4✔
3235
                    final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3236
                            variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3237
                    ValueTypeList.ValueList<IValueType<IValue>, IValue> inputList = variables.getValue(1, ValueTypes.LIST);
6✔
3238
                    if (inputList.getRawValue().isInfinite()) {
4!
3239
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
3240
                                OPERATOR_REDUCE.getLocalizedNameFull()));
×
3241
                    }
3242
                    for (IValue listValue : inputList.getRawValue()) {
11✔
3243
                        accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3244
                    }
1✔
3245
                    return accumulator;
2✔
3246
                }
3247
            }).build());
1✔
3248

3249
    /**
3250
     * Apply the given operator on all elements of a list to reduce the list to one value.
3251
     */
3252
    public static final IOperator OPERATOR_REDUCE1 = REGISTRY.register(OperatorBuilders.OPERATOR
14✔
3253
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3254
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
3255
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce1")
4✔
3256
            .conditionalOutputTypeDeriver((operator, input) -> {
4✔
3257
                try {
3258
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[1].getValue()).getRawValue();
7✔
3259
                    return a.getValueType();
3✔
3260
                } catch (EvaluationException e) {
×
3261
                    return operator.getOutputType();
×
3262
                }
3263
            })
3264
            .function(new OperatorBase.IFunction() {
4✔
3265
                @Override
3266
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3267
                    ValueTypeList.ValueList valueList = variables.getValue(1, ValueTypes.LIST);
6✔
3268
                    if (valueList.getRawValue().isInfinite()) {
4!
3269
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
3270
                                OPERATOR_REDUCE1.getLocalizedNameFull()));
×
3271
                    }
3272
                    Iterator<IValue> iter = valueList.getRawValue().iterator();
4✔
3273
                    if (!iter.hasNext()) {
3✔
3274
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REDUCE_EMPTY));
6✔
3275
                    }
3276

3277
                    IValue accumulator = iter.next();
4✔
3278
                    final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3279
                            variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3280

3281
                    while (iter.hasNext()) {
3✔
3282
                        IValue listValue = iter.next();
4✔
3283
                        accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3284
                    }
1✔
3285
                    return accumulator;
2✔
3286
                }
3287
            }).build());
1✔
3288

3289
    /**
3290
     * Apply for a given operator a given value.
3291
     */
3292
    public static final IOperator OPERATOR_BY_NAME = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3293
            .inputType(ValueTypes.STRING).output(ValueTypes.OPERATOR)
4✔
3294
            .symbol("op_by_name").operatorName("by_name").interactName("operatorByName")
6✔
3295
            .function(input -> {
1✔
3296
                ValueTypeString.ValueString name = input.getValue(0, ValueTypes.STRING);
6✔
3297
                try {
3298
                    Identifier id = Identifier.parse(name.getRawValue());
4✔
3299
                    IOperator operator = Operators.REGISTRY.getOperator(id);
4✔
3300
                    if (operator == null) {
2✔
3301
                        throw new EvaluationException(Component.translatable(
11✔
3302
                                L10NValues.OPERATOR_ERROR_OPERATORNOTFOUND, name.getRawValue()));
2✔
3303
                    }
3304
                    return ValueTypeOperator.ValueOperator.of(operator);
3✔
3305
                } catch (IdentifierException e) {
×
3306
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
3307
                }
3308
            }).build());
1✔
3309

3310
    /**
3311
     * ----------------------------------- NBT OPERATORS -----------------------------------
3312
     */
3313

3314
    /**
3315
     * The number of entries in an NBT tag
3316
     */
3317
    public static final IOperator NBT_COMPOUND_SIZE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3318
            .output(ValueTypes.INTEGER).operatorName("compound_size").symbol("NBT{}.size").interactName("size")
9✔
3319
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_TO_INT.build(
2✔
3320
                opt -> opt.map(CompoundTag::size).orElse(0)
8✔
3321
            )).build());
1✔
3322

3323
    /**
3324
     * The list of keys in an NBT tag
3325
     */
3326
    public static final IOperator NBT_COMPOUND_KEYS = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3327
            .output(ValueTypes.LIST).operatorName("compound_keys").symbol("NBT{}.keys").interactName("keys")
8✔
3328
            .function(variables -> {
1✔
3329
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3330
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtKeys(value.getRawValue()));
7✔
3331
            }).build());
1✔
3332

3333
    /**
3334
     * If an NBT tag has the given key
3335
     */
3336
    public static final IOperator NBT_COMPOUND_HASKEY = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3337
            .output(ValueTypes.BOOLEAN).operatorName("compound_haskey").symbol("NBT{}.has_key").interactName("hasKey")
9✔
3338
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3339
                    Optional::isPresent
3340
            )).build());
1✔
3341

3342
    /**
3343
     * The NBT value type of an entry
3344
     */
3345
    public static final IOperator NBT_COMPOUND_VALUE_TYPE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3346
            .output(ValueTypes.STRING).operatorName("compound_type").symbol("NBT{}.type").interactName("type")
9✔
3347
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(tag -> {
2✔
3348
                if (tag.isPresent()) {
3✔
3349
                    try {
3350
                        return TagTypes.getType(tag.get().getId()).getName();
7✔
3351
                    } catch (IndexOutOfBoundsException e) {
×
3352

3353
                    }
3354
                }
3355
                return "null";
2✔
3356
            })).build());
1✔
3357

3358
    /**
3359
     * The NBT tag value
3360
     */
3361
    public static final IOperator NBT_COMPOUND_VALUE_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3362
            .output(ValueTypes.NBT).operatorName("compound_value_tag").symbol("NBT{}.get_tag").interactName("getTag")
9✔
3363
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(o -> o)).build());
5✔
3364

3365
    /**
3366
     * The NBT boolean value
3367
     */
3368
    public static final IOperator NBT_COMPOUND_VALUE_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3369
            .output(ValueTypes.BOOLEAN).operatorName("compound_value_boolean").symbol("NBT{}.get_boolean").interactName("getBoolean")
9✔
3370
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3371
                    o -> o.flatMap(Tag::asBoolean).orElse(false)
8✔
3372
            )).build());
1✔
3373

3374
    /**
3375
     * The NBT integer value
3376
     */
3377
    public static final IOperator NBT_COMPOUND_VALUE_INTEGER = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3378
            .output(ValueTypes.INTEGER).operatorName("compound_value_integer").symbol("NBT{}.get_integer").interactName("getInteger")
9✔
3379
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_INT.build(
2✔
3380
                    o -> o.flatMap(Tag::asInt).orElse(0)
8✔
3381
            )).build());
1✔
3382

3383
    /**
3384
     * The NBT long value
3385
     */
3386
    public static final IOperator NBT_COMPOUND_VALUE_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3387
            .output(ValueTypes.LONG).operatorName("compound_value_long").symbol("NBT{}.get_long").interactName("getLong")
9✔
3388
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG.build(
2✔
3389
                    o -> o.flatMap(Tag::asLong).orElse(0L)
8✔
3390
            )).build());
1✔
3391

3392
    /**
3393
     * The NBT double value
3394
     */
3395
    public static final IOperator NBT_COMPOUND_VALUE_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3396
            .output(ValueTypes.DOUBLE).operatorName("compound_value_double").symbol("NBT{}.get_double").interactName("getDouble")
9✔
3397
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE.build(
2✔
3398
                    o -> o.flatMap(Tag::asDouble).orElse(0D)
8✔
3399
            )).build());
1✔
3400

3401
    /**
3402
     * The NBT string value
3403
     */
3404
    public static final IOperator NBT_COMPOUND_VALUE_STRING = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3405
            .output(ValueTypes.STRING).operatorName("compound_value_string").symbol("NBT{}.get_string").interactName("getString")
9✔
3406
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(
2✔
3407
                    o -> o.flatMap(Tag::asString).orElse("")
7✔
3408
            )).build());
1✔
3409

3410
    /**
3411
     * The NBT compound value
3412
     */
3413
    public static final IOperator NBT_COMPOUND_VALUE_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3414
            .output(ValueTypes.NBT).operatorName("compound_value_compound").symbol("NBT{}.get_compound").interactName("getCompound")
9✔
3415
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(
2✔
3416
                    o -> o.map(tag -> tag instanceof CompoundTag ? (CompoundTag) tag : new CompoundTag())
11!
3417
            )).build());
1✔
3418

3419
    /**
3420
     * The NBT tag list value
3421
     */
3422
    public static final IOperator NBT_COMPOUND_VALUE_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3423
            .output(ValueTypes.LIST).operatorName("compound_value_list_tag").symbol("NBT{}.get_list_tag").interactName("getListTag")
8✔
3424
            .function(variables -> {
1✔
3425
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3426
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3427
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListTag(key.getRawValue(), value.getRawValue()));
9✔
3428
            }).build());
1✔
3429

3430
    /**
3431
     * The NBT boolean list value
3432
     */
3433
    public static final IOperator NBT_COMPOUND_VALUE_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3434
            .output(ValueTypes.LIST).operatorName("compound_value_list_byte").symbol("NBT{}.get_list_byte").interactName("getListByte")
8✔
3435
            .function(variables -> {
1✔
3436
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3437
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3438
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListByte(key.getRawValue(), value.getRawValue()));
9✔
3439
            }).build());
1✔
3440

3441
    /**
3442
     * The NBT int list value
3443
     */
3444
    public static final IOperator NBT_COMPOUND_VALUE_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3445
            .output(ValueTypes.LIST).operatorName("compound_value_list_int").symbol("NBT{}.get_list_int").interactName("getListInt")
8✔
3446
            .function(variables -> {
1✔
3447
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3448
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3449
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListInt(key.getRawValue(), value.getRawValue()));
9✔
3450
            }).build());
1✔
3451

3452
    /**
3453
     * The NBT long list value
3454
     */
3455
    public static final IOperator NBT_COMPOUND_VALUE_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3456
            .output(ValueTypes.LIST).operatorName("compound_value_list_long").symbol("NBT{}.get_list_long").interactName("getListLong")
8✔
3457
            .function(variables -> {
1✔
3458
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3459
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3460
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListLong(key.getRawValue(), value.getRawValue()));
9✔
3461
            }).build());
1✔
3462

3463
    /**
3464
     * Remove an entry from an NBT compound
3465
     */
3466
    public static final IOperator NBT_COMPOUND_WITHOUT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3467
            .output(ValueTypes.NBT).operatorName("compound_without").symbol("NBT{}.without").interactName("without")
8✔
3468
            .function(variables -> {
1✔
3469
                ValueTypeNbt.ValueNbt valueNbt = variables.getValue(0, ValueTypes.NBT);
6✔
3470
                Optional<Tag> tag = valueNbt.getRawValue();
3✔
3471
                if (tag.isPresent()) {
3!
3472
                    if (!(tag.get() instanceof CompoundTag)) {
4!
3473
                        return ValueTypeNbt.ValueNbt.of();
×
3474
                    }
3475
                    ValueTypeString.ValueString valueString = variables.getValue(1, ValueTypes.STRING);
6✔
3476
                    String key = valueString.getRawValue();
3✔
3477
                    CompoundTag tagCompound = (CompoundTag) tag.get();
4✔
3478
                    if (tagCompound.contains(key)) {
4!
3479
                        // Copy the tag to ensure immutability
3480
                        tagCompound = tagCompound.copy();
3✔
3481
                        tagCompound.remove(key);
4✔
3482
                    }
3483
                    return ValueTypeNbt.ValueNbt.of(tagCompound);
3✔
3484
                }
3485
                return valueNbt;
×
3486
            }).build());
1✔
3487

3488

3489

3490
    /**
3491
     * Set an NBT compound boolean value
3492
     */
3493
    public static final IOperator NBT_COMPOUND_WITH_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3494
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3495
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.BOOLEAN)
2✔
3496
            .operatorName("compound_with_boolean").symbol("NBT{}.with_boolean").interactName("withBoolean")
7✔
3497
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3498
                ValueTypeBoolean.ValueBoolean value = input.getRight().getValue(0, ValueTypes.BOOLEAN);
8✔
3499
                input.getLeft().ifPresent(tag -> tag.putBoolean(input.getMiddle(), value.getRawValue()));
15✔
3500
                return input.getLeft();
4✔
3501
            })).build());
1✔
3502

3503
    /**
3504
     * Set an NBT compound short value
3505
     */
3506
    public static final IOperator NBT_COMPOUND_WITH_SHORT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3507
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3508
            .operatorName("compound_with_short").symbol("NBT{}.with_short").interactName("withShort")
7✔
3509
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3510
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3511
                input.getLeft().ifPresent(tag -> tag.putShort(input.getMiddle(), (short) value.getRawValue()));
16✔
3512
                return input.getLeft();
4✔
3513
            })).build());
1✔
3514

3515
    /**
3516
     * Set an NBT compound integer value
3517
     */
3518
    public static final IOperator NBT_COMPOUND_WITH_INTEGER = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3519
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3520
            .operatorName("compound_with_integer").symbol("NBT{}.with_integer").interactName("withInteger")
7✔
3521
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3522
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3523
                input.getLeft().ifPresent(tag -> tag.putInt(input.getMiddle(), value.getRawValue()));
15✔
3524
                return input.getLeft();
4✔
3525
            })).build());
1✔
3526

3527
    /**
3528
     * Set an NBT compound long value
3529
     */
3530
    public static final IOperator NBT_COMPOUND_WITH_LONG = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3531
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LONG)
2✔
3532
            .operatorName("compound_with_long").symbol("NBT{}.with_long").interactName("withLong")
7✔
3533
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3534
                ValueTypeLong.ValueLong value = input.getRight().getValue(0, ValueTypes.LONG);
8✔
3535
                input.getLeft().ifPresent(tag -> tag.putLong(input.getMiddle(), value.getRawValue()));
15✔
3536
                return input.getLeft();
4✔
3537
            })).build());
1✔
3538

3539
    /**
3540
     * Set an NBT compound double value
3541
     */
3542
    public static final IOperator NBT_COMPOUND_WITH_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3543
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3544
            .operatorName("compound_with_double").symbol("NBT{}.with_double").interactName("withDouble")
7✔
3545
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3546
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3547
                input.getLeft().ifPresent(tag -> tag.putDouble(input.getMiddle(), value.getRawValue()));
15✔
3548
                return input.getLeft();
4✔
3549
            })).build());
1✔
3550

3551
    /**
3552
     * Set an NBT compound float value
3553
     */
3554
    public static final IOperator NBT_COMPOUND_WITH_FLOAT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3555
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3556
            .operatorName("compound_with_float").symbol("NBT{}.with_float").interactName("withFloat")
7✔
3557
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3558
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3559
                input.getLeft().ifPresent(tag -> tag.putFloat(input.getMiddle(), (float) value.getRawValue()));
16✔
3560
                return input.getLeft();
4✔
3561
            })).build());
1✔
3562

3563
    /**
3564
     * Set an NBT compound string value
3565
     */
3566
    public static final IOperator NBT_COMPOUND_WITH_STRING = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3567
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING)
2✔
3568
            .operatorName("compound_with_string").symbol("NBT{}.with_string").interactName("withString")
7✔
3569
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3570
                ValueTypeString.ValueString value = input.getRight().getValue(0, ValueTypes.STRING);
8✔
3571
                input.getLeft().ifPresent(tag -> tag.putString(input.getMiddle(), value.getRawValue()));
15✔
3572
                return input.getLeft();
4✔
3573
            })).build());
1✔
3574

3575
    /**
3576
     * Set an NBT compound compound value
3577
     */
3578
    public static final IOperator NBT_COMPOUND_WITH_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3579
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.NBT)
2✔
3580
            .operatorName("compound_with_tag").symbol("NBT{}.with_tag").interactName("withTag")
7✔
3581
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3582
                ValueTypeNbt.ValueNbt value = input.getRight().getValue(0, ValueTypes.NBT);
8✔
3583
                input.getLeft()
6✔
3584
                        .ifPresent(tag -> value.getRawValue()
7✔
3585
                                .ifPresent(v -> tag.put(input.getMiddle(), v)));
9✔
3586
                return input.getLeft();
4✔
3587
            })).build());
1✔
3588

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

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

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

3640
    /**
3641
     * Set an NBT compound long list value
3642
     */
3643
    public static final IOperator NBT_COMPOUND_WITH_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3644
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3645
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3646
            .operatorName("compound_with_list_long").symbol("NBT{}.with_list_long").interactName("withListLong")
9✔
3647
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3648
                @Override
3649
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3650
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3651
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3652
                            NbtHelpers.getListNbtLong(value, NBT_COMPOUND_WITH_LIST_LONG.getLocalizedNameFull())));
2✔
3653
                    return input.getLeft();
4✔
3654
                }
3655
            })).build());
1✔
3656

3657
    /**
3658
     * Check if the first NBT compound tag is a subset of the second NBT compound tag.
3659
     */
3660
    public static final IOperator NBT_COMPOUND_SUBSET = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3661
            .output(ValueTypes.BOOLEAN).operatorName("compound_subset").symbol("NBT{}.⊆").interactName("isSubset")
8✔
3662
            .function(variables -> {
1✔
3663
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3664
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3665
                if (valueNbt0.getRawValue().isPresent()
5!
3666
                        && valueNbt1.getRawValue().isPresent()
4!
3667
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3668
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3669
                    return ValueTypeBoolean.ValueBoolean.of(NbtHelpers.nbtMatchesSubset((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get(), true));
12✔
3670
                }
3671
                return ValueTypeBoolean.ValueBoolean.of(false);
×
3672
            }).build());
1✔
3673

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

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

3708
    /**
3709
     * The difference of the given NBT compound tags. Nested tags will be subtracted recusively.
3710
     */
3711
    public static final IOperator NBT_COMPOUND_MINUS = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3712
            .output(ValueTypes.NBT).operatorName("compound_minus").symbol("NBT{}.∖").interactName("minus")
8✔
3713
            .function(variables -> {
1✔
3714
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3715
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3716
                if (valueNbt0.getRawValue().isPresent()
5!
3717
                        && valueNbt1.getRawValue().isPresent()
4!
3718
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3719
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3720
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.minus((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
11✔
3721
                }
3722
                return ValueTypeNbt.ValueNbt.of();
×
3723
            }).build());
1✔
3724

3725
    /**
3726
     * The boolean value of an NBT value
3727
     */
3728
    public static final IOperator NBT_AS_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3729
            .output(ValueTypes.BOOLEAN).operatorName("as_boolean").symbol("NBT.as_boolean").interactName("asBoolean")
9✔
3730
            .function(OperatorBuilders.FUNCTION_NBT_TO_BOOLEAN.build(
2✔
3731
                    o -> o.flatMap(Tag::asBoolean).orElse(false)
8✔
3732
            )).build());
1✔
3733

3734
    /**
3735
     * The byte value of an NBT value
3736
     */
3737
    public static final IOperator NBT_AS_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3738
            .output(ValueTypes.INTEGER).operatorName("as_byte").symbol("NBT.as_byte").interactName("asByte")
9✔
3739
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3740
                    o -> o.flatMap(Tag::asByte).map(s -> (int) s).orElse(0)
14✔
3741
            )).build());
1✔
3742

3743
    /**
3744
     * The short value of an NBT value
3745
     */
3746
    public static final IOperator NBT_AS_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3747
            .output(ValueTypes.INTEGER).operatorName("as_short").symbol("NBT.as_short").interactName("asShort")
9✔
3748
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3749
                    o -> o.flatMap(Tag::asShort).map(s -> (int) s).orElse(0)
14✔
3750
            )).build());
1✔
3751

3752
    /**
3753
     * The int value of an NBT value
3754
     */
3755
    public static final IOperator NBT_AS_INT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3756
            .output(ValueTypes.INTEGER).operatorName("as_int").symbol("NBT.as_int").interactName("asInt")
9✔
3757
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3758
                    o -> o.flatMap(Tag::asInt).orElse(0)
8✔
3759
            )).build());
1✔
3760

3761
    /**
3762
     * The long value of an NBT value
3763
     */
3764
    public static final IOperator NBT_AS_LONG = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3765
            .output(ValueTypes.LONG).operatorName("as_long").symbol("NBT.as_long").interactName("asLong")
9✔
3766
            .function(OperatorBuilders.FUNCTION_NBT_TO_LONG.build(
2✔
3767
                    o -> o.flatMap(Tag::asLong).orElse(0L)
8✔
3768
            )).build());
1✔
3769

3770
    /**
3771
     * The double value of an NBT value
3772
     */
3773
    public static final IOperator NBT_AS_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3774
            .output(ValueTypes.DOUBLE).operatorName("as_double").symbol("NBT.as_double").interactName("asDouble")
9✔
3775
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3776
                    o -> o.flatMap(Tag::asDouble).orElse(0D)
8✔
3777
            )).build());
1✔
3778

3779
    /**
3780
     * The float value of an NBT value
3781
     */
3782
    public static final IOperator NBT_AS_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3783
            .output(ValueTypes.DOUBLE).operatorName("as_float").symbol("NBT.as_float").interactName("asFloat")
9✔
3784
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3785
                    o -> o.flatMap(Tag::asFloat).map(f -> (double) f).orElse(0D)
15✔
3786
            )).build());
1✔
3787

3788
    /**
3789
     * The string value of an NBT value
3790
     */
3791
    public static final IOperator NBT_AS_STRING = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3792
            .output(ValueTypes.STRING).operatorName("as_string").symbol("NBT.as_string").interactName("asString")
9✔
3793
            .function(OperatorBuilders.FUNCTION_NBT_TO_STRING.build(
2✔
3794
                    o -> o.flatMap(Tag::asString).orElse("")
7✔
3795
            )).build());
1✔
3796

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

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

3817
    /**
3818
     * The int list value of an NBT value
3819
     */
3820
    public static final IOperator NBT_AS_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3821
            .output(ValueTypes.LIST).operatorName("as_int_list").symbol("NBT.as_int_list").interactName("asIntList")
8✔
3822
            .function(variables -> {
1✔
3823
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3824
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListInt(value.getRawValue()));
7✔
3825
            }).build());
1✔
3826

3827
    /**
3828
     * The long list value of an NBT value
3829
     */
3830
    public static final IOperator NBT_AS_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3831
            .output(ValueTypes.LIST).operatorName("as_long_list").symbol("NBT.as_long_list").interactName("asLongList")
8✔
3832
            .function(variables -> {
1✔
3833
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3834
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListLong(value.getRawValue()));
7✔
3835
            }).build());
1✔
3836

3837
    /**
3838
     * The NBT value of a boolean value
3839
     */
3840
    public static final IOperator NBT_FROM_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3841
            .inputType(ValueTypes.BOOLEAN).output(ValueTypes.NBT)
4✔
3842
            .operatorName("from_boolean").symbol("NBT.from_boolean").interactName("asNbt")
6✔
3843
            .function(variables -> {
1✔
3844
                ValueTypeBoolean.ValueBoolean value = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
3845
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf(value.getRawValue()));
5✔
3846
            }).build());
1✔
3847

3848
    /**
3849
     * The NBT value of a short value
3850
     */
3851
    public static final IOperator NBT_FROM_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3852
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3853
            .operatorName("from_short").symbol("NBT.from_short").interactName("asNbt", "short", true)
8✔
3854
            .function(variables -> {
1✔
3855
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3856
                return ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) value.getRawValue()));
6✔
3857
            }).build());
1✔
3858

3859
    /**
3860
     * The NBT value of a byte value
3861
     */
3862
    public static final IOperator NBT_FROM_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3863
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3864
            .operatorName("from_byte").symbol("NBT.from_byte").interactName("asNbt", "byte", true)
8✔
3865
            .function(variables -> {
1✔
3866
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3867
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf((byte) value.getRawValue()));
6✔
3868
            }).build());
1✔
3869

3870
    /**
3871
     * The NBT value of an int value
3872
     */
3873
    public static final IOperator NBT_FROM_INT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3874
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3875
            .operatorName("from_int").symbol("NBT.from_int").interactName("asNbt")
6✔
3876
            .function(variables -> {
1✔
3877
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3878
                return ValueTypeNbt.ValueNbt.of(IntTag.valueOf(value.getRawValue()));
5✔
3879
            }).build());
1✔
3880

3881
    /**
3882
     * The NBT value of a long value
3883
     */
3884
    public static final IOperator NBT_FROM_LONG = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3885
            .inputType(ValueTypes.LONG).output(ValueTypes.NBT)
4✔
3886
            .operatorName("from_long").symbol("NBT.from_long").interactName("asNbt")
6✔
3887
            .function(variables -> {
1✔
3888
                ValueTypeLong.ValueLong value = variables.getValue(0, ValueTypes.LONG);
6✔
3889
                return ValueTypeNbt.ValueNbt.of(LongTag.valueOf(value.getRawValue()));
5✔
3890
            }).build());
1✔
3891

3892
    /**
3893
     * The NBT value of a double value
3894
     */
3895
    public static final IOperator NBT_FROM_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3896
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3897
            .operatorName("from_double").symbol("NBT.from_double").interactName("asNbt")
6✔
3898
            .function(variables -> {
1✔
3899
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3900
                return ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(value.getRawValue()));
5✔
3901
            }).build());
1✔
3902

3903
    /**
3904
     * The NBT value of a float value
3905
     */
3906
    public static final IOperator NBT_FROM_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3907
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3908
            .operatorName("from_float").symbol("NBT.from_float").interactName("asNbt", "float", true)
8✔
3909
            .function(variables -> {
1✔
3910
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3911
                return ValueTypeNbt.ValueNbt.of(FloatTag.valueOf((float) value.getRawValue()));
6✔
3912
            }).build());
1✔
3913

3914
    /**
3915
     * The NBT value of a string value
3916
     */
3917
    public static final IOperator NBT_FROM_STRING = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3918
            .inputType(ValueTypes.STRING).output(ValueTypes.NBT)
4✔
3919
            .operatorName("from_string").symbol("NBT.from_string").interactName("asNbt")
6✔
3920
            .function(variables -> {
1✔
3921
                ValueTypeString.ValueString value = variables.getValue(0, ValueTypes.STRING);
6✔
3922
                return ValueTypeNbt.ValueNbt.of(StringTag.valueOf(value.getRawValue()));
5✔
3923
            }).build());
1✔
3924

3925
    /**
3926
     * The NBT value of a tag list value
3927
     */
3928
    public static final IOperator NBT_FROM_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3929
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3930
            .operatorName("from_tag_list").symbol("NBT.from_tag_list").interactName("asNbt", "tagList", true)
10✔
3931
            .function(new OperatorBase.IFunction() {
4✔
3932
                @Override
3933
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3934
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3935
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtTag(value, NBT_FROM_TAG_LIST.getLocalizedNameFull()));
6✔
3936
                }
3937
            }).build());
1✔
3938

3939
    /**
3940
     * The NBT value of a byte list value
3941
     */
3942
    public static final IOperator NBT_FROM_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3943
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3944
            .operatorName("from_byte_list").symbol("NBT.from_byte_list").interactName("asNbt", "byteList", true)
10✔
3945
            .function(new OperatorBase.IFunction() {
4✔
3946
                @Override
3947
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3948
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3949
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtByte(value, NBT_FROM_BYTE_LIST.getLocalizedNameFull()));
6✔
3950
                }
3951
            }).build());
1✔
3952

3953
    /**
3954
     * The NBT value of a int list value
3955
     */
3956
    public static final IOperator NBT_FROM_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3957
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3958
            .operatorName("from_int_list").symbol("NBT.from_int_list").interactName("asNbt", "intList", true)
10✔
3959
            .function(new OperatorBase.IFunction() {
4✔
3960
                @Override
3961
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3962
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3963
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtInt(value, NBT_FROM_INT_LIST.getLocalizedNameFull()));
6✔
3964
                }
3965
            }).build());
1✔
3966

3967
    /**
3968
     * The NBT value of a long list value
3969
     */
3970
    public static final IOperator NBT_FROM_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3971
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3972
            .operatorName("from_long_list").symbol("NBT.from_long_list").interactName("asNbt", "longList", true)
10✔
3973
            .function(new OperatorBase.IFunction() {
4✔
3974
                @Override
3975
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3976
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3977
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtLong(value, NBT_FROM_LONG_LIST.getLocalizedNameFull()));
6✔
3978
                }
3979
            }).build());
1✔
3980

3981
    /**
3982
     * Apply the given NBT path expression on the given NBT value and get the first result.
3983
     */
3984
    public static final IOperator NBT_PATH_MATCH_FIRST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3985
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.NBT)
4✔
3986
            .operatorName("path_match_first").symbol("NBT.path_match_first").interactName("nbtPathMatchFirst")
6✔
3987
            .function(variables -> {
1✔
3988
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3989
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3990
                INbtPathExpression expression = null;
2✔
3991
                try {
3992
                    expression = NbtPath.parse(string.getRawValue());
4✔
3993
                } catch (NbtParseException e) {
×
3994
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3995
                            string.getRawValue(),
×
3996
                            e.getMessage()));
×
3997
                }
1✔
3998
                if (!nbt.getRawValue().isPresent()) {
4!
3999
                    return ValueTypeNbt.ValueNbt.of();
×
4000
                }
4001
                return ValueTypeNbt.ValueNbt.of(expression.match(nbt.getRawValue().get()).getMatches().findAny());
10✔
4002
            }).build());
1✔
4003

4004
    /**
4005
     * Apply the given NBT path expression on the given NBT value and get all results.
4006
     */
4007
    public static final IOperator NBT_PATH_MATCH_ALL = REGISTRY.register(OperatorBuilders.NBT_2
14✔
4008
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.LIST)
4✔
4009
            .operatorName("path_match_all").symbol("NBT.path_match_all").interactName("nbtPathMatchAll")
6✔
4010
            .function(variables -> {
1✔
4011
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
4012
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
4013
                INbtPathExpression expression = null;
2✔
4014
                try {
4015
                    expression = NbtPath.parse(string.getRawValue());
4✔
4016
                } catch (NbtParseException e) {
×
4017
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
4018
                            string.getRawValue(),
×
4019
                            e.getMessage()));
×
4020
                }
1✔
4021
                if (!nbt.getRawValue().isPresent()) {
4!
4022
                    return ValueTypeList.ValueList.ofAll(ValueTypes.NBT);
×
4023
                }
4024
                List<ValueTypeNbt.ValueNbt> matches = expression.match(nbt.getRawValue().get()).getMatches()
8✔
4025
                        .map(ValueTypeNbt.ValueNbt::of)
1✔
4026
                        .collect(Collectors.toList());
4✔
4027
                return ValueTypeList.ValueList.ofList(ValueTypes.NBT, matches);
4✔
4028
            }).build());
1✔
4029

4030
    /**
4031
     * Test the given NBT path expression on the given NBT value.
4032
     */
4033
    public static final IOperator NBT_PATH_TEST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
4034
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.BOOLEAN)
4✔
4035
            .operatorName("path_test").symbol("NBT.path_test").interactName("nbtPathTest")
6✔
4036
            .function(variables -> {
1✔
4037
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
4038
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
4039
                INbtPathExpression expression = null;
2✔
4040
                try {
4041
                    expression = NbtPath.parse(string.getRawValue());
4✔
4042
                } catch (NbtParseException e) {
×
4043
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
4044
                            string.getRawValue(),
×
4045
                            e.getMessage()));
×
4046
                }
1✔
4047
                if (!nbt.getRawValue().isPresent()) {
4!
4048
                    return ValueTypeBoolean.ValueBoolean.of(false);
×
4049
                }
4050
                return ValueTypeBoolean.ValueBoolean.of(expression.test(nbt.getRawValue().get()));
8✔
4051
            }).build());
1✔
4052

4053
    /**
4054
     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------
4055
     */
4056

4057
    /**
4058
     * The list of items.
4059
     */
4060
    public static final IOperator INGREDIENTS_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
4061
            .output(ValueTypes.LIST).operatorInteract("items").symbol("Ingr.items")
6✔
4062
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ITEMSTACK))
4✔
4063
            .build());
1✔
4064

4065
    /**
4066
     * The list of fluids
4067
     */
4068
    public static final IOperator INGREDIENTS_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
4069
            .output(ValueTypes.LIST).operatorInteract("fluids").symbol("Ingr.fluids")
6✔
4070
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.FLUIDSTACK))
4✔
4071
            .build());
1✔
4072

4073
    /**
4074
     * The list of fluids
4075
     */
4076
    public static final IOperator INGREDIENTS_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
4077
            .output(ValueTypes.LIST).operatorInteract("energies").symbol("Ingr.energies")
6✔
4078
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ENERGY))
4✔
4079
            .build());
1✔
4080

4081
    /**
4082
     * Set an ingredient item
4083
     */
4084
    public static final IOperator INGREDIENTS_WITH_ITEM = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_ITEMSTACK
5✔
4085
            .operatorName("with_item").symbol("Ingr.with_item").interactName("withItem")
6✔
4086
            .function(variables -> {
1✔
4087
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4088
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4089
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(2, ValueTypes.OBJECT_ITEMSTACK);
6✔
4090
                if (value.getRawValue().isEmpty()) {
4!
4091
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4092
                }
4093
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4094
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4095
                        index.getRawValue(), IngredientComponent.ITEMSTACK, itemStack.getRawValue()));
5✔
4096
            }).build());
1✔
4097

4098
    /**
4099
     * Set an ingredient fluid
4100
     */
4101
    public static final IOperator INGREDIENTS_WITH_FLUID = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_FLUIDSTACK
5✔
4102
            .operatorName("with_fluid").symbol("Ingr.with_fluid").interactName("withFluid")
6✔
4103
            .function(variables -> {
1✔
4104
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4105
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4106
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = variables.getValue(2, ValueTypes.OBJECT_FLUIDSTACK);
6✔
4107
                if (value.getRawValue().isEmpty()) {
4!
4108
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4109
                }
4110
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4111
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4112
                        index.getRawValue(), IngredientComponent.FLUIDSTACK, fluidStack.getRawValue()));
5✔
4113
            }).build());
1✔
4114

4115
    /**
4116
     * Set an ingredient energy
4117
     */
4118
    public static final IOperator INGREDIENTS_WITH_ENERGY = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_LONG
5✔
4119
            .operatorName("with_energy").symbol("Ingr.with_energy").interactName("withEnergy")
6✔
4120
            .function(variables -> {
1✔
4121
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4122
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4123
                ValueTypeLong.ValueLong energy = variables.getValue(2, ValueTypes.LONG);
6✔
4124
                if (value.getRawValue().isEmpty()) {
4!
4125
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4126
                }
4127
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4128
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4129
                        index.getRawValue(), IngredientComponent.ENERGY, energy.getRawValue()));
6✔
4130
            }).build());
1✔
4131

4132
    /**
4133
     * Set the list of items
4134
     */
4135
    public static final IOperator INGREDIENTS_WITH_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4136
            .operatorName("with_items").symbol("Ingr.with_items").interactName("withItems")
6✔
4137
            .function(variables -> {
1✔
4138
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4139
                ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4140
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4141
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4142
                }
4143
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4144
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4145
                        IngredientComponent.ITEMSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ITEMSTACK, list)));
2✔
4146
            }).build());
1✔
4147

4148
    /**
4149
     * Set the list of fluids
4150
     */
4151
    public static final IOperator INGREDIENTS_WITH_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4152
            .operatorName("with_fluids").symbol("Ingr.with_fluids").interactName("withFluids")
6✔
4153
            .function(variables -> {
1✔
4154
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4155
                ValueTypeList.ValueList<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4156
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4157
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4158
                }
4159
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4160
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4161
                        IngredientComponent.FLUIDSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.FLUIDSTACK, list)));
2✔
4162
            }).build());
1✔
4163

4164
    /**
4165
     * Set the list of energies
4166
     */
4167
    public static final IOperator INGREDIENTS_WITH_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4168
            .renderPattern(IConfigRenderPattern.INFIX_VERYLONG)
2✔
4169
            .operatorName("with_energies").symbol("Ingr.with_energies").interactName("withEnergies")
6✔
4170
            .function(variables -> {
1✔
4171
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4172
                ValueTypeList.ValueList<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = variables.getValue(1, ValueTypes.LIST);
6✔
4173
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4174
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4175
                }
4176
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4177
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4178
                        IngredientComponent.ENERGY, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ENERGY, list)));
2✔
4179
            }).build());
1✔
4180

4181
    /**
4182
     * ----------------------------------- RECIPE OPERATORS -----------------------------------
4183
     */
4184

4185
    /**
4186
     * The input ingredients of a recipe
4187
     */
4188
    public static final IOperator RECIPE_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4189
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4190
            .operatorInteract("input").symbol("recipe_in")
4✔
4191
            .function(variables -> {
1✔
4192
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4193
                if (value.getRawValue().isPresent()) {
4!
4194
                    return ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(value.getRawValue().get()));
7✔
4195
                }
4196
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4197
            }).build());
1✔
4198

4199
    /**
4200
     * The output ingredients of a recipe
4201
     */
4202
    public static final IOperator RECIPE_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4203
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4204
            .operatorInteract("output").symbol("recipe_out")
4✔
4205
            .function(variables -> {
1✔
4206
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4207
                if (value.getRawValue().isPresent()) {
4!
4208
                    return ValueObjectTypeIngredients.ValueIngredients.of(value.getRawValue().get().getOutput());
7✔
4209
                }
4210
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4211
            }).build());
1✔
4212

4213
    /**
4214
     * Set the input ingredients of a recipe
4215
     */
4216
    public static final IOperator RECIPE_WITH_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4217
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4218
            .operatorName("with_input").symbol("Recipe.with_in").interactName("withInput")
6✔
4219
            .function(variables -> {
1✔
4220
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4221
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4222
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4223
                    IMixedIngredients ingredients = valueIngredients.getRawValue().get();
5✔
4224
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4225
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4226
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4227
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4228
                                .stream()
4✔
4229
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4230
                                .collect(Collectors.toList()));
3✔
4231
                    }
1✔
4232
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4233
                            inputs,
4234
                            valueRecipe.getRawValue().get().getOutput()
5✔
4235
                    ));
4236
                }
4237
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4238
            }).build());
1✔
4239

4240
    /**
4241
     * Set the output ingredients of a recipe
4242
     */
4243
    public static final IOperator RECIPE_WITH_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4244
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4245
            .operatorName("with_output").symbol("Recipe.with_out").interactName("withOutput")
6✔
4246
            .function(variables -> {
1✔
4247
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4248
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4249
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4250
                    IRecipeDefinition recipe = valueRecipe.getRawValue().get();
5✔
4251
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4252
                    for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {
11✔
4253
                        inputs.put(component, (List) recipe.getInputs(component));
7✔
4254
                    }
1✔
4255
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4256
                            inputs,
4257
                            valueIngredients.getRawValue().get()
4✔
4258
                    ));
4259
                }
4260
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4261
            }).build());
1✔
4262

4263
    /**
4264
     * Create a recipe from two the given I/O ingredients
4265
     */
4266
    public static final IOperator RECIPE_WITH_INPUT_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_PREFIX
5✔
4267
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4268
            .operatorName("with_input_output").symbol("Recipe.with_io").interactName("withInputOutput")
6✔
4269
            .function(variables -> {
1✔
4270
                ValueObjectTypeIngredients.ValueIngredients valueIn = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4271
                ValueObjectTypeIngredients.ValueIngredients valueOut = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4272
                if (valueIn.getRawValue().isPresent() && valueOut.getRawValue().isPresent()) {
8!
4273
                    IMixedIngredients ingredients = valueIn.getRawValue().get();
5✔
4274
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4275
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4276
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4277
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4278
                                .stream()
4✔
4279
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4280
                                .collect(Collectors.toList()));
3✔
4281
                    }
1✔
4282
                    try {
4283
                        return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4284
                                inputs,
4285
                                valueOut.getRawValue().get()
4✔
4286
                        ));
4287
                    } catch (IllegalArgumentException e) {
×
4288
                        throw new EvaluationException(Component.literal(e.getMessage()));
×
4289
                    }
4290
                }
4291
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4292
            }).build());
1✔
4293

4294
    /**
4295
     * ------------------------------------ PARSE OPERATORS ------------------------------------
4296
     */
4297

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

4307
    /**
4308
     * Double Parse operator which takes a string of a form Double.parseDouble(),
4309
     * `/([+-]?)(Inf(inity)?|\u221E)/i`, or Long.decode() can consume.
4310
     */
4311
    public static final IOperator PARSE_DOUBLE = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.DOUBLE, v -> {
8✔
4312
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4313
      try {
4314
        return ValueTypeDouble.ValueDouble.of(Double.parseDouble(value.getRawValue()));
5✔
4315
      } catch (NumberFormatException e) {
1✔
4316
        try {
4317
          // \u221E = infinity symbol
4318
          Pattern p = Pattern.compile("\\A([+-]?)(Inf(inity)?|\u221E)\\z", Pattern.CASE_INSENSITIVE);
4✔
4319
          Matcher m = p.matcher(value.getRawValue().trim());
6✔
4320
          if (m.matches()){
3✔
4321
            if (m.group(1).equals("-")){
6✔
4322
              return ValueTypeDouble.ValueDouble.of(Double.NEGATIVE_INFINITY);
3✔
4323
            }
4324
            return ValueTypeDouble.ValueDouble.of(Double.POSITIVE_INFINITY);
3✔
4325
          }
4326
          // Try as a long
4327
          return ValueTypeDouble.ValueDouble.of((double) Long.decode(value.getRawValue()));
7✔
4328
        } catch (NumberFormatException e2) {
1✔
4329
            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4330
                    Component.translatable(ValueTypes.DOUBLE.getTranslationKey())));
3✔
4331
        }
4332
      }
4333
    }));
4334

4335
    /**
4336
     * Integer Parse operator which takes a string of a form Integer.decode() can consume.
4337
     */
4338
    public static final IOperator PARSE_INTEGER = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.INTEGER, v -> {
8✔
4339
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4340
      try{
4341
        return ValueTypeInteger.ValueInteger.of(Integer.decode(value.getRawValue()));
6✔
4342
      } catch (NumberFormatException e) {
1✔
4343
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4344
                  Component.translatable(ValueTypes.INTEGER.getTranslationKey())));
3✔
4345
      }
4346
    }));
4347

4348
    /**
4349
     * Long Parse operator which takes a string of a form Long.decode() can consume.
4350
     */
4351
    public static final IOperator PARSE_LONG = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.LONG, v -> {
8✔
4352
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4353
      try {
4354
        return ValueTypeLong.ValueLong.of(Long.decode(value.getRawValue()));
6✔
4355
      } catch (NumberFormatException e) {
1✔
4356
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4357
                  Component.translatable(ValueTypes.LONG.getTranslationKey())));
3✔
4358
      }
4359
    }));
4360

4361
    /**
4362
     * NBT Parse operator which takes a string of a form ValueTypeNbt().deserialize() can consume.
4363
     */
4364
    public static final IOperator PARSE_NBT = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.NBT, v -> {
8✔
4365
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4366
      try {
4367
        return ValueTypeNbt.ValueNbt.of(Helpers.TAG_PARSER.parseFully(value.getRawValue()));
7✔
4368
      } catch (CommandSyntaxException e) {
1✔
4369
        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4370
                Component.translatable(ValueTypes.NBT.getTranslationKey())));
3✔
4371
      }
4372
    }));
4373

4374
    /**
4375
     * ----------------------------------- GENERAL OPERATORS -----------------------------------
4376
     */
4377

4378
    /**
4379
     * Choice operator with one boolean input, two any inputs and one output any.
4380
     */
4381
    public static final GeneralOperator GENERAL_CHOICE = REGISTRY.register(new GeneralChoiceOperator("?", "choice", "choice"));
10✔
4382

4383
    /**
4384
     * Identity operator with one any input and one any output
4385
     */
4386
    public static final GeneralOperator GENERAL_IDENTITY = REGISTRY.register(new GeneralIdentityOperator("id", "identity", "identity"));
10✔
4387

4388
    /**
4389
     * Constant operator with two any inputs and one any output
4390
     */
4391
    public static final GeneralOperator GENERAL_CONSTANT = REGISTRY.register(new GeneralConstantOperator("K", "constant", "constant"));
11✔
4392

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