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

CyclopsMC / IntegratedDynamics / 16552051255

27 Jul 2025 01:58PM UTC coverage: 53.206% (+8.0%) from 45.161%
16552051255

push

github

rubensworks
Resolve minor TODOs

2888 of 8740 branches covered (33.04%)

Branch coverage included in aggregate %.

17341 of 29280 relevant lines covered (59.22%)

3.08 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

595

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

843
    /**
844
     * ----------------------------------- NULLABLE OPERATORS -----------------------------------
845
     */
846

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

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

864
    /**
865
     * ----------------------------------- LIST OPERATORS -----------------------------------
866
     */
867

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1261
    /**
1262
     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------
1263
     */
1264

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

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

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

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

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

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

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

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

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

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

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

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

1474
    /**
1475
     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------
1476
     */
1477

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1816

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

1828

1829

1830

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

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

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

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

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

1913
                return ValueTypeInteger.ValueInteger.of(count);
3✔
1914
            }).build());
1✔
1915

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

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

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

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

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

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

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

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

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

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

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

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

2064
    /**
2065
     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------
2066
     */
2067

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2607
    /**
2608
     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------
2609
     */
2610

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2912
    /**
2913
     * ----------------------------------- OPERATOR OPERATORS -----------------------------------
2914
     */
2915

2916
    /**
2917
     * Apply for a given operator a given value.
2918
     */
2919
    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2920
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2921
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply")
9✔
2922
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY))
4✔
2923
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2924
                    input -> {
2925
                        IOperator innerOperator = input.getLeft();
4✔
2926
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2927
                        IVariable variable = variables.getVariables()[0];
5✔
2928
                        return ValueHelpers.evaluateOperator(innerOperator, variable);
9✔
2929
                    })).build());
1✔
2930
    static {
2931
        REGISTRY.registerSerializer(new CurriedOperator.Serializer());
5✔
2932
    }
2933

2934
    /**
2935
     * Apply for a given operator the given 2 values.
2936
     */
2937
    public static final IOperator OPERATOR_APPLY_2 = REGISTRY.register(OperatorBuilders.OPERATOR
5✔
2938
            .renderPattern(IConfigRenderPattern.INFIX_2)
2✔
2939
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
15✔
2940
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
2941
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply2")
13✔
2942
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
2943
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2944
                    input -> {
2945
                        IOperator innerOperator = input.getLeft();
4✔
2946
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2947
                        IVariable variable0 = variables.getVariables()[0];
5✔
2948
                        IVariable variable1 = variables.getVariables()[1];
5✔
2949
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1);
13✔
2950
                    })).build());
1✔
2951

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

2971
    /**
2972
     * Apply for a given operator the given list of values.
2973
     */
2974
    public static final IOperator OPERATOR_APPLY_N = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2975
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST)
11✔
2976
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.LIST)
2✔
2977
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply_n")
9✔
2978
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.LIST))
4✔
2979
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
2980
                    input -> {
2981
                        IOperator innerOperator = input.getLeft();
4✔
2982
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2983
                        IValueTypeListProxy<IValueType<IValue>, IValue> list = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
2984
                        return ValueHelpers.evaluateOperator(innerOperator, Iterables.toArray(list, IValue.class));
7✔
2985
                    })).build());
1✔
2986

2987
    /**
2988
     * Apply for a given operator with zero arguments.
2989
     */
2990
    public static final IOperator OPERATOR_APPLY_0 = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
2991
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2992
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply0")
5✔
2993
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(new IValueType[0]))
4✔
2994
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2995
                    input -> {
2996
                        IOperator innerOperator = input.getLeft();
4✔
2997
                        return ValueHelpers.evaluateOperator(innerOperator, new IVariable[0]);
5✔
2998
                    })).build());
1✔
2999

3000
    /**
3001
     * Apply the given operator on all elements of a list, resulting in a new list of mapped values.
3002
     */
3003
    public static final IOperator OPERATOR_MAP = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3004
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3005
            .output(ValueTypes.LIST).symbolOperatorInteract("map")
5✔
3006
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3007
                    input -> {
3008
                        final IOperator innerOperator = input.getLeft();
4✔
3009
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3010
                        ValueTypeList.ValueList inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3011
                        return ValueTypeList.ValueList.ofFactory(
6✔
3012
                                new ValueTypeListProxyOperatorMapped(innerOperator, inputList.getRawValue()));
2✔
3013
                    })).build());
1✔
3014

3015
    /**
3016
     * Filter a list of elements by matching them all with the given predicate.
3017
     */
3018
    public static final IOperator OPERATOR_FILTER = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3019
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3020
            .output(ValueTypes.LIST).symbolOperatorInteract("filter")
7✔
3021
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3022
                    new IOperatorValuePropagator<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue>() {
3✔
3023
                        @Override
3024
                        public IValue getOutput(Pair<IOperator, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3025
                            final IOperator innerOperator = input.getLeft();
4✔
3026
                            OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3027
                            ValueTypeList.ValueList<?, ?> inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3028
                            List<IValue> filtered = Lists.newArrayList();
2✔
3029
                            for (IValue value : inputList.getRawValue()) {
11✔
3030
                                IValue result = ValueHelpers.evaluateOperator(innerOperator, value);
9✔
3031
                                ValueHelpers.validatePredicateOutput(innerOperator, result);
3✔
3032
                                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
3033
                                    filtered.add(value);
4✔
3034
                                }
3035
                            }
1✔
3036
                            IValueType valueType = inputList.getRawValue().getValueType();
4✔
3037
                            return ValueTypeList.ValueList.ofList(valueType, filtered);
4✔
3038
                        }
3039
                    })).build());
1✔
3040

3041
    /**
3042
     * Takes the conjunction of two predicates.
3043
     */
3044
    public static final IOperator OPERATOR_CONJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3045
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3046
            .output(ValueTypes.OPERATOR).symbol(".&&.").operatorInteract("conjunction")
7✔
3047
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3048
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Conjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3049
            )).build());
1✔
3050
    static {
3051
        REGISTRY.registerSerializer(new CombinedOperator.Conjunction.Serializer());
5✔
3052
    }
3053

3054
    /**
3055
     * Takes the disjunction of two predicates.
3056
     */
3057
    public static final IOperator OPERATOR_DISJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3058
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3059
            .output(ValueTypes.OPERATOR).symbol(".||.").operatorInteract("disjunction")
