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

CyclopsMC / IntegratedDynamics / 14811858327

03 May 2025 02:39PM UTC coverage: 45.566% (+0.2%) from 45.387%
14811858327

push

github

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

2559 of 8405 branches covered (30.45%)

Branch coverage included in aggregate %.

11881 of 23285 relevant lines covered (51.02%)

2.43 hits per line

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

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

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

78
import java.util.*;
79
import java.util.stream.Collectors;
80

81
/**
82
 * Collection of available operators.
83
 *
84
 * @author rubensworks
85
 */
86
public final class Operators {
×
87

88
    public static final IOperatorRegistry REGISTRY = constructRegistry();
2✔
89

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

99
    public static void load() {}
1✔
100

101
    /**
102
     * ----------------------------------- LOGICAL OPERATORS -----------------------------------
103
     */
104

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

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

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

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

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

155
    /**
156
     * ----------------------------------- ARITHMETIC OPERATORS -----------------------------------
157
     */
158

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

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

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

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

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

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

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

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

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

231
    /**
232
     * ----------------------------------- INTEGER OPERATORS -----------------------------------
233
     */
234

235
     private static final ValueTypeInteger.ValueInteger ZERO = ValueTypeInteger.ValueInteger.of(0);
3✔
236

237
    /**
238
     * ----------------------------------- DOUBLE OPERATORS -----------------------------------
239
     */
240

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

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

260
    /**
261
     * ----------------------------------- RELATIONAL OPERATORS -----------------------------------
262
     */
263

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

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

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

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

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

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

343
    /**
344
     * ----------------------------------- BINARY OPERATORS -----------------------------------
345
     */
346

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

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

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

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

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

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

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

416
    /**
417
     * ----------------------------------- STRING OPERATORS -----------------------------------
418
     */
419

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

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

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

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

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

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

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

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

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

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

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

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

590

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

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

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

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

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

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

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

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

764
                    return ValueTypeString.ValueString.of(sb.toString());
4✔
765
                }
766
            }).build()
1✔
767
    );
768

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

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

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

798
    /**
799
     * ----------------------------------- NUMBER OPERATORS -----------------------------------
800
     */
801

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

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

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

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

838
    /**
839
     * ----------------------------------- NULLABLE OPERATORS -----------------------------------
840
     */
841

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

853
    /**
854
     * Check if something is not null
855
     */
856
    public static final IOperator NULLABLE_ISNOTNULL = REGISTRY.register(new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT)
13✔
857
            .apply(NULLABLE_ISNULL).build("∅", "isnotnull", "isNotNull", IConfigRenderPattern.PREFIX_1, "general"));
7✔
858

859
    /**
860
     * ----------------------------------- LIST OPERATORS -----------------------------------
861
     */
862

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1206
                    return ValueTypeList.ValueList.ofList(rawList1.getValueType(), result.stream().toList());
7✔
1207
                }
1208
            }).build());
1✔
1209

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

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

1256
    /**
1257
     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------
1258
     */
1259

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

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

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

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

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

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

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

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

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

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

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

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

1469
    /**
1470
     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------
1471
     */
1472

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1811

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

1823

1824

1825

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

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

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

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

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

1908
                return ValueTypeInteger.ValueInteger.of(count);
3✔
1909
            }).build());
1✔
1910

1911
    /**
1912
     * Item Stack size operator with one input itemstack and one output NBT tag.
1913
     */
1914
    public static final IOperator OBJECT_ITEMSTACK_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1915
            .output(ValueTypes.NBT).symbol("NBT()").operatorName("nbt").interactName("nbt")
8✔
1916
            .function(input -> {
1✔
1917
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1918
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
1919
                return ValueTypeNbt.ValueNbt.of(itemStack.getRawValue().isEmpty() || itemStack.getRawValue().getComponents().isEmpty() ?
11!
1920
                        Optional.empty() :
2✔
1921
                        Optional.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), itemStack.getRawValue().getComponentsPatch()).getOrThrow()));
12✔
1922
            }).build());
1✔
1923

1924
    /**
1925
     * Item Stack has_nbt operator with one input itemstack and one output boolean.
1926
     */
1927
    public static final IOperator OBJECT_ITEMSTACK_HASDATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
1928
            .output(ValueTypes.BOOLEAN).symbol("has_nbt").operatorName("hasnbt").interactName("hasNbt")
9✔
1929
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1930
                    itemStack -> !itemStack.isEmpty() && itemStack.getComponents().stream().anyMatch(t -> !t.type().isTransient())
21!
1931
            )).build());
1✔
1932

1933
    /**
1934
     * Get the data component keys of an itemstack.
1935
     */
1936
    public static final IOperator OBJECT_ITEMSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1937
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
1938
            .function(input -> {
1✔
1939
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1940
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
1941
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
1942
                        itemStack.getRawValue().isEmpty() ?
4!
1943
                                Lists.newArrayList() :
×
1944
                                itemStack.getRawValue().getComponents().keySet().stream()
5✔
1945
                                        .filter(c -> !c.isTransient())
8!
1946
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
1947
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
7✔
1948
                                        .toList());
2✔
1949
            }).build());
1✔
1950

1951
    /**
1952
     * Get the data component value by key
1953
     */
1954
    public static final IOperator OBJECT_ITEMSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1955
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING)
2✔
1956
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
1957
            .function(input -> {
1✔
1958
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1959

1960
                // Determine data component type
1961
                DataComponentType<?> dataComponentType;
1962
                try {
1963
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
1964
                } catch (ResourceLocationException e) {
×
1965
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
1966
                }
1✔
1967

1968
                // Fetch component value
1969
                TypedDataComponent<?> typedComponent = itemStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
1970
                if (typedComponent == null) {
2✔
1971
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
1972
                }
1973

1974
                // Encode component value
1975
                try {
1976
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
1977
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
1978
                } catch (IllegalStateException e) {
×
1979
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
1980
                }
1981
            }).build());
1✔
1982

1983
    /**
1984
     * Get the data component value by key
1985
     */
1986
    public static final IOperator OBJECT_ITEMSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_3
18✔
1987
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
1988
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
1989
            .function(input -> {
1✔
1990
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1991

1992
                // Skip further processing if input value is empty
1993
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
1994
                if (!tagOptional.isPresent()) {
3!
1995
                    return itemStack;
×
1996
                }
1997

1998
                // Determine data component type
1999
                DataComponentType<?> dataComponentType;
2000
                try {
2001
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2002
                } catch (ResourceLocationException e) {
×
2003
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2004
                }
1✔
2005

2006
                // Encode component value
2007
                try {
2008
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2009
                    itemStack = ValueObjectTypeItemStack.ValueItemStack.of(itemStack.getRawValue().copy());
5✔
2010
                    itemStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2011
                    return itemStack;
2✔
2012
                } catch (IllegalStateException e) {
×
2013
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2014
                }
2015
            }).build());
1✔
2016

2017
    /**
2018
     * Get the tooltip of an itemstack in list form.
2019
     */
2020
    public static final IOperator OBJECT_ITEMSTACK_TOOLTIP = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
2021
            .output(ValueTypes.LIST).symbol("tooltip").operatorName("tooltip").interactName("tooltip")
8✔
2022
            .function(input -> {
1✔
2023
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
2024
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
4✔
2025
                        itemStack.getRawValue().getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.Default.NORMAL).stream()
7✔
2026
                                .map(c -> ValueTypeString.ValueString.of(c.getString()))
5✔
2027
                                .toList());
1✔
2028
            }).build());
1✔
2029
    /**
2030
     * Get the tooltip of an itemstack in list form, using the provided player entity as the player context.
2031
     */
2032
    public static final IOperator OBJECT_ITEMSTACK_ENTITY_TOOLTIP = REGISTRY.register(OperatorBuilders.ENTITY_1_ITEMSTACK_1
14✔
2033
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2034
            .output(ValueTypes.LIST).symbol("entity_item_tooltip").operatorName("entityitemtooltip").interactName("entityItemTooltip")
8✔
2035
            .function(variables -> {
1✔
2036
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2037
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
2038
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2039
                    Player entity = (Player) a.getRawValue().get();
×
2040
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
×
2041
                            itemStack.getRawValue().getTooltipLines(Item.TooltipContext.of(entity.level()), entity, TooltipFlag.Default.NORMAL).stream()
×
2042
                                    .map(c -> ValueTypeString.ValueString.of(c.getString()))
×
2043
                                    .toList());
×
2044
                }
2045
                return ValueTypes.LIST.getDefault();
×
2046
            }).build());
1✔
2047

2048
    /**
2049
     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------
2050
     */
2051

2052
    /**
2053
     * If the entity is a mob
2054
     */
2055
    public static final IOperator OBJECT_ENTITY_ISMOB = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2056
            .output(ValueTypes.BOOLEAN).symbol("is_mob").operatorName("ismob").interactName("isMob")
9✔
2057
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2058
                entity -> entity instanceof Enemy
4✔
2059
            )).build());
1✔
2060

2061
    /**
2062
     * If the entity is an animal
2063
     */
2064
    public static final IOperator OBJECT_ENTITY_ISANIMAL = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2065
            .output(ValueTypes.BOOLEAN).symbol("is_animal").operatorName("isanimal").interactName("isAnimal")
9✔
2066
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2067
                entity -> entity instanceof Animal && !(entity instanceof Enemy)
11!
2068
            )).build());
1✔
2069

2070
    /**
2071
     * If the entity is an item
2072
     */
2073
    public static final IOperator OBJECT_ENTITY_ISITEM = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2074
            .output(ValueTypes.BOOLEAN).symbol("is_item").operatorName("isitem").interactName("isItem")
9✔
2075
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2076
                entity -> entity instanceof ItemEntity
4✔
2077
            )).build());
1✔
2078

2079
    /**
2080
     * If the entity is a player
2081
     */
2082
    public static final IOperator OBJECT_ENTITY_ISPLAYER = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2083
            .output(ValueTypes.BOOLEAN).symbol("is_player").operatorName("isplayer").interactName("isPlayer")
9✔
2084
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2085
                entity -> entity instanceof Player
4✔
2086
            )).build());
1✔
2087

2088
    /**
2089
     * If the entity is a minecart
2090
     */
2091
    public static final IOperator OBJECT_ENTITY_ISMINECART = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2092
            .output(ValueTypes.BOOLEAN).symbol("is_minecart").operatorName("isminecart").interactName("isMinecart")
9✔
2093
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2094
                entity -> entity instanceof AbstractMinecart
×
2095
            )).build());
1✔
2096

2097
    /**
2098
     * The itemstack from the entity
2099
     */
2100
    public static final IOperator OBJECT_ENTITY_ITEMSTACK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX
5✔
2101
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("item")
4✔
2102
            .function(variables -> {
1✔
2103
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2104
                Optional<Entity> a = valueEntity.getRawValue();
3✔
2105
                return ValueObjectTypeItemStack.ValueItemStack.of((a.isPresent() && a.get() instanceof ItemEntity) ? ((ItemEntity) a.get()).getItem() : ItemStack.EMPTY);
15!
2106
            }).build());
1✔
2107

2108
    /**
2109
     * The entity health
2110
     */
2111
    public static final IOperator OBJECT_ENTITY_HEALTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2112
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("health")
5✔
2113
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2114
                entity -> entity instanceof LivingEntity ? ((LivingEntity) entity).getHealth() : 0.0
11✔
2115
            )).build());
1✔
2116

