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

CyclopsMC / IntegratedDynamics / 21094834146

17 Jan 2026 01:13PM UTC coverage: 53.079% (-0.2%) from 53.285%
21094834146

push

github

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

2871 of 8774 branches covered (32.72%)

Branch coverage included in aggregate %.

17456 of 29522 relevant lines covered (59.13%)

3.07 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

599

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1894

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

1906

1907

1908

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3014
    /**
3015
     * Apply for a given operator a given value.
3016
     */
3017
    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
3018
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
3019
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply")
9✔
3020
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY))
4✔
3021
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
3022
                    input -> {
3023
                        IOperator innerOperator = input.getLeft();
4✔
3024
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
3025
                        IVariable variable = variables.getVariables()[0];
5✔
3026
                        return ValueHelpers.evaluateOperator(innerOperator, variable);
9✔
3027
                    })).build());
1✔
3028
    static {
3029
        REGISTRY.registerSerializer(new CurriedOperator.Serializer());
5✔
3030
    }
3031

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3489

3490

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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