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

CyclopsMC / IntegratedDynamics / 13739195914

08 Mar 2025 03:58PM UTC coverage: 39.059% (+0.06%) from 39.002%
13739195914

push

github

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

1966 of 8373 branches covered (23.48%)

Branch coverage included in aggregate %.

10307 of 23049 relevant lines covered (44.72%)

2.1 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

241
    /**
242
     * Relational == operator with two inputs of any type (but equal) and one output boolean.
243
     */
244
    public static final IOperator RELATIONAL_EQUALS = REGISTRY.register(OperatorBuilders.RELATIONAL
6✔
245
            .inputTypes(2, ValueTypes.CATEGORY_ANY).renderPattern(IConfigRenderPattern.INFIX)
4✔
246
            .symbol("==").operatorInteract("equals")
4✔
247
            .function(
2✔
248
                variables -> ValueTypeBoolean.ValueBoolean.of(variables.getValue(0).equals(variables.getValue(1)))
9✔
249
            )
250
            .typeValidator((operator, input) -> {
1✔
251
                // Input size checking
252
                int requiredInputLength = operator.getRequiredInputLength();
3✔
253
                if(input.length != requiredInputLength) {
4✔
254
                    return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,
8✔
255
                            operator.getOperatorName(), input.length, requiredInputLength);
13✔
256
                }
257
                // Input types checking
258
                IValueType temporarySecondInputType = null;
2✔
259
                for(int i = 0; i < requiredInputLength; i++) {
7✔
260
                    IValueType inputType = input[i];
4✔
261
                    if (inputType instanceof IValueTypeNumber) {
3✔
262
                        inputType = ValueTypes.CATEGORY_NUMBER;
2✔
263
                    }
264
                    if(inputType == null) {
2!
265
                        return Component.translatable(L10NValues.OPERATOR_ERROR_NULLTYPE, operator.getOperatorName(), Integer.toString(i));
×
266
                    }
267
                    if(i == 0) {
2✔
268
                        temporarySecondInputType = inputType;
3✔
269
                    } else if(i == 1) {
3!
270
                        if(!ValueHelpers.correspondsTo(temporarySecondInputType, inputType)) {
4✔
271
                            return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPE,
8✔
272
                                    operator.getOperatorName(), Component.translatable(inputType.getTranslationKey()),
11✔
273
                                    Integer.toString(i), Component.translatable(temporarySecondInputType.getTranslationKey()));
8✔
274
                        }
275
                    }
276
                }
277
                return null;
2✔
278
            })
279
            .build());
1✔
280

281
    /**
282
     * Relational &gt; operator with two input integers and one output boolean.
283
     */
284
    public static final IOperator RELATIONAL_GT = REGISTRY.register(OperatorBuilders.RELATIONAL_2
6✔
285
            .inputTypes(2, ValueTypes.CATEGORY_NUMBER).symbol(">").operatorName("gt").interactName("greaterThan")
8✔
286
            .function(
1✔
287
                variables -> ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NUMBER.greaterThan(variables.getVariables()[0], variables.getVariables()[1]))
12✔
288
            ).build());
1✔
289

290
    /**
291
     * Relational &gt; operator with two input integers and one output boolean.
292
     */
293
    public static final IOperator RELATIONAL_LT = REGISTRY.register(OperatorBuilders.RELATIONAL_2
6✔
294
            .inputTypes(2, ValueTypes.CATEGORY_NUMBER).symbol("<").operatorName("lt").interactName("lessThan")
8✔
295
            .function(
1✔
296
                variables -> ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NUMBER.lessThan(variables.getVariables()[0], variables.getVariables()[1]))
12✔
297
            ).build());
1✔
298

299
    /**
300
     * Relational != operator with two inputs of any type (but equal) and one output boolean.
301
     */
302
    public static final IOperator RELATIONAL_NOTEQUALS = REGISTRY.register(
13✔
303
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(RELATIONAL_EQUALS).build(
7✔
304
                    "!=", "notequals", "notEquals", IConfigRenderPattern.INFIX, "relational"));
305

306
    /**
307
     * Relational &gt;= operator with two inputs of any type (but equal) and one output boolean.
308
     */
309
    public static final IOperator RELATIONAL_GE = REGISTRY.register(
17✔
310
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_OR).apply(RELATIONAL_EQUALS, RELATIONAL_GT).build(
7✔
311
                    ">=", "ge", "greaterThanOrEquals", IConfigRenderPattern.INFIX, "relational"));
312

313
    /**
314
     * Relational &lt;= operator with two inputs of any type (but equal) and one output boolean.
315
     */
316
    public static final IOperator RELATIONAL_LE = REGISTRY.register(
17✔
317
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_OR).apply(RELATIONAL_EQUALS, RELATIONAL_LT).build(
7✔
318
                    "<=", "le", "lessThanOrEquals", IConfigRenderPattern.INFIX, "relational"));
319

320
    /**
321
     * ----------------------------------- BINARY OPERATORS -----------------------------------
322
     */
323

324
    /**
325
     * Binary AND operator with two input integers and one output integers.
326
     */
327
    public static final IOperator BINARY_AND = REGISTRY.register(OperatorBuilders.BINARY_2.symbol("&").operatorName("and").interactName("binaryAnd")
11✔
328
            .function(variables -> {
1✔
329
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
330
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
331
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() & b.getRawValue());
7✔
332
            }).build());
1✔
333

334
    /**
335
     * Binary OR operator with two input integers and one output integers.
336
     */
337
    public static final IOperator BINARY_OR = REGISTRY.register(OperatorBuilders.BINARY_2.symbol("|").operatorName("or").interactName("binaryOr")
11✔
338
            .function(variables -> {
1✔
339
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
340
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
341
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() | b.getRawValue());
7✔
342
            }).build());
1✔
343

344
    /**
345
     * Binary XOR operator with two input integers and one output integers.
346
     */
347
    public static final IOperator BINARY_XOR = REGISTRY.register(OperatorBuilders.BINARY_2.symbol("^").operatorInteract("xor")
9✔
348
            .function(variables -> {
1✔
349
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
350
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
351
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() ^ b.getRawValue());
7✔
352
            }).build());
1✔
353

354
    /**
355
     * Binary COMPLEMENT operator with one input integers and one output integers.
356
     */
357
    public static final IOperator BINARY_COMPLEMENT = REGISTRY.register(OperatorBuilders.BINARY_1_PREFIX.symbol("~").operatorInteract("complement")
9✔
358
            .function(variables -> {
1✔
359
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
360
                return ValueTypeInteger.ValueInteger.of(~a.getRawValue());
6✔
361
            }).build());
1✔
362

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

373
    /**
374
     * Binary &gt;&gt; operator with two input integers and one output integers.
375
     */
376
    public static final IOperator BINARY_RSHIFT = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(">>").operatorName("rshift").interactName("rightShift")
11✔
377
            .function(variables -> {
1✔
378
                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);
6✔
379
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
380
                return ValueTypeInteger.ValueInteger.of(a.getRawValue() >> b.getRawValue());
7✔
381
            }).build());
1✔
382

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

393
    /**
394
     * ----------------------------------- STRING OPERATORS -----------------------------------
395
     */
396

397
    /**
398
     * String length operator with one input string and one output integer.
399
     */
400
    public static final IOperator STRING_LENGTH = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX.symbol("len").operatorInteract("length")
9✔
401
            .output(ValueTypes.INTEGER).function(variables -> {
3✔
402
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
403
                return ValueTypeInteger.ValueInteger.of(a.getRawValue().length());
5✔
404
            }).build());
1✔
405

406
    /**
407
     * String concat operator with two input strings and one output string.
408
     */
409
    public static final IOperator STRING_CONCAT = REGISTRY.register(OperatorBuilders.STRING_2.symbol("+").operatorInteract("concat")
9✔
410
            .function(variables -> {
1✔
411
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
412
                ValueTypeString.ValueString b = variables.getValue(1, ValueTypes.STRING);
6✔
413
                return ValueTypeString.ValueString.of(a.getRawValue() + b.getRawValue());
7✔
414
            }).build());
1✔
415

416
    /**
417
     * String contains operator which checks whether a given (literal) string is contained in the given string.
418
     */
419
    public static final IOperator STRING_CONTAINS = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperatorInteract("contains")
7✔
420
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
421
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
422
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
423
                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().contains(search.getRawValue()));
7✔
424
            }).build());
1✔
425

426
    /**
427
     * String match operator which checks whether a given regular expression is contained within a string.
428
     */
429
    public static final IOperator STRING_CONTAINS_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("contains_regex").interactName("containsRegex")
9✔
430
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
431
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
432
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
433
                try {
434
                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
435
                    return ValueTypeBoolean.ValueBoolean.of(m.find());
4✔
436
                } catch (PatternSyntaxException e) {
1✔
437
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
438
                            pattern.getRawValue()));
2✔
439
                }
440
            }).build());
1✔
441

442
    /**
443
     * String match operator which checks whether a given regular expression matches a string.
444
     */
445
    public static final IOperator STRING_MATCHES_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("matches_regex").interactName("matchesRegex")
9✔
446
            .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
447
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
448
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
449
                try {
450
                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
451
                    return ValueTypeBoolean.ValueBoolean.of(m.matches());
4✔
452
                } catch (PatternSyntaxException e) {
1✔
453
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
454
                            pattern.getRawValue()));
2✔
455
                }
456
            }).build());
1✔
457

458
    /**
459
     * String operator which returns the integral index of the first position where the search string appears in the given string.
460
     */
461
    public static final IOperator STRING_INDEX_OF = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("index_of").interactName("indexOf")
9✔
462
        .output(ValueTypes.INTEGER).function(variables -> {
3✔
463
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
464
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
465
                return ValueTypeInteger.ValueInteger.of(str.getRawValue().indexOf(search.getRawValue()));
7✔
466
            }).build());
1✔
467

468
    /**
469
     * String operator which returns the integral index where the a substring matching the regular expression appears in the given string.
470
     */
471
    public static final IOperator STRING_INDEX_OF_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("index_of_regex").interactName("indexOfRegex")
9✔
472
        .output(ValueTypes.INTEGER).function(variables -> {
3✔
473
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
474
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
475
                try {
476
                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
477
                    if (m.find()) {
3✔
478
                        return ValueTypeInteger.ValueInteger.of(m.start());
4✔
479
                    } else {
480
                        return ValueTypeInteger.ValueInteger.of(-1);
3✔
481
                    }
482
                } catch (PatternSyntaxException e) {
1✔
483
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
484
                            pattern.getRawValue()));
2✔
485
                }
486
            }).build());
1✔
487

488
    /**
489
     * String match operator which checks whether a given string matches the beginning of the given string.
490
     */
491
    public static final IOperator STRING_STARTS_WITH = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator("starts_with").interactName("startsWith")
9✔
492
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
493
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
494
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
495
                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().startsWith(search.getRawValue()));
7✔
496
            }).build());
1✔
497

498
    /**
499
     * String match operator which checks whether a given string matches the end of the given string.
500
     */
501
    public static final IOperator STRING_ENDS_WITH = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator("ends_with").interactName("endsWith")
9✔
502
        .output(ValueTypes.BOOLEAN).function(variables -> {
3✔
503
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
504
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
505
                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().endsWith(search.getRawValue()));
7✔
506
            }).build());
1✔
507

508
    /**
509
     * String operator which splits on the given (literal) delimiter the input string .
510
     */
511
    public static final IOperator STRING_SPLIT_ON = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator("split_on").interactName("splitOn")
9✔
512
        .output(ValueTypes.LIST).function(variables -> {
3✔
513
                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
514
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
515
                List<String> pieces = Arrays.asList(str.getRawValue().split(java.util.regex.Pattern.quote(search.getRawValue())));
8✔
516
                List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
517
                for (String piece : pieces) {
10✔
518
                    values.add(ValueTypeString.ValueString.of(piece));
5✔
519
                }
1✔
520
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
521
            }).build());
1✔
522

523
    /**
524
     * String operator which splits on the given (regular expression) delimiter the input string.
525
     */
526
    public static final IOperator STRING_SPLIT_ON_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("split_on_regex").interactName("splitOnRegex")
9✔
527
        .output(ValueTypes.LIST).function(variables -> {
3✔
528
                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
529
                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
530
                try {
531
                    List<String> pieces = Arrays.asList(Pattern.compile(pattern.getRawValue()).split(str.getRawValue()));
8✔
532
                    List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
533
                    for (String piece : pieces) {
10✔
534
                        values.add(ValueTypeString.ValueString.of(piece));
5✔
535
                    }
1✔
536
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
537
                } catch (PatternSyntaxException e) {
1✔
538
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
539
                            pattern.getRawValue()));
2✔
540
                }
541
            }).build());
1✔
542

543
    /**
544
     * String operator which takes the substring of the given string between the two integer indices.
545
     */
546
    public static final IOperator STRING_SUBSTRING = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract("substring")
7✔
547
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
15✔
548
        .inputTypes(ValueTypes.INTEGER, ValueTypes.INTEGER, ValueTypes.STRING)
2✔
549
        .output(ValueTypes.STRING)
2✔
550
        .function(variables -> {
1✔
551
            ValueTypeInteger.ValueInteger from = variables.getValue(0, ValueTypes.INTEGER);
6✔
552
            ValueTypeInteger.ValueInteger to = variables.getValue(1, ValueTypes.INTEGER);
6✔
553
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
554
            if (from.getRawValue() > to.getRawValue()) {
5✔
555
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_TOGREATERTHANFROM));
6✔
556
            }
557
            if (from.getRawValue() < 0 || to.getRawValue() < 0) {
6!
558
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_INDEXNEGATIVE));
6✔
559
            }
560
            int stringLength = str.getRawValue().length();
4✔
561
            if (from.getRawValue() > stringLength || to.getRawValue() > stringLength) {
8!
562
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_LONGERTHANSTRING));
6✔
563
            }
564
            return ValueTypeString.ValueString.of(str.getRawValue().substring(from.getRawValue(), to.getRawValue()));
9✔
565
        }).build());
1✔
566

567

568
    /**
569
     * 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.
570
     */
571
    public static final IOperator STRING_REGEX_GROUP = REGISTRY.register(OperatorBuilders.STRING.symbolOperator("regex_group").interactName("regexGroup")
9✔
572
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
15✔
573
        .inputTypes(ValueTypes.STRING, ValueTypes.INTEGER, ValueTypes.STRING)
2✔
574
        .output(ValueTypes.STRING)
2✔
575
        .function(variables -> {
1✔
576
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
577
            ValueTypeInteger.ValueInteger group = variables.getValue(1, ValueTypes.INTEGER);
6✔
578
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
579
            if (group.getRawValue() < 0) {
3✔
580
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_INDEXNEGATIVE));
6✔
581
            }
582
            try {
583
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
584
                if (m.find()) {
3!
585
                    String result = m.group(group.getRawValue());
5✔
586
                    return ValueTypeString.ValueString.of(result == null ? "" : result);
5!
587
                } else {
588
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCH,
×
589
                            str.getRawValue(), pattern.getRawValue()));
×
590
                }
591
            } catch (PatternSyntaxException e) {
1✔
592
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
593
                        pattern.getRawValue()));
2✔
594
            } catch (IndexOutOfBoundsException e) {
1✔
595
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCHGROUP,
11✔
596
                        str.getRawValue(), pattern.getRawValue(), group.getRawValue()));
13✔
597
            }
598
        }).build()
1✔
599
    );
600

601
    /**
602
     * 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.
603
     */
604
    public static final IOperator STRING_REGEX_GROUPS = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator("regex_groups").interactName("regexGroups")
9✔
605
        .output(ValueTypes.LIST)
2✔
606
        .function(variables -> {
1✔
607
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
608
            ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);
6✔
609
            try {
610
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
611
                if (m.find()) {
3✔
612
                    List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
613
                    for (int i = 0; i <= m.groupCount(); i++) {
8✔
614
                        String result = m.group(i);
4✔
615
                        values.add(ValueTypeString.ValueString.of(result == null ? "" : result));
7!
616
                    }
617
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
618
                } else {
619
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, Collections.<ValueTypeString.ValueString>emptyList());
4✔
620
                }
621
            } catch (PatternSyntaxException e) {
1✔
622
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
623
                        pattern.getRawValue()));
2✔
624
            }
625
        }).build()
1✔
626
    );
627

628
    /**
629
     * String operator which finds all matches of the regular expression in the given string and returns the given group for each match.
630
     */
631
    public static final IOperator STRING_REGEX_SCAN = REGISTRY.register(OperatorBuilders.STRING.symbolOperator("regex_scan").interactName("regexScan")
9✔
632
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
15✔
633
        .inputTypes(ValueTypes.STRING, ValueTypes.INTEGER, ValueTypes.STRING)
2✔
634
        .output(ValueTypes.LIST)
2✔
635
        .function(variables -> {
1✔
636
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
637
            ValueTypeInteger.ValueInteger group = variables.getValue(1, ValueTypes.INTEGER);
6✔
638
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
639
            if (group.getRawValue() < 0) {
3!
640
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEXSCAN_INDEXNEGATIVE));
×
641
            }
642
            try {
643
                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());
7✔
644
                List<ValueTypeString.ValueString> values = Lists.newArrayList();
2✔
645
                while (m.find()) {
3✔
646
                    String match = m.group(group.getRawValue());
5✔
647
                    if (match != null) {
2✔
648
                        values.add(ValueTypeString.ValueString.of(match));
5✔
649
                    }
650
                }
1✔
651
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);
4✔
652
            } catch (PatternSyntaxException e) {
1✔
653
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
11✔
654
                        pattern.getRawValue()));
2✔
655
            } catch (IndexOutOfBoundsException e) {
×
656
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEXSCAN_NOMATCHGROUP,
×
657
                        str.getRawValue(), pattern.getRawValue(), group.getRawValue()));
×
658
            }
659
        }).build()
1✔
660
    );
661

662
    /**
663
     * String operator which, finds all the matches of the (literal) search and replaces them with the given replacement, in the input string.
664
     */
665
    public static final IOperator STRING_REPLACE = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract("replace")
7✔
666
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
3✔
667
        .inputTypes(3, ValueTypes.STRING)
2✔
668
        .output(ValueTypes.STRING)
2✔
669
        .function(variables -> {
1✔
670
            ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);
6✔
671
            ValueTypeString.ValueString replacement = variables.getValue(1, ValueTypes.STRING);
6✔
672
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
673
            return ValueTypeString.ValueString.of(str.getRawValue().replaceAll(java.util.regex.Pattern.quote(search.getRawValue()), java.util.regex.Matcher.quoteReplacement(replacement.getRawValue())));
11✔
674
        }).build()
1✔
675
    );
676

677
    /**
678
     * String operator which, finds all the matches of the regular expression pattern and replaces them with the given replacement, in the input string.
679
     */
680
    public static final IOperator STRING_REPLACE_REGEX = REGISTRY.register(OperatorBuilders.STRING.symbolOperator("replace_regex").interactName("replaceRegex")
9✔
681
        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
3✔
682
        .inputTypes(3, ValueTypes.STRING)
2✔
683
        .output(ValueTypes.STRING)
2✔
684
        .function(variables -> {
1✔
685
            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);
6✔
686
            ValueTypeString.ValueString replacement = variables.getValue(1, ValueTypes.STRING);
6✔
687
            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);
6✔
688
            try {
689
                return ValueTypeString.ValueString.of(Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue()).replaceAll(replacement.getRawValue()));
11✔
690
            } catch (PatternSyntaxException e) {
×
691
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,
×
692
                        pattern.getRawValue()));
×
693
            } catch (IndexOutOfBoundsException e) {
×
694
                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REPLACEREGEX_INVALIDGROUP,
×
695
                        replacement.getRawValue(), e.getMessage()));
×
696
            }
697
        }).build()
1✔
698
    );
699

700
    /**
701
     * String operator to join a list using a string delimiter
702
     */
703
    public static final IOperator STRING_JOIN = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract("join")
7✔
704
            .renderPattern(IConfigRenderPattern.PREFIX_2)
11✔
705
            .inputTypes(ValueTypes.STRING, ValueTypes.LIST)
2✔
706
            .output(ValueTypes.STRING)
4✔
707
            .function(new OperatorBase.IFunction() {
4✔
708
                @Override
709
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
710
                    // Prepare values
711
                    ValueTypeString.ValueString delimiter = variables.getValue(0, ValueTypes.STRING);
6✔
712
                    ValueTypeList.ValueList<?, ?> elements = variables.getValue(1, ValueTypes.LIST);
6✔
713
                    if (!ValueHelpers.correspondsTo(elements.getRawValue().getValueType(), ValueTypes.STRING)) {
6✔
714
                        throw new EvaluationException(Component.translatable(
11✔
715
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
716
                                Component.translatable(elements.getRawValue().getValueType().getTranslationKey()),
8✔
717
                                Component.translatable(ValueTypes.STRING.getTranslationKey())));
3✔
718
                    }
719

720
                    // Don't allow joining on an infinite list
721
                    if (elements.getRawValue().isInfinite()) {
4✔
722
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
11✔
723
                                STRING_JOIN.getLocalizedNameFull()));
2✔
724
                    }
725

726
                    // Join in O(n), while type-checking each element, as the list may have been of ANY type.
727
                    StringBuilder sb = new StringBuilder();