2117
    /**
2118
     * The entity width
2119
     */
2120
    public static final IOperator OBJECT_ENTITY_WIDTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2121
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("width")
5✔
2122
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2123
                entity -> entity != null ? entity.getBbWidth() : 0.0
8!
2124
            )).build());
1✔
2125

2126
    /**
2127
     * The entity width
2128
     */
2129
    public static final IOperator OBJECT_ENTITY_HEIGHT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2130
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("height")
5✔
2131
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2132
                entity -> entity != null ? entity.getBbHeight() : 0.0
8!
2133
            )).build());
1✔
2134

2135
    /**
2136
     * If the entity is burning
2137
     */
2138
    public static final IOperator OBJECT_ENTITY_ISBURNING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2139
            .output(ValueTypes.BOOLEAN).symbol("is_burning").operatorName("isburning").interactName("entityIsBurning")
9✔
2140
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2141
                entity -> entity != null && entity.isOnFire()
10!
2142
            )).build());
1✔
2143

2144
    /**
2145
     * If the entity is wet
2146
     */
2147
    public static final IOperator OBJECT_ENTITY_ISWET = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2148
            .output(ValueTypes.BOOLEAN).symbol("is_wet").operatorName("iswet").interactName("isWet")
9✔
2149
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2150
                entity -> entity != null && entity.isInWaterOrRain()
10!
2151
            )).build());
1✔
2152

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

2162
    /**
2163
     * If the entity is eating
2164
     */
2165
    public static final IOperator OBJECT_ENTITY_ISEATING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2166
            .output(ValueTypes.BOOLEAN).symbol("is_eating").operatorName("iseating").interactName("isEating")
9✔
2167
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2168
                entity -> entity instanceof LivingEntity && ((LivingEntity) entity).getUseItemRemainingTicks() > 0
12!
2169
            )).build());
1✔
2170

2171
    /**
2172
     * The list of armor itemstacks from an entity
2173
     */
2174
    public static final IOperator OBJECT_ENTITY_ARMORINVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2175
            .output(ValueTypes.LIST).symbol("armor_inventory").operatorName("armorinventory").interactName("armorInventory")
8✔
2176
            .function(variables -> {
1✔
2177
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2178
                Optional<Entity> a = valueEntity.getRawValue();
×
2179
                if(a.isPresent()) {
×
2180
                    Entity entity = a.get();
×
2181
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityArmorInventory(entity.level(), entity));
×
2182
                } else {
2183
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2184
                }
2185
            }).build());
1✔
2186

2187
    /**
2188
     * The list of itemstacks from an entity
2189
     */
2190
    public static final IOperator OBJECT_ENTITY_INVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2191
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
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 ValueTypeListProxyEntityInventory(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 name of the mod owning this entity
2205
     */
2206
    public static final IOperator OBJECT_ENTITY_MODNAME = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2207
            .symbolOperatorInteract("mod")
13✔
2208
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2209
                    (OperatorBase.SafeVariablesGetter variables) -> {
2210
                        ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2211
                        if(a.getRawValue().isPresent()) {
4!
2212
                            Entity entity = a.getRawValue().get();
5✔
2213
                            return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());
5✔
2214
                        }
2215
                        return ResourceLocation.parse("");
×
2216
                    },
2217
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2218
            )))
2219
            .build());
1✔
2220

2221
    /**
2222
     * The block the given player is currently looking at.
2223
     */
2224
    public static final IOperator OBJECT_PLAYER_TARGETBLOCK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_BLOCK)
7✔
2225
            .symbol("target_block").operatorName("targetblock").interactName("targetBlock")
6✔
2226
            .function(variables -> {
1✔
2227
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2228
                BlockState blockState = null;
×
2229
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2230
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2231
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.BLOCK_INTERACTION_RANGE);
×
2232
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2233
                    double eyeHeight = entity.getEyeHeight();
×
2234
                    Vec3 lookVec = entity.getLookAngle();
×
2235
                    Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());
×
2236
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2237

2238
                    ClipContext rayTraceContext = new ClipContext(origin, direction, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity);
×
2239
                    BlockHitResult mop = entity.level().clip(rayTraceContext);
×
2240
                    if(mop != null && mop.getType() == HitResult.Type.BLOCK) {
×
2241
                        blockState = entity.level().getBlockState(mop.getBlockPos());
×
2242
                    }
2243
                }
2244
                return ValueObjectTypeBlock.ValueBlock.of(blockState);
×
2245
            }).build());
1✔
2246

2247
    /**
2248
     * The entity the given player is currently looking at.
2249
     */
2250
    public static final IOperator OBJECT_PLAYER_TARGETENTITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ENTITY)
7✔
2251
            .symbol("target_entity").operatorName("targetentity").interactName("targetEntity")
6✔
2252
            .function(variables -> {
1✔
2253
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2254
                Entity entityOut = null;
×
2255
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2256
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2257
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.ENTITY_INTERACTION_RANGE);
×
2258
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2259

2260
                    // Copied and modified from GameRenderer#getMouseOver
2261
                    Vec3 origin = entity.getEyePosition(1.0F);
×
2262
                    double reachDistanceSquared = reachDistance * reachDistance;
×
2263

2264
                    Vec3 lookVec = entity.getViewVector(1.0F);
×
2265
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2266
                    AABB boundingBox = entity.getBoundingBox()
×
2267
                            .expandTowards(lookVec.scale(reachDistance))
×
2268
                            .inflate(1.0D, 1.0D, 1.0D);
×
2269
                    EntityHitResult entityraytraceresult = ProjectileUtil.getEntityHitResult(entity, origin, direction,
×
2270
                            boundingBox, e -> !e.isSpectator() && e.isPickable(), reachDistanceSquared);
×
2271
                    if (entityraytraceresult != null) {
×
2272
                        Entity entity1 = entityraytraceresult.getEntity();
×
2273
                        Vec3 vec3d3 = entityraytraceresult.getLocation();
×
2274
                        double distanceSquared = origin.distanceToSqr(vec3d3);
×
2275
                        if (distanceSquared < reachDistanceSquared
×
2276
                                && (entity1 instanceof LivingEntity || entity1 instanceof ItemFrame)) {
2277
                            entityOut = entity1;
×
2278
                        }
2279
                    }
2280
                }
2281
                return ValueObjectTypeEntity.ValueEntity.of(entityOut);
×
2282
            }).build());
1✔
2283

2284
    /**
2285
     * If the given player has an external gui open.
2286
     */
2287
    public static final IOperator OBJECT_PLAYER_HASGUIOPEN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2288
            .symbol("has_gui_open").operatorName("hasguiopen").interactName("hasGuiOpen")
6✔
2289
            .function(variables -> {
1✔
2290
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2291
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2292
                    Player entity = (Player) a.getRawValue().get();
×
2293
                    return ValueTypeBoolean.ValueBoolean.of(entity.containerMenu != entity.inventoryMenu);
×
2294
                }
2295
                return ValueTypeBoolean.ValueBoolean.of(false);
×
2296
            }).build());
1✔
2297

2298
    /**
2299
     * The item the given entity is currently holding in its main hand.
2300
     */
2301
    public static final IOperator OBJECT_ENTITY_HELDITEM_MAIN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2302
            .symbol("held_item_1").operatorName("helditem").interactName("heldItem")
6✔
2303
            .function(variables -> {
1✔
2304
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2305
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2306
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2307
                    itemStack = ((LivingEntity) a.getRawValue().get()).getMainHandItem();
6✔
2308
                }
2309
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2310
            }).build());
1✔
2311

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

2326
    /**
2327
     * The entity's mounted entity
2328
     */
2329
    public static final IOperator OBJECT_ENTITY_MOUNTED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.LIST)
7✔
2330
            .symbolOperator("mounted").interactName("mounted")
4✔
2331
            .function(variables -> {
1✔
2332
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2333
                List<ValueObjectTypeEntity.ValueEntity> passengers = Lists.newArrayList();
2✔
2334
                if(a.getRawValue().isPresent()) {
4!
2335
                    for (Entity passenger : a.getRawValue().get().getPassengers()) {
14✔
2336
                        passengers.add(ValueObjectTypeEntity.ValueEntity.of(passenger));
5✔
2337
                    }
1✔
2338
                }
2339
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, passengers);
4✔
2340
            }).build());
1✔
2341

2342
    /**
2343
     * The item frame's contents
2344
     */
2345
    public static final IOperator OBJECT_ITEMFRAME_CONTENTS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2346
            .symbol("itemframe_contents").operatorName("itemframecontents").interactName("itemFrameContents")
6✔
2347
            .function(variables -> {
1✔
2348
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2349
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2350
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2351
                    itemStack = ((ItemFrame) a.getRawValue().get()).getItem();
6✔
2352
                }
2353
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2354
            }).build());
1✔
2355

2356
    /**
2357
     * The item frame's rotation
2358
     */
2359
    public static final IOperator OBJECT_ITEMFRAME_ROTATION = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2360
            .symbol("itemframe_rotation").operatorName("itemframerotation").interactName("itemFrameRotation")
6✔
2361
            .function(variables -> {
1✔
2362
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2363
                Integer rotation = 0;
3✔
2364
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2365
                    rotation = ((ItemFrame) a.getRawValue().get()).getRotation();
7✔
2366
                }
2367
                return ValueTypeInteger.ValueInteger.of(rotation);
4✔
2368
            }).build());
1✔
2369

2370
    /**
2371
     * The hurtsound of this entity.
2372
     */
2373
    public static final IOperator OBJECT_ENTITY_HURTSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2374
            .symbolOperator("hurtsound").interactName("hurtSound")
4✔
2375
            .function(variables -> {
1✔
2376
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2377
                String hurtSound = "";
2✔
2378
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2379
                    String sound = ((LivingEntity) a.getRawValue().get()).getHurtSound(a.getRawValue().get().damageSources().generic()).location().toString();
14✔
2380
                    if (sound != null) {
2!
2381
                        hurtSound = sound;
2✔
2382
                    }
2383
                }
2384
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2385
            }).build());
1✔
2386

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

2404
    /**
2405
     * The age of this entity.
2406
     */
2407
    public static final IOperator OBJECT_ENTITY_AGE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2408
            .symbolOperatorInteract("age")
2✔
2409
            .function(variables -> {
1✔
2410
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2411
                int age = 0;
2✔
2412
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2413
                    age = ((LivingEntity) a.getRawValue().get()).getNoActionTime();
6✔
2414
                }
2415
                return ValueTypeInteger.ValueInteger.of(age);
3✔
2416
            }).build());
1✔
2417

2418
    /**
2419
     * If the entity is a child.
2420
     */
2421
    public static final IOperator OBJECT_ENTITY_ISCHILD = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2422
            .symbol("is_child").operatorName("ischild").interactName("isChild")
6✔
2423
            .function(variables -> {
1✔
2424
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2425
                boolean child = false;
2✔
2426
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2427
                    child = ((LivingEntity) a.getRawValue().get()).isBaby();
6✔
2428
                }
2429
                return ValueTypeBoolean.ValueBoolean.of(child);
3✔
2430
            }).build());
1✔
2431

2432
    /**
2433
     * If the entity can be bred.
2434
     */
2435
    public static final IOperator OBJECT_ENTITY_CANBREED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2436
            .symbol("canbreed").operatorName("canbreed").interactName("canBreed")
