diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 7ec510d4..24ecf6b7 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -102,6 +102,7 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkStatus; import net.minecraft.world.chunk.WorldChunk; import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.gen.ChunkRandom; import net.minecraft.world.loot.context.LootContext; import net.minecraft.world.loot.context.LootContextParameters; import org.apache.commons.lang3.tuple.Pair; @@ -607,6 +608,8 @@ public class CarpetExpression *

Numeric function, indicating hardness of a block.

*

blast_resistance(pos)

*

Numeric function, indicating blast_resistance of a block.

+ *

in_slime_chunk(pos)

+ *

Boolean indicating if the given block position is in a slime chunk.

*

top(type, pos)

*

Returns the Y value of the topmost block at given x, z coords (y value of a block is not important), according to the @@ -623,7 +626,7 @@ public class CarpetExpression * top('ocean_floor', x, y, z) => 41 * *

loaded(pos)

- *

Boolean function, true if the block is accessible forthe game mechanics. + *

Boolean function, true if the block is accessible for the game mechanics. * Normally scarpet doesn't check if operates on * loaded area - the game will automatically load missing blocks. We see this as advantage. * Vanilla fill/clone commands only check the specified corners for loadness.

@@ -1018,6 +1021,19 @@ public class CarpetExpression this.expr.addLazyFunction("blast_resistance", -1, (c, t, lv) -> genericStateTest(c, "blast_resistance", lv, (s, p, w) -> new NumericValue(s.getBlock().getBlastResistance()))); + this.expr.addLazyFunction("in_slime_chunk", -1, (c, t, lv) -> + { + BlockPos pos = BlockValue.fromParams((CarpetContext)c, lv, 0).block.getPos(); + ChunkPos chunkPos = new ChunkPos(pos); + Value ret = new NumericValue(ChunkRandom.create( + chunkPos.x, chunkPos.z, + ((CarpetContext)c).s.getWorld().getSeed(), + 987234911L + ).nextInt(10) == 0); + return (_c, _t) -> ret; + }); + + this.expr.addLazyFunction("top", -1, (c, t, lv) -> { String type = lv.get(0).evalValue(c).getString().toLowerCase(Locale.ROOT); @@ -2957,6 +2973,8 @@ public class CarpetExpression * loop(1000,game_tick()) // runs the game as fast as it can for 1000 ticks * loop(1000,game_tick(100)) // runs the game twice as slow for 1000 ticks * + *

seed()

+ *

Returns current world seed.

*

current_dimension()

*

Returns current dimension that the script runs in.

*

in_dimension(smth, expr)

@@ -3399,6 +3417,12 @@ public class CarpetExpression return (cc, tt) -> Value.TRUE; }); + this.expr.addLazyFunction("seed", -1, (c, t, lv) -> { + ServerCommandSource s = ((CarpetContext)c).s; + Value ret = new NumericValue(s.getWorld().getSeed()); + return (cc, tt) -> ret; + }); + this.expr.addLazyFunction("current_dimension", 0, (c, t, lv) -> { ServerCommandSource s = ((CarpetContext)c).s; Value retval = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION.getId(s.getWorld().dimension.getType()))); diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index d5ad5457..4e7a63d4 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -2720,12 +2720,15 @@ public class Expression * length('foo') => 3 * * - *

rand(expr)

+ *

rand(expr), rand(expr, seed)

*

returns a random number from 0.0 * (inclusive) to expr (exclusive). * In boolean context (in conditions, boolean functions, or bool), returns * false if the randomly selected value is less than 1. This means that rand(2) returns true half - * of the time and rand(5) returns true for 80% (4/5) of the time

+ * of the time and rand(5) returns true for 80% (4/5) of the time. If seed is not provided, uses a random seed. + * If seed is provided, each consecutive call to rand() will act like 'next' call to the same random object. + * Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number (per app), then your + * random sequence will revert to the beginning.

*
      * map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
      * map(range(10), bool(rand(2))) => [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
@@ -2982,7 +2985,12 @@ public class Expression
         addUnaryFunction("type", v -> new StringValue(v.getTypeString()));
 
         addUnaryFunction("length", v -> new NumericValue(v.length()));
-        addLazyFunction("rand", 1, (c, t, lv) -> {
+        addLazyFunction("rand", -1, (c, t, lv) -> {
+            int argsize = lv.size();
+            Random randomizer = Expression.randomizer;
+            if (argsize != 1 && argsize != 2)
+                throw new InternalExpressionException("'rand' takes one (range) or two arguments (range and seed)");
+            if (argsize == 2) randomizer = c.host.getRandom(NumericValue.asNumber(lv.get(1).evalValue(c)).getLong());
             Value argument = lv.get(0).evalValue(c);
             if (argument instanceof ListValue)
             {
diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java
index ad2ba58f..63ef1dc2 100644
--- a/src/main/java/carpet/script/ScriptHost.java
+++ b/src/main/java/carpet/script/ScriptHost.java
@@ -4,11 +4,13 @@ import carpet.script.bundled.Module;
 import carpet.script.exception.InternalExpressionException;
 import carpet.script.value.FunctionValue;
 import carpet.script.value.Value;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
@@ -16,6 +18,14 @@ import java.util.stream.Stream;
 public abstract class ScriptHost
 {
     public static Map systemGlobals = new ConcurrentHashMap<>();
+    private static Map randomizers = new Long2ObjectOpenHashMap<>();
+
+    public Random getRandom(long aLong)
+    {
+        if (randomizers.size() > 1024)
+            randomizers.clear();
+        return randomizers.computeIfAbsent(aLong, Random::new);
+    }
 
 
     public static class ModuleData