4✔
728
                    for (IValue value : elements.getRawValue()) {
11✔
729
                        if (value.getType() != ValueTypes.STRING) {
4✔
730
                            throw new EvaluationException(Component.translatable(
11✔
731
                                    L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
732
                                    Component.translatable(value.getType().getTranslationKey()),
7✔
733
                                    Component.translatable(ValueTypes.STRING.getTranslationKey())));
3✔
734
                        }
735
                        if (sb.length() > 0) {
3✔
736
                            sb.append(delimiter.getRawValue());
5✔
737
                        }
738
                        sb.append(((ValueTypeString.ValueString) value).getRawValue());
6✔
739
                    }
1✔
740

741
                    return ValueTypeString.ValueString.of(sb.toString());
4✔
742
                }
743
            }).build()
1✔
744
    );
745

746
    /**
747
     * Get a name value type name.
748
     */
749
    public static final IOperator NAMED_NAME = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperatorInteract("name")
7✔
750
            .inputType(ValueTypes.CATEGORY_NAMED).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
751
            .function(
1✔
752
                variables -> ValueTypeString.ValueString.of(ValueTypes.CATEGORY_NAMED.getName(variables.getVariables()[0]))
8✔
753
            ).build());
1✔
754

755
    /**
756
     * Get a unique name value type name.
757
     */
758
    public static final IOperator UNIQUELYNAMED_UNIQUENAME = REGISTRY.register(OperatorBuilders.STRING_2.symbol("uname").operatorName("unique_name").interactName("uniqueName")
11✔
759
            .inputType(ValueTypes.CATEGORY_UNIQUELY_NAMED).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
760
            .function(
1✔
761
                variables -> ValueTypeString.ValueString.of(ValueTypes.CATEGORY_UNIQUELY_NAMED.getUniqueName(variables.getVariables()[0]))
×
762
            ).build());
1✔
763

764
    /**
765
     * Throw a custom error
766
     */
767
    public static final IOperator STRING_ERROR  = REGISTRY.register(OperatorBuilders.STRING_2.symbol("error").operatorName("string_error").interactName("stringError")
11✔
768
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
769
            .function(
1✔
770
                (variables) -> {
771
                    throw new EvaluationException(Component.translatable(variables.getValue(0, ValueTypes.STRING).getRawValue()));
11✔
772
                }
773
            ).build());
1✔
774

775
    /**
776
     * ----------------------------------- NUMBER OPERATORS -----------------------------------
777
     */
778

779
    /**
780
     * Number round operator with one input double and one output integers.
781
     */
782
    public static final IOperator NUMBER_ROUND = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
783
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol("|| ||").operatorInteract("round")
8✔
784
            .function(
1✔
785
                variables -> ValueTypes.CATEGORY_NUMBER.round(variables.getVariables()[0])
7✔
786
            ).build());
1✔
787

788
    /**
789
     * Number ceil operator with one input double and one output integers.
790
     */
791
    public static final IOperator NUMBER_CEIL = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
792
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol("⌈ ⌉").operatorInteract("ceil")
8✔
793
            .function(
1✔
794
                variables -> ValueTypes.CATEGORY_NUMBER.ceil(variables.getVariables()[0])
7✔
795
            ).build());
1✔
796

797
    /**
798
     * Number floor operator with one input double and one output integers.
799
     */
800
    public static final IOperator NUMBER_FLOOR = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX
5✔
801
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol("⌊ ⌋").operatorInteract("floor")
8✔
802
            .function(
1✔
803
                variables -> ValueTypes.CATEGORY_NUMBER.floor(variables.getVariables()[0])
7✔
804
            ).build());
1✔
805

806
    /**
807
     * Accepts a number, and returns a string roughly representing that number
808
     */
809
    public static final IOperator NUMBER_COMPACT = REGISTRY.register(OperatorBuilders.NUMBER_1_LONG
5✔
810
            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.STRING).symbolOperatorInteract("compact")
6✔
811
            .function(
1✔
812
                variables -> ValueTypes.CATEGORY_NUMBER.compact(variables.getVariables()[0])
7✔
813
            ).build());
1✔
814

815
    /**
816
     * ----------------------------------- NULLABLE OPERATORS -----------------------------------
817
     */
818

819
    /**
820
     * Check if something is null
821
     */
822
    public static final IOperator NULLABLE_ISNULL = REGISTRY.register(OperatorBuilders.NULLABLE_1_PREFIX.symbol("o").operatorName("isnull").interactName("isNull")
11✔
823
            .inputType(ValueTypes.CATEGORY_ANY).output(ValueTypes.BOOLEAN).function(variables -> {
5✔
824
                if(ValueHelpers.correspondsTo(variables.getVariables()[0].getType(), ValueTypes.CATEGORY_NULLABLE)) {
8!
825
                    return ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NULLABLE.isNull(variables.getVariables()[0]));
×
826
                }
827
                return ValueTypeBoolean.ValueBoolean.of(false);
3✔
828
            }).build());
1✔
829

830
    /**
831
     * Check if something is not null
832
     */
833
    public static final IOperator NULLABLE_ISNOTNULL = REGISTRY.register(new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT)
13✔
834
            .apply(NULLABLE_ISNULL).build("∅", "isnotnull", "isNotNull", IConfigRenderPattern.PREFIX_1, "general"));
7✔
835

836
    /**
837
     * ----------------------------------- LIST OPERATORS -----------------------------------
838
     */
839

840
    /**
841
     * List operator with one input list and one output integer
842
     */
843
    public static final IOperator LIST_LENGTH = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.INTEGER).symbol("| |").operatorInteract("length")
11✔
844
            .function(variables -> {
1✔
845
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
846
                IValueTypeListProxy a = valueList.getRawValue();
3✔
847
                return ValueTypeInteger.ValueInteger.of(a.getLength());
4✔
848
            }).build());
1✔
849

850
    /**
851
     * Check if a list is empty
852
     */
853
    public static final IOperator LIST_EMPTY = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.BOOLEAN).symbol("∅").operatorName("empty").interactName("isEmpty")
13✔
854
            .function(variables -> {
1✔
855
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
856
                IValueTypeListProxy a = valueList.getRawValue();
3✔
857
                return ValueTypeBoolean.ValueBoolean.of(a.getLength() == 0);
8✔
858
            }).build());
1✔
859

860
    /**
861
     * Check if a list is not empty
862
     */
863
    public static final IOperator LIST_NOT_EMPTY = REGISTRY.register(
13✔
864
            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LIST_EMPTY).build(
7✔
865
                    "o", "notempty", "isNotEmpty", IConfigRenderPattern.PREFIX_1, "list"));
866

867
    /**
868
     * List operator with one input list and one output integer
869
     */
870
    public static final IOperator LIST_ELEMENT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
14✔
871
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER}).output(ValueTypes.CATEGORY_ANY)
4✔
872
            .renderPattern(IConfigRenderPattern.INFIX).symbolOperatorInteract("get")
4✔
873
            .function(variables -> {
2✔
874
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
875
                IValueTypeListProxy a = valueList.getRawValue();
3✔
876
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
877
                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {
8!
878
                    return a.get(b.getRawValue());
5✔
879
                } else {
880
                    throw new EvaluationException(Component.translatable(
11✔
881
                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, b.getRawValue(), a.getLength()));
9✔
882
                }
883
            }).conditionalOutputTypeDeriver((operator, input) -> {
1✔
884
                try {
885
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();
×
886
                    return a.getValueType();
×
887
                } catch (ClassCastException | EvaluationException e) {
×
888
                    return operator.getOutputType();
×
889
                }
890
            }).build());
1✔
891

892
    /**
893
     * List operator with one input list, one output integer, and one default value
894
     */
895
    public static final IOperator LIST_ELEMENT_DEFAULT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
18✔
896
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.CATEGORY_ANY}).output(ValueTypes.CATEGORY_ANY)
4✔
897
            .renderPattern(IConfigRenderPattern.INFIX_2_LONG).symbolOperator("get_or_default").interactName("getOrDefault")
6✔
898
            .function(variables -> {
2✔
899
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
900
                IValueTypeListProxy a = valueList.getRawValue();
3✔
901
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
902
                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {
8✔
903
                    return a.get(b.getRawValue());
5✔
904
                } else {
905
                    if (!ValueHelpers.correspondsTo(a.getValueType(), variables.getVariables()[2].getType())) {
9!
906
                        throw new EvaluationException(Component.translatable(
×
907
                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
908
                                Component.translatable(a.getValueType().getTranslationKey()),
×
909
                                Component.translatable(variables.getVariables()[2].getType().getTranslationKey())));
×
910
                    }
911
                    return variables.getValue(2);
4✔
912
                }
913
            }).conditionalOutputTypeDeriver(
1✔
914
                (operator, input) -> input[2].getType()
×
915
            ).build());
1✔
916

917
    /**
918
     * List contains operator that takes a list, a list element to look for and returns a boolean.
919
     */
920
    public static final IOperator LIST_CONTAINS = REGISTRY.register(OperatorBuilders.LIST
14✔
921
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
922
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
923
            .output(ValueTypes.BOOLEAN).symbolOperatorInteract("contains")
4✔
924
            .function(variables -> {
1✔
925
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
926
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
927
                IValue input = variables.getValue(1);
4✔
928
                for (IValue value : list) {
10✔
929
                    if (value.equals(input)) {
4✔
930
                        return ValueTypeBoolean.ValueBoolean.of(true);
3✔
931
                    }
932
                }
1✔
933
                return ValueTypeBoolean.ValueBoolean.of(false);
3✔
934
            }).build());
1✔
935

936
    /**
937
     * List contains operator that takes a list, a predicate that maps a list element to a boolean, a list element and returns a boolean.
938
     */
939
    public static final IOperator LIST_CONTAINS_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
940
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
941
            .renderPattern(IConfigRenderPattern.INFIX)
2✔
942
            .output(ValueTypes.BOOLEAN).symbolOperator("contains_p").interactName("containsPredicate")
6✔
943
            .function(variables -> {
1✔
944
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
945
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
946
                IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
947
                for (IValue value : list) {
10✔
948
                    IValue result = ValueHelpers.evaluateOperator(operator, value);
9✔
949
                    ValueHelpers.validatePredicateOutput(operator, result);
3✔
950
                    if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
951
                        return ValueTypeBoolean.ValueBoolean.of(true);
3✔
952
                    }
953
                }
1✔
954
                return ValueTypeBoolean.ValueBoolean.of(false);
3✔
955
            }).build());
1✔
956

957
    /**
958
     * List operator with one input list, and element and one output integer
959
     */
960
    public static final IOperator LIST_COUNT = REGISTRY.register(OperatorBuilders.LIST
14✔
961
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
962
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.INTEGER)
4✔
963
            .symbolOperatorInteract("count")
4✔
964
            .function(new OperatorBase.IFunction() {
4✔
965
                @Override
966
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
967
                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
968
                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
969
                    if (list.isInfinite()) {
3✔
970
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
11✔
971
                                LIST_COUNT.getLocalizedNameFull()));
2✔
972
                    }
973
                    IValue value = variables.getValue(1);
4✔
974
                    int count = 0;
2✔
975
                    for (IValue listValue : list) {
10✔
976
                        if (listValue.equals(value)) {
4✔
977
                            count++;
1✔
978
                        }
979
                    }
1✔
980
                    return ValueTypeInteger.ValueInteger.of(count);
3✔
981
                }
982
            }).build());
1✔
983

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

1013
    /**
1014
     * Append an element to the given list
1015
     */
1016
    public static final IOperator LIST_APPEND = REGISTRY.register(OperatorBuilders.LIST
14✔
1017
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
1018
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1019
            .symbolOperatorInteract("append")
2✔
1020
            .function(variables -> {
1✔
1021
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1022
                IValueTypeListProxy a = valueList.getRawValue();
3✔
1023
                IValue value = variables.getValue(1);
4✔
1024
                if (!ValueHelpers.correspondsTo(a.getValueType(), value.getType())) {
6✔
1025
                    throw new EvaluationException(Component.translatable(
11✔
1026
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1027
                            Component.translatable(a.getValueType().getTranslationKey()),
7✔
1028
                            Component.translatable(value.getType().getTranslationKey())));
4✔
1029
                }
1030
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyAppend(a, value));
7✔
1031
            }).build());
1✔
1032

1033
    /**
1034
     * Concatenate two lists
1035
     */
1036
    public static final IOperator LIST_CONCAT = REGISTRY.register(OperatorBuilders.LIST
14✔
1037
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})
2✔
1038
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1039
            .symbolOperatorInteract("concat")
2✔
1040
            .function(variables -> {
1✔
1041
                ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);
6✔
1042
                IValueTypeListProxy a = valueList0.getRawValue();
3✔
1043
                ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);
6✔
1044
                IValueTypeListProxy b = valueList1.getRawValue();
3✔
1045
                if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {
6!
1046
                    throw new EvaluationException(Component.translatable(
×
1047
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1048
                            Component.translatable(a.getValueType().getTranslationKey()),
×
1049
                            Component.translatable(b.getValueType().getTranslationKey())));
×
1050
                }
1051
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyConcat(a, b));
15✔
1052
            }).build());
1✔
1053

1054
    /**
1055
     * Build a list lazily using a start value and an operator that is applied to the previous element to get a next element.
1056
     */
1057
    public static final IOperator LIST_LAZYBUILT = REGISTRY.register(OperatorBuilders.LIST
14✔
1058
            .inputTypes(new IValueType[]{ValueTypes.CATEGORY_ANY, ValueTypes.OPERATOR})
2✔
1059
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1060
            .symbolOperator("lazybuilt").interactName("lazyBuilt")
4✔
1061
            .function(variables -> {
1✔
1062
                IValue a = variables.getValue(0);
4✔
1063
                IOperator operator = OperatorBuilders.getSafeOperator(variables.getValue(1, ValueTypes.OPERATOR), a.getType());
9✔
1064
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyLazyBuilt<>(a, operator));
7✔
1065
            }).build());
1✔
1066

1067
    /**
1068
     * Get the first element of the given list.
1069
     */
1070
    public static final IOperator LIST_HEAD = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX
10✔
1071
            .inputTypes(new IValueType[]{ValueTypes.LIST}).output(ValueTypes.CATEGORY_ANY)
4✔
1072
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).symbolOperatorInteract("head")
4✔
1073
            .function(variables -> {
2✔
1074
                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);
6✔
1075
                IValueTypeListProxy a = list.getRawValue();
3✔
1076
                if (a.getLength() > 0) {
3!
1077
                    return a.get(0);
4✔
1078
                } else {
1079
                    throw new EvaluationException(Component.translatable(
×
1080
                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, 0, a.getLength()));
×
1081
                }
1082
            }).conditionalOutputTypeDeriver((operator, input) -> {
1✔
1083
                try {
1084
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();
×
1085
                    return a.getValueType();
×
1086
                } catch (EvaluationException e) {
×
1087
                    return operator.getOutputType();
×
1088
                }
1089
            }).build());
1✔
1090

1091
    /**
1092
     * Append an element to the given list.
1093
     */
1094
    public static final IOperator LIST_TAIL = REGISTRY.register(OperatorBuilders.LIST
10✔
1095
            .inputTypes(new IValueType[]{ValueTypes.LIST})
2✔
1096
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)
4✔
1097
            .symbolOperatorInteract("tail")
2✔
1098
            .function(variables -> {
1✔
1099
                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);
6✔
1100
                IValueTypeListProxy a = list.getRawValue();
3✔
1101
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyTail(a));
6✔
1102
            }).build());
1✔
1103

1104
    /**
1105
     * Deduplicate the given list elements based on the given predicate.
1106
     */
1107
    public static final IOperator LIST_UNIQ_PREDICATE = REGISTRY.register(OperatorBuilders.LIST
14✔
1108
            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})
2✔
1109
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1110
            .symbolOperator("uniq_p").interactName("uniquePredicate")
4✔
1111
            .function(variables -> {
1✔
1112
                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);
6✔
1113
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1114
                final IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));
7✔
1115
                List<IValue> values = new ArrayList<>();
4✔
1116
                outerLoop:
1117
                for(IValue value : list) {
10✔
1118
                    for(IValue existing : values) {
10✔
1119
                        IValue result;
1120
                        try {
1121
                            result = ValueHelpers.evaluateOperator(operator, value, existing);
13✔
1122
                            ValueHelpers.validatePredicateOutput(operator, result);
3✔
1123
                        } catch (EvaluationException e) {
×
1124
                            throw Lombok.sneakyThrow(e);
×
1125
                        }
1✔
1126
                        if(((ValueTypeBoolean.ValueBoolean) result).getRawValue()) continue outerLoop;
5✔
1127
                    }
1✔
1128
                    values.add(value);
4✔
1129
                }
1✔
1130
                return ValueTypeList.ValueList.ofList(list.getValueType(), values);
5✔
1131
            }).build());
1✔
1132

1133
    /**
1134
     * Deduplicate the given list elements.
1135
     */
1136
    public static final IOperator LIST_UNIQ = REGISTRY.register(OperatorBuilders.LIST
5✔
1137
            .inputType(ValueTypes.LIST)
2✔
1138
            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)
4✔
1139
            .symbolOperator("uniq").interactName("unique")
4✔
1140
            .function(variables -> {
1✔
1141
                ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);
6✔
1142
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1143
                return ValueTypeList.ValueList.ofList(list.getValueType(), new ArrayList<>(Sets.newLinkedHashSet(list)));
9✔
1144
            }).build());
1✔
1145

1146
    /**
1147
     * Take a subset of the given list from the given index (inclusive) to the given index (exclusive).
1148
     */
1149
    public static final IOperator LIST_SLICE = REGISTRY.register(OperatorBuilders.LIST
18✔
1150
            .inputTypes(ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.INTEGER)
2✔
1151
            .renderPattern(IConfigRenderPattern.PREFIX_3).output(ValueTypes.LIST)
4✔
1152
            .symbolOperatorInteract("slice")
2✔
1153
            .function(variables -> {
1✔
1154
                ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);
6✔
1155
                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();
3✔
1156
                ValueTypeInteger.ValueInteger from = variables.getValue(1, ValueTypes.INTEGER);
6✔
1157
                ValueTypeInteger.ValueInteger to = variables.getValue(2, ValueTypes.INTEGER);
6✔
1158
                if (from.getRawValue() >= to.getRawValue()) {
5✔
1159
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_TOGREATERTHANFROM));
6✔
1160
                }
1161
                if (from.getRawValue() < 0 || to.getRawValue() < 0){
6!
1162
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_INDEXNEGATIVE));
6✔
1163
                }
1164
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxySlice<>(list, from.getRawValue(), to.getRawValue()));
10✔
1165
            }).build());
1✔
1166

1167
    public static final IOperator LIST_INTERSECTION = REGISTRY.register(OperatorBuilders.LIST
14✔
1168
            .inputTypes(ValueTypes.LIST, ValueTypes.LIST)
2✔
1169
            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)
4✔
1170
            .symbol("∩").operatorInteract("intersection")
6✔
1171
            .function(new OperatorBase.IFunction() {
4✔
1172
                @Override
1173
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
1174
                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList1 = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
1175
                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList2 = variables.getValue(1, ValueTypes.LIST).getRawValue();
7✔
1176
                    if (rawList1.isInfinite() || rawList2.isInfinite()) {
6!
1177
                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,
×
1178
                                LIST_INTERSECTION.getLocalizedNameFull()));
×
1179
                    }
1180
                    LinkedHashSet<IValue> result = Sets.newLinkedHashSet(rawList1);
3✔
1181
                    result.retainAll(Sets.newLinkedHashSet(rawList2));
5✔
1182

1183
                    return ValueTypeList.ValueList.ofList(rawList1.getValueType(), result.stream().toList());
7✔
1184
                }
1185
            }).build());
1✔
1186

1187
    /**
1188
     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------
1189
     */
1190

1191
    /**
1192
     * Block isOpaque operator with one input block and one output boolean.
1193
     */
1194
    public static final IOperator OBJECT_BLOCK_OPAQUE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN).symbolOperator("opaque").interactName("isOpaque")
11✔
1195
            .function(variables -> {
1✔
1196
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1197
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent() && a.getRawValue().get().isSolidRender());
15!
1198
            }).build());
1✔
1199

1200
    /**
1201
     * The itemstack representation of the block
1202
     */
1203
    public static final IOperator OBJECT_BLOCK_ITEMSTACK = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK).symbolOperator("itemstack").interactName("itemStack")
11✔
1204
            .function(variables -> {
1✔
1205
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1206
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getRawValue().isPresent() ? IModHelpers.get().getBlockHelpers().getItemStackFromBlockState(a.getRawValue().get()) : ItemStack.EMPTY);
14!
1207
            }).build());
1✔
1208

1209
    /**
1210
     * The name of the mod owning this block
1211
     */
1212
    public static final IOperator OBJECT_BLOCK_MODNAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING).symbolOperatorInteract("mod")
20✔
1213
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1214
                    (OperatorBase.SafeVariablesGetter variables) -> {
1215
                        ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1216
                        return a.getRawValue().isPresent() ? BuiltInRegistries.BLOCK.getKey(a.getRawValue().get().getBlock()) : ResourceLocation.parse("");
13!
1217
                    },
1218
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
1219
            ))).build());
1✔
1220