6✔
2437
            .function(variables -> {
1✔
2438
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2439
                boolean canBreed = false;
2✔
2440
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof AgeableMob) {
9!
2441
                    canBreed = ((AgeableMob) a.getRawValue().get()).getAge() == 0;
10✔
2442
                }
2443
                return ValueTypeBoolean.ValueBoolean.of(canBreed);
3✔
2444
            }).build());
1✔
2445

2446
    /**
2447
     * If the entity is in love.
2448
     */
2449
    public static final IOperator OBJECT_ENTITY_ISINLOVE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2450
            .symbol("is_in_love").operatorName("isinlove").interactName("isInLove")
6✔
2451
            .function(variables -> {
1✔
2452
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2453
                boolean inLove = false;
2✔
2454
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof Animal) {
9!
2455
                    inLove = ((Animal) a.getRawValue().get()).isInLove();
6✔
2456
                }
2457
                return ValueTypeBoolean.ValueBoolean.of(inLove);
3✔
2458
            }).build());
1✔
2459

2460
    /**
2461
     * If the entity can be bred with the given item.
2462
     */
2463
    public static final IOperator OBJECT_ENTITY_CANBREEDWITH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
14✔
2464
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2465
            .output(ValueTypes.BOOLEAN)
2✔
2466
            .symbol("can_breed_with").operatorName("canbreedwith").interactName("canBreedWith")
6✔
2467
            .renderPattern(IConfigRenderPattern.INFIX_LONG)
2✔
2468
            .function(variables -> {
1✔
2469
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2470
                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
2471
                boolean canBreedWith = false;
2✔
2472
                if (a.getRawValue().isPresent() && !b.getRawValue().isEmpty() && a.getRawValue().get() instanceof Animal) {
13!
2473
                    canBreedWith = ((Animal) a.getRawValue().get()).isFood(b.getRawValue());
8✔
2474
                }
2475
                return ValueTypeBoolean.ValueBoolean.of(canBreedWith);
3✔
2476
            }).build());
1✔
2477

2478
    /**
2479
     * If the entity is shearable.
2480
     */
2481
    public static final IOperator OBJECT_ENTITY_ISSHEARABLE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2482
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
2483
            .function(variables -> {
1✔
2484
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2485
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
2486
                        && a.getRawValue().get() instanceof IShearable
5✔
2487
                        && ((IShearable) a.getRawValue().get()).isShearable(null, ItemStack.EMPTY, null, null));
12✔
2488
            }).build());
1✔
2489

2490
    /**
2491
     * The entity serialized to NBT.
2492
     */
2493
    public static final IOperator OBJECT_ENTITY_NBT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2494
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2495
            .function(input -> {
1✔
2496
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2497
                try {
2498
                    if (entity.getRawValue().isPresent()) {
4!
2499
                        return ValueTypeNbt.ValueNbt.of(entity.getRawValue().get().serializeNBT(ServerLifecycleHooks.getCurrentServer().registryAccess()));
9✔
2500
                    }
2501
                } catch (Exception e) {
×
2502
                    // Catch possible errors during NBT writing
2503
                }
×
2504
                return ValueTypes.NBT.getDefault();
×
2505
            }).build());
1✔
2506

2507
    /**
2508
     * The entity type.
2509
     */
2510
    public static final IOperator OBJECT_ENTITY_TYPE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2511
            .output(ValueTypes.STRING).symbol("entity_type").operatorName("entitytype").interactName("type")
8✔
2512
            .function(input -> {
1✔
2513
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2514
                String entityType = "";
2✔
2515
                if (entity.getRawValue().isPresent()) {
4!
2516
                    Entity e = entity.getRawValue().get();
5✔
2517
                    entityType = BuiltInRegistries.ENTITY_TYPE.getKey(e.getType()).toString();
6✔
2518
                }
2519
                return ValueTypeString.ValueString.of(entityType);
3✔
2520
            }).build());
1✔
2521

2522
    /**
2523
     * The entity items.
2524
     */
2525
    public static final IOperator OBJECT_ENTITY_ITEMS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2526
            .output(ValueTypes.LIST).symbol("entity_items").operatorName("entityitems").interactName("items")
8✔
2527
            .function(input -> {
1✔
2528
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2529
                Optional<Entity> a = valueEntity.getRawValue();
×
2530
                if(a.isPresent()) {
×
2531
                    Entity entity = a.get();
×
2532
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityItems(entity.level(), entity, null));
×
2533
                } else {
2534
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.emptyList());
×
2535
                }
2536
            }).build());
1✔
2537

2538
    /**
2539
     * The entity fluids.
2540
     */
2541
    public static final IOperator OBJECT_ENTITY_FLUIDS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2542
            .output(ValueTypes.LIST).symbol("entity_fluids").operatorName("entityfluids").interactName("fluids")
8✔
2543
            .function(input -> {
1✔
2544
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2545
                Optional<Entity> a = valueEntity.getRawValue();
×
2546
                if(a.isPresent()) {
×
2547
                    Entity entity = a.get();
×
2548
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityFluids(entity.level(), entity, null));
×
2549
                } else {
2550
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, Collections.emptyList());
×
2551
                }
2552
            }).build());
1✔
2553

2554
    /**
2555
     * The entity energy stored.
2556
     */
2557
    public static final IOperator OBJECT_ENTITY_ENERGY_STORED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2558
            .output(ValueTypes.INTEGER).symbol("entity_stored_fe").operatorName("entityenergystored").interactName("energy")
8✔
2559
            .function(input -> {
1✔
2560
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2561
                Optional<Entity> a = valueEntity.getRawValue();
×
2562
                if(a.isPresent()) {
×
2563
                    Entity entity = a.get();
×
2564
                    IEnergyStorage energyStorage = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
×
2565
                    return ValueTypeInteger.ValueInteger.of(energyStorage != null ? energyStorage.getEnergyStored() : 0);
×
2566
                }
2567
                return ValueTypeInteger.ValueInteger.of(0);
×
2568
            }).build());
1✔
2569

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

2586
    /**
2587
     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------
2588
     */
2589

2590
    /**
2591
     * The amount of fluid in the fluidstack
2592
     */
2593
    public static final IOperator OBJECT_FLUIDSTACK_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2594
            .output(ValueTypes.INTEGER).symbolOperatorInteract("amount")
5✔
2595
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2596
                    FluidStack::getAmount
2597
            )).build());
1✔
2598

2599
    /**
2600
     * The block from the fluidstack
2601
     */
2602
    public static final IOperator OBJECT_FLUIDSTACK_BLOCK = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2603
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
2604
            .function(variables -> {
1✔
2605
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2606
                FluidStack a = valueFluidStack.getRawValue();
3✔
2607
                return ValueObjectTypeBlock.ValueBlock.of(!a.isEmpty() ? a.getFluid().getFluidType().getStateForPlacement(null, null, a).createLegacyBlock() : null);
14!
2608
            }).build());
1✔
2609

2610
    /**
2611
     * The fluidstack luminosity
2612
     */
2613
    public static final IOperator OBJECT_FLUIDSTACK_LIGHT_LEVEL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2614
            .output(ValueTypes.INTEGER).symbolOperator("light_level").interactName("lightLevel")
7✔
2615
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2616
                fluidStack -> fluidStack.getFluid().getFluidType().getLightLevel(fluidStack)
7✔
2617
            )).build());
1✔
2618

2619
    /**
2620
     * The fluidstack density
2621
     */
2622
    public static final IOperator OBJECT_FLUIDSTACK_DENSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2623
            .output(ValueTypes.INTEGER).symbolOperatorInteract("density")
5✔
2624
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2625
                fluidStack -> fluidStack.getFluid().getFluidType().getDensity(fluidStack)
7✔
2626
            )).build());
1✔
2627

2628
    /**
2629
     * The fluidstack temperature
2630
     */
2631
    public static final IOperator OBJECT_FLUIDSTACK_TEMPERATURE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2632
            .output(ValueTypes.INTEGER).symbolOperatorInteract("temperature")
5✔
2633
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2634
                    fluidStack -> fluidStack.getFluid().getFluidType().getTemperature(fluidStack)
7✔
2635
            )).build());
1✔
2636

2637
    /**
2638
     * The fluidstack viscosity
2639
     */
2640
    public static final IOperator OBJECT_FLUIDSTACK_VISCOSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2641
            .output(ValueTypes.INTEGER).symbolOperatorInteract("viscosity")
5✔
2642
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2643
                fluidStack -> fluidStack.getFluid().getFluidType().getViscosity(fluidStack)
7✔
2644
            )).build());
1✔
2645

2646
    /**
2647
     * If the fluidstack is gaseous
2648
     */
2649
    public static final IOperator OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2650
            .output(ValueTypes.BOOLEAN).symbolOperator("lighter_than_air").interactName("isLighterThanAir")
7✔
2651
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_BOOLEAN.build(
2✔
2652
                fluidStack -> fluidStack.getFluid().getFluidType().isLighterThanAir()
6✔
2653
            )).build());
1✔
2654

2655
    /**
2656
     * The rarity of the fluidstack
2657
     */
2658
    public static final IOperator OBJECT_FLUIDSTACK_RARITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2659
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
2660
            .function(variables -> {
1✔
2661
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2662
                FluidStack a = valueFluidStack.getRawValue();
3✔
2663
                return ValueTypeString.ValueString.of(a.getFluid().getFluidType().getRarity(a).name());
8✔
2664
            }).build());
1✔
2665

2666
    /**
2667
     * The bucket empty sound of the fluidstack
2668
     */
2669
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2670
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_empty").interactName("bucketEmptySound")
6✔
2671
            .function(variables -> {
1✔
2672
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2673
                FluidStack a = valueFluidStack.getRawValue();
3✔
2674
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_EMPTY))
10✔
2675
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2676
                        .orElse(""));
2✔
2677
            }).build());
1✔
2678

2679
    /**
2680
     * The fluid vaporize sound of the fluidstack
2681
     */
2682
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2683
            .output(ValueTypes.STRING).symbolOperator("sound_fluid_vaporize").interactName("fluidVaporizeSound")
6✔
2684
            .function(variables -> {
1✔
2685
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2686
                FluidStack a = valueFluidStack.getRawValue();
3✔
2687
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.FLUID_VAPORIZE))
10✔
2688
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2689
                        .orElse(""));
2✔
2690
            }).build());
1✔
2691

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

2705
    /**
2706
     * The bucket of the fluidstack
2707
     */
2708
    public static final IOperator OBJECT_FLUIDSTACK_BUCKET = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2709
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("bucket")
4✔
2710
            .function(variables -> {
1✔
2711
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2712
                FluidStack a = valueFluidStack.getRawValue();
3✔
2713
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getFluid().getFluidType().getBucket(a));
7✔
2714
            }).build());
1✔
2715

2716
    /**
2717
     * If the fluid types of the two given fluidstacks are equal
2718
     */
2719
    public static final IOperator OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
5✔
2720
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwfluidequal").interactName("isRawEqual")
8✔
2721
            .function(variables -> {
1✔
2722
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack0 = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2723
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack1 = variables.getValue(1, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2724
                return ValueTypeBoolean.ValueBoolean.of(FluidStack.isSameFluid(valueFluidStack0.getRawValue(), valueFluidStack1.getRawValue()));
7✔
2725
            }).build());
1✔
2726

2727
    /**
2728
     * The name of the mod owning this fluid
2729
     */
2730
    public static final IOperator OBJECT_FLUIDSTACK_MODNAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2731
            .symbolOperatorInteract("mod")
