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

CyclopsMC / IntegratedDynamics / 14811801504

03 May 2025 02:32PM UTC coverage: 45.26% (+0.09%) from 45.171%
14811801504

push

github

rubensworks
Bump mod version

2561 of 8411 branches covered (30.45%)

Branch coverage included in aggregate %.

11722 of 23147 relevant lines covered (50.64%)

2.4 hits per line

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

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

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

80
import java.util.*;
81
import java.util.stream.Collectors;
82

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

90
    public static final IOperatorRegistry REGISTRY = constructRegistry();
2✔
91

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

101
    public static void load() {}
1✔
102

103
    /**
104
     * ----------------------------------- LOGICAL OPERATORS -----------------------------------
105
     */
106

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

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

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

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

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

157
    /**
158
     * ----------------------------------- ARITHMETIC OPERATORS -----------------------------------
159
     */
160

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

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

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

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

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

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

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

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

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

233
    /**
234
     * ----------------------------------- INTEGER OPERATORS -----------------------------------
235
     */
236

237
     private static final ValueTypeInteger.ValueInteger ZERO = ValueTypeInteger.ValueInteger.of(0);
3✔
238

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

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

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

262
    /**
263
     * ----------------------------------- RELATIONAL OPERATORS -----------------------------------
264
     */
265

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

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

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

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

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

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

345
    /**
346
     * ----------------------------------- BINARY OPERATORS -----------------------------------
347
     */
348

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

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

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

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

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

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

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

418
    /**
419
     * ----------------------------------- STRING OPERATORS -----------------------------------
420
     */
421

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

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

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

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

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

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

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

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

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

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

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

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

592

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

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

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

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

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

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

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

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

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

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

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

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

800
    /**
801
     * ----------------------------------- NUMBER OPERATORS -----------------------------------
802
     */
803

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

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

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

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

840
    /**
841
     * ----------------------------------- NULLABLE OPERATORS -----------------------------------
842
     */
843

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

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

861
    /**
862
     * ----------------------------------- LIST OPERATORS -----------------------------------
863
     */
864

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1258
    /**
1259
     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------
1260
     */
1261

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

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

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

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

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

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

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

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

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

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

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

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

1471
    /**
1472
     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------
1473
     */
1474

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1717
    /**
1718
     * If the given item can be used as fuel
1719
     */
1720
    public static final IOperator OBJECT_ITEMSTACK_CANBURN = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1721
            .output(ValueTypes.BOOLEAN)
2✔
1722
            .symbol("can_burn").operatorName("canburn").interactName("canBurn")
7✔
1723
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN
1✔
1724
                    .build(AbstractFurnaceBlockEntity::isFuel))
1✔
1725
            .build());
1✔
1726

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

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

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

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

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

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

1813

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

1825

1826

1827

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

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

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

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

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

1910
                return ValueTypeInteger.ValueInteger.of(count);
3✔
1911
            }).build());
1✔
1912

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

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

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

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

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

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

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

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

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

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

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

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

2050
    /**
2051
     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------
2052
     */
2053

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2588
    /**
2589
     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------
2590
     */
2591

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2893
    /**
2894
     * ----------------------------------- OPERATOR OPERATORS -----------------------------------
2895
     */
2896

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3176
    /**
3177
     * ----------------------------------- NBT OPERATORS -----------------------------------
3178
     */
3179

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

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

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

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

3219
                    }
3220
                }
3221
                return "null";
2✔
3222
            })).build());
1✔
3223

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

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

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

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

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

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

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

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

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

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

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

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

3354

3355

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3919
    /**
3920
     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------
3921
     */
3922

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

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

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

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

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

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

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

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

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

4047
    /**
4048
     * ----------------------------------- RECIPE OPERATORS -----------------------------------
4049
     */
4050

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

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

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

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

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

4160
    /**
4161
     * ------------------------------------ PARSE OPERATORS ------------------------------------
4162
     */
4163

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

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

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

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

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

4240
    /**
4241
     * ----------------------------------- GENERAL OPERATORS -----------------------------------
4242
     */
4243

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

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

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

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