1221
    /**
1222
     * The breaksound of the block
1223
     */
1224
    public static final IOperator OBJECT_BLOCK_BREAKSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1225
            .symbol("break_sound").operatorName("breaksound").interactName("breakSound")
21✔
1226
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1227
                    OperatorBuilders.BLOCK_SOUND,
1228
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getBreakSound().location().toString() : "",
11!
1229
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1230
            ))).build());
1✔
1231
    /**
1232
     * The placesound of the block
1233
     */
1234
    public static final IOperator OBJECT_BLOCK_PLACESOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1235
            .symbol("place_sound").operatorName("placesound").interactName("placeSound")
21✔
1236
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1237
                    OperatorBuilders.BLOCK_SOUND,
1238
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getPlaceSound().location().toString() : "",
11!
1239
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1240
            ))).build());
1✔
1241
    /**
1242
     * The stepsound of the block
1243
     */
1244
    public static final IOperator OBJECT_BLOCK_STEPSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1245
            .symbol("step_sound").operatorName("stepsound").interactName("stepSound")
21✔
1246
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1247
                    OperatorBuilders.BLOCK_SOUND,
1248
                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getStepSound().location().toString() : "",
11!
1249
                    OperatorBuilders.PROPAGATOR_STRING_VALUE
1250
            ))).build());
1✔
1251

1252
    /**
1253
     * If the block is shearable
1254
     */
1255
    public static final IOperator OBJECT_BLOCK_ISSHEARABLE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
1256
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
1257
            .function(variables -> {
1✔
1258
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1259
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
1260
                        && a.getRawValue().get().getBlock() instanceof IShearable
7✔
1261
                        && ((IShearable) a.getRawValue().get().getBlock()).isShearable(null, ItemStack.EMPTY, null, null));
14!
1262
            }).build());
1✔
1263

1264
    /**
1265
     * The block when this block is planted
1266
     */
1267
    public static final IOperator OBJECT_BLOCK_PLANTAGE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1268
            .output(ValueTypes.INTEGER).symbol("plant_age").operatorName("plantage").interactName("plantAge")
8✔
1269
            .function(variables -> {
1✔
1270
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1271
                int age = 0;
2✔
1272
                if (a.getRawValue().isPresent()) {
4!
1273
                    for (Property<?> prop : a.getRawValue().get().getProperties()) {
14✔
1274
                        if (prop.getName().equals("age") && prop.getValueClass() == Integer.class) {
9!
1275
                            age = (Integer) a.getRawValue().get().getValue(prop);
9✔
1276
                        }
1277
                    }
1✔
1278
                }
1279
                return ValueTypeInteger.ValueInteger.of(age);
3✔
1280
            }).build());
1✔
1281

1282
    /**
1283
     * Get a block by name.
1284
     */
1285
    public static final IOperator OBJECT_BLOCK_BY_NAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1286
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_BLOCK)
4✔
1287
            .symbol("block_by_name").operatorName("blockbyname").interactName("blockByName")
7✔
1288
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
1289
                    .build(input -> {
1✔
1290
                        Block block = BuiltInRegistries.BLOCK.getValue(input);
5✔
1291
                        return ValueObjectTypeBlock.ValueBlock.of(block.defaultBlockState());
4✔
1292
                    })).build());
1✔
1293

1294
    /**
1295
     * Get the block properties as NBT compound tag.
1296
     */
1297
    public static final IOperator OBJECT_BLOCK_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1298
            .output(ValueTypes.NBT)
2✔
1299
            .symbol("block_props").operatorName("blockproperties").interactName("properties")
6✔
1300
            .function(variables -> {
1✔
1301
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1302
                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {
6✔
1303
                    CompoundTag tag = new CompoundTag();
4✔
1304
                    for (Property property : blockState.getProperties()) {
11✔
1305
                        Comparable<?> value = blockState.getValue(property);
4✔
1306
                        tag.putString(property.getName(), property.getName(value));
7✔
1307
                    }
1✔
1308
                    return tag;
2✔
1309
                }));
1310
            }).build());
1✔
1311

1312
    /**
1313
     * Get the given block applied with the given properties.
1314
     */
1315
    public static final IOperator OBJECT_BLOCK_WITH_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_INFIX_VERYLONG
14✔
1316
            .inputTypes(ValueTypes.OBJECT_BLOCK, ValueTypes.NBT).output(ValueTypes.OBJECT_BLOCK)
4✔
1317
            .symbol("block_with_props").operatorName("blockfromproperties").interactName("withProperties")
6✔
1318
            .function(variables -> {
1✔
1319
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1320
                ValueTypeNbt.ValueNbt b = variables.getValue(1, ValueTypes.NBT);
6✔
1321
                if (a.getRawValue().isPresent() && a.getRawValue().isPresent()) {
8!
1322
                    BlockState blockState = a.getRawValue().get();
5✔
1323
                    Tag tagRaw = b.getRawValue().get();
5✔
1324
                    if (tagRaw instanceof CompoundTag) {
3!
1325
                        CompoundTag tag = (CompoundTag) tagRaw;
3✔
1326
                        for (Property property : blockState.getProperties()) {
11✔
1327
                            if (tag.contains(property.getName(), Tag.TAG_STRING)) {
6!
1328
                                Optional<Comparable> valueOptional = property.getValue(tag.getString(property.getName()));
7✔
1329
                                if (valueOptional.isPresent()) {
3!
1330
                                    blockState = blockState.setValue(property, valueOptional.get());
8✔
1331
                                }
1332
                            }
1333
                        }
1✔
1334
                        return ValueObjectTypeBlock.ValueBlock.of(blockState);
3✔
1335
                    }
1336
                }
1337
                return a;
×
1338
            }).build());
1✔
1339

1340
    /**
1341
     * Get all possible block properties as NBT compound tag with list values.
1342
     */
1343
    public static final IOperator OBJECT_BLOCK_POSSIBLE_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG
5✔
1344
            .output(ValueTypes.NBT)
2✔
1345
            .symbol("block_all_props").operatorName("blockpossibleproperties").interactName("possibleProperties")
6✔
1346
            .function(variables -> {
1✔
1347
                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);
6✔
1348
                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {
6✔
1349
                    CompoundTag tag = new CompoundTag();
4✔
1350
                    for (Property property : blockState.getProperties()) {
11✔
1351
                        ListTag list = new ListTag();
4✔
1352
                        for (Comparable value : (Collection<Comparable>) property.getPossibleValues()) {
11✔
1353
                            list.add(StringTag.valueOf(property.getName(value)));
7✔
1354
                        }
1✔
1355
                        tag.put(property.getName(), list);
6✔
1356
                    }
1✔
1357
                    return tag;
2✔
1358
                }));
1359
            }).build());
1✔
1360

1361
    /**
1362
     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------
1363
     */
1364

1365
    /**
1366
     * Item Stack size operator with one input itemstack and one output integer.
1367
     */
1368
    public static final IOperator OBJECT_ITEMSTACK_SIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1369
            .output(ValueTypes.INTEGER).symbolOperator("size").interactName("size")
7✔
1370
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1371
                itemStack -> !itemStack.isEmpty() ? itemStack.getCount() : 0
8!
1372
            )).build());
1✔
1373

1374
    /**
1375
     * Item Stack maxsize operator with one input itemstack and one output integer.
1376
     */
1377
    public static final IOperator OBJECT_ITEMSTACK_MAXSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1378
            .output(ValueTypes.INTEGER).symbolOperator("maxsize").interactName("maxSize")
7✔
1379
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1380
                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxStackSize() : 0
8!
1381
            )).build());
1✔
1382

1383
    /**
1384
     * Item Stack isstackable operator with one input itemstack and one output boolean.
1385
     */
1386
    public static final IOperator OBJECT_ITEMSTACK_ISSTACKABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1387
            .output(ValueTypes.BOOLEAN).symbolOperator("stackable").interactName("isStackable")
7✔
1388
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1389
                itemStack -> !itemStack.isEmpty() && itemStack.isStackable()
11!
1390
            )).build());
1✔
1391

1392
    /**
1393
     * Item Stack isdamageable operator with one input itemstack and one output boolean.
1394
     */
1395
    public static final IOperator OBJECT_ITEMSTACK_ISDAMAGEABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1396
            .output(ValueTypes.BOOLEAN).symbolOperator("damageable").interactName("isDamageable")
7✔
1397
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1398
                itemStack -> !itemStack.isEmpty() && itemStack.isDamageableItem()
11!
1399
            )).build());
1✔
1400

1401
    /**
1402
     * Item Stack damage operator with one input itemstack and one output integer.
1403
     */
1404
    public static final IOperator OBJECT_ITEMSTACK_DAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1405
            .output(ValueTypes.INTEGER).symbolOperator("damage").interactName("damage")
7✔
1406
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1407
                itemStack -> !itemStack.isEmpty() ? itemStack.getDamageValue() : 0
8!
1408
            )).build());
1✔
1409

1410
    /**
1411
     * Item Stack maxdamage operator with one input itemstack and one output integer.
1412
     */
1413
    public static final IOperator OBJECT_ITEMSTACK_MAXDAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1414
            .output(ValueTypes.INTEGER).symbol("max_damage").operatorName("maxdamage").interactName("maxDamage")
9✔
1415
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1416
                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxDamage() : 0
8!
1417
            )).build());
1✔
1418

1419
    /**
1420
     * Item Stack isenchanted operator with one input itemstack and one output boolean.
1421
     */
1422
    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTED = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1423
            .output(ValueTypes.BOOLEAN).symbolOperator("enchanted").interactName("isEnchanted")
7✔
1424
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1425
                itemStack -> !itemStack.isEmpty() && itemStack.isEnchanted()
11!
1426
            )).build());
1✔
1427

1428
    /**
1429
     * Item Stack isenchantable operator with one input itemstack and one output boolean.
1430
     */
1431
    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1432
            .output(ValueTypes.BOOLEAN).symbolOperator("enchantable").interactName("isEnchantable")
7✔
1433
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1434
                itemStack -> !itemStack.isEmpty() && itemStack.isEnchantable()
11!
1435
            )).build());
1✔
1436

1437
    /**
1438
     * Item Stack repair cost with one input itemstack and one output integer.
1439
     */
1440
    public static final IOperator OBJECT_ITEMSTACK_REPAIRCOST = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1441
            .output(ValueTypes.INTEGER)
2✔
1442
            .symbol("repair_cost").operatorName("repaircost").interactName("repairCost")
7✔
1443
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1444
                itemStack -> !itemStack.isEmpty() ? itemStack.get(DataComponents.REPAIR_COST) : 0
9!
1445
            )).build());
1✔
1446

1447
    /**
1448
     * Get the rarity of an itemstack.
1449
     */
1450
    public static final IOperator OBJECT_ITEMSTACK_RARITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1451
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
1452
            .function(variables -> {
1✔
1453
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1454
                return ValueTypeString.ValueString.of(!a.getRawValue().isEmpty() ? a.getRawValue().getRarity().name() : "");
11!
1455
            }).build());
1✔
1456

1457
    /**
1458
     * Get the strength of an itemstack against a block as a double.
1459
     */
1460
    public static final IOperator OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
14✔
1461
            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.DOUBLE)
4✔
1462
            .symbolOperatorInteract("strength")
2✔
1463
            .function(variables -> {
1✔
1464
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1465
                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);
6✔
1466
                return ValueTypeDouble.ValueDouble.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() ? a.getRawValue().getDestroySpeed(b.getRawValue().get()) : 0);
19!
1467
            }).build());
1✔
1468

1469
    /**
1470
     * If the given itemstack can be used to harvest the given block.
1471
     */
1472
    public static final IOperator OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1473
            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.BOOLEAN)
4✔
1474
            .symbol("can_harvest").operatorName("canharvest").interactName("canHarvest")
6✔
1475
            .function(variables -> {
1✔
1476
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1477
                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);
6✔
1478
                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() && a.getRawValue().isCorrectToolForDrops(b.getRawValue().get()));
21!
1479
            }).build());
1✔
1480

1481
    /**
1482
     * The block from the stack
1483
     */
1484
    public static final IOperator OBJECT_ITEMSTACK_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1485
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
1486
            .function(variables -> {
1✔
1487
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1488
                return ValueObjectTypeBlock.ValueBlock.of((!a.getRawValue().isEmpty() && a.getRawValue().getItem() instanceof BlockItem) ? IModHelpers.get().getBlockHelpers().getBlockStateFromItemStack(a.getRawValue()) : null);
17!
1489
            }).build());
1✔
1490

1491
    /**
1492
     * If the given stack has a fluid.
1493
     */
1494
    public static final IOperator OBJECT_ITEMSTACK_ISFLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1495
            .output(ValueTypes.BOOLEAN)
2✔
1496
            .symbol("is_fluidstack").operatorName("isfluidstack").interactName("isFluidStack")
7✔
1497
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1498
                itemStack -> !itemStack.isEmpty() && !Helpers.getFluidStack(itemStack).isEmpty()
12!
1499
            )).build());
1✔
1500

1501
    /**
1502
     * The fluidstack from the stack
1503
     */
1504
    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1505
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("fluidstack").interactName("fluidStack")
6✔
1506
            .function(variables -> {
1✔
1507
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1508
                return ValueObjectTypeFluidStack.ValueFluidStack.of(!a.getRawValue().isEmpty() ? Helpers.getFluidStack(a.getRawValue()) : null);
10!
1509
            }).build());
1✔
1510

1511
    /**
1512
     * The capacity of the fluidstack from the stack.
1513
     */
1514
    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1515
            .output(ValueTypes.INTEGER)
2✔
1516
            .symbol("fluidstack_capacity").operatorName("fluidstackcapacity").interactName("fluidCapacity")
7✔
1517
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(
2✔
1518
                itemStack -> !itemStack.isEmpty() ? Helpers.getFluidStackCapacity(itemStack) : 0
8!
1519
            )).build());
1✔
1520

1521
    /**
1522
     * If the data components of the given stacks are equal.
1523
     */
1524
    public static final IOperator OBJECT_ITEMSTACK_ISDATAEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1525
            .output(ValueTypes.BOOLEAN).symbol("=NBT=").operatorName("isnbtequal").interactName("isNbtEqual")
8✔
1526
            .function(variables -> {
1✔
1527
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1528
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1529
                ItemStack a = valueStack0.getRawValue();
3✔
1530
                ItemStack b = valueStack1.getRawValue();
3✔
1531
                boolean equal = false;
2✔
1532
                if(!a.isEmpty() && !b.isEmpty()) {
6!
1533
                    equal = ItemStack.isSameItem(a, b) && ItemMatch.areItemStacksEqual(a, b, ItemMatch.DATA);
14✔
1534
                } else if(a.isEmpty() && b.isEmpty()) {
×
1535
                    equal = true;
×
1536
                }
1537
                return ValueTypeBoolean.ValueBoolean.of(equal);
3✔
1538
            }).build());
1✔
1539

1540
    /**
1541
     * If the raw items of the given stacks are equal, ignoring data components but including damage value.
1542
     */
1543
    public static final IOperator OBJECT_ITEMSTACK_ISITEMEQUALNODATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1544
            .output(ValueTypes.BOOLEAN).symbol("=NoNBT=").operatorName("isitemequalnonbt").interactName("isEqualNonNbt")
8✔
1545
            .function(variables -> {
1✔
1546
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
×
1547
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
1548
                ItemStack a = valueStack0.getRawValue();
×
1549
                ItemStack b = valueStack1.getRawValue();
×
1550
                boolean equal = false;
×
1551
                if(!a.isEmpty() && !b.isEmpty()) {
×
1552
                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);
×
1553
                } else if(a.isEmpty() && b.isEmpty()) {
×
1554
                    equal = true;
×
1555
                }
1556
                return ValueTypeBoolean.ValueBoolean.of(equal);
×
1557
            }).build());
1✔
1558

1559
    /**
1560
     * If the raw items of the given stacks are equal, ignoring data components and damage value.
1561
     */
1562
    public static final IOperator OBJECT_ITEMSTACK_ISRAWITEMEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2
5✔
1563
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwitemequal").interactName("isEqualRaw")
8✔
1564
            .function(variables -> {
1✔
1565
                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1566
                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1567
                ItemStack a = valueStack0.getRawValue();
3✔
1568
                ItemStack b = valueStack1.getRawValue();
3✔
1569
                boolean equal = false;
2✔
1570
                if(!a.isEmpty() && !b.isEmpty()) {
6!
1571
                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);
6✔
1572
                } else if(a.isEmpty() && b.isEmpty()) {
×
1573
                    equal = true;
×
1574
                }
1575
                return ValueTypeBoolean.ValueBoolean.of(equal);
3✔
1576
            }).build());
1✔
1577

1578
    /**
1579
     * The name of the mod owning this item
1580
     */
1581
    public static final IOperator OBJECT_ITEMSTACK_MODNAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
1582
            .symbolOperatorInteract("mod")
13✔
1583
            .function(new IterativeFunction(Lists.newArrayList(
3✔
1584
                    (OperatorBase.SafeVariablesGetter variables) -> {
1585
                        ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1586
                        return !a.getRawValue().isEmpty() ? BuiltInRegistries.ITEM.getKey(a.getRawValue().getItem()) : ResourceLocation.parse("");
11!
1587
                    },
1588
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
1589
            ))).build());
1✔
1590

1591
    /**
1592
     * The fuel burn time of the given item
1593
     */
1594
    public static final IOperator OBJECT_ITEMSTACK_FUELBURNTIME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1595
            .output(ValueTypes.INTEGER)
2✔
1596
            .symbol("burn_time").operatorName("burntime").interactName("burnTime")
7✔
1597
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(itemStack -> {
2✔
1598
                if (!itemStack.isEmpty()) {
3!
1599
                    int burnTime = itemStack.getBurnTime(null, ServerLifecycleHooks.getCurrentServer().fuelValues());
6✔
1600
                    return EventHooks.getItemBurnTime(itemStack, burnTime == -1
7!
1601
                            ? itemStack.getBurnTime(RecipeType.SMELTING, ServerLifecycleHooks.getCurrentServer().fuelValues())
×
1602
                            : burnTime, null, ServerLifecycleHooks.getCurrentServer().fuelValues());
4✔
1603
                }
1604
                return 0;
×
1605
            })).build());
1✔
1606

1607
    /**
1608
     * If the given item can be used as fuel
1609
     */
1610
    public static final IOperator OBJECT_ITEMSTACK_CANBURN = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1611
            .output(ValueTypes.BOOLEAN)
2✔
1612
            .symbol("can_burn").operatorName("canburn").interactName("canBurn")
7✔
1613
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN
1✔
1614
                    .build(stack -> stack.getBurnTime(null, ServerLifecycleHooks.getCurrentServer().fuelValues()) > 0))
12✔
1615
            .build());
1✔
1616

1617
    /**
1618
     * The tag entries of the given item
1619
     */
1620
    public static final IOperator OBJECT_ITEMSTACK_TAG = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1621
            .output(ValueTypes.LIST)
2✔
1622
            .symbol("tag_names").operatorName("tag").interactName("tags")
6✔
1623
            .function(variables -> {
1✔
1624
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1625
                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();
2✔
1626
                if(!a.getRawValue().isEmpty()) {
4!
1627
                    a.getRawValue().getItem().builtInRegistryHolder().tags()
7✔
1628
                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString
6✔
1629
                                    .of(owningTag.location().toString())));
3✔
1630
                }
1631
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());
5✔
1632
            }).build());
1✔
1633

1634
    /**
1635
     * Get a list of items that correspond to the given tag key.
1636
     */
1637
    public static final IOperator OBJECT_ITEMSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX
5✔
1638
            .output(ValueTypes.LIST)
2✔
1639
            .symbol("tag_values").operatorName("tag").interactName("itemsByTag")
6✔
1640
            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)
4✔
1641
            .function(variables -> {
1✔
1642
                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);
6✔
1643
                ImmutableList.Builder<ValueObjectTypeItemStack.ValueItemStack> builder = ImmutableList.builder();
2✔
1644
                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {
4!
1645
                    try {
1646
                        Helpers.getTagValues(a.getRawValue())
4✔
1647
                                .map(ValueObjectTypeItemStack.ValueItemStack::of)
3✔
1648
                                .forEach(builder::add);
4✔
1649
                    } catch (ResourceLocationException e) {
×
1650
                        throw new EvaluationException(Component.translatable(e.getMessage()));
×
1651
                    }
1✔
1652
                }
1653
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, builder.build());
5✔
1654
            }).build());
1✔
1655

1656
    /**
1657
     * ItemStack operator that applies the given stacksize to the given itemstack and creates a new ItemStack.
1658
     */
1659
    public static final IOperator OBJECT_ITEMSTACK_WITHSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_INTEGER_1
5✔
1660
            .output(ValueTypes.OBJECT_ITEMSTACK)
2✔
1661
            .symbol("with_size").operatorName("withsize").interactName("withSize")
6✔
1662
            .function(variables -> {
1✔
1663
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1664
                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);
6✔
1665
                if (!a.getRawValue().isEmpty()) {
4!
1666
                    ItemStack itemStack = a.getRawValue().copy();
4✔
1667
                    itemStack.setCount(b.getRawValue());
4✔
1668
                    return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
1669
                }
1670
                return a;
×
1671
            }).build());
1✔
1672