13✔
2732
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2733
                    (OperatorBase.SafeVariablesGetter variables) -> {
2734
                        ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2735
                        return BuiltInRegistries.FLUID.getKey(a.getRawValue().getFluid());
6✔
2736
                    },
2737
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2738
            ))).build());
1✔
2739

2740
    /**
2741
     * The tag of the given fluidstack.
2742
     */
2743
    public static final IOperator OBJECT_FLUIDSTACK_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2744
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2745
            .function(input -> {
1✔
2746
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2747
                if (fluidStack.getRawValue().getComponentsPatch().isEmpty()) {
5✔
2748
                    return ValueTypeNbt.ValueNbt.of(Optional.empty());
3✔
2749
                }
2750
                return ValueTypeNbt.ValueNbt.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), fluidStack.getRawValue().getComponentsPatch()).getOrThrow());
13✔
2751
            }).build());
1✔
2752

2753
    /**
2754
     * Create a new fluidstack with the given amount.
2755
     */
2756
    public static final IOperator OBJECT_FLUIDSTACK_WITH_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
14✔
2757
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.INTEGER)
2✔
2758
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("with_amount").interactName("withAmount")
6✔
2759
            .function(variables -> {
1✔
2760
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2761
                ValueTypeInteger.ValueInteger valueInteger = variables.getValue(1, ValueTypes.INTEGER);
6✔
2762
                FluidStack fluidStack = valueFluidStack.getRawValue().copy();
4✔
2763
                fluidStack.setAmount(valueInteger.getRawValue());
4✔
2764
                return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
2765
            }).build());
1✔
2766

2767
    /**
2768
     * Get the data component keys of an fluidstack.
2769
     */
2770
    public static final IOperator OBJECT_FLUIDSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2771
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
2772
            .function(input -> {
1✔
2773
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2774
                // Explicitly check for fluid emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2775
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
2776
                        fluidStack.getRawValue().isEmpty() ?
4!
2777
                                Lists.newArrayList() :
×
2778
                                fluidStack.getRawValue().getComponents().keySet().stream()
5✔
2779
                                        .filter(c -> !c.isTransient())
8!
2780
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
2781
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
1✔
2782
                                        .toList());
2✔
2783
            }).build());
1✔
2784

2785
    /**
2786
     * Get the data component value by key
2787
     */
2788
    public static final IOperator OBJECT_FLUIDSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2_LONG
14✔
2789
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING)
2✔
2790
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
2791
            .function(input -> {
1✔
2792
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2793

2794
                // Determine data component type
2795
                DataComponentType<?> dataComponentType;
2796
                try {
2797
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2798
                } catch (ResourceLocationException e) {
×
2799
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2800
                }
1✔
2801

2802
                // Fetch component value
2803
                TypedDataComponent<?> typedComponent = fluidStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
2804
                if (typedComponent == null) {
2✔
2805
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
2806
                }
2807

2808
                // Encode component value
2809
                try {
2810
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
2811
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
2812
                } catch (IllegalStateException e) {
×
2813
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
2814
                }
2815
            }).build());
1✔
2816

2817
    /**
2818
     * Set the data component value by key
2819
     */
2820
    public static final IOperator OBJECT_FLUIDSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_3
18✔
2821
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2822
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2823
            .function(input -> {
1✔
2824
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2825

2826
                // Skip further processing if input value is empty
2827
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2828
                if (!tagOptional.isPresent()) {
3!
2829
                    return fluidStack;
×
2830
                }
2831

2832
                // Determine data component type
2833
                DataComponentType<?> dataComponentType;
2834
                try {
2835
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2836
                } catch (ResourceLocationException e) {
×
2837
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2838
                }
1✔
2839

2840
                // Encode component value
2841
                try {
2842
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2843
                    fluidStack = ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack.getRawValue().copy());
5✔
2844
                    fluidStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2845
                    return fluidStack;
2✔
2846
                } catch (IllegalStateException e) {
×
2847
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2848
                }
2849
            }).build());
1✔
2850

2851
    /**
2852
     * The tag entries of the given fluidstack
2853
     */
2854
    public static final IOperator OBJECT_FLUIDSTACK_TAG = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2855
            .output(ValueTypes.LIST)
2✔
2856
            .symbol("fluid_tag_names").operatorName("tag").interactName("tags")
6✔
2857
            .function(variables -> {
1✔
2858
                ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2859
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
2860
                if(!a.getRawValue().isEmpty()) {
4✔
2861
                    a.getRawValue().getFluid().builtInRegistryHolder().tags()
7✔
2862
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
2863
                                    .of(owningTag.location().toString())));
3✔
2864
                }
2865
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
2866
            }).build());
1✔
2867

2868
    /**
2869
     * Get a list of fluidstacks that correspond to the given tag key.
2870
     */
2871
    public static final IOperator OBJECT_FLUIDSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
2872
            .output(ValueTypes.LIST)
2✔
2873
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2874
            .symbol("fluid_tag_values").operatorName("fluidtag").interactName("fluidsByTag")
6✔
2875
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
2876
            .function(variables -> {
1✔
2877
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
2878
                ImmutableList.Builder<ValueObjectTypeFluidStack.ValueFluidStack> builder = ImmutableList.builder();
2✔
2879
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
2880
                    try {
2881
                        Helpers.getFluidTagValues(a.getRawValue())
4✔
2882
                                .map(ValueObjectTypeFluidStack.ValueFluidStack::of)
3✔
2883
                                .forEach(builder::add);
4✔
2884
                    } catch (ResourceLocationException e) {
×
2885
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
2886
                    }
1✔
2887
                }
2888
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, builder.build());
5✔
2889
            }).build());
1✔
2890

2891
    /**
2892
     * ----------------------------------- OPERATOR OPERATORS -----------------------------------
2893
     */
2894

2895
    /**
2896
     * Apply for a given operator a given value.
2897
     */
2898
    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2899
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2900
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply")
9✔
2901
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY))
4✔
2902
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2903
                    input -> {
2904
                        IOperator innerOperator = input.getLeft();
4✔
2905
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2906
                        IVariable variable = variables.getVariables()[0];
5✔
2907
                        return ValueHelpers.evaluateOperator(innerOperator, variable);
9✔
2908
                    })).build());
1✔
2909
    static {
2910
        REGISTRY.registerSerializer(new CurriedOperator.Serializer());
5✔
2911
    }
2912

2913
    /**
2914
     * Apply for a given operator the given 2 values.
2915
     */
2916
    public static final IOperator OPERATOR_APPLY_2 = REGISTRY.register(OperatorBuilders.OPERATOR
5✔
2917
            .renderPattern(IConfigRenderPattern.INFIX_2)
2✔
2918
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
15✔
2919
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
2920
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply2")
13✔
2921
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
2922
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2923
                    input -> {
2924
                        IOperator innerOperator = input.getLeft();
4✔
2925
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2926
                        IVariable variable0 = variables.getVariables()[0];
5✔
2927
                        IVariable variable1 = variables.getVariables()[1];
5✔
2928
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1);
13✔
2929
                    })).build());
1✔
2930

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

2950
    /**
2951
     * Apply for a given operator the given list of values.
2952
     */
2953
    public static final IOperator OPERATOR_APPLY_N = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2954
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST)
11✔
2955
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.LIST)
2✔
2956
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply_n")
9✔
2957
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.LIST))
4✔
2958
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
2959
                    input -> {
2960
                        IOperator innerOperator = input.getLeft();
4✔
2961
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2962
                        IValueTypeListProxy<IValueType<IValue>, IValue> list = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
2963
                        return ValueHelpers.evaluateOperator(innerOperator, Iterables.toArray(list, IValue.class));
7✔
2964
                    })).build());
1✔
2965

2966
    /**
2967
     * Apply for a given operator with zero arguments.
2968
     */
2969
    public static final IOperator OPERATOR_APPLY_0 = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
2970
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2971
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply0")
5✔
2972
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(new IValueType[0]))
4✔
2973
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2974
                    input -> {
2975
                        IOperator innerOperator = input.getLeft();
4✔
2976
                        return ValueHelpers.evaluateOperator(innerOperator, new IVariable[0]);
5✔
2977
                    })).build());
1✔
2978

2979
    /**
2980
     * Apply the given operator on all elements of a list, resulting in a new list of mapped values.
2981
     */
2982
    public static final IOperator OPERATOR_MAP = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
2983
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
2984
            .output(ValueTypes.LIST).symbolOperatorInteract("map")
5✔
2985
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
2986
                    input -> {
2987
                        final IOperator innerOperator = input.getLeft();
4✔
2988
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2989
                        ValueTypeList.ValueList inputList = variables.getValue(0, ValueTypes.LIST);
6✔
2990
                        return ValueTypeList.ValueList.ofFactory(
6✔
2991
                                new ValueTypeListProxyOperatorMapped(innerOperator, inputList.getRawValue()));
2✔
2992
                    })).build());
1✔
2993

2994
    /**
2995
     * Filter a list of elements by matching them all with the given predicate.
2996
     */
2997
    public static final IOperator OPERATOR_FILTER = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
2998
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
2999
            .output(ValueTypes.LIST).symbolOperatorInteract("filter")
7✔
3000
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
3001
                    new IOperatorValuePropagator<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue>() {
3✔
3002
                        @Override
3003
                        public IValue getOutput(Pair<IOperator, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3004
                            final IOperator innerOperator = input.getLeft();
4✔
3005
                            OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3006
                            ValueTypeList.ValueList<?, ?> inputList = variables.getValue(0, ValueTypes.LIST);
6✔
3007
                            List<IValue> filtered = Lists.newArrayList();
2✔
3008
                            for (IValue value : inputList.getRawValue()) {
11✔
3009
                                IValue result = ValueHelpers.evaluateOperator(innerOperator, value);
9✔
3010
                                ValueHelpers.validatePredicateOutput(innerOperator, result);
3✔
3011
                                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
3012
                                    filtered.add(value);
4✔
3013
                                }
3014
                            }
1✔
3015
                            IValueType valueType = inputList.getRawValue().getValueType();
4✔
3016
                            return ValueTypeList.ValueList.ofList(valueType, filtered);
4✔
3017
                        }
3018
                    })).build());
1✔
3019

3020
    /**
3021
     * Takes the conjunction of two predicates.
3022
     */
3023
    public static final IOperator OPERATOR_CONJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3024
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3025
            .output(ValueTypes.OPERATOR).symbol(".&&.").operatorInteract("conjunction")
7✔
3026
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3027
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Conjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3028
            )).build());
1✔
3029
    static {
3030
        REGISTRY.registerSerializer(new CombinedOperator.Conjunction.Serializer());
5✔
3031
    }
3032

3033
    /**
3034
     * Takes the disjunction of two predicates.
3035
     */
3036
    public static final IOperator OPERATOR_DISJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3037
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3038
            .output(ValueTypes.OPERATOR).symbol(".||.").operatorInteract("disjunction")
7✔
3039
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
3040
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Disjunction.asOperator(input.getLeft(), input.getRight()))
17✔
3041
            )).build());
1✔
3042
    static {
3043
        REGISTRY.registerSerializer(new CombinedOperator.Disjunction.Serializer());
5✔
3044
    }
3045

3046
    /**
3047
     * Takes the negation of a predicate.
3048
     */
3049
    public static final IOperator OPERATOR_NEGATION = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3050
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3051
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3052
            .output(ValueTypes.OPERATOR).symbol("!.").operatorInteract("negation")