7✔
3060
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3061
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Disjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3062
            )).build());
1✔
3063
    static {
3064
        REGISTRY.registerSerializer(new CombinedOperator.Disjunction.Serializer());
5✔
3065
    }
3066

3067
    /**
3068
     * Takes the negation of a predicate.
3069
     */
3070
    public static final IOperator OPERATOR_NEGATION = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3071
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3072
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3073
            .output(ValueTypes.OPERATOR).symbol("!.").operatorInteract("negation")
7✔
3074
            .function(OperatorBuilders.FUNCTION_ONE_PREDICATE.build(
2✔
3075
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Negation.asOperator(input))
4✔
3076
            )).build());
1✔
3077
    static {
3078
        REGISTRY.registerSerializer(new CombinedOperator.Negation.Serializer());
5✔
3079
    }
3080

3081
    /**
3082
     * Create a new operator that pipes the output from the first operator to the second operator.
3083
     */
3084
    public static final IOperator OPERATOR_PIPE = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3085
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3086
            .output(ValueTypes.OPERATOR).symbol(".").operatorInteract("pipe")
7✔
3087
            .function(OperatorBuilders.FUNCTION_TWO_OPERATORS.build(
2✔
3088
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe.asOperator(input.getLeft(), input.getRight()))
17✔
3089
            )).build());
1✔
3090
    static {
3091
        REGISTRY.registerSerializer(new CombinedOperator.Pipe.Serializer());
5✔
3092
    }
3093

3094
    /**
3095
     * 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.
3096
     */
3097
    public static final IOperator OPERATOR_PIPE2 = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3098
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3099
            .renderPattern(IConfigRenderPattern.INFIX_2_LATE)
2✔
3100
            .output(ValueTypes.OPERATOR).symbol(".2").operatorInteract("pipe2")
7✔
3101
            .function(OperatorBuilders.FUNCTION_THREE_OPERATORS.build(
2✔
3102
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe2.asOperator(input.getLeft(), input.getMiddle(), input.getRight()))
23✔
3103
            )).build());
1✔
3104
    static {
3105
        REGISTRY.registerSerializer(new CombinedOperator.Pipe2.Serializer());
5✔
3106
    }
3107

3108
    /**
3109
     * Flip the input parameters of an operator with two inputs.
3110
     */
3111
    public static final IOperator OPERATOR_FLIP = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3112
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3113
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3114
            .output(ValueTypes.OPERATOR).symbolOperatorInteract("flip")
5✔
3115
            .function(OperatorBuilders.FUNCTION_ONE_OPERATOR.build(
2✔
3116
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Flip.asOperator(input))
4✔
3117
            )).build());
1✔
3118
    static {
3119
        REGISTRY.registerSerializer(new CombinedOperator.Flip.Serializer());
5✔
3120
    }
3121

3122
    /**
3123
     * Apply the given operator on all elements of a list to reduce the list to one value.
3124
     */
3125
    public static final IOperator OPERATOR_REDUCE = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3126
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
3127
            .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
2✔
3128
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce")
4✔
3129
            .conditionalOutputTypeDeriver((operator, input) -> input[2].getType())
7✔
3130
            .function(variables -> {
1✔
3131
                IValue accumulator = variables.getValue(2);
4✔
3132
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3133
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3134
                ValueTypeList.ValueList<IValueType<IValue>, IValue> inputList = variables.getValue(1, ValueTypes.LIST);
6✔
3135
                for (IValue listValue : inputList.getRawValue()) {
11✔
3136
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3137
                }
1✔
3138
                return accumulator;
2✔
3139
            }).build());
1✔
3140

3141
    /**
3142
     * Apply the given operator on all elements of a list to reduce the list to one value.
3143
     */
3144
    public static final IOperator OPERATOR_REDUCE1 = REGISTRY.register(OperatorBuilders.OPERATOR
14✔
3145
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3146
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
3147
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce1")
4✔
3148
            .conditionalOutputTypeDeriver((operator, input) -> {
2✔
3149
                try {
3150
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[1].getValue()).getRawValue();
7✔
3151
                    return a.getValueType();
3✔
3152
                } catch (EvaluationException e) {
×
3153
                    return operator.getOutputType();
×
3154
                }
3155
            })
3156
            .function(variables -> {
1✔
3157
                ValueTypeList.ValueList valueList = variables.getValue(1, ValueTypes.LIST);
6✔
3158
                Iterator<IValue> iter = valueList.getRawValue().iterator();
4✔
3159
                if (!iter.hasNext()) {
3✔
3160
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REDUCE_EMPTY));
6✔
3161
                }
3162

3163
                IValue accumulator = iter.next();
4✔
3164
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3165
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3166

3167
                while (iter.hasNext()) {
3✔
3168
                    IValue listValue = iter.next();
4✔
3169
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3170
                }
1✔
3171
                return accumulator;
2✔
3172
            }).build());
1✔
3173

3174
    /**
3175
     * Apply for a given operator a given value.
3176
     */
3177
    public static final IOperator OPERATOR_BY_NAME = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3178
            .inputType(ValueTypes.STRING).output(ValueTypes.OPERATOR)
4✔
3179
            .symbol("op_by_name").operatorName("by_name").interactName("operatorByName")
6✔
3180
            .function(input -> {
1✔
3181
                ValueTypeString.ValueString name = input.getValue(0, ValueTypes.STRING);
6✔
3182
                try {
3183
                    ResourceLocation id = ResourceLocation.parse(name.getRawValue());
4✔
3184
                    IOperator operator = Operators.REGISTRY.getOperator(id);
4✔
3185
                    if (operator == null) {
2✔
3186
                        throw new EvaluationException(Component.translatable(
11✔
3187
                                L10NValues.OPERATOR_ERROR_OPERATORNOTFOUND, name.getRawValue()));
2✔
3188
                    }
3189
                    return ValueTypeOperator.ValueOperator.of(operator);
3✔
3190
                } catch (ResourceLocationException e) {
×
3191
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
3192
                }
3193
            }).build());
1✔
3194

3195
    /**
3196
     * ----------------------------------- NBT OPERATORS -----------------------------------
3197
     */
3198

3199
    /**
3200
     * The number of entries in an NBT tag
3201
     */