1673
    /**
1674
     * Check if the item is an RF container item
1675
     */
1676
    public static final IOperator OBJECT_ITEMSTACK_ISFECONTAINER = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1677
            .output(ValueTypes.BOOLEAN)
2✔
1678
            .symbol("is_fe_container").operatorName("isfecontainer").interactName("isFeContainer")
7✔
1679
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_BOOLEAN.build(
2✔
1680
                Objects::nonNull
1681
            )).build());
1✔
1682

1683
    /**
1684
     * Get the storage energy
1685
     */
1686
    public static final IOperator OBJECT_ITEMSTACK_STOREDFE = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1687
            .output(ValueTypes.INTEGER)
2✔
1688
            .symbol("stored_fe").operatorName("storedfe").interactName("feStored")
7✔
1689
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_INT.build(
2✔
1690
                input -> input != null ? input.getEnergyStored() : 0
8✔
1691
            )).build());
1✔
1692

1693
    /**
1694
     * Get the energy capacity
1695
     */
1696
    public static final IOperator OBJECT_ITEMSTACK_FECAPACITY = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1697
            .output(ValueTypes.INTEGER)
2✔
1698
            .symbol("capacity_fe").operatorName("fecapacity").interactName("feCapacity")
7✔
1699
            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_INT.build(
2✔
1700
                input -> input != null ? input.getMaxEnergyStored() : 0
8✔
1701
            )).build());
1✔
1702

1703

1704
    /**
1705
     * If the given item has an inventory.
1706
     */
1707
    public static final IOperator OBJECT_ITEMSTACK_HASINVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1708
            .output(ValueTypes.BOOLEAN)
2✔
1709
            .symbol("has_inventory").operatorName("hasinventory").interactName("hasInventory")
6✔
1710
            .function(variables -> {
1✔
1711
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1712
                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM) != null);
14!
1713
            }).build());
1✔
1714

1715

1716

1717

1718
    /**
1719
     * Retrieve the inventory size of the given item handler contents.
1720
     */
1721
    public static final IOperator OBJECT_ITEMSTACK_INVENTORYSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1722
            .output(ValueTypes.INTEGER)
2✔
1723
            .symbol("inventory_size").operatorName("inventorysize").interactName("inventorySize")
6✔
1724
            .function(variables -> {
1✔
1725
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1726
                IItemHandler itemHandler = a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM);
6✔
1727
                return ValueTypeInteger.ValueInteger.of(itemHandler != null ? itemHandler.getSlots() : 0);
8✔
1728
            }).build());
1✔
1729

1730
    /**
1731
     * Retrieve the inventory of the given item handler contents.
1732
     */
1733
    public static final IOperator OBJECT_ITEMSTACK_INVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1734
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
1735
            .function(variables -> {
1✔
1736
                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1737
                IItemHandler itemHandler = a.getRawValue().getCapability(Capabilities.ItemHandler.ITEM);
6✔
1738
                if (itemHandler != null) {
2✔
1739
                    List<ValueObjectTypeItemStack.ValueItemStack> values = Lists.newArrayListWithCapacity(itemHandler.getSlots());
4✔
1740
                    for (int i = 0; i < itemHandler.getSlots(); i++) {
8✔
1741
                        values.add(ValueObjectTypeItemStack.ValueItemStack.of(itemHandler.getStackInSlot(i)));
7✔
1742
                    }
1743
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, values);
4✔
1744
                }
1745
                return ValueTypes.LIST.getDefault();
3✔
1746
            }).build());
1✔
1747

1748
    /**
1749
     * Get an item by name.
1750
     */
1751
    public static final IOperator OBJECT_ITEMSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
1752
            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_ITEMSTACK)
4✔
1753
            .symbol("item_by_name").operatorName("itembyname").interactName("itemByName")
7✔
1754
            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION
1✔
1755
                    .build(input -> {
1✔
1756
                        Item item = BuiltInRegistries.ITEM.getValue(input);
5✔
1757
                        ItemStack itemStack = ItemStack.EMPTY;
2✔
1758
                        if (item != null) {
2!
1759
                            itemStack = new ItemStack(item);
5✔
1760
                        }
1761
                        return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
1762
                    })).build());
1✔
1763

1764
    /**
1765
     * Get the total item count of the given item in a list.
1766
     */
1767
    public static final IOperator OBJECT_ITEMSTACK_LIST_COUNT = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1768
            .inputTypes(ValueTypes.LIST, ValueTypes.OBJECT_ITEMSTACK)
2✔
1769
            .output(ValueTypes.INTEGER)
2✔
1770
            .symbol("item_list_count").operatorName("itemlistcount").interactName("itemListCount")
6✔
1771
            .function(variables -> {
1✔
1772
                ValueTypeList.ValueList<IValueType<IValue>, IValue> a = variables.getValue(0, ValueTypes.LIST);
6✔
1773
                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
1774
                if (!ValueHelpers.correspondsTo(a.getRawValue().getValueType(), ValueTypes.OBJECT_ITEMSTACK)) {
6!
1775
                    MutableComponent error = Component.translatable(
×
1776
                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,
1777
                            Component.translatable(a.getRawValue().getValueType().getTranslationKey()),
×
1778
                            Component.translatable(ValueTypes.OBJECT_ITEMSTACK.getTranslationKey()));
×
1779
                    throw new EvaluationException(error);
×
1780
                }
1781

1782
                ItemStack itemStack = b.getRawValue();
3✔
1783
                int count = 0;
2✔
1784
                for (IValue listValueRaw : a.getRawValue()) {
11✔
1785
                    if (listValueRaw.getType().correspondsTo(ValueTypes.OBJECT_ITEMSTACK)) {
5!
1786
                        ValueObjectTypeItemStack.ValueItemStack listValue = (ValueObjectTypeItemStack.ValueItemStack) listValueRaw;
3✔
1787
                        if (!listValue.getRawValue().isEmpty()) {
4!
1788
                            ItemStack listItem = listValue.getRawValue();
3✔
1789
                            if (!itemStack.isEmpty()) {
3!
1790
                                if (ItemStack.isSameItemSameComponents(itemStack, listItem)) {
4✔
1791
                                    count += listItem.getCount();
6✔
1792
                                }
1793
                            } else {
1794
                                count += listItem.getCount();
×
1795
                            }
1796
                        }
1797
                    }
1798
                }
1✔
1799

1800
                return ValueTypeInteger.ValueInteger.of(count);
3✔
1801
            }).build());
1✔
1802

1803
    /**
1804
     * Item Stack size operator with one input itemstack and one output NBT tag.
1805
     */
1806
    public static final IOperator OBJECT_ITEMSTACK_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1807
            .output(ValueTypes.NBT).symbol("NBT()").operatorName("nbt").interactName("nbt")
8✔
1808
            .function(input -> {
1✔
1809
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1810
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
1811
                return ValueTypeNbt.ValueNbt.of(itemStack.getRawValue().isEmpty() || itemStack.getRawValue().getComponents().isEmpty() ?
11!
1812
                        Optional.empty() :
2✔
1813
                        Optional.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), itemStack.getRawValue().getComponentsPatch()).getOrThrow()));
12✔
1814
            }).build());
1✔
1815

1816
    /**
1817
     * Item Stack has_nbt operator with one input itemstack and one output boolean.
1818
     */
1819
    public static final IOperator OBJECT_ITEMSTACK_HASDATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG
5✔
1820
            .output(ValueTypes.BOOLEAN).symbol("has_nbt").operatorName("hasnbt").interactName("hasNbt")
9✔
1821
            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(
2✔
1822
                    itemStack -> !itemStack.isEmpty() && itemStack.getComponents().stream().anyMatch(t -> !t.type().isTransient())
21!
1823
            )).build());
1✔
1824

1825
    /**
1826
     * Get the data component keys of an itemstack.
1827
     */
1828
    public static final IOperator OBJECT_ITEMSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1829
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
1830
            .function(input -> {
1✔
1831
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1832
                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
1833
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
1834
                        itemStack.getRawValue().isEmpty() ?
4!
1835
                                Lists.newArrayList() :
×
1836
                                itemStack.getRawValue().getComponents().keySet().stream()
5✔
1837
                                        .filter(c -> !c.isTransient())
8!
1838
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
1839
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
7✔
1840
                                        .toList());
2✔
1841
            }).build());
1✔
1842

1843
    /**
1844
     * Get the data component value by key
1845
     */
1846
    public static final IOperator OBJECT_ITEMSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG
14✔
1847
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING)
2✔
1848
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
1849
            .function(input -> {
1✔
1850
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1851

1852
                // Determine data component type
1853
                DataComponentType<?> dataComponentType;
1854
                try {
1855
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
1856
                } catch (ResourceLocationException e) {
×
1857
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
1858
                }
1✔
1859

1860
                // Fetch component value
1861
                TypedDataComponent<?> typedComponent = itemStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
1862
                if (typedComponent == null) {
2✔
1863
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
1864
                }
1865

1866
                // Encode component value
1867
                try {
1868
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
1869
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
1870
                } catch (IllegalStateException e) {
×
1871
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
1872
                }
1873
            }).build());
1✔
1874

1875
    /**
1876
     * Get the data component value by key
1877
     */
1878
    public static final IOperator OBJECT_ITEMSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_3
18✔
1879
            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
1880
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
1881
            .function(input -> {
1✔
1882
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1883

1884
                // Skip further processing if input value is empty
1885
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
1886
                if (!tagOptional.isPresent()) {
3!
1887
                    return itemStack;
×
1888
                }
1889

1890
                // Determine data component type
1891
                DataComponentType<?> dataComponentType;
1892
                try {
1893
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
1894
                } catch (ResourceLocationException e) {
×
1895
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
1896
                }
1✔
1897

1898
                // Encode component value
1899
                try {
1900
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
1901
                    itemStack = ValueObjectTypeItemStack.ValueItemStack.of(itemStack.getRawValue().copy());
5✔
1902
                    itemStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
1903
                    return itemStack;
2✔
1904
                } catch (IllegalStateException e) {
×
1905
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
1906
                }
1907
            }).build());
1✔
1908

1909
    /**
1910
     * Get the tooltip of an itemstack in list form.
1911
     */
1912
    public static final IOperator OBJECT_ITEMSTACK_TOOLTIP = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG
5✔
1913
            .output(ValueTypes.LIST).symbol("tooltip").operatorName("tooltip").interactName("tooltip")
8✔
1914
            .function(input -> {
1✔
1915
                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);
6✔
1916
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
4✔
1917
                        itemStack.getRawValue().getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.Default.NORMAL).stream()
7✔
1918
                                .map(c -> ValueTypeString.ValueString.of(c.getString()))
5✔
1919
                                .toList());
1✔
1920
            }).build());
1✔
1921
    /**
1922
     * Get the tooltip of an itemstack in list form, using the provided player entity as the player context.
1923
     */
1924
    public static final IOperator OBJECT_ITEMSTACK_ENTITY_TOOLTIP = REGISTRY.register(OperatorBuilders.ENTITY_1_ITEMSTACK_1
14✔
1925
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
1926
            .output(ValueTypes.LIST).symbol("entity_item_tooltip").operatorName("entityitemtooltip").interactName("entityItemTooltip")
8✔
1927
            .function(variables -> {
1✔
1928
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
1929
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
×
1930
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
1931
                    Player entity = (Player) a.getRawValue().get();
×
1932
                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
×
1933
                            itemStack.getRawValue().getTooltipLines(Item.TooltipContext.of(entity.level()), entity, TooltipFlag.Default.NORMAL).stream()
×
1934
                                    .map(c -> ValueTypeString.ValueString.of(c.getString()))
×
1935
                                    .toList());
×
1936
                }
1937
                return ValueTypes.LIST.getDefault();
×
1938
            }).build());
1✔
1939

1940
    /**
1941
     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------
1942
     */
1943

1944
    /**
1945
     * If the entity is a mob
1946
     */
1947
    public static final IOperator OBJECT_ENTITY_ISMOB = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
1948
            .output(ValueTypes.BOOLEAN).symbol("is_mob").operatorName("ismob").interactName("isMob")
9✔
1949
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
1950
                entity -> entity instanceof Enemy
4✔
1951
            )).build());
1✔
1952

1953
    /**
1954
     * If the entity is an animal
1955
     */
1956
    public static final IOperator OBJECT_ENTITY_ISANIMAL = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
1957
            .output(ValueTypes.BOOLEAN).symbol("is_animal").operatorName("isanimal").interactName("isAnimal")
9✔
1958
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
1959
                entity -> entity instanceof Animal && !(entity instanceof Enemy)
11!
1960
            )).build());
1✔
1961

1962
    /**
1963
     * If the entity is an item
1964
     */
1965
    public static final IOperator OBJECT_ENTITY_ISITEM = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
1966
            .output(ValueTypes.BOOLEAN).symbol("is_item").operatorName("isitem").interactName("isItem")
9✔
1967
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
1968
                entity -> entity instanceof ItemEntity
4✔
1969
            )).build());
1✔
1970

1971
    /**
1972
     * If the entity is a player
1973
     */
1974
    public static final IOperator OBJECT_ENTITY_ISPLAYER = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
1975
            .output(ValueTypes.BOOLEAN).symbol("is_player").operatorName("isplayer").interactName("isPlayer")
9✔
1976
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
1977
                entity -> entity instanceof Player
4✔
1978
            )).build());
1✔
1979

1980
    /**
1981
     * If the entity is a minecart
1982
     */
1983
    public static final IOperator OBJECT_ENTITY_ISMINECART = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
1984
            .output(ValueTypes.BOOLEAN).symbol("is_minecart").operatorName("isminecart").interactName("isMinecart")
9✔
1985
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
1986
                entity -> entity instanceof AbstractMinecart
×
1987
            )).build());
1✔
1988

1989
    /**
1990
     * The itemstack from the entity
1991
     */
1992
    public static final IOperator OBJECT_ENTITY_ITEMSTACK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX
5✔
1993
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("item")
4✔
1994
            .function(variables -> {
1✔
1995
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
1996
                Optional<Entity> a = valueEntity.getRawValue();
3✔
1997
                return ValueObjectTypeItemStack.ValueItemStack.of((a.isPresent() && a.get() instanceof ItemEntity) ? ((ItemEntity) a.get()).getItem() : ItemStack.EMPTY);
15!
1998
            }).build());
1✔
1999

2000
    /**
2001
     * The entity health
2002
     */
2003
    public static final IOperator OBJECT_ENTITY_HEALTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2004
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("health")
5✔
2005
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2006
                entity -> entity instanceof LivingEntity ? ((LivingEntity) entity).getHealth() : 0.0
11✔
2007
            )).build());
1✔
2008

2009
    /**
2010
     * The entity width
2011
     */
2012
    public static final IOperator OBJECT_ENTITY_WIDTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2013
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("width")
5✔
2014
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2015
                entity -> entity != null ? entity.getBbWidth() : 0.0
8!
2016
            )).build());
1✔
2017

2018
    /**
2019
     * The entity width
2020
     */
2021
    public static final IOperator OBJECT_ENTITY_HEIGHT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2022
            .output(ValueTypes.DOUBLE).symbolOperatorInteract("height")
5✔
2023
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(
2✔
2024
                entity -> entity != null ? entity.getBbHeight() : 0.0
8!
2025
            )).build());
1✔
2026

2027
    /**
2028
     * If the entity is burning
2029
     */
2030
    public static final IOperator OBJECT_ENTITY_ISBURNING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2031
            .output(ValueTypes.BOOLEAN).symbol("is_burning").operatorName("isburning").interactName("entityIsBurning")
9✔
2032
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2033
                entity -> entity != null && entity.isOnFire()
10!
2034
            )).build());
1✔
2035

2036
    /**
2037
     * If the entity is wet
2038
     */
2039
    public static final IOperator OBJECT_ENTITY_ISWET = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2040
            .output(ValueTypes.BOOLEAN).symbol("is_wet").operatorName("iswet").interactName("isWet")
9✔
2041
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2042
                entity -> entity != null && entity.isInWaterOrRain()
10!
2043
            )).build());
1✔
2044

2045
    /**
2046
     * If the entity is crouching
2047
     */
2048
    public static final IOperator OBJECT_ENTITY_ISCROUCHING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2049
            .output(ValueTypes.BOOLEAN).symbol("is_crouching").operatorName("iscrouching").interactName("isCrouching")
9✔
2050
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2051
                entity -> entity != null && entity.isCrouching()
10!
2052
            )).build());
1✔
2053

2054
    /**
2055
     * If the entity is eating
2056
     */
2057
    public static final IOperator OBJECT_ENTITY_ISEATING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2058
            .output(ValueTypes.BOOLEAN).symbol("is_eating").operatorName("iseating").interactName("isEating")
9✔
2059
            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(
2✔
2060
                entity -> entity instanceof LivingEntity && ((LivingEntity) entity).getUseItemRemainingTicks() > 0
12!
2061
            )).build());
1✔
2062

2063
    /**
2064
     * The list of armor itemstacks from an entity
2065
     */
2066
    public static final IOperator OBJECT_ENTITY_ARMORINVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2067
            .output(ValueTypes.LIST).symbol("armor_inventory").operatorName("armorinventory").interactName("armorInventory")
8✔
2068
            .function(variables -> {
1✔
2069
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2070
                Optional<Entity> a = valueEntity.getRawValue();
×
2071
                if(a.isPresent()) {
×
2072
                    Entity entity = a.get();
×
2073
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityArmorInventory(entity.level(), entity));
×
2074
                } else {
2075
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2076
                }
2077
            }).build());
1✔
2078

2079
    /**
2080
     * The list of itemstacks from an entity
2081
     */
2082
    public static final IOperator OBJECT_ENTITY_INVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2083
            .output(ValueTypes.LIST).symbolOperator("inventory").interactName("inventory")
6✔
2084
            .function(variables -> {
1✔
2085
                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2086
                Optional<Entity> a = valueEntity.getRawValue();
×
2087
                if(a.isPresent()) {
×
2088
                    Entity entity = a.get();
×
2089
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityInventory(entity.level(), entity));
×
2090
                } else {
2091
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());
×
2092
                }
2093
            }).build());
1✔
2094

2095
    /**
2096
     * The name of the mod owning this entity
2097
     */
2098
    public static final IOperator OBJECT_ENTITY_MODNAME = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2099
            .symbolOperatorInteract("mod")
13✔
2100
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2101
                    (OperatorBase.SafeVariablesGetter variables) -> {
2102
                        ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2103
                        if(a.getRawValue().isPresent()) {
4!
2104
                            Entity entity = a.getRawValue().get();
5✔
2105
                            return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());
5✔
2106
                        }
2107
                        return ResourceLocation.parse("");
×
2108
                    },
2109
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2110
            )))
2111
            .build());
1✔
2112

2113
    /**
2114
     * The block the given player is currently looking at.
2115
     */
2116
    public static final IOperator OBJECT_PLAYER_TARGETBLOCK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_BLOCK)
7✔
2117
            .symbol("target_block").operatorName("targetblock").interactName("targetBlock")
6✔
2118
            .function(variables -> {
1✔
2119
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2120
                BlockState blockState = null;
×
2121
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2122
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2123
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.BLOCK_INTERACTION_RANGE);
×
2124
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2125
                    double eyeHeight = entity.getEyeHeight();
×
2126
                    Vec3 lookVec = entity.getLookAngle();
×
2127
                    Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());
×
2128
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2129

2130
                    ClipContext rayTraceContext = new ClipContext(origin, direction, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity);
×
2131
                    BlockHitResult mop = entity.level().clip(rayTraceContext);
×
2132
                    if(mop != null && mop.getType() == HitResult.Type.BLOCK) {
×
2133
                        blockState = entity.level().getBlockState(mop.getBlockPos());
×
2134
                    }
2135
                }
2136
                return ValueObjectTypeBlock.ValueBlock.of(blockState);
×
2137
            }).build());
1✔
2138

2139
    /**
2140
     * The entity the given player is currently looking at.
2141
     */
2142
    public static final IOperator OBJECT_PLAYER_TARGETENTITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ENTITY)
7✔
2143
            .symbol("target_entity").operatorName("targetentity").interactName("targetEntity")
6✔
2144
            .function(variables -> {
1✔
2145
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2146
                Entity entityOut = null;
×
2147
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
×
2148
                    LivingEntity entity = (LivingEntity) a.getRawValue().get();
×
2149
                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.ENTITY_INTERACTION_RANGE);
×
2150
                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
2151

2152
                    // Copied and modified from GameRenderer#getMouseOver
2153
                    Vec3 origin = entity.getEyePosition(1.0F);
×
2154
                    double reachDistanceSquared = reachDistance * reachDistance;
×
2155

2156
                    Vec3 lookVec = entity.getViewVector(1.0F);
×
2157
                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
2158
                    AABB boundingBox = entity.getBoundingBox()
×
2159
                            .expandTowards(lookVec.scale(reachDistance))
×
2160
                            .inflate(1.0D, 1.0D, 1.0D);
×
2161
                    EntityHitResult entityraytraceresult = ProjectileUtil.getEntityHitResult(entity, origin, direction,
×
2162
                            boundingBox, e -> !e.isSpectator() && e.isPickable(), reachDistanceSquared);
×
2163
                    if (entityraytraceresult != null) {
×
2164
                        Entity entity1 = entityraytraceresult.getEntity();
×
2165
                        Vec3 vec3d3 = entityraytraceresult.getLocation();
×
2166
                        double distanceSquared = origin.distanceToSqr(vec3d3);
×
2167
                        if (distanceSquared < reachDistanceSquared
×
2168
                                && (entity1 instanceof LivingEntity || entity1 instanceof ItemFrame)) {
2169
                            entityOut = entity1;
×
2170
                        }
2171
                    }
2172
                }