7✔
3053
            .function(OperatorBuilders.FUNCTION_ONE_PREDICATE.build(
2✔
3054
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Negation.asOperator(input))
4✔
3055
            )).build());
1✔
3056
    static {
3057
        REGISTRY.registerSerializer(new CombinedOperator.Negation.Serializer());
5✔
3058
    }
3059

3060
    /**
3061
     * Create a new operator that pipes the output from the first operator to the second operator.
3062
     */
3063
    public static final IOperator OPERATOR_PIPE = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
3064
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3065
            .output(ValueTypes.OPERATOR).symbol(".").operatorInteract("pipe")
7✔
3066
            .function(OperatorBuilders.FUNCTION_TWO_OPERATORS.build(
2✔
3067
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe.asOperator(input.getLeft(), input.getRight()))
17✔
3068
            )).build());
1✔
3069
    static {
3070
        REGISTRY.registerSerializer(new CombinedOperator.Pipe.Serializer());
5✔
3071
    }
3072

3073
    /**
3074
     * 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.
3075
     */
3076
    public static final IOperator OPERATOR_PIPE2 = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3077
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
3078
            .renderPattern(IConfigRenderPattern.INFIX_2_LATE)
2✔
3079
            .output(ValueTypes.OPERATOR).symbol(".2").operatorInteract("pipe2")
7✔
3080
            .function(OperatorBuilders.FUNCTION_THREE_OPERATORS.build(
2✔
3081
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe2.asOperator(input.getLeft(), input.getMiddle(), input.getRight()))
23✔
3082
            )).build());
1✔
3083
    static {
3084
        REGISTRY.registerSerializer(new CombinedOperator.Pipe2.Serializer());
5✔
3085
    }
3086

3087
    /**
3088
     * Flip the input parameters of an operator with two inputs.
3089
     */
3090
    public static final IOperator OPERATOR_FLIP = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3091
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
3092
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
3093
            .output(ValueTypes.OPERATOR).symbolOperatorInteract("flip")
5✔
3094
            .function(OperatorBuilders.FUNCTION_ONE_OPERATOR.build(
2✔
3095
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Flip.asOperator(input))
4✔
3096
            )).build());
1✔
3097
    static {
3098
        REGISTRY.registerSerializer(new CombinedOperator.Flip.Serializer());
5✔
3099
    }
3100

3101
    /**
3102
     * Apply the given operator on all elements of a list to reduce the list to one value.
3103
     */
3104
    public static final IOperator OPERATOR_REDUCE = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
3105
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
3106
            .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
2✔
3107
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce")
4✔
3108
            .conditionalOutputTypeDeriver((operator, input) -> input[2].getType())
7✔
3109
            .function(variables -> {
1✔
3110
                IValue accumulator = variables.getValue(2);
4✔
3111
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3112
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3113
                ValueTypeList.ValueList<IValueType<IValue>, IValue> inputList = variables.getValue(1, ValueTypes.LIST);
6✔
3114
                for (IValue listValue : inputList.getRawValue()) {
11✔
3115
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3116
                }
1✔
3117
                return accumulator;
2✔
3118
            }).build());
1✔
3119

3120
    /**
3121
     * Apply the given operator on all elements of a list to reduce the list to one value.
3122
     */
3123
    public static final IOperator OPERATOR_REDUCE1 = REGISTRY.register(OperatorBuilders.OPERATOR
14✔
3124
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
3125
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
3126
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce1")
4✔
3127
            .conditionalOutputTypeDeriver((operator, input) -> {
2✔
3128
                try {
3129
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[1].getValue()).getRawValue();
7✔
3130
                    return a.getValueType();
3✔
3131
                } catch (EvaluationException e) {
×
3132
                    return operator.getOutputType();
×
3133
                }
3134
            })
3135
            .function(variables -> {
1✔
3136
                ValueTypeList.ValueList valueList = variables.getValue(1, ValueTypes.LIST);
6✔
3137
                Iterator<IValue> iter = valueList.getRawValue().iterator();
4✔
3138
                if (!iter.hasNext()) {
3✔
3139
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REDUCE_EMPTY));
6✔
3140
                }
3141

3142
                IValue accumulator = iter.next();
4✔
3143
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
3144
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
3145

3146
                while (iter.hasNext()) {
3✔
3147
                    IValue listValue = iter.next();
4✔
3148
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3149
                }
1✔
3150
                return accumulator;
2✔
3151
            }).build());
1✔
3152

3153
    /**
3154
     * Apply for a given operator a given value.
3155
     */
3156
    public static final IOperator OPERATOR_BY_NAME = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3157
            .inputType(ValueTypes.STRING).output(ValueTypes.OPERATOR)
4✔
3158
            .symbol("op_by_name").operatorName("by_name").interactName("operatorByName")
6✔
3159
            .function(input -> {
1✔
3160
                ValueTypeString.ValueString name = input.getValue(0, ValueTypes.STRING);
6✔
3161
                try {
3162
                    ResourceLocation id = ResourceLocation.parse(name.getRawValue());
4✔
3163
                    IOperator operator = Operators.REGISTRY.getOperator(id);
4✔
3164
                    if (operator == null) {
2✔
3165
                        throw new EvaluationException(Component.translatable(
11✔
3166
                                L10NValues.OPERATOR_ERROR_OPERATORNOTFOUND, name.getRawValue()));
2✔
3167
                    }
3168
                    return ValueTypeOperator.ValueOperator.of(operator);
3✔
3169
                } catch (ResourceLocationException e) {
×
3170
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
3171
                }
3172
            }).build());
1✔
3173

3174
    /**
3175
     * ----------------------------------- NBT OPERATORS -----------------------------------
3176
     */
3177

3178
    /**
3179
     * The number of entries in an NBT tag
3180
     */
3181
    public static final IOperator NBT_COMPOUND_SIZE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3182
            .output(ValueTypes.INTEGER).operatorName("compound_size").symbol("NBT{}.size").interactName("size")
9✔
3183
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_TO_INT.build(
2✔
3184
                opt -> opt.map(CompoundTag::size).orElse(0)
8✔
3185
            )).build());
1✔
3186

3187
    /**
3188
     * The list of keys in an NBT tag
3189
     */
3190
    public static final IOperator NBT_COMPOUND_KEYS = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3191
            .output(ValueTypes.LIST).operatorName("compound_keys").symbol("NBT{}.keys").interactName("keys")
8✔
3192
            .function(variables -> {
1✔
3193
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3194
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtKeys(value.getRawValue()));
7✔
3195
            }).build());
1✔
3196

3197
    /**
3198
     * If an NBT tag has the given key
3199
     */
3200
    public static final IOperator NBT_COMPOUND_HASKEY = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3201
            .output(ValueTypes.BOOLEAN).operatorName("compound_haskey").symbol("NBT{}.has_key").interactName("hasKey")
9✔
3202
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3203
                    Optional::isPresent
3204
            )).build());
1✔
3205

3206
    /**
3207
     * The NBT value type of an entry
3208
     */
3209
    public static final IOperator NBT_COMPOUND_VALUE_TYPE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3210
            .output(ValueTypes.STRING).operatorName("compound_type").symbol("NBT{}.type").interactName("type")
9✔
3211
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(tag -> {
2✔
3212
                if (tag.isPresent()) {
3✔
3213
                    try {
3214
                        return TagTypes.getType(tag.get().getId()).getName();
7✔
3215
                    } catch (IndexOutOfBoundsException e) {
×
3216

3217
                    }
3218
                }
3219
                return "null";
2✔
3220
            })).build());
1✔
3221

3222
    /**
3223
     * The NBT tag value
3224
     */
3225
    public static final IOperator NBT_COMPOUND_VALUE_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3226
            .output(ValueTypes.NBT).operatorName("compound_value_tag").symbol("NBT{}.get_tag").interactName("getTag")
9✔
3227
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(o -> o)).build());
5✔
3228

3229
    /**
3230
     * The NBT boolean value
3231
     */
3232
    public static final IOperator NBT_COMPOUND_VALUE_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3233
            .output(ValueTypes.BOOLEAN).operatorName("compound_value_boolean").symbol("NBT{}.get_boolean").interactName("getBoolean")
9✔
3234
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3235
                    o -> o.map(tag -> tag instanceof NumericTag && ((NumericTag) tag).getAsByte() != 0).orElse(false)
19!
3236
            )).build());
1✔
3237

3238
    /**
3239
     * The NBT integer value
3240
     */
3241
    public static final IOperator NBT_COMPOUND_VALUE_INTEGER = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3242
            .output(ValueTypes.INTEGER).operatorName("compound_value_integer").symbol("NBT{}.get_integer").interactName("getInteger")
9✔
3243
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_INT.build(
2✔
3244
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
17!
3245
            )).build());
1✔
3246

3247
    /**
3248
     * The NBT long value
3249
     */
3250
    public static final IOperator NBT_COMPOUND_VALUE_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3251
            .output(ValueTypes.LONG).operatorName("compound_value_long").symbol("NBT{}.get_long").interactName("getLong")
9✔
3252
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG.build(
2✔
3253
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsLong() : 0).orElse(0L)
17!
3254
            )).build());
1✔
3255

3256
    /**
3257
     * The NBT double value
3258
     */
3259
    public static final IOperator NBT_COMPOUND_VALUE_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3260
            .output(ValueTypes.DOUBLE).operatorName("compound_value_double").symbol("NBT{}.get_double").interactName("getDouble")
9✔
3261
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE.build(
2✔
3262
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsDouble() : 0).orElse(0D)
17!
3263
            )).build());
1✔
3264

3265
    /**
3266
     * The NBT string value
3267
     */
3268
    public static final IOperator NBT_COMPOUND_VALUE_STRING = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3269
            .output(ValueTypes.STRING).operatorName("compound_value_string").symbol("NBT{}.get_string").interactName("getString")
9✔
3270
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(
2✔
3271
                    o -> o.map(tag -> tag instanceof StringTag ? tag.getAsString() : "").orElse("")
14!
3272
            )).build());
1✔
3273

3274
    /**
3275
     * The NBT compound value
3276
     */
3277
    public static final IOperator NBT_COMPOUND_VALUE_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3278
            .output(ValueTypes.NBT).operatorName("compound_value_compound").symbol("NBT{}.get_compound").interactName("getCompound")
9✔
3279
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(
2✔
3280
                    o -> o.map(tag -> tag instanceof CompoundTag ? (CompoundTag) tag : new CompoundTag())
11!
3281
            )).build());
1✔
3282

3283
    /**
3284
     * The NBT tag list value
3285
     */
3286
    public static final IOperator NBT_COMPOUND_VALUE_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3287
            .output(ValueTypes.LIST).operatorName("compound_value_list_tag").symbol("NBT{}.get_list_tag").interactName("getListTag")
8✔
3288
            .function(variables -> {
1✔
3289
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3290
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3291
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListTag(key.getRawValue(), value.getRawValue()));
9✔
3292
            }).build());
1✔
3293

3294
    /**
3295
     * The NBT boolean list value
3296
     */
3297
    public static final IOperator NBT_COMPOUND_VALUE_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3298
            .output(ValueTypes.LIST).operatorName("compound_value_list_byte").symbol("NBT{}.get_list_byte").interactName("getListByte")
8✔
3299
            .function(variables -> {
1✔
3300
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3301
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3302
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListByte(key.getRawValue(), value.getRawValue()));
9✔
3303
            }).build());