3202
    public static final IOperator NBT_COMPOUND_SIZE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3203
            .output(ValueTypes.INTEGER).operatorName("compound_size").symbol("NBT{}.size").interactName("size")
9✔
3204
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_TO_INT.build(
2✔
3205
                opt -> opt.map(CompoundTag::size).orElse(0)
8✔
3206
            )).build());
1✔
3207

3208
    /**
3209
     * The list of keys in an NBT tag
3210
     */
3211
    public static final IOperator NBT_COMPOUND_KEYS = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3212
            .output(ValueTypes.LIST).operatorName("compound_keys").symbol("NBT{}.keys").interactName("keys")
8✔
3213
            .function(variables -> {
1✔
3214
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3215
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtKeys(value.getRawValue()));
7✔
3216
            }).build());
1✔
3217

3218
    /**
3219
     * If an NBT tag has the given key
3220
     */
3221
    public static final IOperator NBT_COMPOUND_HASKEY = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3222
            .output(ValueTypes.BOOLEAN).operatorName("compound_haskey").symbol("NBT{}.has_key").interactName("hasKey")
9✔
3223
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3224
                    Optional::isPresent
3225
            )).build());
1✔
3226

3227
    /**
3228
     * The NBT value type of an entry
3229
     */
3230
    public static final IOperator NBT_COMPOUND_VALUE_TYPE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3231
            .output(ValueTypes.STRING).operatorName("compound_type").symbol("NBT{}.type").interactName("type")
9✔
3232
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(tag -> {
2✔
3233
                if (tag.isPresent()) {
3✔
3234
                    try {
3235
                        return TagTypes.getType(tag.get().getId()).getName();
7✔
3236
                    } catch (IndexOutOfBoundsException e) {
×
3237

3238
                    }
3239
                }
3240
                return "null";
2✔
3241
            })).build());
1✔
3242

3243
    /**
3244
     * The NBT tag value
3245
     */
3246
    public static final IOperator NBT_COMPOUND_VALUE_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3247
            .output(ValueTypes.NBT).operatorName("compound_value_tag").symbol("NBT{}.get_tag").interactName("getTag")
9✔
3248
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(o -> o)).build());
5✔
3249

3250
    /**
3251
     * The NBT boolean value
3252
     */
3253
    public static final IOperator NBT_COMPOUND_VALUE_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3254
            .output(ValueTypes.BOOLEAN).operatorName("compound_value_boolean").symbol("NBT{}.get_boolean").interactName("getBoolean")
9✔
3255
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3256
                    o -> o.flatMap(Tag::asBoolean).orElse(false)
8✔
3257
            )).build());
1✔
3258

3259
    /**
3260
     * The NBT integer value
3261
     */
3262
    public static final IOperator NBT_COMPOUND_VALUE_INTEGER = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3263
            .output(ValueTypes.INTEGER).operatorName("compound_value_integer").symbol("NBT{}.get_integer").interactName("getInteger")
9✔
3264
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_INT.build(
2✔
3265
                    o -> o.flatMap(Tag::asInt).orElse(0)
8✔
3266
            )).build());
1✔
3267

3268
    /**
3269
     * The NBT long value
3270
     */
3271
    public static final IOperator NBT_COMPOUND_VALUE_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3272
            .output(ValueTypes.LONG).operatorName("compound_value_long").symbol("NBT{}.get_long").interactName("getLong")
9✔
3273
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG.build(
2✔
3274
                    o -> o.flatMap(Tag::asLong).orElse(0L)
8✔
3275
            )).build());
1✔
3276

3277
    /**
3278
     * The NBT double value
3279
     */
3280
    public static final IOperator NBT_COMPOUND_VALUE_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3281
            .output(ValueTypes.DOUBLE).operatorName("compound_value_double").symbol("NBT{}.get_double").interactName("getDouble")
9✔
3282
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE.build(
2✔
3283
                    o -> o.flatMap(Tag::asDouble).orElse(0D)
8✔
3284
            )).build());
1✔
3285

3286
    /**
3287
     * The NBT string value
3288
     */
3289
    public static final IOperator NBT_COMPOUND_VALUE_STRING = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3290
            .output(ValueTypes.STRING).operatorName("compound_value_string").symbol("NBT{}.get_string").interactName("getString")
9✔
3291
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(
2✔
3292
                    o -> o.flatMap(Tag::asString).orElse("")
7✔
3293
            )).build());
1✔
3294

3295
    /**
3296
     * The NBT compound value
3297
     */
3298
    public static final IOperator NBT_COMPOUND_VALUE_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3299
            .output(ValueTypes.NBT).operatorName("compound_value_compound").symbol("NBT{}.get_compound").interactName("getCompound")
9✔
3300
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(
2✔
3301
                    o -> o.map(tag -> tag instanceof CompoundTag ? (CompoundTag) tag : new CompoundTag())
11!
3302
            )).build());
1✔
3303

3304
    /**
3305
     * The NBT tag list value
3306
     */
3307
    public static final IOperator NBT_COMPOUND_VALUE_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3308
            .output(ValueTypes.LIST).operatorName("compound_value_list_tag").symbol("NBT{}.get_list_tag").interactName("getListTag")
8✔
3309
            .function(variables -> {
1✔
3310
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3311
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3312
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListTag(key.getRawValue(), value.getRawValue()));
9✔
3313
            }).build());
1✔
3314

3315
    /**
3316
     * The NBT boolean list value
3317
     */
3318
    public static final IOperator NBT_COMPOUND_VALUE_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3319
            .output(ValueTypes.LIST).operatorName("compound_value_list_byte").symbol("NBT{}.get_list_byte").interactName("getListByte")
8✔
3320
            .function(variables -> {
1✔
3321
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3322
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3323
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListByte(key.getRawValue(), value.getRawValue()));
9✔
3324
            }).build());
1✔
3325

3326
    /**
3327
     * The NBT int list value
3328
     */
3329
    public static final IOperator NBT_COMPOUND_VALUE_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3330
            .output(ValueTypes.LIST).operatorName("compound_value_list_int").symbol("NBT{}.get_list_int").interactName("getListInt")
8✔
3331
            .function(variables -> {
1✔
3332
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3333
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3334
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListInt(key.getRawValue(), value.getRawValue()));
9✔
3335
            }).build());
1✔
3336

3337
    /**
3338
     * The NBT long list value
3339
     */