2173
                return ValueObjectTypeEntity.ValueEntity.of(entityOut);
×
2174
            }).build());
1✔
2175

2176
    /**
2177
     * If the given player has an external gui open.
2178
     */
2179
    public static final IOperator OBJECT_PLAYER_HASGUIOPEN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2180
            .symbol("has_gui_open").operatorName("hasguiopen").interactName("hasGuiOpen")
6✔
2181
            .function(variables -> {
1✔
2182
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2183
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {
×
2184
                    Player entity = (Player) a.getRawValue().get();
×
2185
                    return ValueTypeBoolean.ValueBoolean.of(entity.containerMenu != entity.inventoryMenu);
×
2186
                }
2187
                return ValueTypeBoolean.ValueBoolean.of(false);
×
2188
            }).build());
1✔
2189

2190
    /**
2191
     * The item the given entity is currently holding in its main hand.
2192
     */
2193
    public static final IOperator OBJECT_ENTITY_HELDITEM_MAIN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2194
            .symbol("held_item_1").operatorName("helditem").interactName("heldItem")
6✔
2195
            .function(variables -> {
1✔
2196
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2197
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2198
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2199
                    itemStack = ((LivingEntity) a.getRawValue().get()).getMainHandItem();
6✔
2200
                }
2201
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2202
            }).build());
1✔
2203

2204
    /**
2205
     * The item the given entity is currently holding in its off hand.
2206
     */
2207
    public static final IOperator OBJECT_ENTITY_HELDITEM_OFF = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2208
            .symbol("held_item_2").operatorName("helditemoffhand").interactName("heldItemOffHand")
6✔
2209
            .function(variables -> {
1✔
2210
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2211
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2212
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2213
                    itemStack = ((LivingEntity) a.getRawValue().get()).getOffhandItem();
6✔
2214
                }
2215
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2216
            }).build());
1✔
2217

2218
    /**
2219
     * The entity's mounted entity
2220
     */
2221
    public static final IOperator OBJECT_ENTITY_MOUNTED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.LIST)
7✔
2222
            .symbolOperator("mounted").interactName("mounted")
4✔
2223
            .function(variables -> {
1✔
2224
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2225
                List<ValueObjectTypeEntity.ValueEntity> passengers = Lists.newArrayList();
2✔
2226
                if(a.getRawValue().isPresent()) {
4!
2227
                    for (Entity passenger : a.getRawValue().get().getPassengers()) {
14✔
2228
                        passengers.add(ValueObjectTypeEntity.ValueEntity.of(passenger));
5✔
2229
                    }
1✔
2230
                }
2231
                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, passengers);
4✔
2232
            }).build());
1✔
2233

2234
    /**
2235
     * The item frame's contents
2236
     */
2237
    public static final IOperator OBJECT_ITEMFRAME_CONTENTS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)
7✔
2238
            .symbol("itemframe_contents").operatorName("itemframecontents").interactName("itemFrameContents")
6✔
2239
            .function(variables -> {
1✔
2240
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2241
                ItemStack itemStack = ItemStack.EMPTY;
2✔
2242
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2243
                    itemStack = ((ItemFrame) a.getRawValue().get()).getItem();
6✔
2244
                }
2245
                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);
3✔
2246
            }).build());
1✔
2247

2248
    /**
2249
     * The item frame's rotation
2250
     */
2251
    public static final IOperator OBJECT_ITEMFRAME_ROTATION = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2252
            .symbol("itemframe_rotation").operatorName("itemframerotation").interactName("itemFrameRotation")
6✔
2253
            .function(variables -> {
1✔
2254
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2255
                Integer rotation = 0;
3✔
2256
                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {
9!
2257
                    rotation = ((ItemFrame) a.getRawValue().get()).getRotation();
7✔
2258
                }
2259
                return ValueTypeInteger.ValueInteger.of(rotation);
4✔
2260
            }).build());
1✔
2261

2262
    /**
2263
     * The hurtsound of this entity.
2264
     */
2265
    public static final IOperator OBJECT_ENTITY_HURTSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2266
            .symbolOperator("hurtsound").interactName("hurtSound")
4✔
2267
            .function(variables -> {
1✔
2268
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2269
                String hurtSound = "";
2✔
2270
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2271
                    String sound = ((LivingEntity) a.getRawValue().get()).getHurtSound(a.getRawValue().get().damageSources().generic()).location().toString();
14✔
2272
                    if (sound != null) {
2!
2273
                        hurtSound = sound;
2✔
2274
                    }
2275
                }
2276
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2277
            }).build());
1✔
2278

2279
    /**
2280
     * The deathsound of this entity.
2281
     */
2282
    public static final IOperator OBJECT_ENTITY_DEATHSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2283
            .symbolOperator("deathsound").interactName("deathSound")
4✔
2284
            .function(variables -> {
1✔
2285
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2286
                String hurtSound = "";
2✔
2287
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2288
                    String sound = ((LivingEntity) a.getRawValue().get()).getDeathSound().location().toString();
8✔
2289
                    if (sound != null) {
2!
2290
                        hurtSound = sound;
2✔
2291
                    }
2292
                }
2293
                return ValueTypeString.ValueString.of(hurtSound);
3✔
2294
            }).build());
1✔
2295

2296
    /**
2297
     * The age of this entity.
2298
     */
2299
    public static final IOperator OBJECT_ENTITY_AGE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)
7✔
2300
            .symbolOperatorInteract("age")
2✔
2301
            .function(variables -> {
1✔
2302
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2303
                int age = 0;
2✔
2304
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2305
                    age = ((LivingEntity) a.getRawValue().get()).getNoActionTime();
6✔
2306
                }
2307
                return ValueTypeInteger.ValueInteger.of(age);
3✔
2308
            }).build());
1✔
2309

2310
    /**
2311
     * If the entity is a child.
2312
     */
2313
    public static final IOperator OBJECT_ENTITY_ISCHILD = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2314
            .symbol("is_child").operatorName("ischild").interactName("isChild")
6✔
2315
            .function(variables -> {
1✔
2316
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2317
                boolean child = false;
2✔
2318
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {
9!
2319
                    child = ((LivingEntity) a.getRawValue().get()).isBaby();
6✔
2320
                }
2321
                return ValueTypeBoolean.ValueBoolean.of(child);
3✔
2322
            }).build());
1✔
2323

2324
    /**
2325
     * If the entity can be bred.
2326
     */
2327
    public static final IOperator OBJECT_ENTITY_CANBREED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2328
            .symbol("canbreed").operatorName("canbreed").interactName("canBreed")
6✔
2329
            .function(variables -> {
1✔
2330
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2331
                boolean canBreed = false;
2✔
2332
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof AgeableMob) {
9!
2333
                    canBreed = ((AgeableMob) a.getRawValue().get()).getAge() == 0;
10✔
2334
                }
2335
                return ValueTypeBoolean.ValueBoolean.of(canBreed);
3✔
2336
            }).build());
1✔
2337

2338
    /**
2339
     * If the entity is in love.
2340
     */
2341
    public static final IOperator OBJECT_ENTITY_ISINLOVE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2342
            .symbol("is_in_love").operatorName("isinlove").interactName("isInLove")
6✔
2343
            .function(variables -> {
1✔
2344
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2345
                boolean inLove = false;
2✔
2346
                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof Animal) {
9!
2347
                    inLove = ((Animal) a.getRawValue().get()).isInLove();
6✔
2348
                }
2349
                return ValueTypeBoolean.ValueBoolean.of(inLove);
3✔
2350
            }).build());
1✔
2351

2352
    /**
2353
     * If the entity can be bred with the given item.
2354
     */
2355
    public static final IOperator OBJECT_ENTITY_CANBREEDWITH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
14✔
2356
            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)
2✔
2357
            .output(ValueTypes.BOOLEAN)
2✔
2358
            .symbol("can_breed_with").operatorName("canbreedwith").interactName("canBreedWith")
6✔
2359
            .renderPattern(IConfigRenderPattern.INFIX_LONG)
2✔
2360
            .function(variables -> {
1✔
2361
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2362
                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);
6✔
2363
                boolean canBreedWith = false;
2✔
2364
                if (a.getRawValue().isPresent() && !b.getRawValue().isEmpty() && a.getRawValue().get() instanceof Animal) {
13!
2365
                    canBreedWith = ((Animal) a.getRawValue().get()).isFood(b.getRawValue());
8✔
2366
                }
2367
                return ValueTypeBoolean.ValueBoolean.of(canBreedWith);
3✔
2368
            }).build());
1✔
2369

2370
    /**
2371
     * If the entity is shearable.
2372
     */
2373
    public static final IOperator OBJECT_ENTITY_ISSHEARABLE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)
7✔
2374
            .symbol("is_shearable").operatorName("isshearable").interactName("isShearable")
6✔
2375
            .function(variables -> {
1✔
2376
                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2377
                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()
7!
2378
                        && a.getRawValue().get() instanceof IShearable
5✔
2379
                        && ((IShearable) a.getRawValue().get()).isShearable(null, ItemStack.EMPTY, null, null));
12✔
2380
            }).build());
1✔
2381

2382
    /**
2383
     * The entity serialized to NBT.
2384
     */
2385
    public static final IOperator OBJECT_ENTITY_NBT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2386
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2387
            .function(input -> {
1✔
2388
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2389
                try {
2390
                    if (entity.getRawValue().isPresent()) {
4!
2391
                        return ValueTypeNbt.ValueNbt.of(entity.getRawValue().get().serializeNBT(ServerLifecycleHooks.getCurrentServer().registryAccess()));
9✔
2392
                    }
2393
                } catch (Exception e) {
×
2394
                    // Catch possible errors during NBT writing
2395
                }
×
2396
                return ValueTypes.NBT.getDefault();
×
2397
            }).build());
1✔
2398

2399
    /**
2400
     * The entity type.
2401
     */
2402
    public static final IOperator OBJECT_ENTITY_TYPE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2403
            .output(ValueTypes.STRING).symbol("entity_type").operatorName("entitytype").interactName("type")
8✔
2404
            .function(input -> {
1✔
2405
                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
6✔
2406
                String entityType = "";
2✔
2407
                if (entity.getRawValue().isPresent()) {
4!
2408
                    Entity e = entity.getRawValue().get();
5✔
2409
                    entityType = BuiltInRegistries.ENTITY_TYPE.getKey(e.getType()).toString();
6✔
2410
                }
2411
                return ValueTypeString.ValueString.of(entityType);
3✔
2412
            }).build());
1✔
2413

2414
    /**
2415
     * The entity items.
2416
     */
2417
    public static final IOperator OBJECT_ENTITY_ITEMS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2418
            .output(ValueTypes.LIST).symbol("entity_items").operatorName("entityitems").interactName("items")
8✔
2419
            .function(input -> {
1✔
2420
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2421
                Optional<Entity> a = valueEntity.getRawValue();
×
2422
                if(a.isPresent()) {
×
2423
                    Entity entity = a.get();
×
2424
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityItems(entity.level(), entity, null));
×
2425
                } else {
2426
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.emptyList());
×
2427
                }
2428
            }).build());
1✔
2429

2430
    /**
2431
     * The entity fluids.
2432
     */
2433
    public static final IOperator OBJECT_ENTITY_FLUIDS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2434
            .output(ValueTypes.LIST).symbol("entity_fluids").operatorName("entityfluids").interactName("fluids")
8✔
2435
            .function(input -> {
1✔
2436
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2437
                Optional<Entity> a = valueEntity.getRawValue();
×
2438
                if(a.isPresent()) {
×
2439
                    Entity entity = a.get();
×
2440
                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityFluids(entity.level(), entity, null));
×
2441
                } else {
2442
                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, Collections.emptyList());
×
2443
                }
2444
            }).build());
1✔
2445

2446
    /**
2447
     * The entity energy stored.
2448
     */
2449
    public static final IOperator OBJECT_ENTITY_ENERGY_STORED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2450
            .output(ValueTypes.INTEGER).symbol("entity_stored_fe").operatorName("entityenergystored").interactName("energy")
8✔
2451
            .function(input -> {
1✔
2452
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2453
                Optional<Entity> a = valueEntity.getRawValue();
×
2454
                if(a.isPresent()) {
×
2455
                    Entity entity = a.get();
×
2456
                    IEnergyStorage energyStorage = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
×
2457
                    return ValueTypeInteger.ValueInteger.of(energyStorage != null ? energyStorage.getEnergyStored() : 0);
×
2458
                }
2459
                return ValueTypeInteger.ValueInteger.of(0);
×
2460
            }).build());
1✔
2461

2462
    /**
2463
     * The entity energy stored.
2464
     */
2465
    public static final IOperator OBJECT_ENTITY_ENERGY_CAPACITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG
5✔
2466
            .output(ValueTypes.INTEGER).symbol("entity_capacity_fe").operatorName("entityenergycapacity").interactName("energyCapacity")
8✔
2467
            .function(input -> {
1✔
2468
                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);
×
2469
                Optional<Entity> a = valueEntity.getRawValue();
×
2470
                if(a.isPresent()) {
×
2471
                    Entity entity = a.get();
×
2472
                    IEnergyStorage energyStorage = entity.getCapability(Capabilities.EnergyStorage.ENTITY, null);
×
2473
                    return ValueTypeInteger.ValueInteger.of(energyStorage != null ? energyStorage.getMaxEnergyStored() : 0);
×
2474
                }
2475
                return ValueTypeInteger.ValueInteger.of(0);
×
2476
            }).build());
1✔
2477

2478
    /**
2479
     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------
2480
     */
2481

2482
    /**
2483
     * The amount of fluid in the fluidstack
2484
     */
2485
    public static final IOperator OBJECT_FLUIDSTACK_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2486
            .output(ValueTypes.INTEGER).symbolOperatorInteract("amount")
5✔
2487
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2488
                    FluidStack::getAmount
2489
            )).build());
1✔
2490

2491
    /**
2492
     * The block from the fluidstack
2493
     */
2494
    public static final IOperator OBJECT_FLUIDSTACK_BLOCK = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2495
            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract("block")
4✔
2496
            .function(variables -> {
1✔
2497
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2498
                FluidStack a = valueFluidStack.getRawValue();
3✔
2499
                return ValueObjectTypeBlock.ValueBlock.of(!a.isEmpty() ? a.getFluid().getFluidType().getStateForPlacement(null, null, a).createLegacyBlock() : null);
14!
2500
            }).build());
1✔
2501

2502
    /**
2503
     * The fluidstack luminosity
2504
     */
2505
    public static final IOperator OBJECT_FLUIDSTACK_LIGHT_LEVEL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2506
            .output(ValueTypes.INTEGER).symbolOperator("light_level").interactName("lightLevel")
7✔
2507
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2508
                fluidStack -> fluidStack.getFluid().getFluidType().getLightLevel(fluidStack)
7✔
2509
            )).build());
1✔
2510

2511
    /**
2512
     * The fluidstack density
2513
     */
2514
    public static final IOperator OBJECT_FLUIDSTACK_DENSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2515
            .output(ValueTypes.INTEGER).symbolOperatorInteract("density")
5✔
2516
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2517
                fluidStack -> fluidStack.getFluid().getFluidType().getDensity(fluidStack)
7✔
2518
            )).build());
1✔
2519

2520
    /**
2521
     * The fluidstack temperature
2522
     */
2523
    public static final IOperator OBJECT_FLUIDSTACK_TEMPERATURE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2524
            .output(ValueTypes.INTEGER).symbolOperatorInteract("temperature")
5✔
2525
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2526
                    fluidStack -> fluidStack.getFluid().getFluidType().getTemperature(fluidStack)
7✔
2527
            )).build());
1✔
2528

2529
    /**
2530
     * The fluidstack viscosity
2531
     */
2532
    public static final IOperator OBJECT_FLUIDSTACK_VISCOSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2533
            .output(ValueTypes.INTEGER).symbolOperatorInteract("viscosity")
5✔
2534
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(
2✔
2535
                fluidStack -> fluidStack.getFluid().getFluidType().getViscosity(fluidStack)
7✔
2536
            )).build());
1✔
2537

2538
    /**
2539
     * If the fluidstack is gaseous
2540
     */
2541
    public static final IOperator OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2542
            .output(ValueTypes.BOOLEAN).symbolOperator("lighter_than_air").interactName("isLighterThanAir")
7✔
2543
            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_BOOLEAN.build(
2✔
2544
                fluidStack -> fluidStack.getFluid().getFluidType().isLighterThanAir()
6✔
2545
            )).build());
1✔
2546

2547
    /**
2548
     * The rarity of the fluidstack
2549
     */
2550
    public static final IOperator OBJECT_FLUIDSTACK_RARITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2551
            .output(ValueTypes.STRING).symbolOperatorInteract("rarity")
4✔
2552
            .function(variables -> {
1✔
2553
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2554
                FluidStack a = valueFluidStack.getRawValue();
3✔
2555
                return ValueTypeString.ValueString.of(a.getFluid().getFluidType().getRarity(a).name());
8✔
2556
            }).build());
1✔
2557

2558
    /**
2559
     * The bucket empty sound of the fluidstack
2560
     */
2561
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2562
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_empty").interactName("bucketEmptySound")
6✔
2563
            .function(variables -> {
1✔
2564
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2565
                FluidStack a = valueFluidStack.getRawValue();
3✔
2566
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_EMPTY))
10✔
2567
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2568
                        .orElse(""));
2✔
2569
            }).build());
1✔
2570

2571
    /**
2572
     * The fluid vaporize sound of the fluidstack
2573
     */
2574
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2575
            .output(ValueTypes.STRING).symbolOperator("sound_fluid_vaporize").interactName("fluidVaporizeSound")
6✔
2576
            .function(variables -> {
1✔
2577
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2578
                FluidStack a = valueFluidStack.getRawValue();
3✔
2579
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.FLUID_VAPORIZE))
10✔
2580
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2581
                        .orElse(""));
2✔
2582
            }).build());
1✔
2583

2584
    /**
2585
     * The bucket fill sound of the fluidstack
2586
     */
2587
    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2588
            .output(ValueTypes.STRING).symbolOperator("sound_bucket_fill").interactName("bucketFillSound")
6✔
2589
            .function(variables -> {
1✔
2590
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2591
                FluidStack a = valueFluidStack.getRawValue();
3✔
2592
                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_FILL))
10✔
2593
                        .map(soundEvent -> soundEvent.location().toString())
6✔
2594
                        .orElse(""));
2✔
2595
            }).build());
1✔
2596

2597
    /**
2598
     * The bucket of the fluidstack
2599
     */
2600
    public static final IOperator OBJECT_FLUIDSTACK_BUCKET = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2601
            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract("bucket")
4✔
2602
            .function(variables -> {
1✔
2603
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2604
                FluidStack a = valueFluidStack.getRawValue();
3✔
2605
                return ValueObjectTypeItemStack.ValueItemStack.of(a.getFluid().getFluidType().getBucket(a));
7✔
2606
            }).build());
1✔
2607

2608
    /**
2609
     * If the fluid types of the two given fluidstacks are equal
2610
     */
2611
    public static final IOperator OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
5✔
2612
            .output(ValueTypes.BOOLEAN).symbol("=Raw=").operatorName("israwfluidequal").interactName("isRawEqual")
8✔
2613
            .function(variables -> {
1✔
2614
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack0 = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2615
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack1 = variables.getValue(1, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2616
                return ValueTypeBoolean.ValueBoolean.of(FluidStack.isSameFluid(valueFluidStack0.getRawValue(), valueFluidStack1.getRawValue()));
7✔
2617
            }).build());
1✔
2618

2619
    /**
2620
     * The name of the mod owning this fluid
2621
     */
2622
    public static final IOperator OBJECT_FLUIDSTACK_MODNAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)
7✔
2623
            .symbolOperatorInteract("mod")
13✔
2624
            .function(new IterativeFunction(Lists.newArrayList(
3✔
2625
                    (OperatorBase.SafeVariablesGetter variables) -> {
2626
                        ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2627
                        return BuiltInRegistries.FLUID.getKey(a.getRawValue().getFluid());
6✔
2628
                    },
2629
                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME
2630
            ))).build());
1✔
2631

2632
    /**
2633
     * The tag of the given fluidstack.
2634
     */
2635
    public static final IOperator OBJECT_FLUIDSTACK_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2636
            .output(ValueTypes.NBT).symbol("NBT()").operatorInteract("nbt")
6✔
2637
            .function(input -> {
1✔
2638
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2639
                if (fluidStack.getRawValue().getComponentsPatch().isEmpty()) {
5✔
2640
                    return ValueTypeNbt.ValueNbt.of(Optional.empty());
3✔
2641
                }
2642
                return ValueTypeNbt.ValueNbt.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), fluidStack.getRawValue().getComponentsPatch()).getOrThrow());
13✔
2643
            }).build());
1✔
2644