1✔
3304

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

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

3327
    /**
3328
     * Remove an entry from an NBT compound
3329
     */
3330
    public static final IOperator NBT_COMPOUND_WITHOUT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3331
            .output(ValueTypes.NBT).operatorName("compound_without").symbol("NBT{}.without").interactName("without")
8✔
3332
            .function(variables -> {
1✔
3333
                ValueTypeNbt.ValueNbt valueNbt = variables.getValue(0, ValueTypes.NBT);
6✔
3334
                Optional<Tag> tag = valueNbt.getRawValue();
3✔
3335
                if (tag.isPresent()) {
3!
3336
                    if (!(tag.get() instanceof CompoundTag)) {
4!
3337
                        return ValueTypeNbt.ValueNbt.of();
×
3338
                    }
3339
                    ValueTypeString.ValueString valueString = variables.getValue(1, ValueTypes.STRING);
6✔
3340
                    String key = valueString.getRawValue();
3✔
3341
                    CompoundTag tagCompound = (CompoundTag) tag.get();
4✔
3342
                    if (tagCompound.contains(key)) {
4!
3343
                        // Copy the tag to ensure immutability
3344
                        tagCompound = tagCompound.copy();
3✔
3345
                        tagCompound.remove(key);
3✔
3346
                    }
3347
                    return ValueTypeNbt.ValueNbt.of(tagCompound);
3✔
3348
                }
3349
                return valueNbt;
×
3350
            }).build());
1✔
3351

3352

3353

3354
    /**
3355
     * Set an NBT compound boolean value
3356
     */
3357
    public static final IOperator NBT_COMPOUND_WITH_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3358
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3359
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.BOOLEAN)
2✔
3360
            .operatorName("compound_with_boolean").symbol("NBT{}.with_boolean").interactName("withBoolean")
7✔
3361
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3362
                ValueTypeBoolean.ValueBoolean value = input.getRight().getValue(0, ValueTypes.BOOLEAN);
8✔
3363
                input.getLeft().ifPresent(tag -> tag.putBoolean(input.getMiddle(), value.getRawValue()));
15✔
3364
                return input.getLeft();
4✔
3365
            })).build());
1✔
3366

3367
    /**
3368
     * Set an NBT compound short value
3369
     */
3370
    public static final IOperator NBT_COMPOUND_WITH_SHORT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3371
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3372
            .operatorName("compound_with_short").symbol("NBT{}.with_short").interactName("withShort")
7✔
3373
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3374
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3375
                input.getLeft().ifPresent(tag -> tag.putShort(input.getMiddle(), (short) value.getRawValue()));
16✔
3376
                return input.getLeft();
4✔
3377
            })).build());
1✔
3378

3379
    /**
3380
     * Set an NBT compound integer value
3381
     */
3382
    public static final IOperator NBT_COMPOUND_WITH_INTEGER = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3383
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3384
            .operatorName("compound_with_integer").symbol("NBT{}.with_integer").interactName("withInteger")
7✔
3385
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3386
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3387
                input.getLeft().ifPresent(tag -> tag.putInt(input.getMiddle(), value.getRawValue()));
15✔
3388
                return input.getLeft();
4✔
3389
            })).build());
1✔
3390

3391
    /**
3392
     * Set an NBT compound long value
3393
     */
3394
    public static final IOperator NBT_COMPOUND_WITH_LONG = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3395
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LONG)
2✔
3396
            .operatorName("compound_with_long").symbol("NBT{}.with_long").interactName("withLong")
7✔
3397
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3398
                ValueTypeLong.ValueLong value = input.getRight().getValue(0, ValueTypes.LONG);
8✔
3399
                input.getLeft().ifPresent(tag -> tag.putLong(input.getMiddle(), value.getRawValue()));
15✔
3400
                return input.getLeft();
4✔
3401
            })).build());
1✔
3402

3403
    /**
3404
     * Set an NBT compound double value
3405
     */
3406
    public static final IOperator NBT_COMPOUND_WITH_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3407
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3408
            .operatorName("compound_with_double").symbol("NBT{}.with_double").interactName("withDouble")
7✔
3409
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3410
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3411
                input.getLeft().ifPresent(tag -> tag.putDouble(input.getMiddle(), value.getRawValue()));
15✔
3412
                return input.getLeft();
4✔
3413
            })).build());
1✔
3414

3415
    /**
3416
     * Set an NBT compound float value
3417
     */
3418
    public static final IOperator NBT_COMPOUND_WITH_FLOAT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3419
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3420
            .operatorName("compound_with_float").symbol("NBT{}.with_float").interactName("withFloat")
7✔
3421
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3422
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3423
                input.getLeft().ifPresent(tag -> tag.putFloat(input.getMiddle(), (float) value.getRawValue()));
16✔
3424
                return input.getLeft();
4✔
3425
            })).build());
1✔
3426

3427
    /**
3428
     * Set an NBT compound string value
3429
     */
3430
    public static final IOperator NBT_COMPOUND_WITH_STRING = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3431
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING)
2✔
3432
            .operatorName("compound_with_string").symbol("NBT{}.with_string").interactName("withString")
7✔
3433
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3434
                ValueTypeString.ValueString value = input.getRight().getValue(0, ValueTypes.STRING);
8✔
3435
                input.getLeft().ifPresent(tag -> tag.putString(input.getMiddle(), value.getRawValue()));
15✔
3436
                return input.getLeft();
4✔
3437
            })).build());
1✔
3438

3439
    /**
3440
     * Set an NBT compound compound value
3441
     */
3442
    public static final IOperator NBT_COMPOUND_WITH_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3443
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.NBT)
2✔
3444
            .operatorName("compound_with_tag").symbol("NBT{}.with_tag").interactName("withTag")
7✔
3445
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3446
                ValueTypeNbt.ValueNbt value = input.getRight().getValue(0, ValueTypes.NBT);
8✔
3447
                input.getLeft()
6✔
3448
                        .ifPresent(tag -> value.getRawValue()
7✔
3449
                                .ifPresent(v -> tag.put(input.getMiddle(), v)));
9✔
3450
                return input.getLeft();
4✔
3451
            })).build());
1✔
3452

3453
    /**
3454
     * Set an NBT compound tag list value
3455
     */
3456
    public static final IOperator NBT_COMPOUND_WITH_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3457
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3458
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3459
            .operatorName("compound_with_list_tag").symbol("NBT{}.with_tag_list").interactName("withTagList")
9✔
3460
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3461
                @Override
3462
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3463
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3464
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3465
                            NbtHelpers.getListNbtTag(value, NBT_COMPOUND_WITH_LIST_TAG.getLocalizedNameFull())));
2✔
3466
                    return input.getLeft();
4✔
3467
                }
3468
            })).build());
1✔
3469

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

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

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

3521
    /**
3522
     * Check if the first NBT compound tag is a subset of the second NBT compound tag.
3523
     */
3524
    public static final IOperator NBT_COMPOUND_SUBSET = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3525
            .output(ValueTypes.BOOLEAN).operatorName("compound_subset").symbol("NBT{}.⊆").interactName("isSubset")
8✔
3526
            .function(variables -> {
1✔
3527
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3528
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3529
                if (valueNbt0.getRawValue().isPresent()
5!
3530
                        && valueNbt1.getRawValue().isPresent()
4!
3531
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3532
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3533
                    return ValueTypeBoolean.ValueBoolean.of(NbtHelpers.nbtMatchesSubset((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get(), true));
12✔
3534
                }
3535
                return ValueTypeBoolean.ValueBoolean.of(false);
×
3536
            }).build());
1✔
3537

3538
    /**
3539
     * The union of the given NBT compound tags. Nested tags will be joined recusively.
3540
     */
3541
    public static final IOperator NBT_COMPOUND_UNION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3542
            .output(ValueTypes.NBT).operatorName("compound_union").symbol("NBT{}.∪").interactName("union")
8✔
3543
            .function(variables -> {
1✔
3544
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3545
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3546
                if (valueNbt0.getRawValue().isPresent()
5!
3547
                        && valueNbt1.getRawValue().isPresent()
4!
3548
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3549
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3550
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.union((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3551
                }
3552
                return ValueTypeNbt.ValueNbt.of();
×
3553
            }).build());
1✔
3554

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

3572
    /**
3573
     * The difference of the given NBT compound tags. Nested tags will be subtracted recusively.
3574
     */
3575
    public static final IOperator NBT_COMPOUND_MINUS = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3576
            .output(ValueTypes.NBT).operatorName("compound_minus").symbol("NBT{}.∖").interactName("minus")
8✔
3577
            .function(variables -> {
1✔
3578
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3579
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3580
                if (valueNbt0.getRawValue().isPresent()
5!
3581
                        && valueNbt1.getRawValue().isPresent()
4!
3582
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3583
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3584
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.minus((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
11✔
3585
                }
3586
                return ValueTypeNbt.ValueNbt.of();
×
3587
            }).build());
1✔
3588

3589
    /**
3590
     * The boolean value of an NBT value
3591
     */
3592
    public static final IOperator NBT_AS_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3593
            .output(ValueTypes.BOOLEAN).operatorName("as_boolean").symbol("NBT.as_boolean").interactName("asBoolean")
9✔
3594
            .function(OperatorBuilders.FUNCTION_NBT_TO_BOOLEAN.build(
2✔
3595
                    o -> o.map(tag -> tag instanceof ByteTag && ((ByteTag) tag).getAsByte() != 0).orElse(false)
20!
3596
            )).build());
1✔
3597

3598
    /**
3599
     * The byte value of an NBT value
3600
     */
3601
    public static final IOperator NBT_AS_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3602
            .output(ValueTypes.INTEGER).operatorName("as_byte").symbol("NBT.as_byte").interactName("asByte")
9✔
3603
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3604
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3605
            )).build());
1✔
3606

3607
    /**
3608
     * The short value of an NBT value
3609
     */
3610
    public static final IOperator NBT_AS_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3611
            .output(ValueTypes.INTEGER).operatorName("as_short").symbol("NBT.as_short").interactName("asShort")
9✔
3612
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3613
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3614
            )).build());
1✔
3615

3616
    /**
3617
     * The int value of an NBT value
3618
     */
3619
    public static final IOperator NBT_AS_INT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3620
            .output(ValueTypes.INTEGER).operatorName("as_int").symbol("NBT.as_int").interactName("asInt")
9✔
3621
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3622
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3623
            )).build());
1✔
3624

3625
    /**
3626
     * The long value of an NBT value
3627
     */
3628
    public static final IOperator NBT_AS_LONG = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3629
            .output(ValueTypes.LONG).operatorName("as_long").symbol("NBT.as_long").interactName("asLong")
9✔
3630
            .function(OperatorBuilders.FUNCTION_NBT_TO_LONG.build(
2✔
3631
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsLong() : 0L).orElse(0L)
18✔
3632
            )).build());
1✔
3633

3634
    /**
3635
     * The double value of an NBT value
3636
     */
3637
    public static final IOperator NBT_AS_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3638
            .output(ValueTypes.DOUBLE).operatorName("as_double").symbol("NBT.as_double").interactName("asDouble")
9✔
3639
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3640
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsDouble() : 0D).orElse(0D)
18✔
3641
            )).build());
1✔
3642

3643
    /**
3644
     * The float value of an NBT value
3645
     */
3646
    public static final IOperator NBT_AS_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3647
            .output(ValueTypes.DOUBLE).operatorName("as_float").symbol("NBT.as_float").interactName("asFloat")