3340
    public static final IOperator NBT_COMPOUND_VALUE_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3341
            .output(ValueTypes.LIST).operatorName("compound_value_list_long").symbol("NBT{}.get_list_long").interactName("getListLong")
8✔
3342
            .function(variables -> {
1✔
3343
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3344
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3345
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListLong(key.getRawValue(), value.getRawValue()));
9✔
3346
            }).build());
1✔
3347

3348
    /**
3349
     * Remove an entry from an NBT compound
3350
     */
3351
    public static final IOperator NBT_COMPOUND_WITHOUT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3352
            .output(ValueTypes.NBT).operatorName("compound_without").symbol("NBT{}.without").interactName("without")
8✔
3353
            .function(variables -> {
1✔
3354
                ValueTypeNbt.ValueNbt valueNbt = variables.getValue(0, ValueTypes.NBT);
6✔
3355
                Optional<Tag> tag = valueNbt.getRawValue();
3✔
3356
                if (tag.isPresent()) {
3!
3357
                    if (!(tag.get() instanceof CompoundTag)) {
4!
3358
                        return ValueTypeNbt.ValueNbt.of();
×
3359
                    }
3360
                    ValueTypeString.ValueString valueString = variables.getValue(1, ValueTypes.STRING);
6✔
3361
                    String key = valueString.getRawValue();
3✔
3362
                    CompoundTag tagCompound = (CompoundTag) tag.get();
4✔
3363
                    if (tagCompound.contains(key)) {
4!
3364
                        // Copy the tag to ensure immutability
3365
                        tagCompound = tagCompound.copy();
3✔
3366
                        tagCompound.remove(key);
3✔
3367
                    }
3368
                    return ValueTypeNbt.ValueNbt.of(tagCompound);
3✔
3369
                }
3370
                return valueNbt;
×
3371
            }).build());
1✔
3372

3373

3374

3375
    /**
3376
     * Set an NBT compound boolean value
3377
     */
3378
    public static final IOperator NBT_COMPOUND_WITH_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3379
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3380
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.BOOLEAN)
2✔
3381
            .operatorName("compound_with_boolean").symbol("NBT{}.with_boolean").interactName("withBoolean")
7✔
3382
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3383
                ValueTypeBoolean.ValueBoolean value = input.getRight().getValue(0, ValueTypes.BOOLEAN);
8✔
3384
                input.getLeft().ifPresent(tag -> tag.putBoolean(input.getMiddle(), value.getRawValue()));
15✔
3385
                return input.getLeft();
4✔
3386
            })).build());
1✔
3387

3388
    /**
3389
     * Set an NBT compound short value
3390
     */
3391
    public static final IOperator NBT_COMPOUND_WITH_SHORT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3392
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3393
            .operatorName("compound_with_short").symbol("NBT{}.with_short").interactName("withShort")
7✔
3394
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3395
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3396
                input.getLeft().ifPresent(tag -> tag.putShort(input.getMiddle(), (short) value.getRawValue()));
16✔
3397
                return input.getLeft();
4✔
3398
            })).build());
1✔
3399

3400
    /**
3401
     * Set an NBT compound integer value
3402
     */
3403
    public static final IOperator NBT_COMPOUND_WITH_INTEGER = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3404
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3405
            .operatorName("compound_with_integer").symbol("NBT{}.with_integer").interactName("withInteger")
7✔
3406
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3407
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3408
                input.getLeft().ifPresent(tag -> tag.putInt(input.getMiddle(), value.getRawValue()));
15✔
3409
                return input.getLeft();
4✔
3410
            })).build());
1✔
3411

3412
    /**
3413
     * Set an NBT compound long value
3414
     */
3415
    public static final IOperator NBT_COMPOUND_WITH_LONG = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3416
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LONG)
2✔
3417
            .operatorName("compound_with_long").symbol("NBT{}.with_long").interactName("withLong")
7✔
3418
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3419
                ValueTypeLong.ValueLong value = input.getRight().getValue(0, ValueTypes.LONG);
8✔
3420
                input.getLeft().ifPresent(tag -> tag.putLong(input.getMiddle(), value.getRawValue()));
15✔
3421
                return input.getLeft();
4✔
3422
            })).build());
1✔
3423

3424
    /**
3425
     * Set an NBT compound double value
3426
     */
3427
    public static final IOperator NBT_COMPOUND_WITH_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3428
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3429
            .operatorName("compound_with_double").symbol("NBT{}.with_double").interactName("withDouble")
7✔
3430
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3431
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3432
                input.getLeft().ifPresent(tag -> tag.putDouble(input.getMiddle(), value.getRawValue()));
15✔
3433
                return input.getLeft();
4✔
3434
            })).build());
1✔
3435

3436
    /**
3437
     * Set an NBT compound float value
3438
     */
3439
    public static final IOperator NBT_COMPOUND_WITH_FLOAT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3440
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3441
            .operatorName("compound_with_float").symbol("NBT{}.with_float").interactName("withFloat")
7✔
3442
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3443
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3444
                input.getLeft().ifPresent(tag -> tag.putFloat(input.getMiddle(), (float) value.getRawValue()));
16✔
3445
                return input.getLeft();
4✔
3446
            })).build());
1✔
3447

3448
    /**
3449
     * Set an NBT compound string value
3450
     */
3451
    public static final IOperator NBT_COMPOUND_WITH_STRING = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3452
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING)
2✔
3453
            .operatorName("compound_with_string").symbol("NBT{}.with_string").interactName("withString")
7✔
3454
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3455
                ValueTypeString.ValueString value = input.getRight().getValue(0, ValueTypes.STRING);
8✔
3456
                input.getLeft().ifPresent(tag -> tag.putString(input.getMiddle(), value.getRawValue()));
15✔
3457
                return input.getLeft();
4✔
3458
            })).build());
1✔
3459

3460
    /**
3461
     * Set an NBT compound compound value
3462
     */
3463
    public static final IOperator NBT_COMPOUND_WITH_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3464
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.NBT)
2✔
3465
            .operatorName("compound_with_tag").symbol("NBT{}.with_tag").interactName("withTag")
7✔
3466
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3467
                ValueTypeNbt.ValueNbt value = input.getRight().getValue(0, ValueTypes.NBT);
8✔
3468
                input.getLeft()
