diff --git a/src/main/java/carpet/helpers/FeatureGenerator.java b/src/main/java/carpet/helpers/FeatureGenerator.java index 8b02f469..6c5b8e9e 100644 --- a/src/main/java/carpet/helpers/FeatureGenerator.java +++ b/src/main/java/carpet/helpers/FeatureGenerator.java @@ -148,11 +148,11 @@ public class FeatureGenerator { structureToFeature.put("Monument", Collections.singletonList("monument")); structureToFeature.put("EndCity", Collections.singletonList("end_city")); - structureToFeature.put("Ocean_Ruin", Arrays.asList("oceanruin", "oceanruin_warm", "oceanruin_small", "oceanruin_warm_small", "oceanruin_tall", "oceanruin_warm_tall")); + structureToFeature.put("Ocean_Ruin", Arrays.asList("ocean_ruin", "ocean_ruin_warm", "ocean_ruin_small", "ocean_ruin_warm_small", "ocean_ruin_tall", "ocean_ruin_warm_tall")); structureToFeature.put("Village", Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy")); structureToFeature.put("Mansion", Collections.singletonList("mansion")); structureToFeature.put("Buried_Treasure", Collections.singletonList("treasure")); - structureToFeature.put("Swamp_Hut", Collections.singletonList("witchhut")); + structureToFeature.put("Swamp_Hut", Collections.singletonList("witch_hut")); structureToFeature.put("Stronghold", Collections.singletonList("stronghold")); structureToFeature.put("Desert_Pyramid", Collections.singletonList("desert_temple")); structureToFeature.put("Jungle_Pyramid", Collections.singletonList("jungle_temple")); @@ -175,20 +175,20 @@ public class FeatureGenerator put("igloo", ((StructureFeatureInterface)Feature.IGLOO)::gridAnywhere); put("shipwreck", gridCustomStructure(Feature.SHIPWRECK, new ShipwreckFeatureConfig(true), Biomes.PLAINS)); put("shipwreck2", gridCustomStructure(Feature.SHIPWRECK, new ShipwreckFeatureConfig(false), Biomes.PLAINS)); - put("witchhut", ((StructureFeatureInterface)Feature.SWAMP_HUT)::gridAnywhere); + put("witch_hut", ((StructureFeatureInterface)Feature.SWAMP_HUT)::gridAnywhere); put("stronghold", ((StructureFeatureInterface)Feature.STRONGHOLD)::gridAnywhere); - put("oceanruin_small", gridCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_small", gridCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 0.0F, 0.5F), Biomes.PLAINS)); - put("oceanruin_warm_small", gridCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_warm_small", gridCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 0.0F, 0.5F), Biomes.PLAINS)); - put("oceanruin_tall", gridCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_tall", gridCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 1.0F, 0.0F), Biomes.PLAINS)); - put("oceanruin_warm_tall", gridCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_warm_tall", gridCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 1.0F, 0.0F), Biomes.PLAINS)); - put("oceanruin", gridCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin", gridCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 1.0F, 1.0F), Biomes.PLAINS)); - put("oceanruin_warm", gridCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_warm", gridCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 1.0F, 1.0F), Biomes.PLAINS)); put("treasure", ((StructureFeatureInterface)Feature.BURIED_TREASURE)::gridAnywhere); @@ -291,20 +291,20 @@ public class FeatureGenerator put("igloo", ((StructureFeatureInterface)Feature.IGLOO)::plopAnywhere); put("shipwreck", spawnCustomStructure(Feature.SHIPWRECK, new ShipwreckFeatureConfig(true), Biomes.PLAINS)); put("shipwreck2", spawnCustomStructure(Feature.SHIPWRECK, new ShipwreckFeatureConfig(false), Biomes.PLAINS)); - put("witchhut", ((StructureFeatureInterface)Feature.SWAMP_HUT)::plopAnywhere); + put("witch_hut", ((StructureFeatureInterface)Feature.SWAMP_HUT)::plopAnywhere); put("stronghold", ((StructureFeatureInterface)Feature.STRONGHOLD)::plopAnywhere); - put("oceanruin_small", spawnCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_small", spawnCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 0.0F, 0.5F), Biomes.PLAINS)); - put("oceanruin_warm_small", spawnCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_warm_small", spawnCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 0.0F, 0.5F), Biomes.PLAINS)); - put("oceanruin_tall", spawnCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_tall", spawnCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 1.0F, 0.0F), Biomes.PLAINS)); - put("oceanruin_warm_tall", spawnCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_warm_tall", spawnCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 1.0F, 0.0F), Biomes.PLAINS)); - put("oceanruin", spawnCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin", spawnCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 1.0F, 1.0F), Biomes.PLAINS)); - put("oceanruin_warm", spawnCustomStructure(Feature.OCEAN_RUIN, + put("ocean_ruin_warm", spawnCustomStructure(Feature.OCEAN_RUIN, new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 1.0F, 1.0F), Biomes.PLAINS)); put("treasure", ((StructureFeatureInterface)Feature.BURIED_TREASURE)::plopAnywhere); diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index f97f3f96..9783a422 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -103,6 +103,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.loot.context.LootContext; import net.minecraft.loot.context.LootContextParameters; import org.apache.commons.lang3.tuple.Pair; @@ -608,6 +609,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 @@ -624,7 +627,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.
seed()Returns current world seed.
*current_dimension()Returns current dimension that the script runs in.
*in_dimension(smth, expr)igloo: Iglooshipwreck: Shipwreck, version1?shipwreck2: Shipwreck, version2?witchhutoceanruin, oceanruin_small, oceanruin_tall: Stone variants of ocean ruins.oceanruin_warm, oceanruin_warm_small, oceanruin_warm_tall: Sandstone variants of ocean ruins.witch_hutocean_ruin, ocean_ruin_small, ocean_ruin_tall: Stone variants of ocean ruins.ocean_ruin_warm, ocean_ruin_warm_small, ocean_ruin_warm_tall: Sandstone variants of ocean ruins.treasure: A treasure chest. Yes, its a whole structure.pillager_outpost: A pillager outpost.mineshaft: A mineshaft.break and continue statements, the supplied value can be used as a boolean check instead.
*
* filter(range(100), !(_%5), _*_>1000) => [0, 5, 10, 15, 20, 25, 30]
- * map(filter(entities_list('all'),_=='Witch'), query(_,'pos') ) => [[1082.5, 57, 1243.5]]
+ * map(filter(entity_list('*'),_=='Witch'), query(_,'pos') ) => [[1082.5, 57, 1243.5]]
*
*
* first(list,expr(_,_i))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
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
diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java
index c79ca8a5..7bd65d1a 100644
--- a/src/main/java/carpet/script/value/MapValue.java
+++ b/src/main/java/carpet/script/value/MapValue.java
@@ -124,7 +124,7 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
@Override
public void append(Value v)
{
- put(v);
+ map.put(v, Value.NULL);
}
@Override
diff --git a/src/main/resources/assets/carpet/scripts/structures.sc b/src/main/resources/assets/carpet/scripts/structures.sc
new file mode 100644
index 00000000..a6b46b07
--- /dev/null
+++ b/src/main/resources/assets/carpet/scripts/structures.sc
@@ -0,0 +1,78 @@
+import('math', '_euclidean_sq', '_euclidean');
+
+global_current_structures = m();
+global_overlay_active = false;
+global_range = 3;
+global_max_pieces = 4;
+
+__command() -> '';
+
+monument() -> __toggle('monument');
+fortress() -> __toggle('fortress');
+mansion() -> __toggle('mansion');
+jungle_temple() -> __toggle('jungle_temple');
+desert_temple() -> __toggle('desert_temple');
+end_city() -> __toggle('end_city');
+igloo() -> __toggle('igloo');
+shipwreck() -> __toggle('shipwreck');
+witch_hut() -> __toggle('witch_hut');
+stronghold() -> __toggle('stronghold');
+ocean_ruin() -> __toggle('ocean_ruin');
+treasure() -> __toggle('treasure');
+pillager_outpost() -> __toggle('pillager_outpost');
+mineshaft() -> __toggle('mineshaft');
+village() -> __toggle('village');
+
+__toggle(structure) ->
+(
+ p = player()
+ if( has(global_current_structures:structure),
+ delete(global_current_structures:structure);
+ 'disabled '+structure+' overlays';
+ ,
+ global_current_structures:structure = null;
+ if (!global_overlay_active, __structure_renderer(p~'name'));
+ 'enabled '+structure+' overlays';
+ )
+);
+
+__structure_renderer(player_name) ->
+(
+ if (length(global_current_structures)==0, global_overlay_active = false; return());
+ p = player(player_name);
+ if (!p, global_overlay_active = false; return());
+ global_overlay_active = true;
+ in_dimension(p,
+ ppos = pos(p);
+ starts = m();
+ for(range(-global_range,global_range), cx =_;
+ for (range(-global_range,global_range), cz = _;
+ ref_pos = ppos + l(16*cx,0,16*cz);
+ for(filter(structure_references(ref_pos), has(global_current_structures:_)),
+ name = _;
+ for(structure_references(ref_pos, name),
+ starts += l(_, name);
+ )
+ )
+ )
+ );
+ for (starts, l(position, name) = _;
+ structure_data = structures(position):name;
+ if (!structure_data, continue()); // messed up references - shouldn't happen
+ structure_pieces = structures(position, name);
+ if (!structure_pieces, continue()); // messed up references - shouldn't happen
+ l(from, to) = structure_data;
+ total_size = _euclidean(from, to);
+ density = max(10, total_size/10);
+ particle_rect('dust 0 1 1 2', from, to+1, density);
+ structure_pieces = slice(sort_key(structure_pieces, _euclidean_sq((_:2+_:3)/2,ppos)), 0, global_max_pieces);
+ for (structure_pieces, l(piece, direction, from, to) = _;
+ factor = 1-_i/global_max_pieces/2;
+ total_size = _euclidean(from, to);
+ density = max(_i+1, total_size/10);
+ particle_rect(str('dust %.2f %.2f 1 1',factor, factor), from, to+1, density);
+ )
+ )
+ );
+ schedule(3, '__structure_renderer', player_name);
+);
\ No newline at end of file