9✔
3648
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3649
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsFloat() : 0D).orElse(0D)
19✔
3650
            )).build());
1✔
3651

3652
    /**
3653
     * The string value of an NBT value
3654
     */
3655
    public static final IOperator NBT_AS_STRING = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3656
            .output(ValueTypes.STRING).operatorName("as_string").symbol("NBT.as_string").interactName("asString")
9✔
3657
            .function(OperatorBuilders.FUNCTION_NBT_TO_STRING.build(
2✔
3658
                    o -> o.map(tag -> tag instanceof StringTag ? ((StringTag) tag).getAsString() : "").orElse("")
16✔
3659
            )).build());
1✔
3660

3661
    /**
3662
     * The tag list value of an NBT value
3663
     */
3664
    public static final IOperator NBT_AS_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3665
            .output(ValueTypes.LIST).operatorName("as_tag_list").symbol("NBT.as_tag_list").interactName("asTagList")
8✔
3666
            .function(variables -> {
1✔
3667
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3668
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListTag(value.getRawValue()));
7✔
3669
            }).build());
1✔
3670

3671
    /**
3672
     * The byte list value of an NBT value
3673
     */
3674
    public static final IOperator NBT_AS_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3675
            .output(ValueTypes.LIST).operatorName("as_byte_list").symbol("NBT.as_byte_list").interactName("asByteList")
8✔
3676
            .function(variables -> {
1✔
3677
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3678
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListByte(value.getRawValue()));
7✔
3679
            }).build());
1✔
3680

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

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

3701
    /**
3702
     * The NBT value of a boolean value
3703
     */
3704
    public static final IOperator NBT_FROM_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3705
            .inputType(ValueTypes.BOOLEAN).output(ValueTypes.NBT)
4✔
3706
            .operatorName("from_boolean").symbol("NBT.from_boolean").interactName("asNbt")
6✔
3707
            .function(variables -> {
1✔
3708
                ValueTypeBoolean.ValueBoolean value = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
3709
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf(value.getRawValue()));
5✔
3710
            }).build());
1✔
3711

3712
    /**
3713
     * The NBT value of a short value
3714
     */
3715
    public static final IOperator NBT_FROM_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3716
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3717
            .operatorName("from_short").symbol("NBT.from_short").interactName("asNbt", "short", true)
8✔
3718
            .function(variables -> {
1✔
3719
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3720
                return ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) value.getRawValue()));
6✔
3721
            }).build());
1✔
3722

3723
    /**
3724
     * The NBT value of a byte value
3725
     */
3726
    public static final IOperator NBT_FROM_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3727
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3728
            .operatorName("from_byte").symbol("NBT.from_byte").interactName("asNbt", "byte", true)
8✔
3729
            .function(variables -> {
1✔
3730
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3731
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf((byte) value.getRawValue()));
6✔
3732
            }).build());
1✔
3733

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

3745
    /**
3746
     * The NBT value of a long value
3747
     */
3748
    public static final IOperator NBT_FROM_LONG = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3749
            .inputType(ValueTypes.LONG).output(ValueTypes.NBT)
4✔
3750
            .operatorName("from_long").symbol("NBT.from_long").interactName("asNbt")
6✔
3751
            .function(variables -> {
1✔
3752
                ValueTypeLong.ValueLong value = variables.getValue(0, ValueTypes.LONG);
6✔
3753
                return ValueTypeNbt.ValueNbt.of(LongTag.valueOf(value.getRawValue()));
5✔
3754
            }).build());
1✔
3755

3756
    /**
3757
     * The NBT value of a double value
3758
     */
3759
    public static final IOperator NBT_FROM_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3760
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3761
            .operatorName("from_double").symbol("NBT.from_double").interactName("asNbt")
6✔
3762
            .function(variables -> {
1✔
3763
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3764
                return ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(value.getRawValue()));
5✔
3765
            }).build());
1✔
3766

3767
    /**
3768
     * The NBT value of a float value
3769
     */
3770
    public static final IOperator NBT_FROM_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3771
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3772
            .operatorName("from_float").symbol("NBT.from_float").interactName("asNbt", "float", true)
8✔
3773
            .function(variables -> {
1✔
3774
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3775
                return ValueTypeNbt.ValueNbt.of(FloatTag.valueOf((float) value.getRawValue()));
6✔
3776
            }).build());
1✔
3777

3778
    /**
3779
     * The NBT value of a string value
3780
     */
3781
    public static final IOperator NBT_FROM_STRING = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3782
            .inputType(ValueTypes.STRING).output(ValueTypes.NBT)
4✔
3783
            .operatorName("from_string").symbol("NBT.from_string").interactName("asNbt")
6✔
3784
            .function(variables -> {
1✔
3785
                ValueTypeString.ValueString value = variables.getValue(0, ValueTypes.STRING);
6✔
3786
                return ValueTypeNbt.ValueNbt.of(StringTag.valueOf(value.getRawValue()));
5✔
3787
            }).build());
1✔
3788

3789
    /**
3790
     * The NBT value of a tag list value
3791
     */
3792
    public static final IOperator NBT_FROM_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3793
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3794
            .operatorName("from_tag_list").symbol("NBT.from_tag_list").interactName("asNbt", "tagList", true)
10✔
3795
            .function(new OperatorBase.IFunction() {
4✔
3796
                @Override
3797
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3798
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3799
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtTag(value, NBT_FROM_TAG_LIST.getLocalizedNameFull()));
6✔
3800
                }
3801
            }).build());
1✔
3802

3803
    /**
3804
     * The NBT value of a byte list value
3805
     */
3806
    public static final IOperator NBT_FROM_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3807
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3808
            .operatorName("from_byte_list").symbol("NBT.from_byte_list").interactName("asNbt", "byteList", true)
10✔
3809
            .function(new OperatorBase.IFunction() {
4✔
3810
                @Override
3811
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3812
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3813
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtByte(value, NBT_FROM_BYTE_LIST.getLocalizedNameFull()));
6✔
3814
                }
3815
            }).build());
1✔
3816

3817
    /**
3818
     * The NBT value of a int list value
3819
     */
3820
    public static final IOperator NBT_FROM_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3821
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3822
            .operatorName("from_int_list").symbol("NBT.from_int_list").interactName("asNbt", "intList", true)
10✔
3823
            .function(new OperatorBase.IFunction() {
4✔
3824
                @Override
3825
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3826
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3827
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtInt(value, NBT_FROM_INT_LIST.getLocalizedNameFull()));
6✔
3828
                }
3829
            }).build());
1✔
3830

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

3845
    /**
3846
     * Apply the given NBT path expression on the given NBT value and get the first result.
3847
     */
3848
    public static final IOperator NBT_PATH_MATCH_FIRST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3849
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.NBT)
4✔
3850
            .operatorName("path_match_first").symbol("NBT.path_match_first").interactName("nbtPathMatchFirst")
6✔
3851
            .function(variables -> {
1✔
3852
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3853
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3854
                INbtPathExpression expression = null;
2✔
3855
                try {
3856
                    expression = NbtPath.parse(string.getRawValue());
4✔
3857
                } catch (NbtParseException e) {
×
3858
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3859
                            string.getRawValue(),
×
3860
                            e.getMessage()));
×
3861
                }
1✔
3862
                if (!nbt.getRawValue().isPresent()) {
4!
3863
                    return ValueTypeNbt.ValueNbt.of();
×
3864
                }
3865
                return ValueTypeNbt.ValueNbt.of(expression.match(nbt.getRawValue().get()).getMatches().findAny());
10✔
3866
            }).build());
1✔
3867

3868
    /**
3869
     * Apply the given NBT path expression on the given NBT value and get all results.
3870
     */
3871
    public static final IOperator NBT_PATH_MATCH_ALL = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3872
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.LIST)
4✔
3873
            .operatorName("path_match_all").symbol("NBT.path_match_all").interactName("nbtPathMatchAll")
6✔
3874
            .function(variables -> {
1✔
3875
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3876
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3877
                INbtPathExpression expression = null;
2✔
3878
                try {
3879
                    expression = NbtPath.parse(string.getRawValue());
4✔
3880
                } catch (NbtParseException e) {
×
3881
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3882
                            string.getRawValue(),
×
3883
                            e.getMessage()));
×
3884
                }
1✔
3885
                if (!nbt.getRawValue().isPresent()) {
4!
3886
                    return ValueTypeList.ValueList.ofAll(ValueTypes.NBT);
×
3887
                }
3888
                List<ValueTypeNbt.ValueNbt> matches = expression.match(nbt.getRawValue().get()).getMatches()
8✔
3889
                        .map(ValueTypeNbt.ValueNbt::of)
1✔
3890
                        .collect(Collectors.toList());
4✔
3891
                return ValueTypeList.ValueList.ofList(ValueTypes.NBT, matches);
4✔
3892
            }).build());
1✔
3893

3894
    /**
3895
     * Test the given NBT path expression on the given NBT value.
3896
     */
3897
    public static final IOperator NBT_PATH_TEST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3898
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.BOOLEAN)
4✔
3899
            .operatorName("path_test").symbol("NBT.path_test").interactName("nbtPathTest")
6✔
3900
            .function(variables -> {
1✔
3901
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3902
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3903
                INbtPathExpression expression = null;
2✔
3904
                try {
3905
                    expression = NbtPath.parse(string.getRawValue());
4✔
3906
                } catch (NbtParseException e) {
×
3907
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3908
                            string.getRawValue(),
×
3909
                            e.getMessage()));
×
3910
                }
1✔
3911
                if (!nbt.getRawValue().isPresent()) {
4!
3912
                    return ValueTypeBoolean.ValueBoolean.of(false);
×
3913
                }
3914
                return ValueTypeBoolean.ValueBoolean.of(expression.test(nbt.getRawValue().get()));
8✔
3915
            }).build());
1✔
3916

3917
    /**
3918
     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------
3919
     */
3920

3921
    /**
3922
     * The list of items.
3923
     */
3924
    public static final IOperator INGREDIENTS_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3925
            .output(ValueTypes.LIST).operatorInteract("items").symbol("Ingr.items")
6✔
3926
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ITEMSTACK))
4✔
3927
            .build());
1✔
3928

3929
    /**
3930
     * The list of fluids
3931
     */
3932
    public static final IOperator INGREDIENTS_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3933
            .output(ValueTypes.LIST).operatorInteract("fluids").symbol("Ingr.fluids")
6✔
3934
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.FLUIDSTACK))
4✔
3935
            .build());
1✔
3936

3937
    /**
3938
     * The list of fluids
3939
     */
3940
    public static final IOperator INGREDIENTS_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3941
            .output(ValueTypes.LIST).operatorInteract("energies").symbol("Ingr.energies")
6✔
3942
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ENERGY))
4✔
3943
            .build());
1✔
3944

3945
    /**
3946
     * Set an ingredient item
3947
     */
3948
    public static final IOperator INGREDIENTS_WITH_ITEM = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_ITEMSTACK
5✔
3949
            .operatorName("with_item").symbol("Ingr.with_item").interactName("withItem")
6✔
3950
            .function(variables -> {
1✔
3951
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3952
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3953
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(2, ValueTypes.OBJECT_ITEMSTACK);
6✔
3954
                if (value.getRawValue().isEmpty()) {
4!
3955
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3956
                }
3957
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3958
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3959
                        index.getRawValue(), IngredientComponent.ITEMSTACK, itemStack.getRawValue()));