6✔
3469
                        .ifPresent(tag -> value.getRawValue()
7✔
3470
                                .ifPresent(v -> tag.put(input.getMiddle(), v)));
9✔
3471
                return input.getLeft();
4✔
3472
            })).build());
1✔
3473

3474
    /**
3475
     * Set an NBT compound tag list value
3476
     */
3477
    public static final IOperator NBT_COMPOUND_WITH_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3478
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3479
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3480
            .operatorName("compound_with_list_tag").symbol("NBT{}.with_tag_list").interactName("withTagList")
9✔
3481
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3482
                @Override
3483
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3484
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3485
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3486
                            NbtHelpers.getListNbtTag(value, NBT_COMPOUND_WITH_LIST_TAG.getLocalizedNameFull())));
2✔
3487
                    return input.getLeft();
4✔
3488
                }
3489
            })).build());
1✔
3490

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

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

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

3542
    /**
3543
     * Check if the first NBT compound tag is a subset of the second NBT compound tag.
3544
     */
3545
    public static final IOperator NBT_COMPOUND_SUBSET = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3546
            .output(ValueTypes.BOOLEAN).operatorName("compound_subset").symbol("NBT{}.⊆").interactName("isSubset")
8✔
3547
            .function(variables -> {
1✔
3548
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3549
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3550
                if (valueNbt0.getRawValue().isPresent()
5!
3551
                        && valueNbt1.getRawValue().isPresent()
4!
3552
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3553
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3554
                    return ValueTypeBoolean.ValueBoolean.of(NbtHelpers.nbtMatchesSubset((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get(), true));
12✔
3555
                }
3556
                return ValueTypeBoolean.ValueBoolean.of(false);
×
3557
            }).build());
1✔
3558

3559
    /**
3560
     * The union of the given NBT compound tags. Nested tags will be joined recusively.
3561
     */
3562
    public static final IOperator NBT_COMPOUND_UNION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3563
            .output(ValueTypes.NBT).operatorName("compound_union").symbol("NBT{}.∪").interactName("union")
8✔
3564
            .function(variables -> {
1✔
3565
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3566
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3567
                if (valueNbt0.getRawValue().isPresent()
5!
3568
                        && valueNbt1.getRawValue().isPresent()
4!
3569
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3570
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3571
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.union((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3572
                }
3573
                return ValueTypeNbt.ValueNbt.of();
×
3574
            }).build());
1✔
3575

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

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

3610
    /**
3611
     * The boolean value of an NBT value
3612
     */
3613
    public static final IOperator NBT_AS_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3614
            .output(ValueTypes.BOOLEAN).operatorName("as_boolean").symbol("NBT.as_boolean").interactName("asBoolean")
9✔
3615
            .function(OperatorBuilders.FUNCTION_NBT_TO_BOOLEAN.build(
2✔
3616
                    o -> o.flatMap(Tag::asBoolean).orElse(false)
8✔
3617
            )).build());
1✔
3618

3619
    /**
3620
     * The byte value of an NBT value
3621
     */
3622
    public static final IOperator NBT_AS_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3623
            .output(ValueTypes.INTEGER).operatorName("as_byte").symbol("NBT.as_byte").interactName("asByte")
9✔
3624
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3625
                    o -> o.flatMap(Tag::asByte).map(s -> (int) s).orElse(0)
14✔
3626
            )).build());
1✔
3627

3628
    /**
3629
     * The short value of an NBT value
3630
     */
3631
    public static final IOperator NBT_AS_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3632
            .output(ValueTypes.INTEGER).operatorName("as_short").symbol("NBT.as_short").interactName("asShort")
9✔
3633
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3634
                    o -> o.flatMap(Tag::asShort).map(s -> (int) s).orElse(0)
14✔
3635
            )).build());
1✔
3636

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

3646
    /**
3647
     * The long value of an NBT value
3648
     */
3649
    public static final IOperator NBT_AS_LONG = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3650
            .output(ValueTypes.LONG).operatorName("as_long").symbol("NBT.as_long").interactName("asLong")
9✔
3651
            .function(OperatorBuilders.FUNCTION_NBT_TO_LONG.build(
2✔
3652
                    o -> o.flatMap(Tag::asLong).orElse(0L)
8✔
3653
            )).build());
1✔
3654

3655
    /**
3656
     * The double value of an NBT value
3657
     */
3658
    public static final IOperator NBT_AS_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3659
            .output(ValueTypes.DOUBLE).operatorName("as_double").symbol("NBT.as_double").interactName("asDouble")
9✔
3660
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3661
                    o -> o.flatMap(Tag::asDouble).orElse(0D)
8✔
3662
            )).build());
1✔
3663

3664
    /**
3665
     * The float value of an NBT value
3666
     */
3667
    public static final IOperator NBT_AS_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3668
            .output(ValueTypes.DOUBLE).operatorName("as_float").symbol("NBT.as_float").interactName("asFloat")
9✔
3669
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3670
                    o -> o.flatMap(Tag::asFloat).map(f -> (double) f).orElse(0D)
15✔
3671
            )).build());
1✔
3672

3673
    /**
3674
     * The string value of an NBT value
3675
     */
3676
    public static final IOperator NBT_AS_STRING = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3677
            .output(ValueTypes.STRING).operatorName("as_string").symbol("NBT.as_string").interactName("asString")
9✔
3678
            .function(OperatorBuilders.FUNCTION_NBT_TO_STRING.build(
2✔
3679
                    o -> o.flatMap(Tag::asString).orElse("")
7✔
3680
            )).build());
1✔
3681

3682
    /**
3683
     * The tag list value of an NBT value
3684
     */
3685
    public static final IOperator NBT_AS_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3686
            .output(ValueTypes.LIST).operatorName("as_tag_list").symbol("NBT.as_tag_list").interactName("asTagList")
8✔
3687
            .function(variables -> {
1✔
3688
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3689
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListTag(value.getRawValue()));
7✔
3690
            }).build());
1✔
3691

3692
    /**
3693
     * The byte list value of an NBT value
3694
     */
3695
    public static final IOperator NBT_AS_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3696
            .output(ValueTypes.LIST).operatorName("as_byte_list").symbol("NBT.as_byte_list").interactName("asByteList")
8✔
3697
            .function(variables -> {
1✔
3698
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3699
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListByte(value.getRawValue()));
7✔
3700
            }).build());
1✔
3701

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

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