2645
    /**
2646
     * Create a new fluidstack with the given amount.
2647
     */
2648
    public static final IOperator OBJECT_FLUIDSTACK_WITH_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2
14✔
2649
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.INTEGER)
2✔
2650
            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator("with_amount").interactName("withAmount")
6✔
2651
            .function(variables -> {
1✔
2652
                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2653
                ValueTypeInteger.ValueInteger valueInteger = variables.getValue(1, ValueTypes.INTEGER);
6✔
2654
                FluidStack fluidStack = valueFluidStack.getRawValue().copy();
4✔
2655
                fluidStack.setAmount(valueInteger.getRawValue());
4✔
2656
                return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);
3✔
2657
            }).build());
1✔
2658

2659
    /**
2660
     * Get the data component keys of an fluidstack.
2661
     */
2662
    public static final IOperator OBJECT_FLUIDSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG
5✔
2663
            .output(ValueTypes.LIST).symbol("data_keys").operatorName("datakeys").interactName("dataKeys")
8✔
2664
            .function(input -> {
1✔
2665
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2666
                // Explicitly check for fluid emptiness first, because vanilla sometimes persists NBT when setting stacks to empty
2667
                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,
3✔
2668
                        fluidStack.getRawValue().isEmpty() ?
4!
2669
                                Lists.newArrayList() :
×
2670
                                fluidStack.getRawValue().getComponents().keySet().stream()
5✔
2671
                                        .filter(c -> !c.isTransient())
8!
2672
                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))
8✔
2673
                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))
1✔
2674
                                        .toList());
2✔
2675
            }).build());
1✔
2676

2677
    /**
2678
     * Get the data component value by key
2679
     */
2680
    public static final IOperator OBJECT_FLUIDSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2_LONG
14✔
2681
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING)
2✔
2682
            .output(ValueTypes.NBT).symbol("data_value").operatorName("datavalue").interactName("dataValue")
8✔
2683
            .function(input -> {
1✔
2684
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2685

2686
                // Determine data component type
2687
                DataComponentType<?> dataComponentType;
2688
                try {
2689
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2690
                } catch (ResourceLocationException e) {
×
2691
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2692
                }
1✔
2693

2694
                // Fetch component value
2695
                TypedDataComponent<?> typedComponent = fluidStack.getRawValue().getComponents().getTyped(dataComponentType);
6✔
2696
                if (typedComponent == null) {
2✔
2697
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
4✔
2698
                }
2699

2700
                // Encode component value
2701
                try {
2702
                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();
9✔
2703
                    return ValueTypeNbt.ValueNbt.of(tag);
3✔
2704
                } catch (IllegalStateException e) {
×
2705
                    return ValueTypeNbt.ValueNbt.of((Tag) null);
×
2706
                }
2707
            }).build());
1✔
2708

2709
    /**
2710
     * Get the data component value by key
2711
     */
2712
    public static final IOperator OBJECT_FLUIDSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_3
18✔
2713
            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING, ValueTypes.NBT)
2✔
2714
            .output(ValueTypes.NBT).symbol("with_data").operatorName("withdata").interactName("withData")
8✔
2715
            .function(input -> {
1✔
2716
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);
6✔
2717

2718
                // Skip further processing if input value is empty
2719
                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();
7✔
2720
                if (!tagOptional.isPresent()) {
3!
2721
                    return fluidStack;
×
2722
                }
2723

2724
                // Determine data component type
2725
                DataComponentType<?> dataComponentType;
2726
                try {
2727
                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(ResourceLocation.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));
11✔
2728
                } catch (ResourceLocationException e) {
×
2729
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2730
                }
1✔
2731

2732
                // Encode component value
2733
                try {
2734
                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();
14✔
2735
                    fluidStack = ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack.getRawValue().copy());
5✔
2736
                    fluidStack.getRawValue().set((DataComponentType) dataComponentType, value);
6✔
2737
                    return fluidStack;
2✔
2738
                } catch (IllegalStateException e) {
×
2739
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
2740
                }
2741
            }).build());
1✔
2742

2743
    /**
2744
     * ----------------------------------- OPERATOR OPERATORS -----------------------------------
2745
     */
2746

2747
    /**
2748
     * Apply for a given operator a given value.
2749
     */
2750
    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2751
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2752
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply")
9✔
2753
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY))
4✔
2754
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2755
                    input -> {
2756
                        IOperator innerOperator = input.getLeft();
4✔
2757
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2758
                        IVariable variable = variables.getVariables()[0];
5✔
2759
                        return ValueHelpers.evaluateOperator(innerOperator, variable);
9✔
2760
                    })).build());
1✔
2761
    static {
2762
        REGISTRY.registerSerializer(new CurriedOperator.Serializer());
5✔
2763
    }
2764

2765
    /**
2766
     * Apply for a given operator the given 2 values.
2767
     */
2768
    public static final IOperator OPERATOR_APPLY_2 = REGISTRY.register(OperatorBuilders.OPERATOR
5✔
2769
            .renderPattern(IConfigRenderPattern.INFIX_2)
2✔
2770
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
15✔
2771
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
2772
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply2")
13✔
2773
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
2774
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2775
                    input -> {
2776
                        IOperator innerOperator = input.getLeft();
4✔
2777
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2778
                        IVariable variable0 = variables.getVariables()[0];
5✔
2779
                        IVariable variable1 = variables.getVariables()[1];
5✔
2780
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1);
13✔
2781
                    })).build());
1✔
2782

2783
    /**
2784
     * Apply for a given operator the given 3 values.
2785
     */
2786
    public static final IOperator OPERATOR_APPLY_3 = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2787
            .renderPattern(IConfigRenderPattern.INFIX_3)
2✔
2788
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
19✔
2789
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)
2✔
2790
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply3")
17✔
2791
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))
4✔
2792
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2793
                    input -> {
2794
                        IOperator innerOperator = input.getLeft();
4✔
2795
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2796
                        IVariable variable0 = variables.getVariables()[0];
5✔
2797
                        IVariable variable1 = variables.getVariables()[1];
5✔
2798
                        IVariable variable2 = variables.getVariables()[2];
5✔
2799
                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1, variable2);
17✔
2800
                    })).build());
1✔
2801

2802
    /**
2803
     * Apply for a given operator the given list of values.
2804
     */
2805
    public static final IOperator OPERATOR_APPLY_N = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
5✔
2806
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST)
11✔
2807
            .inputTypes(ValueTypes.OPERATOR, ValueTypes.LIST)
2✔
2808
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply_n")
9✔
2809
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.LIST))
4✔
2810
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
2811
                    input -> {
2812
                        IOperator innerOperator = input.getLeft();
4✔
2813
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2814
                        IValueTypeListProxy<IValueType<IValue>, IValue> list = variables.getValue(0, ValueTypes.LIST).getRawValue();
7✔
2815
                        return ValueHelpers.evaluateOperator(innerOperator, Iterables.toArray(list, IValue.class));
7✔
2816
                    })).build());
1✔
2817

2818
    /**
2819
     * Apply for a given operator with zero arguments.
2820
     */
2821
    public static final IOperator OPERATOR_APPLY_0 = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
2822
            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)
2✔
2823
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("apply0")
5✔
2824
            .typeValidator(OperatorBuilders.createOperatorTypeValidator(new IValueType[0]))
4✔
2825
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(
2✔
2826
                    input -> {
2827
                        IOperator innerOperator = input.getLeft();
4✔
2828
                        return ValueHelpers.evaluateOperator(innerOperator, new IVariable[0]);
5✔
2829
                    })).build());
1✔
2830

2831
    /**
2832
     * Apply the given operator on all elements of a list, resulting in a new list of mapped values.
2833
     */
2834
    public static final IOperator OPERATOR_MAP = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
2835
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
2836
            .output(ValueTypes.LIST).symbolOperatorInteract("map")
5✔
2837
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
2838
                    input -> {
2839
                        final IOperator innerOperator = input.getLeft();
4✔
2840
                        OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2841
                        ValueTypeList.ValueList inputList = variables.getValue(0, ValueTypes.LIST);
6✔
2842
                        return ValueTypeList.ValueList.ofFactory(
6✔
2843
                                new ValueTypeListProxyOperatorMapped(innerOperator, inputList.getRawValue()));
2✔
2844
                    })).build());
1✔
2845

2846
    /**
2847
     * Filter a list of elements by matching them all with the given predicate.
2848
     */
2849
    public static final IOperator OPERATOR_FILTER = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
2850
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
2851
            .output(ValueTypes.LIST).symbolOperatorInteract("filter")
7✔
2852
            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(
2✔
2853
                    new IOperatorValuePropagator<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue>() {
3✔
2854
                        @Override
2855
                        public IValue getOutput(Pair<IOperator, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
2856
                            final IOperator innerOperator = input.getLeft();
4✔
2857
                            OperatorBase.SafeVariablesGetter variables = input.getRight();
4✔
2858
                            ValueTypeList.ValueList<?, ?> inputList = variables.getValue(0, ValueTypes.LIST);
6✔
2859
                            List<IValue> filtered = Lists.newArrayList();
2✔
2860
                            for (IValue value : inputList.getRawValue()) {
11✔
2861
                                IValue result = ValueHelpers.evaluateOperator(innerOperator, value);
9✔
2862
                                ValueHelpers.validatePredicateOutput(innerOperator, result);
3✔
2863
                                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {
4✔
2864
                                    filtered.add(value);
4✔
2865
                                }
2866
                            }
1✔
2867
                            IValueType valueType = inputList.getRawValue().getValueType();
4✔
2868
                            return ValueTypeList.ValueList.ofList(valueType, filtered);
4✔
2869
                        }
2870
                    })).build());
1✔
2871

2872
    /**
2873
     * Takes the conjunction of two predicates.
2874
     */
2875
    public static final IOperator OPERATOR_CONJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
2876
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
2877
            .output(ValueTypes.OPERATOR).symbol(".&&.").operatorInteract("conjunction")
7✔
2878
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
2879
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Conjunction.asOperator(input.getLeft(), input.getRight()))
17✔
2880
            )).build());
1✔
2881
    static {
2882
        REGISTRY.registerSerializer(new CombinedOperator.Conjunction.Serializer());
5✔
2883
    }
2884

2885
    /**
2886
     * Takes the disjunction of two predicates.
2887
     */
2888
    public static final IOperator OPERATOR_DISJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
2889
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
2890
            .output(ValueTypes.OPERATOR).symbol(".||.").operatorInteract("disjunction")
7✔
2891
            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(
2✔
2892
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Disjunction.asOperator(input.getLeft(), input.getRight()))
17✔
2893
            )).build());
1✔
2894
    static {
2895
        REGISTRY.registerSerializer(new CombinedOperator.Disjunction.Serializer());
5✔
2896
    }
2897

2898
    /**
2899
     * Takes the negation of a predicate.
2900
     */
2901
    public static final IOperator OPERATOR_NEGATION = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
2902
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
2903
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
2904
            .output(ValueTypes.OPERATOR).symbol("!.").operatorInteract("negation")
7✔
2905
            .function(OperatorBuilders.FUNCTION_ONE_PREDICATE.build(
2✔
2906
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Negation.asOperator(input))
4✔
2907
            )).build());
1✔
2908
    static {
2909
        REGISTRY.registerSerializer(new CombinedOperator.Negation.Serializer());
5✔
2910
    }
2911

2912
    /**
2913
     * Create a new operator that pipes the output from the first operator to the second operator.
2914
     */
2915
    public static final IOperator OPERATOR_PIPE = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG
14✔
2916
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
2917
            .output(ValueTypes.OPERATOR).symbol(".").operatorInteract("pipe")
7✔
2918
            .function(OperatorBuilders.FUNCTION_TWO_OPERATORS.build(
2✔
2919
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe.asOperator(input.getLeft(), input.getRight()))
17✔
2920
            )).build());
1✔
2921
    static {
2922
        REGISTRY.registerSerializer(new CombinedOperator.Pipe.Serializer());
5✔
2923
    }
2924

2925
    /**
2926
     * 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.
2927
     */
2928
    public static final IOperator OPERATOR_PIPE2 = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
2929
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR, ValueTypes.OPERATOR})
2✔
2930
            .renderPattern(IConfigRenderPattern.INFIX_2_LATE)
2✔
2931
            .output(ValueTypes.OPERATOR).symbol(".2").operatorInteract("pipe2")
7✔
2932
            .function(OperatorBuilders.FUNCTION_THREE_OPERATORS.build(
2✔
2933
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe2.asOperator(input.getLeft(), input.getMiddle(), input.getRight()))
23✔
2934
            )).build());
1✔
2935
    static {
2936
        REGISTRY.registerSerializer(new CombinedOperator.Pipe2.Serializer());
5✔
2937
    }
2938

2939
    /**
2940
     * Flip the input parameters of an operator with two inputs.
2941
     */
2942
    public static final IOperator OPERATOR_FLIP = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
2943
            .renderPattern(IConfigRenderPattern.PREFIX_1)
7✔
2944
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})
2✔
2945
            .output(ValueTypes.OPERATOR).symbolOperatorInteract("flip")
5✔
2946
            .function(OperatorBuilders.FUNCTION_ONE_OPERATOR.build(
2✔
2947
                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Flip.asOperator(input))
4✔
2948
            )).build());
1✔
2949
    static {
2950
        REGISTRY.registerSerializer(new CombinedOperator.Flip.Serializer());
5✔
2951
    }
2952

2953
    /**
2954
     * Apply the given operator on all elements of a list to reduce the list to one value.
2955
     */
2956
    public static final IOperator OPERATOR_REDUCE = REGISTRY.register(OperatorBuilders.OPERATOR
18✔
2957
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.CATEGORY_ANY})
2✔
2958
            .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)
2✔
2959
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce")
4✔
2960
            .conditionalOutputTypeDeriver((operator, input) -> input[2].getType())
7✔
2961
            .function(variables -> {
1✔
2962
                IValue accumulator = variables.getValue(2);
4✔
2963
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
2964
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
2965
                ValueTypeList.ValueList<IValueType<IValue>, IValue> inputList = variables.getValue(1, ValueTypes.LIST);
6✔
2966
                for (IValue listValue : inputList.getRawValue()) {
11✔
2967
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
2968
                }
1✔
2969
                return accumulator;
2✔
2970
            }).build());
1✔
2971

2972
    /**
2973
     * Apply the given operator on all elements of a list to reduce the list to one value.
2974
     */
2975
    public static final IOperator OPERATOR_REDUCE1 = REGISTRY.register(OperatorBuilders.OPERATOR
14✔
2976
            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})
2✔
2977
            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)
2✔
2978
            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract("reduce1")
4✔
2979
            .conditionalOutputTypeDeriver((operator, input) -> {
2✔
2980
                try {
2981
                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[1].getValue()).getRawValue();
7✔
2982
                    return a.getValueType();
3✔
2983
                } catch (EvaluationException e) {
×
2984
                    return operator.getOutputType();
×
2985
                }
2986
            })
2987
            .function(variables -> {
1✔
2988
                ValueTypeList.ValueList valueList = variables.getValue(1, ValueTypes.LIST);
6✔
2989
                Iterator<IValue> iter = valueList.getRawValue().iterator();
4✔
2990
                if (!iter.hasNext()) {
3✔
2991
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REDUCE_EMPTY));
6✔
2992
                }
2993

2994
                IValue accumulator = iter.next();
4✔
2995
                final IOperator innerOperator = OperatorBuilders.getSafeOperator(
5✔
2996
                        variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());
4✔
2997

2998
                while (iter.hasNext()) {
3✔
2999
                    IValue listValue = iter.next();
4✔
3000
                    accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);
13✔
3001
                }
1✔
3002
                return accumulator;
2✔
3003
            }).build());
1✔
3004

3005
    /**
3006
     * Apply for a given operator a given value.
3007
     */
3008
    public static final IOperator OPERATOR_BY_NAME = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG
5✔
3009
            .inputType(ValueTypes.STRING).output(ValueTypes.OPERATOR)
4✔
3010
            .symbol("op_by_name").operatorName("by_name").interactName("operatorByName")
6✔
3011
            .function(input -> {
1✔
3012
                ValueTypeString.ValueString name = input.getValue(0, ValueTypes.STRING);
6✔
3013
                try {
3014
                    ResourceLocation id = ResourceLocation.parse(name.getRawValue());
4✔
3015
                    IOperator operator = Operators.REGISTRY.getOperator(id);
4✔
3016
                    if (operator == null) {
2✔
3017
                        throw new EvaluationException(Component.translatable(
11✔
3018
                                L10NValues.OPERATOR_ERROR_OPERATORNOTFOUND, name.getRawValue()));
2✔
3019
                    }
3020
                    return ValueTypeOperator.ValueOperator.of(operator);
3✔
3021
                } catch (ResourceLocationException e) {
×
3022
                    throw new EvaluationException(Component.literal(e.getMessage()));
×
3023
                }
3024
            }).build());
1✔
3025

3026
    /**
3027
     * ----------------------------------- NBT OPERATORS -----------------------------------
3028
     */
3029

3030
    /**
3031
     * The number of entries in an NBT tag
3032
     */
3033
    public static final IOperator NBT_COMPOUND_SIZE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3034
            .output(ValueTypes.INTEGER).operatorName("compound_size").symbol("NBT{}.size").interactName("size")
9✔
3035
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_TO_INT.build(
2✔
3036
                opt -> opt.map(CompoundTag::size).orElse(0)
8✔
3037
            )).build());
1✔
3038

3039
    /**
3040
     * The list of keys in an NBT tag
3041
     */
3042
    public static final IOperator NBT_COMPOUND_KEYS = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3043
            .output(ValueTypes.LIST).operatorName("compound_keys").symbol("NBT{}.keys").interactName("keys")
8✔
3044
            .function(variables -> {
1✔
3045
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3046
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtKeys(value.getRawValue()));
7✔
3047
            }).build());
1✔
3048

3049
    /**
3050
     * If an NBT tag has the given key
3051
     */
3052
    public static final IOperator NBT_COMPOUND_HASKEY = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3053
            .output(ValueTypes.BOOLEAN).operatorName("compound_haskey").symbol("NBT{}.has_key").interactName("hasKey")
9✔
3054
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3055
                    Optional::isPresent
3056
            )).build());
1✔
3057

3058
    /**
3059
     * The NBT value type of an entry
3060
     */
3061
    public static final IOperator NBT_COMPOUND_VALUE_TYPE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3062
            .output(ValueTypes.STRING).operatorName("compound_type").symbol("NBT{}.type").interactName("type")
9✔
3063
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(tag -> {
2✔
3064
                if (tag.isPresent()) {
3✔
3065
                    try {
3066
                        return TagTypes.getType(tag.get().getId()).getName();
7✔
3067
                    } catch (IndexOutOfBoundsException e) {
×
3068

3069
                    }
3070
                }
3071
                return "null";
2✔
3072
            })).build());
1✔
3073

3074
    /**
3075
     * The NBT tag value
3076
     */
3077
    public static final IOperator NBT_COMPOUND_VALUE_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3078
            .output(ValueTypes.NBT).operatorName("compound_value_tag").symbol("NBT{}.get_tag").interactName("getTag")
9✔
3079
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(o -> o)).build());
5✔
3080

3081
    /**
3082
     * The NBT boolean value
3083
     */
3084
    public static final IOperator NBT_COMPOUND_VALUE_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3085
            .output(ValueTypes.BOOLEAN).operatorName("compound_value_boolean").symbol("NBT{}.get_boolean").interactName("getBoolean")
9✔
3086
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(
2✔
3087
                    o -> o.map(tag -> tag instanceof NumericTag && ((NumericTag) tag).getAsByte() != 0).orElse(false)
19!
3088
            )).build());
1✔
3089

3090
    /**
3091
     * The NBT integer value
3092
     */
3093
    public static final IOperator NBT_COMPOUND_VALUE_INTEGER = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3094
            .output(ValueTypes.INTEGER).operatorName("compound_value_integer").symbol("NBT{}.get_integer").interactName("getInteger")
9✔
3095
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_INT.build(
2✔
3096
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
17!
3097
            )).build());
1✔
3098

3099
    /**
3100
     * The NBT long value
3101
     */
3102
    public static final IOperator NBT_COMPOUND_VALUE_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3103
            .output(ValueTypes.LONG).operatorName("compound_value_long").symbol("NBT{}.get_long").interactName("getLong")
9✔
3104
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG.build(
2✔
3105
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsLong() : 0).orElse(0L)
17!
3106
            )).build());
1✔
3107

3108
    /**
3109
     * The NBT double value
3110
     */
3111
    public static final IOperator NBT_COMPOUND_VALUE_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3112
            .output(ValueTypes.DOUBLE).operatorName("compound_value_double").symbol("NBT{}.get_double").interactName("getDouble")
9✔
3113
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE.build(
2✔
3114
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsDouble() : 0).orElse(0D)
17!
3115
            )).build());
1✔
3116

3117
    /**
3118
     * The NBT string value
3119
     */
3120
    public static final IOperator NBT_COMPOUND_VALUE_STRING = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3121
            .output(ValueTypes.STRING).operatorName("compound_value_string").symbol("NBT{}.get_string").interactName("getString")
9✔
3122
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(
2✔
3123
                    o -> o.map(tag -> tag instanceof StringTag ? tag.getAsString() : "").orElse("")
14!
3124
            )).build());