5✔
3960
            }).build());
1✔
3961

3962
    /**
3963
     * Set an ingredient fluid
3964
     */
3965
    public static final IOperator INGREDIENTS_WITH_FLUID = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_FLUIDSTACK
5✔
3966
            .operatorName("with_fluid").symbol("Ingr.with_fluid").interactName("withFluid")
6✔
3967
            .function(variables -> {
1✔
3968
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3969
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3970
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = variables.getValue(2, ValueTypes.OBJECT_FLUIDSTACK);
6✔
3971
                if (value.getRawValue().isEmpty()) {
4!
3972
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3973
                }
3974
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3975
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3976
                        index.getRawValue(), IngredientComponent.FLUIDSTACK, fluidStack.getRawValue()));
5✔
3977
            }).build());
1✔
3978

3979
    /**
3980
     * Set an ingredient energy
3981
     */
3982
    public static final IOperator INGREDIENTS_WITH_ENERGY = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_LONG
5✔
3983
            .operatorName("with_energy").symbol("Ingr.with_energy").interactName("withEnergy")
6✔
3984
            .function(variables -> {
1✔
3985
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3986
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3987
                ValueTypeLong.ValueLong energy = variables.getValue(2, ValueTypes.LONG);
6✔
3988
                if (value.getRawValue().isEmpty()) {
4!
3989
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3990
                }
3991
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3992
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3993
                        index.getRawValue(), IngredientComponent.ENERGY, energy.getRawValue()));
6✔
3994
            }).build());
1✔
3995

3996
    /**
3997
     * Set the list of items
3998
     */
3999
    public static final IOperator INGREDIENTS_WITH_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4000
            .operatorName("with_items").symbol("Ingr.with_items").interactName("withItems")
6✔
4001
            .function(variables -> {
1✔
4002
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4003
                ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4004
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4005
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4006
                }
4007
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4008
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4009
                        IngredientComponent.ITEMSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ITEMSTACK, list)));
2✔
4010
            }).build());
1✔
4011

4012
    /**
4013
     * Set the list of fluids
4014
     */
4015
    public static final IOperator INGREDIENTS_WITH_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4016
            .operatorName("with_fluids").symbol("Ingr.with_fluids").interactName("withFluids")
6✔
4017
            .function(variables -> {
1✔
4018
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4019
                ValueTypeList.ValueList<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
4020
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4021
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4022
                }
4023
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4024
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4025
                        IngredientComponent.FLUIDSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.FLUIDSTACK, list)));
2✔
4026
            }).build());
1✔
4027

4028
    /**
4029
     * Set the list of energies
4030
     */
4031
    public static final IOperator INGREDIENTS_WITH_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
4032
            .renderPattern(IConfigRenderPattern.INFIX_VERYLONG)
2✔
4033
            .operatorName("with_energies").symbol("Ingr.with_energies").interactName("withEnergies")
6✔
4034
            .function(variables -> {
1✔
4035
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4036
                ValueTypeList.ValueList<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = variables.getValue(1, ValueTypes.LIST);
6✔
4037
                if (valueIngredients.getRawValue().isEmpty()) {
4!
4038
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
4039
                }
4040
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
4041
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
4042
                        IngredientComponent.ENERGY, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ENERGY, list)));
2✔
4043
            }).build());
1✔
4044

4045
    /**
4046
     * ----------------------------------- RECIPE OPERATORS -----------------------------------
4047
     */
4048

4049
    /**
4050
     * The input ingredients of a recipe
4051
     */
4052
    public static final IOperator RECIPE_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4053
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4054
            .operatorInteract("input").symbol("recipe_in")
4✔
4055
            .function(variables -> {
1✔
4056
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4057
                if (value.getRawValue().isPresent()) {
4!
4058
                    return ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(value.getRawValue().get()));
7✔
4059
                }
4060
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4061
            }).build());
1✔
4062

4063
    /**
4064
     * The output ingredients of a recipe
4065
     */
4066
    public static final IOperator RECIPE_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
4067
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
4068
            .operatorInteract("output").symbol("recipe_out")
4✔
4069
            .function(variables -> {
1✔
4070
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4071
                if (value.getRawValue().isPresent()) {
4!
4072
                    return ValueObjectTypeIngredients.ValueIngredients.of(value.getRawValue().get().getOutput());
7✔
4073
                }
4074
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
4075
            }).build());
1✔
4076

4077
    /**
4078
     * Set the input ingredients of a recipe
4079
     */
4080
    public static final IOperator RECIPE_WITH_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4081
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4082
            .operatorName("with_input").symbol("Recipe.with_in").interactName("withInput")
6✔
4083
            .function(variables -> {
1✔
4084
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4085
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4086
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4087
                    IMixedIngredients ingredients = valueIngredients.getRawValue().get();
5✔
4088
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4089
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4090
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4091
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4092
                                .stream()
4✔
4093
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4094
                                .collect(Collectors.toList()));
3✔
4095
                    }
1✔
4096
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4097
                            inputs,
4098
                            valueRecipe.getRawValue().get().getOutput()
5✔
4099
                    ));
4100
                }
4101
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4102
            }).build());
1✔
4103

4104
    /**
4105
     * Set the output ingredients of a recipe
4106
     */
4107
    public static final IOperator RECIPE_WITH_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
4108
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4109
            .operatorName("with_output").symbol("Recipe.with_out").interactName("withOutput")
6✔
4110
            .function(variables -> {
1✔
4111
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
4112
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4113
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
4114
                    IRecipeDefinition recipe = valueRecipe.getRawValue().get();
5✔
4115
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4116
                    for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {
11✔
4117
                        inputs.put(component, (List) recipe.getInputs(component));
7✔
4118
                    }
1✔
4119
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4120
                            inputs,
4121
                            valueIngredients.getRawValue().get()
4✔
4122
                    ));
4123
                }
4124
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4125
            }).build());
1✔
4126

4127
    /**
4128
     * Create a recipe from two the given I/O ingredients
4129
     */
4130
    public static final IOperator RECIPE_WITH_INPUT_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_PREFIX
5✔
4131
            .output(ValueTypes.OBJECT_RECIPE)
2✔
4132
            .operatorName("with_input_output").symbol("Recipe.with_io").interactName("withInputOutput")
6✔
4133
            .function(variables -> {
1✔
4134
                ValueObjectTypeIngredients.ValueIngredients valueIn = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
4135
                ValueObjectTypeIngredients.ValueIngredients valueOut = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
4136
                if (valueIn.getRawValue().isPresent() && valueOut.getRawValue().isPresent()) {
8!
4137
                    IMixedIngredients ingredients = valueIn.getRawValue().get();
5✔
4138
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
4139
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
4140
                        IIngredientMatcher matcher = component.getMatcher();
3✔
4141
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
4142
                                .stream()
4✔
4143
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
4144
                                .collect(Collectors.toList()));
3✔
4145
                    }
1✔
4146
                    try {
4147
                        return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4148
                                inputs,
4149
                                valueOut.getRawValue().get()
4✔
4150
                        ));
4151
                    } catch (IllegalArgumentException e) {
×
4152
                        throw new EvaluationException(Component.literal(e.getMessage()));
×
4153
                    }
4154
                }
4155
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4156
            }).build());
1✔
4157

4158
    /**
4159
     * ------------------------------------ PARSE OPERATORS ------------------------------------
4160
     */
4161

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

4171
    /**
4172
     * Double Parse operator which takes a string of a form Double.parseDouble(),
4173
     * `/([+-]?)(Inf(inity)?|\u221E)/i`, or Long.decode() can consume.
4174
     */
4175
    public static final IOperator PARSE_DOUBLE = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.DOUBLE, v -> {
8✔
4176
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4177
      try {
4178
        return ValueTypeDouble.ValueDouble.of(Double.parseDouble(value.getRawValue()));
5✔
4179
      } catch (NumberFormatException e) {
1✔
4180
        try {
4181
          // \u221E = infinity symbol
4182
          Pattern p = Pattern.compile("\\A([+-]?)(Inf(inity)?|\u221E)\\z", Pattern.CASE_INSENSITIVE);
4✔
4183
          Matcher m = p.matcher(value.getRawValue().trim());
6✔
4184
          if (m.matches()){
3✔
4185
            if (m.group(1).equals("-")){
6✔
4186
              return ValueTypeDouble.ValueDouble.of(Double.NEGATIVE_INFINITY);
3✔
4187
            }
4188
            return ValueTypeDouble.ValueDouble.of(Double.POSITIVE_INFINITY);
3✔
4189
          }
4190
          // Try as a long
4191
          return ValueTypeDouble.ValueDouble.of((double) Long.decode(value.getRawValue()));
7✔
4192
        } catch (NumberFormatException e2) {
1✔
4193
            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4194
                    Component.translatable(ValueTypes.DOUBLE.getTranslationKey())));
3✔
4195
        }
4196
      }
4197
    }));
4198

4199
    /**
4200
     * Integer Parse operator which takes a string of a form Integer.decode() can consume.
4201
     */
4202
    public static final IOperator PARSE_INTEGER = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.INTEGER, v -> {
8✔
4203
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4204
      try{
4205
        return ValueTypeInteger.ValueInteger.of(Integer.decode(value.getRawValue()));
6✔
4206
      } catch (NumberFormatException e) {
1✔
4207
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4208
                  Component.translatable(ValueTypes.INTEGER.getTranslationKey())));
3✔
4209
      }
4210
    }));
4211

4212
    /**
4213
     * Long Parse operator which takes a string of a form Long.decode() can consume.
4214
     */
4215
    public static final IOperator PARSE_LONG = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.LONG, v -> {
8✔
4216
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4217
      try {
4218
        return ValueTypeLong.ValueLong.of(Long.decode(value.getRawValue()));
6✔
4219
      } catch (NumberFormatException e) {
1✔
4220
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4221
                  Component.translatable(ValueTypes.LONG.getTranslationKey())));
3✔
4222
      }
4223
    }));
4224

4225
    /**
4226
     * NBT Parse operator which takes a string of a form ValueTypeNbt().deserialize() can consume.
4227
     */
4228
    public static final IOperator PARSE_NBT = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.NBT, v -> {
8✔
4229
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4230
      try {
4231
        return ValueTypeNbt.ValueNbt.of(TagParser.parseTag(value.getRawValue()));
5✔
4232
      } catch (CommandSyntaxException e) {
1✔
4233
        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4234
                Component.translatable(ValueTypes.NBT.getTranslationKey())));
3✔
4235
      }
4236
    }));
4237

4238
    /**
4239
     * ----------------------------------- GENERAL OPERATORS -----------------------------------
4240
     */
4241

4242
    /**
4243
     * Choice operator with one boolean input, two any inputs and one output any.
4244
     */
4245
    public static final GeneralOperator GENERAL_CHOICE = REGISTRY.register(new GeneralChoiceOperator("?", "choice", "choice"));
10✔
4246

4247
    /**
4248
     * Identity operator with one any input and one any output
4249
     */
4250
    public static final GeneralOperator GENERAL_IDENTITY = REGISTRY.register(new GeneralIdentityOperator("id", "identity", "identity"));
10✔
4251

4252
    /**
4253
     * Constant operator with two any inputs and one any output
4254
     */
4255
    public static final GeneralOperator GENERAL_CONSTANT = REGISTRY.register(new GeneralConstantOperator("K", "constant", "constant"));
11✔
4256

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