3722
    /**
3723
     * The NBT value of a boolean value
3724
     */
3725
    public static final IOperator NBT_FROM_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3726
            .inputType(ValueTypes.BOOLEAN).output(ValueTypes.NBT)
4✔
3727
            .operatorName("from_boolean").symbol("NBT.from_boolean").interactName("asNbt")
6✔
3728
            .function(variables -> {
1✔
3729
                ValueTypeBoolean.ValueBoolean value = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
3730
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf(value.getRawValue()));
5✔
3731
            }).build());
1✔
3732

3733
    /**
3734
     * The NBT value of a short value
3735
     */
3736
    public static final IOperator NBT_FROM_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3737
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3738
            .operatorName("from_short").symbol("NBT.from_short").interactName("asNbt", "short", true)
8✔
3739
            .function(variables -> {
1✔
3740
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3741
                return ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) value.getRawValue()));
6✔
3742
            }).build());
1✔
3743

3744
    /**
3745
     * The NBT value of a byte value
3746
     */
3747
    public static final IOperator NBT_FROM_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3748
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3749
            .operatorName("from_byte").symbol("NBT.from_byte").interactName("asNbt", "byte", true)
8✔
3750
            .function(variables -> {
1✔
3751
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3752
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf((byte) value.getRawValue()));
6✔
3753
            }).build());
1✔
3754

3755
    /**
3756
     * The NBT value of an int value
3757
     */
3758
    public static final IOperator NBT_FROM_INT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3759
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3760
            .operatorName("from_int").symbol("NBT.from_int").interactName("asNbt")
6✔
3761
            .function(variables -> {
1✔
3762
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3763
                return ValueTypeNbt.ValueNbt.of(IntTag.valueOf(value.getRawValue()));
5✔
3764
            }).build());
1✔
3765

3766
    /**
3767
     * The NBT value of a long value
3768
     */
3769
    public static final IOperator NBT_FROM_LONG = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3770
            .inputType(ValueTypes.LONG).output(ValueTypes.NBT)
4✔
3771
            .operatorName("from_long").symbol("NBT.from_long").interactName("asNbt")
6✔
3772
            .function(variables -> {
1✔
3773
                ValueTypeLong.ValueLong value = variables.getValue(0, ValueTypes.LONG);
6✔
3774
                return ValueTypeNbt.ValueNbt.of(LongTag.valueOf(value.getRawValue()));
5✔
3775
            }).build());
1✔
3776

3777
    /**
3778
     * The NBT value of a double value
3779
     */
3780
    public static final IOperator NBT_FROM_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3781
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3782
            .operatorName("from_double").symbol("NBT.from_double").interactName("asNbt")
6✔
3783
            .function(variables -> {
1✔
3784
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3785
                return ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(value.getRawValue()));
5✔
3786
            }).build());
1✔
3787

3788
    /**
3789
     * The NBT value of a float value
3790
     */
3791
    public static final IOperator NBT_FROM_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3792
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3793
            .operatorName("from_float").symbol("NBT.from_float").interactName("asNbt", "float", true)
8✔
3794
            .function(variables -> {
1✔
3795
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3796
                return ValueTypeNbt.ValueNbt.of(FloatTag.valueOf((float) value.getRawValue()));
6✔
3797
            }).build());
1✔
3798

3799
    /**
3800
     * The NBT value of a string value
3801
     */
3802
    public static final IOperator NBT_FROM_STRING = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3803
            .inputType(ValueTypes.STRING).output(ValueTypes.NBT)
4✔
3804
            .operatorName("from_string").symbol("NBT.from_string").interactName("asNbt")
6✔
3805
            .function(variables -> {
1✔
3806
                ValueTypeString.ValueString value = variables.getValue(0, ValueTypes.STRING);
6✔
3807
                return ValueTypeNbt.ValueNbt.of(StringTag.valueOf(value.getRawValue()));
5✔
3808
            }).build());
1✔
3809

3810
    /**
3811
     * The NBT value of a tag list value
3812
     */
3813
    public static final IOperator NBT_FROM_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3814
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3815
            .operatorName("from_tag_list").symbol("NBT.from_tag_list").interactName("asNbt", "tagList", true)
10✔
3816
            .function(new OperatorBase.IFunction() {
4✔
3817
                @Override
3818
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3819
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3820
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtTag(value, NBT_FROM_TAG_LIST.getLocalizedNameFull()));
6✔
3821
                }
3822
            }).build());
1✔
3823

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

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

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

3866
    /**
3867
     * Apply the given NBT path expression on the given NBT value and get the first result.
3868
     */
3869
    public static final IOperator NBT_PATH_MATCH_FIRST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3870
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.NBT)
4✔
3871
            .operatorName("path_match_first").symbol("NBT.path_match_first").interactName("nbtPathMatchFirst")
6✔
3872
            .function(variables -> {
1✔
3873
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3874
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3875
                INbtPathExpression expression = null;
2✔
3876
                try {
3877
                    expression = NbtPath.parse(string.getRawValue());
4✔
3878
                } catch (NbtParseException e) {
×
3879
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3880
                            string.getRawValue(),
×
3881
                            e.getMessage()));
×
3882
                }
1✔
3883
                if (!nbt.getRawValue().isPresent()) {
4!
3884
                    return ValueTypeNbt.ValueNbt.of();
×
3885
                }
3886
                return ValueTypeNbt.ValueNbt.of(expression.match(nbt.getRawValue().get()).getMatches().findAny());
10✔
3887
            }).build());
1✔
3888

3889
    /**
3890
     * Apply the given NBT path expression on the given NBT value and get all results.
3891
     */
3892
    public static final IOperator NBT_PATH_MATCH_ALL = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3893
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.LIST)
4✔
3894
            .operatorName("path_match_all").symbol("NBT.path_match_all").interactName("nbtPathMatchAll")
6✔
3895
            .function(variables -> {
1✔
3896
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3897
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3898
                INbtPathExpression expression = null;
2✔
3899
                try {
3900
                    expression = NbtPath.parse(string.getRawValue());
4✔
3901
                } catch (NbtParseException e) {
×
3902
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3903
                            string.getRawValue(),
×
3904
                            e.getMessage()));
×
3905
                }
1✔
3906
                if (!nbt.getRawValue().isPresent()) {
4!
3907
                    return ValueTypeList.ValueList.ofAll(ValueTypes.NBT);
×
3908
                }