1✔
3125

3126
    /**
3127
     * The NBT compound value
3128
     */
3129
    public static final IOperator NBT_COMPOUND_VALUE_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3130
            .output(ValueTypes.NBT).operatorName("compound_value_compound").symbol("NBT{}.get_compound").interactName("getCompound")
9✔
3131
            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(
2✔
3132
                    o -> o.map(tag -> tag instanceof CompoundTag ? (CompoundTag) tag : new CompoundTag())
11!
3133
            )).build());
1✔
3134

3135
    /**
3136
     * The NBT tag list value
3137
     */
3138
    public static final IOperator NBT_COMPOUND_VALUE_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3139
            .output(ValueTypes.LIST).operatorName("compound_value_list_tag").symbol("NBT{}.get_list_tag").interactName("getListTag")
8✔
3140
            .function(variables -> {
1✔
3141
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3142
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3143
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListTag(key.getRawValue(), value.getRawValue()));
9✔
3144
            }).build());
1✔
3145

3146
    /**
3147
     * The NBT boolean list value
3148
     */
3149
    public static final IOperator NBT_COMPOUND_VALUE_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3150
            .output(ValueTypes.LIST).operatorName("compound_value_list_byte").symbol("NBT{}.get_list_byte").interactName("getListByte")
8✔
3151
            .function(variables -> {
1✔
3152
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3153
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3154
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListByte(key.getRawValue(), value.getRawValue()));
9✔
3155
            }).build());
1✔
3156

3157
    /**
3158
     * The NBT int list value
3159
     */
3160
    public static final IOperator NBT_COMPOUND_VALUE_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3161
            .output(ValueTypes.LIST).operatorName("compound_value_list_int").symbol("NBT{}.get_list_int").interactName("getListInt")
8✔
3162
            .function(variables -> {
1✔
3163
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3164
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3165
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListInt(key.getRawValue(), value.getRawValue()));
9✔
3166
            }).build());
1✔
3167

3168
    /**
3169
     * The NBT long list value
3170
     */
3171
    public static final IOperator NBT_COMPOUND_VALUE_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3172
            .output(ValueTypes.LIST).operatorName("compound_value_list_long").symbol("NBT{}.get_list_long").interactName("getListLong")
8✔
3173
            .function(variables -> {
1✔
3174
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3175
                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);
6✔
3176
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListLong(key.getRawValue(), value.getRawValue()));
9✔
3177
            }).build());
1✔
3178

3179
    /**
3180
     * Remove an entry from an NBT compound
3181
     */
3182
    public static final IOperator NBT_COMPOUND_WITHOUT = REGISTRY.register(OperatorBuilders.NBT_2
5✔
3183
            .output(ValueTypes.NBT).operatorName("compound_without").symbol("NBT{}.without").interactName("without")
8✔
3184
            .function(variables -> {
1✔
3185
                ValueTypeNbt.ValueNbt valueNbt = variables.getValue(0, ValueTypes.NBT);
6✔
3186
                Optional<Tag> tag = valueNbt.getRawValue();
3✔
3187
                if (tag.isPresent()) {
3!
3188
                    if (!(tag.get() instanceof CompoundTag)) {
4!
3189
                        return ValueTypeNbt.ValueNbt.of();
×
3190
                    }
3191
                    ValueTypeString.ValueString valueString = variables.getValue(1, ValueTypes.STRING);
6✔
3192
                    String key = valueString.getRawValue();
3✔
3193
                    CompoundTag tagCompound = (CompoundTag) tag.get();
4✔
3194
                    if (tagCompound.contains(key)) {
4!
3195
                        // Copy the tag to ensure immutability
3196
                        tagCompound = tagCompound.copy();
3✔
3197
                        tagCompound.remove(key);
3✔
3198
                    }
3199
                    return ValueTypeNbt.ValueNbt.of(tagCompound);
3✔
3200
                }
3201
                return valueNbt;
×
3202
            }).build());
1✔
3203

3204

3205

3206
    /**
3207
     * Set an NBT compound boolean value
3208
     */
3209
    public static final IOperator NBT_COMPOUND_WITH_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3210
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3211
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.BOOLEAN)
2✔
3212
            .operatorName("compound_with_boolean").symbol("NBT{}.with_boolean").interactName("withBoolean")
7✔
3213
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3214
                ValueTypeBoolean.ValueBoolean value = input.getRight().getValue(0, ValueTypes.BOOLEAN);
8✔
3215
                input.getLeft().ifPresent(tag -> tag.putBoolean(input.getMiddle(), value.getRawValue()));
15✔
3216
                return input.getLeft();
4✔
3217
            })).build());
1✔
3218

3219
    /**
3220
     * Set an NBT compound short value
3221
     */
3222
    public static final IOperator NBT_COMPOUND_WITH_SHORT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3223
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3224
            .operatorName("compound_with_short").symbol("NBT{}.with_short").interactName("withShort")
7✔
3225
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3226
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3227
                input.getLeft().ifPresent(tag -> tag.putShort(input.getMiddle(), (short) value.getRawValue()));
16✔
3228
                return input.getLeft();
4✔
3229
            })).build());
1✔
3230

3231
    /**
3232
     * Set an NBT compound integer value
3233
     */
3234
    public static final IOperator NBT_COMPOUND_WITH_INTEGER = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3235
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)
2✔
3236
            .operatorName("compound_with_integer").symbol("NBT{}.with_integer").interactName("withInteger")
7✔
3237
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3238
                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);
8✔
3239
                input.getLeft().ifPresent(tag -> tag.putInt(input.getMiddle(), value.getRawValue()));
15✔
3240
                return input.getLeft();
4✔
3241
            })).build());
1✔
3242

3243
    /**
3244
     * Set an NBT compound long value
3245
     */
3246
    public static final IOperator NBT_COMPOUND_WITH_LONG = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3247
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LONG)
2✔
3248
            .operatorName("compound_with_long").symbol("NBT{}.with_long").interactName("withLong")
7✔
3249
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3250
                ValueTypeLong.ValueLong value = input.getRight().getValue(0, ValueTypes.LONG);
8✔
3251
                input.getLeft().ifPresent(tag -> tag.putLong(input.getMiddle(), value.getRawValue()));
15✔
3252
                return input.getLeft();
4✔
3253
            })).build());
1✔
3254

3255
    /**
3256
     * Set an NBT compound double value
3257
     */
3258
    public static final IOperator NBT_COMPOUND_WITH_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3259
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3260
            .operatorName("compound_with_double").symbol("NBT{}.with_double").interactName("withDouble")
7✔
3261
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3262
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3263
                input.getLeft().ifPresent(tag -> tag.putDouble(input.getMiddle(), value.getRawValue()));
15✔
3264
                return input.getLeft();
4✔
3265
            })).build());
1✔
3266

3267
    /**
3268
     * Set an NBT compound float value
3269
     */
3270
    public static final IOperator NBT_COMPOUND_WITH_FLOAT = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3271
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)
2✔
3272
            .operatorName("compound_with_float").symbol("NBT{}.with_float").interactName("withFloat")
7✔
3273
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3274
                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);
8✔
3275
                input.getLeft().ifPresent(tag -> tag.putFloat(input.getMiddle(), (float) value.getRawValue()));
16✔
3276
                return input.getLeft();
4✔
3277
            })).build());
1✔
3278

3279
    /**
3280
     * Set an NBT compound string value
3281
     */
3282
    public static final IOperator NBT_COMPOUND_WITH_STRING = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3283
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING)
2✔
3284
            .operatorName("compound_with_string").symbol("NBT{}.with_string").interactName("withString")
7✔
3285
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3286
                ValueTypeString.ValueString value = input.getRight().getValue(0, ValueTypes.STRING);
8✔
3287
                input.getLeft().ifPresent(tag -> tag.putString(input.getMiddle(), value.getRawValue()));
15✔
3288
                return input.getLeft();
4✔
3289
            })).build());
1✔
3290

3291
    /**
3292
     * Set an NBT compound compound value
3293
     */
3294
    public static final IOperator NBT_COMPOUND_WITH_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_3
18✔
3295
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.NBT)
2✔
3296
            .operatorName("compound_with_tag").symbol("NBT{}.with_tag").interactName("withTag")
7✔
3297
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {
2✔
3298
                ValueTypeNbt.ValueNbt value = input.getRight().getValue(0, ValueTypes.NBT);
8✔
3299
                input.getLeft()
6✔
3300
                        .ifPresent(tag -> value.getRawValue()
7✔
3301
                                .ifPresent(v -> tag.put(input.getMiddle(), v)));
9✔
3302
                return input.getLeft();
4✔
3303
            })).build());
1✔
3304

3305
    /**
3306
     * Set an NBT compound tag list value
3307
     */
3308
    public static final IOperator NBT_COMPOUND_WITH_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3309
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3310
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3311
            .operatorName("compound_with_list_tag").symbol("NBT{}.with_tag_list").interactName("withTagList")
9✔
3312
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3313
                @Override
3314
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3315
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3316
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3317
                            NbtHelpers.getListNbtTag(value, NBT_COMPOUND_WITH_LIST_TAG.getLocalizedNameFull())));
2✔
3318
                    return input.getLeft();
4✔
3319
                }
3320
            })).build());
1✔
3321

3322
    /**
3323
     * Set an NBT compound byte list value
3324
     */
3325
    public static final IOperator NBT_COMPOUND_WITH_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3326
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3327
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3328
            .operatorName("compound_with_list_byte").symbol("NBT{}.with_byte_list").interactName("withByteList")
9✔
3329
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3330
                @Override
3331
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3332
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3333
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3334
                            NbtHelpers.getListNbtByte(value, NBT_COMPOUND_WITH_LIST_BYTE.getLocalizedNameFull())));
2✔
3335
                    return input.getLeft();
4✔
3336
                }
3337
            })).build());
1✔
3338

3339
    /**
3340
     * Set an NBT compound int list value
3341
     */
3342
    public static final IOperator NBT_COMPOUND_WITH_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3343
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3344
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3345
            .operatorName("compound_with_list_int").symbol("NBT{}.with_int_list").interactName("withIntList")
9✔
3346
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3347
                @Override
3348
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3349
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3350
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3351
                            NbtHelpers.getListNbtInt(value, NBT_COMPOUND_WITH_LIST_INT.getLocalizedNameFull())));
2✔
3352
                    return input.getLeft();
4✔
3353
                }
3354
            })).build());
1✔
3355

3356
    /**
3357
     * Set an NBT compound long list value
3358
     */
3359
    public static final IOperator NBT_COMPOUND_WITH_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_3
5✔
3360
            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)
15✔
3361
            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)
2✔
3362
            .operatorName("compound_with_list_long").symbol("NBT{}.with_list_long").interactName("withListLong")
9✔
3363
            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {
5✔
3364
                @Override
3365
                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {
3366
                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);
8✔
3367
                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),
16✔
3368
                            NbtHelpers.getListNbtLong(value, NBT_COMPOUND_WITH_LIST_LONG.getLocalizedNameFull())));
2✔
3369
                    return input.getLeft();
4✔
3370
                }
3371
            })).build());
1✔
3372

3373
    /**
3374
     * Check if the first NBT compound tag is a subset of the second NBT compound tag.
3375
     */
3376
    public static final IOperator NBT_COMPOUND_SUBSET = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3377
            .output(ValueTypes.BOOLEAN).operatorName("compound_subset").symbol("NBT{}.⊆").interactName("isSubset")
8✔
3378
            .function(variables -> {
1✔
3379
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3380
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3381
                if (valueNbt0.getRawValue().isPresent()
5!
3382
                        && valueNbt1.getRawValue().isPresent()
4!
3383
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3384
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3385
                    return ValueTypeBoolean.ValueBoolean.of(NbtHelpers.nbtMatchesSubset((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get(), true));
12✔
3386
                }
3387
                return ValueTypeBoolean.ValueBoolean.of(false);
×
3388
            }).build());
1✔
3389

3390
    /**
3391
     * The union of the given NBT compound tags. Nested tags will be joined recusively.
3392
     */
3393
    public static final IOperator NBT_COMPOUND_UNION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3394
            .output(ValueTypes.NBT).operatorName("compound_union").symbol("NBT{}.∪").interactName("union")
8✔
3395
            .function(variables -> {
1✔
3396
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3397
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3398
                if (valueNbt0.getRawValue().isPresent()
5!
3399
                        && valueNbt1.getRawValue().isPresent()
4!
3400
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3401
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3402
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.union((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3403
                }
3404
                return ValueTypeNbt.ValueNbt.of();
×
3405
            }).build());
1✔
3406

3407
    /**
3408
     * The intersection of the given NBT compound tags. Nested tags will be intersected recusively.
3409
     */
3410
    public static final IOperator NBT_COMPOUND_INTERSECTION = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3411
            .output(ValueTypes.NBT).operatorName("compound_intersection").symbol("NBT{}.∩").interactName("intersection")
8✔
3412
            .function(variables -> {
1✔
3413
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3414
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3415
                if (valueNbt0.getRawValue().isPresent()
5!
3416
                        && valueNbt1.getRawValue().isPresent()
4!
3417
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3418
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3419
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.intersection((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
19✔
3420
                }
3421
                return ValueTypeNbt.ValueNbt.of();
×
3422
            }).build());
1✔
3423

3424
    /**
3425
     * The difference of the given NBT compound tags. Nested tags will be subtracted recusively.
3426
     */
3427
    public static final IOperator NBT_COMPOUND_MINUS = REGISTRY.register(OperatorBuilders.NBT_2_NBT
5✔
3428
            .output(ValueTypes.NBT).operatorName("compound_minus").symbol("NBT{}.∖").interactName("minus")
8✔
3429
            .function(variables -> {
1✔
3430
                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);
6✔
3431
                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);
6✔
3432
                if (valueNbt0.getRawValue().isPresent()
5!
3433
                        && valueNbt1.getRawValue().isPresent()
4!
3434
                        && valueNbt0.getRawValue().get() instanceof CompoundTag
5!
3435
                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {
4!
3436
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.minus((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));
11✔
3437
                }
3438
                return ValueTypeNbt.ValueNbt.of();
×
3439
            }).build());
1✔
3440

3441
    /**
3442
     * The boolean value of an NBT value
3443
     */
3444
    public static final IOperator NBT_AS_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3445
            .output(ValueTypes.BOOLEAN).operatorName("as_boolean").symbol("NBT.as_boolean").interactName("asBoolean")
9✔
3446
            .function(OperatorBuilders.FUNCTION_NBT_TO_BOOLEAN.build(
2✔
3447
                    o -> o.map(tag -> tag instanceof ByteTag && ((ByteTag) tag).getAsByte() != 0).orElse(false)
20!
3448
            )).build());
1✔
3449

3450
    /**
3451
     * The byte value of an NBT value
3452
     */
3453
    public static final IOperator NBT_AS_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3454
            .output(ValueTypes.INTEGER).operatorName("as_byte").symbol("NBT.as_byte").interactName("asByte")
9✔
3455
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3456
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3457
            )).build());
1✔
3458

3459
    /**
3460
     * The short value of an NBT value
3461
     */
3462
    public static final IOperator NBT_AS_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3463
            .output(ValueTypes.INTEGER).operatorName("as_short").symbol("NBT.as_short").interactName("asShort")
9✔
3464
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3465
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3466
            )).build());
1✔
3467

3468
    /**
3469
     * The int value of an NBT value
3470
     */
3471
    public static final IOperator NBT_AS_INT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3472
            .output(ValueTypes.INTEGER).operatorName("as_int").symbol("NBT.as_int").interactName("asInt")
9✔
3473
            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(
2✔
3474
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsInt() : 0).orElse(0)
18✔
3475
            )).build());
1✔
3476

3477
    /**
3478
     * The long value of an NBT value
3479
     */
3480
    public static final IOperator NBT_AS_LONG = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3481
            .output(ValueTypes.LONG).operatorName("as_long").symbol("NBT.as_long").interactName("asLong")
9✔
3482
            .function(OperatorBuilders.FUNCTION_NBT_TO_LONG.build(
2✔
3483
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsLong() : 0L).orElse(0L)
18✔
3484
            )).build());
1✔
3485

3486
    /**
3487
     * The double value of an NBT value
3488
     */
3489
    public static final IOperator NBT_AS_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3490
            .output(ValueTypes.DOUBLE).operatorName("as_double").symbol("NBT.as_double").interactName("asDouble")
9✔
3491
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3492
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsDouble() : 0D).orElse(0D)
18✔
3493
            )).build());
1✔
3494

3495
    /**
3496
     * The float value of an NBT value
3497
     */
3498
    public static final IOperator NBT_AS_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3499
            .output(ValueTypes.DOUBLE).operatorName("as_float").symbol("NBT.as_float").interactName("asFloat")
9✔
3500
            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(
2✔
3501
                    o -> o.map(tag -> tag instanceof NumericTag ? ((NumericTag) tag).getAsFloat() : 0D).orElse(0D)
19✔
3502
            )).build());
1✔
3503

3504
    /**
3505
     * The string value of an NBT value
3506
     */
3507
    public static final IOperator NBT_AS_STRING = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3508
            .output(ValueTypes.STRING).operatorName("as_string").symbol("NBT.as_string").interactName("asString")
9✔
3509
            .function(OperatorBuilders.FUNCTION_NBT_TO_STRING.build(
2✔
3510
                    o -> o.map(tag -> tag instanceof StringTag ? ((StringTag) tag).getAsString() : "").orElse("")
16✔
3511
            )).build());
1✔
3512

3513
    /**
3514
     * The tag list value of an NBT value
3515
     */
3516
    public static final IOperator NBT_AS_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3517
            .output(ValueTypes.LIST).operatorName("as_tag_list").symbol("NBT.as_tag_list").interactName("asTagList")
8✔
3518
            .function(variables -> {
1✔
3519
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3520
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListTag(value.getRawValue()));
7✔
3521
            }).build());
1✔
3522

3523
    /**
3524
     * The byte list value of an NBT value
3525
     */
3526
    public static final IOperator NBT_AS_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3527
            .output(ValueTypes.LIST).operatorName("as_byte_list").symbol("NBT.as_byte_list").interactName("asByteList")
8✔
3528
            .function(variables -> {
1✔
3529
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3530
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListByte(value.getRawValue()));
7✔
3531
            }).build());
1✔
3532

3533
    /**
3534
     * The int list value of an NBT value
3535
     */
3536
    public static final IOperator NBT_AS_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3537
            .output(ValueTypes.LIST).operatorName("as_int_list").symbol("NBT.as_int_list").interactName("asIntList")
8✔
3538
            .function(variables -> {
1✔
3539
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3540
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListInt(value.getRawValue()));
7✔
3541
            }).build());
1✔
3542

3543
    /**
3544
     * The long list value of an NBT value
3545
     */
3546
    public static final IOperator NBT_AS_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG
5✔
3547
            .output(ValueTypes.LIST).operatorName("as_long_list").symbol("NBT.as_long_list").interactName("asLongList")
8✔
3548
            .function(variables -> {
1✔
3549
                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);
6✔
3550
                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListLong(value.getRawValue()));
7✔
3551
            }).build());
1✔
3552

3553
    /**
3554
     * The NBT value of a boolean value
3555
     */
3556
    public static final IOperator NBT_FROM_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3557
            .inputType(ValueTypes.BOOLEAN).output(ValueTypes.NBT)
4✔
3558
            .operatorName("from_boolean").symbol("NBT.from_boolean").interactName("asNbt")
6✔
3559
            .function(variables -> {
1✔
3560
                ValueTypeBoolean.ValueBoolean value = variables.getValue(0, ValueTypes.BOOLEAN);
6✔
3561
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf(value.getRawValue()));
5✔
3562
            }).build());
1✔
3563

3564
    /**
3565
     * The NBT value of a short value
3566
     */
3567
    public static final IOperator NBT_FROM_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3568
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3569
            .operatorName("from_short").symbol("NBT.from_short").interactName("asNbt", "short", true)
8✔
3570
            .function(variables -> {
1✔
3571
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3572
                return ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) value.getRawValue()));
6✔
3573
            }).build());
1✔
3574

3575
    /**
3576
     * The NBT value of a byte value
3577
     */
3578
    public static final IOperator NBT_FROM_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3579
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3580
            .operatorName("from_byte").symbol("NBT.from_byte").interactName("asNbt", "byte", true)
8✔
3581
            .function(variables -> {
1✔
3582
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3583
                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf((byte) value.getRawValue()));
6✔
3584
            }).build());
1✔
3585

3586
    /**
3587
     * The NBT value of an int value
3588
     */
3589
    public static final IOperator NBT_FROM_INT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3590
            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)
4✔
3591
            .operatorName("from_int").symbol("NBT.from_int").interactName("asNbt")
6✔
3592
            .function(variables -> {
1✔
3593
                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);
6✔
3594
                return ValueTypeNbt.ValueNbt.of(IntTag.valueOf(value.getRawValue()));
5✔
3595
            }).build());
1✔
3596

3597
    /**
3598
     * The NBT value of a long value
3599
     */
3600
    public static final IOperator NBT_FROM_LONG = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3601
            .inputType(ValueTypes.LONG).output(ValueTypes.NBT)
4✔
3602
            .operatorName("from_long").symbol("NBT.from_long").interactName("asNbt")
6✔
3603
            .function(variables -> {
1✔
3604
                ValueTypeLong.ValueLong value = variables.getValue(0, ValueTypes.LONG);
6✔
3605
                return ValueTypeNbt.ValueNbt.of(LongTag.valueOf(value.getRawValue()));
5✔
3606
            }).build());
1✔
3607

3608
    /**
3609
     * The NBT value of a double value
3610
     */
3611
    public static final IOperator NBT_FROM_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3612
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3613
            .operatorName("from_double").symbol("NBT.from_double").interactName("asNbt")
6✔
3614
            .function(variables -> {
1✔
3615
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3616
                return ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(value.getRawValue()));
5✔
3617
            }).build());
1✔
3618

3619
    /**
3620
     * The NBT value of a float value
3621
     */
3622
    public static final IOperator NBT_FROM_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3623
            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)
4✔
3624
            .operatorName("from_float").symbol("NBT.from_float").interactName("asNbt", "float", true)
8✔
3625
            .function(variables -> {
1✔
3626
                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);
6✔
3627
                return ValueTypeNbt.ValueNbt.of(FloatTag.valueOf((float) value.getRawValue()));
6✔
3628
            }).build());
1✔
3629

3630
    /**
3631
     * The NBT value of a string value
3632
     */
3633
    public static final IOperator NBT_FROM_STRING = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3634
            .inputType(ValueTypes.STRING).output(ValueTypes.NBT)
4✔
3635
            .operatorName("from_string").symbol("NBT.from_string").interactName("asNbt")
6✔
3636
            .function(variables -> {
1✔
3637
                ValueTypeString.ValueString value = variables.getValue(0, ValueTypes.STRING);
6✔
3638
                return ValueTypeNbt.ValueNbt.of(StringTag.valueOf(value.getRawValue()));
5✔
3639
            }).build());
1✔
3640

3641
    /**
3642
     * The NBT value of a tag list value
3643
     */
3644
    public static final IOperator NBT_FROM_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3645
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3646
            .operatorName("from_tag_list").symbol("NBT.from_tag_list").interactName("asNbt", "tagList", true)
10✔
3647
            .function(new OperatorBase.IFunction() {
4✔
3648
                @Override
3649
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3650
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3651
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtTag(value, NBT_FROM_TAG_LIST.getLocalizedNameFull()));
6✔
3652
                }
3653
            }).build());
1✔
3654

3655
    /**
3656
     * The NBT value of a byte list value
3657
     */
3658
    public static final IOperator NBT_FROM_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3659
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3660
            .operatorName("from_byte_list").symbol("NBT.from_byte_list").interactName("asNbt", "byteList", true)
10✔
3661
            .function(new OperatorBase.IFunction() {
4✔
3662
                @Override
3663
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3664
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3665
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtByte(value, NBT_FROM_BYTE_LIST.getLocalizedNameFull()));
6✔
3666
                }
3667
            }).build());
1✔
3668

3669
    /**
3670
     * The NBT value of a int list value
3671
     */
3672
    public static final IOperator NBT_FROM_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3673
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3674
            .operatorName("from_int_list").symbol("NBT.from_int_list").interactName("asNbt", "intList", true)
10✔
3675
            .function(new OperatorBase.IFunction() {
4✔
3676
                @Override
3677
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3678
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3679
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtInt(value, NBT_FROM_INT_LIST.getLocalizedNameFull()));
6✔
3680
                }
3681
            }).build());
1✔
3682

3683
    /**
3684
     * The NBT value of a long list value
3685
     */
3686
    public static final IOperator NBT_FROM_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG
5✔
3687
            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)
4✔
3688
            .operatorName("from_long_list").symbol("NBT.from_long_list").interactName("asNbt", "longList", true)
10✔
3689
            .function(new OperatorBase.IFunction() {
4✔
3690
                @Override
3691
                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {
3692
                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);
6✔
3693
                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtLong(value, NBT_FROM_LONG_LIST.getLocalizedNameFull()));
6✔
3694
                }
3695
            }).build());
1✔
3696

3697
    /**
3698
     * Apply the given NBT path expression on the given NBT value and get the first result.
3699
     */
3700
    public static final IOperator NBT_PATH_MATCH_FIRST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3701
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.NBT)
4✔
3702
            .operatorName("path_match_first").symbol("NBT.path_match_first").interactName("nbtPathMatchFirst")
6✔
3703
            .function(variables -> {
1✔
3704
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3705
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3706
                INbtPathExpression expression = null;
2✔
3707
                try {
3708
                    expression = NbtPath.parse(string.getRawValue());
4✔
3709
                } catch (NbtParseException e) {
×
3710
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3711
                            string.getRawValue(),
×
3712
                            e.getMessage()));
×
3713
                }
1✔
3714
                if (!nbt.getRawValue().isPresent()) {
4!
3715
                    return ValueTypeNbt.ValueNbt.of();
×
3716
                }
3717
                return ValueTypeNbt.ValueNbt.of(expression.match(nbt.getRawValue().get()).getMatches().findAny());
10✔
3718
            }).build());
1✔
3719

3720
    /**
3721
     * Apply the given NBT path expression on the given NBT value and get all results.
3722
     */
3723
    public static final IOperator NBT_PATH_MATCH_ALL = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3724
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.LIST)
4✔
3725
            .operatorName("path_match_all").symbol("NBT.path_match_all").interactName("nbtPathMatchAll")
6✔
3726
            .function(variables -> {
1✔
3727
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3728
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3729
                INbtPathExpression expression = null;
2✔
3730
                try {
3731
                    expression = NbtPath.parse(string.getRawValue());
4✔
3732
                } catch (NbtParseException e) {
×
3733
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3734
                            string.getRawValue(),
×
3735
                            e.getMessage()));
×
3736
                }
1✔
3737
                if (!nbt.getRawValue().isPresent()) {
4!
3738
                    return ValueTypeList.ValueList.ofAll(ValueTypes.NBT);
×
3739
                }
3740
                List<ValueTypeNbt.ValueNbt> matches = expression.match(nbt.getRawValue().get()).getMatches()
8✔
3741
                        .map(ValueTypeNbt.ValueNbt::of)
1✔
3742
                        .collect(Collectors.toList());
4✔
3743
                return ValueTypeList.ValueList.ofList(ValueTypes.NBT, matches);
4✔
3744
            }).build());
1✔
3745

3746
    /**
3747
     * Test the given NBT path expression on the given NBT value.
3748
     */
3749
    public static final IOperator NBT_PATH_TEST = REGISTRY.register(OperatorBuilders.NBT_2
14✔
3750
            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.BOOLEAN)
4✔
3751
            .operatorName("path_test").symbol("NBT.path_test").interactName("nbtPathTest")
6✔
3752
            .function(variables -> {
1✔
3753
                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);
6✔
3754
                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);
6✔
3755
                INbtPathExpression expression = null;
2✔
3756
                try {
3757
                    expression = NbtPath.parse(string.getRawValue());
4✔
3758
                } catch (NbtParseException e) {
×
3759
                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,
×
3760
                            string.getRawValue(),
×
3761
                            e.getMessage()));
×
3762
                }
1✔
3763
                if (!nbt.getRawValue().isPresent()) {
4!
3764
                    return ValueTypeBoolean.ValueBoolean.of(false);
×
3765
                }
3766
                return ValueTypeBoolean.ValueBoolean.of(expression.test(nbt.getRawValue().get()));
8✔
3767
            }).build());
1✔
3768

3769
    /**
3770
     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------
3771
     */
3772

3773
    /**
3774
     * The list of items.
3775
     */
3776
    public static final IOperator INGREDIENTS_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3777
            .output(ValueTypes.LIST).operatorInteract("items").symbol("Ingr.items")
6✔
3778
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ITEMSTACK))
4✔
3779
            .build());
1✔
3780

3781
    /**
3782
     * The list of fluids
3783
     */
3784
    public static final IOperator INGREDIENTS_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3785
            .output(ValueTypes.LIST).operatorInteract("fluids").symbol("Ingr.fluids")
6✔
3786
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.FLUIDSTACK))
4✔
3787
            .build());
1✔
3788

3789
    /**
3790
     * The list of fluids
3791
     */
3792
    public static final IOperator INGREDIENTS_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG
5✔
3793
            .output(ValueTypes.LIST).operatorInteract("energies").symbol("Ingr.energies")
6✔
3794
            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ENERGY))
4✔
3795
            .build());
1✔
3796

3797
    /**
3798
     * Set an ingredient item
3799
     */
3800
    public static final IOperator INGREDIENTS_WITH_ITEM = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_ITEMSTACK
5✔
3801
            .operatorName("with_item").symbol("Ingr.with_item").interactName("withItem")
6✔
3802
            .function(variables -> {
1✔
3803
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3804
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3805
                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(2, ValueTypes.OBJECT_ITEMSTACK);
6✔
3806
                if (value.getRawValue().isEmpty()) {
4!
3807
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3808
                }
3809
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3810
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3811
                        index.getRawValue(), IngredientComponent.ITEMSTACK, itemStack.getRawValue()));
5✔
3812
            }).build());
1✔
3813

3814
    /**
3815
     * Set an ingredient fluid
3816
     */
3817
    public static final IOperator INGREDIENTS_WITH_FLUID = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_FLUIDSTACK
5✔
3818
            .operatorName("with_fluid").symbol("Ingr.with_fluid").interactName("withFluid")
6✔
3819
            .function(variables -> {
1✔
3820
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3821
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3822
                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = variables.getValue(2, ValueTypes.OBJECT_FLUIDSTACK);
6✔
3823
                if (value.getRawValue().isEmpty()) {
4!
3824
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3825
                }
3826
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3827
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3828
                        index.getRawValue(), IngredientComponent.FLUIDSTACK, fluidStack.getRawValue()));
5✔
3829
            }).build());
1✔
3830

3831
    /**
3832
     * Set an ingredient energy
3833
     */
3834
    public static final IOperator INGREDIENTS_WITH_ENERGY = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_LONG
5✔
3835
            .operatorName("with_energy").symbol("Ingr.with_energy").interactName("withEnergy")
6✔
3836
            .function(variables -> {
1✔
3837
                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3838
                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);
6✔
3839
                ValueTypeLong.ValueLong energy = variables.getValue(2, ValueTypes.LONG);
6✔
3840
                if (value.getRawValue().isEmpty()) {
4!
3841
                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3842
                }
3843
                IMixedIngredients baseIngredients = value.getRawValue().get();
5✔
3844
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,
6✔
3845
                        index.getRawValue(), IngredientComponent.ENERGY, energy.getRawValue()));
6✔
3846
            }).build());
1✔
3847

3848
    /**
3849
     * Set the list of items
3850
     */
3851
    public static final IOperator INGREDIENTS_WITH_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
3852
            .operatorName("with_items").symbol("Ingr.with_items").interactName("withItems")
6✔
3853
            .function(variables -> {
1✔
3854
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3855
                ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
3856
                if (valueIngredients.getRawValue().isEmpty()) {
4!
3857
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3858
                }
3859
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
3860
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
3861
                        IngredientComponent.ITEMSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ITEMSTACK, list)));
2✔
3862
            }).build());
1✔
3863

3864
    /**
3865
     * Set the list of fluids
3866
     */
3867
    public static final IOperator INGREDIENTS_WITH_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
3868
            .operatorName("with_fluids").symbol("Ingr.with_fluids").interactName("withFluids")
6✔
3869
            .function(variables -> {
1✔
3870
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3871
                ValueTypeList.ValueList<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> list = variables.getValue(1, ValueTypes.LIST);
6✔
3872
                if (valueIngredients.getRawValue().isEmpty()) {
4!
3873
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3874
                }
3875
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
3876
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
3877
                        IngredientComponent.FLUIDSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.FLUIDSTACK, list)));
2✔
3878
            }).build());
1✔
3879

3880
    /**
3881
     * Set the list of energies
3882
     */
3883
    public static final IOperator INGREDIENTS_WITH_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST
5✔
3884
            .renderPattern(IConfigRenderPattern.INFIX_VERYLONG)
2✔
3885
            .operatorName("with_energies").symbol("Ingr.with_energies").interactName("withEnergies")
6✔
3886
            .function(variables -> {
1✔
3887
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3888
                ValueTypeList.ValueList<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = variables.getValue(1, ValueTypes.LIST);
6✔
3889
                if (valueIngredients.getRawValue().isEmpty()) {
4!
3890
                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));
×
3891
                }
3892
                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();
5✔
3893
                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,
8✔
3894
                        IngredientComponent.ENERGY, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ENERGY, list)));
2✔
3895
            }).build());
1✔
3896

3897
    /**
3898
     * ----------------------------------- RECIPE OPERATORS -----------------------------------
3899
     */
3900

3901
    /**
3902
     * The input ingredients of a recipe
3903
     */
3904
    public static final IOperator RECIPE_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
3905
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
3906
            .operatorInteract("input").symbol("recipe_in")
4✔
3907
            .function(variables -> {
1✔
3908
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
3909
                if (value.getRawValue().isPresent()) {
4!
3910
                    return ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(value.getRawValue().get()));
7✔
3911
                }
3912
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
3913
            }).build());
1✔
3914

3915
    /**
3916
     * The output ingredients of a recipe
3917
     */
3918
    public static final IOperator RECIPE_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG
5✔
3919
            .output(ValueTypes.OBJECT_INGREDIENTS)
2✔
3920
            .operatorInteract("output").symbol("recipe_out")
4✔
3921
            .function(variables -> {
1✔
3922
                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
3923
                if (value.getRawValue().isPresent()) {
4!
3924
                    return ValueObjectTypeIngredients.ValueIngredients.of(value.getRawValue().get().getOutput());
7✔
3925
                }
3926
                return ValueObjectTypeIngredients.ValueIngredients.of(null);
×
3927
            }).build());
1✔
3928

3929
    /**
3930
     * Set the input ingredients of a recipe
3931
     */
3932
    public static final IOperator RECIPE_WITH_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
3933
            .output(ValueTypes.OBJECT_RECIPE)
2✔
3934
            .operatorName("with_input").symbol("Recipe.with_in").interactName("withInput")
6✔
3935
            .function(variables -> {
1✔
3936
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
3937
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
3938
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
3939
                    IMixedIngredients ingredients = valueIngredients.getRawValue().get();
5✔
3940
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
3941
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
3942
                        IIngredientMatcher matcher = component.getMatcher();
3✔
3943
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
3944
                                .stream()
4✔
3945
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
3946
                                .collect(Collectors.toList()));
3✔
3947
                    }
1✔
3948
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
3949
                            inputs,
3950
                            valueRecipe.getRawValue().get().getOutput()
5✔
3951
                    ));
3952
                }
3953
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
3954
            }).build());
1✔
3955

3956
    /**
3957
     * Set the output ingredients of a recipe
3958
     */
3959
    public static final IOperator RECIPE_WITH_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX
5✔
3960
            .output(ValueTypes.OBJECT_RECIPE)
2✔
3961
            .operatorName("with_output").symbol("Recipe.with_out").interactName("withOutput")
6✔
3962
            .function(variables -> {
1✔
3963
                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);
6✔
3964
                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
3965
                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {
8!
3966
                    IRecipeDefinition recipe = valueRecipe.getRawValue().get();
5✔
3967
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
3968
                    for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {
11✔
3969
                        inputs.put(component, (List) recipe.getInputs(component));
7✔
3970
                    }
1✔
3971
                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
3972
                            inputs,
3973
                            valueIngredients.getRawValue().get()
4✔
3974
                    ));
3975
                }
3976
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
3977
            }).build());
1✔
3978

3979
    /**
3980
     * Create a recipe from two the given I/O ingredients
3981
     */
3982
    public static final IOperator RECIPE_WITH_INPUT_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_PREFIX
5✔
3983
            .output(ValueTypes.OBJECT_RECIPE)
2✔
3984
            .operatorName("with_input_output").symbol("Recipe.with_io").interactName("withInputOutput")
6✔
3985
            .function(variables -> {
1✔
3986
                ValueObjectTypeIngredients.ValueIngredients valueIn = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);
6✔
3987
                ValueObjectTypeIngredients.ValueIngredients valueOut = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);
6✔
3988
                if (valueIn.getRawValue().isPresent() && valueOut.getRawValue().isPresent()) {
8!
3989
                    IMixedIngredients ingredients = valueIn.getRawValue().get();
5✔
3990
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
2✔
3991
                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {
11✔
3992
                        IIngredientMatcher matcher = component.getMatcher();
3✔
3993
                        inputs.put(component, (List) ingredients.getInstances(component)
7✔
3994
                                .stream()
4✔
3995
                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))
13✔
3996
                                .collect(Collectors.toList()));
3✔
3997
                    }
1✔
3998
                    try {
3999
                        return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(
6✔
4000
                                inputs,
4001
                                valueOut.getRawValue().get()
4✔
4002
                        ));
4003
                    } catch (IllegalArgumentException e) {
×
4004
                        throw new EvaluationException(Component.literal(e.getMessage()));
×
4005
                    }
4006
                }
4007
                return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
4008
            }).build());
1✔
4009

4010
    /**
4011
     * ------------------------------------ PARSE OPERATORS ------------------------------------
4012
     */
4013

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

4023
    /**
4024
     * Double Parse operator which takes a string of a form Double.parseDouble(),
4025
     * `/([+-]?)(Inf(inity)?|\u221E)/i`, or Long.decode() can consume.
4026
     */
4027
    public static final IOperator PARSE_DOUBLE = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.DOUBLE, v -> {
8✔
4028
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4029
      try {
4030
        return ValueTypeDouble.ValueDouble.of(Double.parseDouble(value.getRawValue()));
5✔
4031
      } catch (NumberFormatException e) {
1✔
4032
        try {
4033
          // \u221E = infinity symbol
4034
          Pattern p = Pattern.compile("\\A([+-]?)(Inf(inity)?|\u221E)\\z", Pattern.CASE_INSENSITIVE);
4✔
4035
          Matcher m = p.matcher(value.getRawValue().trim());
6✔
4036
          if (m.matches()){
3✔
4037
            if (m.group(1).equals("-")){
6✔
4038
              return ValueTypeDouble.ValueDouble.of(Double.NEGATIVE_INFINITY);
3✔
4039
            }
4040
            return ValueTypeDouble.ValueDouble.of(Double.POSITIVE_INFINITY);
3✔
4041
          }
4042
          // Try as a long
4043
          return ValueTypeDouble.ValueDouble.of((double) Long.decode(value.getRawValue()));
7✔
4044
        } catch (NumberFormatException e2) {
1✔
4045
            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4046
                    Component.translatable(ValueTypes.DOUBLE.getTranslationKey())));
3✔
4047
        }
4048
      }
4049
    }));
4050

4051
    /**
4052
     * Integer Parse operator which takes a string of a form Integer.decode() can consume.
4053
     */
4054
    public static final IOperator PARSE_INTEGER = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.INTEGER, v -> {
8✔
4055
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4056
      try{
4057
        return ValueTypeInteger.ValueInteger.of(Integer.decode(value.getRawValue()));
6✔
4058
      } catch (NumberFormatException e) {
1✔
4059
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4060
                  Component.translatable(ValueTypes.INTEGER.getTranslationKey())));
3✔
4061
      }
4062
    }));
4063

4064
    /**
4065
     * Long Parse operator which takes a string of a form Long.decode() can consume.
4066
     */
4067
    public static final IOperator PARSE_LONG = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.LONG, v -> {
8✔
4068
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4069
      try {
4070
        return ValueTypeLong.ValueLong.of(Long.decode(value.getRawValue()));
6✔
4071
      } catch (NumberFormatException e) {
1✔
4072
          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4073
                  Component.translatable(ValueTypes.LONG.getTranslationKey())));
3✔
4074
      }
4075
    }));
4076

4077
    /**
4078
     * NBT Parse operator which takes a string of a form ValueTypeNbt().deserialize() can consume.
4079
     */
4080
    public static final IOperator PARSE_NBT = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.NBT, v -> {
8✔
4081
      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);
6✔
4082
      try {
4083
        return ValueTypeNbt.ValueNbt.of(TagParser.parseTag(value.getRawValue()));
5✔
4084
      } catch (CommandSyntaxException e) {
1✔
4085
        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),
16✔
4086
                Component.translatable(ValueTypes.NBT.getTranslationKey())));
3✔
4087
      }
4088
    }));
4089

4090
    /**
4091
     * ----------------------------------- GENERAL OPERATORS -----------------------------------
4092
     */
4093

4094
    /**
4095
     * Choice operator with one boolean input, two any inputs and one output any.
4096
     */
4097
    public static final GeneralOperator GENERAL_CHOICE = REGISTRY.register(new GeneralChoiceOperator("?", "choice", "choice"));
10✔
4098

4099
    /**
4100
     * Identity operator with one any input and one any output
4101
     */
4102
    public static final GeneralOperator GENERAL_IDENTITY = REGISTRY.register(new GeneralIdentityOperator("id", "identity", "identity"));
10✔
4103

4104
    /**
4105
     * Constant operator with two any inputs and one any output
4106
     */
4107
    public static final GeneralOperator GENERAL_CONSTANT = REGISTRY.register(new GeneralConstantOperator("K", "constant", "constant"));
11✔
4108

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