3909
                List<ValueTypeNbt.ValueNbt> matches = expression.match(nbt.getRawValue().get()).getMatches()
8✔
3910
                        .map(ValueTypeNbt.ValueNbt::of)
1✔
3911
                        .collect(Collectors.toList());
4✔
3912
                return ValueTypeList.ValueList.ofList(ValueTypes.NBT, matches);
4✔
3913
            }).build());
1✔
3914

3915
    /**
3916
     * Test the given NBT path expression on the given NBT value.
3917
     */
3918
    public static final IOperator NBT_PATH_TEST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3919
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.BOOLEAN)
4✔
3920
            .operatorName("path_test").symbol("NBT.path_test").interactName("nbtPathTest")
6✔
3921
            .function(variables -> {
1✔
3922
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3923
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3924
                INbtPathExpression expression = null;
2✔
3925
                try {
3926
                    expression = NbtPath.parse(string.getRawValue());
4✔
3927
                } catch (NbtParseException e) {
×
3928
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3929
                            string.getRawValue(),
×
3930
                            e.getMessage()));
×
3931
                }
1✔
3932
                if (!nbt.getRawValue().isPresent()) {
4!
3933
                    return ValueTypeBoolean.ValueBoolean.of(false);
×
3934
                }
3935
                return ValueTypeBoolean.ValueBoolean.of(expression.test(nbt.getRawValue().get()));
8✔
3936
            }).build());
1✔
3937

3938
    /**
3939
     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------
3940
     */
3941

3942
    /**
3943
     * The list of items.
3944
     */
3945
    public static final IOperator INGREDIENTS_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3946
            .output(ValueTypes.LIST).operatorInteract("items").symbol("Ingr.items")
6✔
3947
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ITEMSTACK))
4✔
3948
            .build());
1✔
3949

3950
    /**
3951
     * The list of fluids
3952
     */
3953
    public static final IOperator INGREDIENTS_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3954
            .output(ValueTypes.LIST).operatorInteract("fluids").symbol("Ingr.fluids")
6✔
3955
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.FLUIDSTACK))
4✔
3956
            .build());
1✔
3957

3958
    /**
3959
     * The list of fluids
3960
     */
3961
    public static final IOperator INGREDIENTS_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3962
            .output(ValueTypes.LIST).operatorInteract("energies").symbol("Ingr.energies")
6✔
3963
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ENERGY))
4✔
3964
            .build());
1✔
3965

3966
    /**
3967
     * Set an ingredient item
3968
     */
3969
    public static final IOperator INGREDIENTS_WITH_ITEM = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_ITEMSTACK
5✔
3970
            .operatorName("with_item").symbol("Ingr.with_item").interactName("withItem")
6✔
3971
            .function(variables -> {
1✔
3972
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3973
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3974
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(2, ValueTypes.OBJECT_ITEMSTACK);
6✔
3975
                if (value.getRawValue().isEmpty()) {
4!
3976
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3977
                }
3978
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3979
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3980
                        index.getRawValue(), IngredientComponent.ITEMSTACK, itemStack.getRawValue()));
5✔
3981
            }).build());
1✔
3982

3983
    /**
3984
     * Set an ingredient fluid
3985
     */
3986
    public static final IOperator INGREDIENTS_WITH_FLUID = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_FLUIDSTACK
5✔
3987
            .operatorName("with_fluid").symbol("Ingr.with_fluid").interactName("withFluid")
6✔
3988
            .function(variables -> {
1✔
3989
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3990
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3991
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = variables.getValue(2, ValueTypes.OBJECT_FLUIDSTACK);
6✔
3992
                if (value.getRawValue().isEmpty()) {
4!
3993
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3994
                }
3995
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3996
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3997
                        index.getRawValue(), IngredientComponent.FLUIDSTACK, fluidStack.getRawValue()));
5✔
3998
            }).build());
1✔
3999

4000
    /**
4001
     * Set an ingredient energy
4002
     */
4003
    public static final IOperator INGREDIENTS_WITH_ENERGY = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_LONG
5✔
4004
            .operatorName("with_energy").symbol("Ingr.with_energy").interactName("withEnergy")
6✔
4005
            .function(variables -> {
1✔
4006
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4007
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
4008
                ValueTypeLong.ValueLong energy = variables.getValue(2, ValueTypes.LONG);
6✔
4009
                if (value.getRawValue().isEmpty()) {
4!
4010
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4011
                }
4012
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
4013
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
4014
                        index.getRawValue(), IngredientComponent.ENERGY, energy.getRawValue()));
6✔
4015
            }).build());
1✔
4016

4017
    /**
4018
     * Set the list of items
4019
     */
4020
    public static final IOperator INGREDIENTS_WITH_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4021
            .operatorName("with_items").symbol("Ingr.with_items").interactName("withItems")
6✔
4022
            .function(variables -> {
1✔
4023
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4024
                ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4025
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4026
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4027
                }
4028
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4029
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4030
                        IngredientComponent.ITEMSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ITEMSTACK, list)));
2✔
4031
            }).build());
1✔
4032

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

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

4066
    /**
4067
     * ----------------------------------- RECIPE OPERATORS -----------------------------------
4068
     */
4069

4070
    /**
4071
     * The input ingredients of a recipe
4072
     */
4073
    public static final IOperator RECIPE_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4074
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4075
            .operatorInteract("input").symbol("recipe_in")
4✔
4076
            .function(variables -> {
1✔
4077
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4078
                if (value.getRawValue().isPresent()) {
4!
4079
                    return ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(value.getRawValue().get()));
7✔
4080
                }
4081
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4082
            }).build());
1✔
4083

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

4098
    /**
4099
     * Set the input ingredients of a recipe
4100
     */
4101
    public static final IOperator RECIPE_WITH_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4102
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4103
            .operatorName("with_input").symbol("Recipe.with_in").interactName("withInput")
6✔
4104
            .function(variables -> {
1✔
4105
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4106
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4107
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4108
                    IMixedIngredients ingredients = valueIngredients.getRawValue().get();
5✔
4109
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4110
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4111
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4112
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4113
                                .stream()
4✔
4114
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4115
                                .collect(Collectors.toList()));
3✔
4116
                    }
1✔
4117
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4118
                            inputs,
4119
                            valueRecipe.getRawValue().get().getOutput()
5✔
4120
                    ));
4121
                }
4122
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4123
            }).build());
1✔
4124

4125
    /**
4126
     * Set the output ingredients of a recipe
4127
     */
4128
    public static final IOperator RECIPE_WITH_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4129
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4130
            .operatorName("with_output").symbol("Recipe.with_out").interactName("withOutput")
6✔
4131
            .function(variables -> {
1✔
4132
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4133
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4134
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4135
                    IRecipeDefinition recipe = valueRecipe.getRawValue().get();
5✔
4136
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4137
                    for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {
11✔
4138
                        inputs.put(component, (List) recipe.getInputs(component));
7✔
4139
                    }
1✔
4140
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4141
                            inputs,
4142
                            valueIngredients.getRawValue().get()
4✔
4143
                    ));
4144
                }
4145
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4146
            }).build());
1✔
4147

4148
    /**
4149
     * Create a recipe from two the given I/O ingredients
4150
     */
4151
    public static final IOperator RECIPE_WITH_INPUT_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_PREFIX
5✔
4152
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4153
            .operatorName("with_input_output").symbol("Recipe.with_io").interactName("withInputOutput")
6✔
4154
            .function(variables -> {
1✔
4155
                ValueObjectTypeIngredients.ValueIngredients valueIn = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4156
                ValueObjectTypeIngredients.ValueIngredients valueOut = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4157
                if (valueIn.getRawValue().isPresent() && valueOut.getRawValue().isPresent()) {
8!
4158
                    IMixedIngredients ingredients = valueIn.getRawValue().get();
5✔
4159
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4160
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4161
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4162
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4163
                                .stream()
4✔
4164
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4165
                                .collect(Collectors.toList()));
3✔
4166
                    }
1✔
4167
                    try {
4168
                        return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4169
                                inputs,
4170
                                valueOut.getRawValue().get()
4✔
4171
                        ));
4172
                    } catch (IllegalArgumentException e) {
×
4173
                        throw new EvaluationException(Component.literal(e.getMessage()));
×
4174
                    }
4175
                }
4176
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4177
            }).build());
1✔
4178

4179
    /**
4180
     * ------------------------------------ PARSE OPERATORS ------------------------------------
4181
     */
4182

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

4192
    /**
4193
     * Double Parse operator which takes a string of a form Double.parseDouble(),
4194
     * `/([+-]?)(Inf(inity)?|\u221E)/i`, or Long.decode() can consume.
4195
     */
4196
    public static final IOperator PARSE_DOUBLE = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.DOUBLE, v -> {
8✔
4197
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4198
      try {
4199
        return ValueTypeDouble.ValueDouble.of(Double.parseDouble(value.getRawValue()));
5✔
4200
      } catch (NumberFormatException e) {
1✔
4201
        try {
4202
          // \u221E = infinity symbol
4203
          Pattern p = Pattern.compile("\\A([+-]?)(Inf(inity)?|\u221E)\\z", Pattern.CASE_INSENSITIVE);
4✔
4204
          Matcher m = p.matcher(value.getRawValue().trim());
6✔
4205
          if (m.matches()){
3✔
4206
            if (m.group(1).equals("-")){
6✔
4207
              return ValueTypeDouble.ValueDouble.of(Double.NEGATIVE_INFINITY);
3✔
4208
            }
4209
            return ValueTypeDouble.ValueDouble.of(Double.POSITIVE_INFINITY);
3✔
4210
          }
4211
          // Try as a long
4212
          return ValueTypeDouble.ValueDouble.of((double) Long.decode(value.getRawValue()));
7✔
4213
        } catch (NumberFormatException e2) {
1✔
4214
            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4215
                    Component.translatable(ValueTypes.DOUBLE.getTranslationKey())));
3✔
4216
        }
4217
      }
4218
    }));
4219

4220
    /**
4221
     * Integer Parse operator which takes a string of a form Integer.decode() can consume.
4222
     */
4223
    public static final IOperator PARSE_INTEGER = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.INTEGER, v -> {
8✔
4224
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4225
      try{
4226
        return ValueTypeInteger.ValueInteger.of(Integer.decode(value.getRawValue()));
6✔
4227
      } catch (NumberFormatException e) {
1✔
4228
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4229
                  Component.translatable(ValueTypes.INTEGER.getTranslationKey())));
3✔
4230
      }
4231
    }));
4232

4233
    /**
4234
     * Long Parse operator which takes a string of a form Long.decode() can consume.
4235
     */
4236
    public static final IOperator PARSE_LONG = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.LONG, v -> {
8✔
4237
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4238
      try {
4239
        return ValueTypeLong.ValueLong.of(Long.decode(value.getRawValue()));
6✔
4240
      } catch (NumberFormatException e) {
1✔
4241
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4242
                  Component.translatable(ValueTypes.LONG.getTranslationKey())));
3✔
4243
      }
4244
    }));
4245

4246
    /**
4247
     * NBT Parse operator which takes a string of a form ValueTypeNbt().deserialize() can consume.
4248
     */
4249
    public static final IOperator PARSE_NBT = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.NBT, v -> {
8✔
4250
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4251
      try {
4252
        return ValueTypeNbt.ValueNbt.of(Helpers.TAG_PARSER.parseFully(value.getRawValue()));
7✔
4253
      } catch (CommandSyntaxException e) {
1✔
4254
        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4255
                Component.translatable(ValueTypes.NBT.getTranslationKey())));
3✔
4256
      }
4257
    }));
4258

4259
    /**
4260
     * ----------------------------------- GENERAL OPERATORS -----------------------------------
4261
     */
4262

4263
    /**
4264
     * Choice operator with one boolean input, two any inputs and one output any.
4265
     */
4266
    public static final GeneralOperator GENERAL_CHOICE = REGISTRY.register(new GeneralChoiceOperator("?", "choice", "choice"));
10✔
4267

4268
    /**
4269
     * Identity operator with one any input and one any output
4270
     */
4271
    public static final GeneralOperator GENERAL_IDENTITY = REGISTRY.register(new GeneralIdentityOperator("id", "identity", "identity"));
10✔
4272

4273
    /**
4274
     * Constant operator with two any inputs and one any output
4275
     */
4276
    public static final GeneralOperator GENERAL_CONSTANT = REGISTRY.register(new GeneralConstantOperator("K", "constant", "constant"));
11✔
4277

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