diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 8ecb1af0..0ebc7a59 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -52,7 +52,9 @@ to the `regular` group. * `monster`, `creature`, `ambient`, `water_creature`, `water_ambient`, `misc` - another categorization of living entities based on their spawn group. Negative descriptor resolves to all living types that don't belong to that category. -* Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: +* All entity tags including those provided with datapacks. Built-in entity tags include: `skeletons`, `raiders`, +`beehive_inhabitors` (bee, duh), `arrows` and `impact_projectiles`. +* Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: `area_effect_cloud`, `armor_stand`, `arrow`, `bat`, `bee`, `blaze`, `boat`, `cat`, `cave_spider`, `chest_minecart`, `chicken`, `cod`, `command_block_minecart`, `cow`, `creeper`, `dolphin`, `donkey`, `dragon_fireball`, `drowned`, `egg`, `elder_guardian`, `end_crystal`, `ender_dragon`, `ender_pearl`, `enderman`, `endermite`, `evoker`, @@ -209,13 +211,21 @@ List of entities riding the entity. Entity that `e` rides. -### `query(e, 'tags')` +### `query(e, 'scoreboard_tags')`, `query(e, 'tags')`(deprecated) -List of entity's tags. +List of entity's scoreboard tags. -### `query(e, 'has_tag',tag)` +### `query(e, 'has_scoreboard_tag',tag)`, `query(e, 'has_tag',tag)`(deprecated) -Boolean, true if the entity is marked with `tag`. +Boolean, true if the entity is marked with a `tag` scoreboad tag. + +### `query(e, 'entity_tags')` + +List of entity tags assigned to the type this entity represents. + +### `query(e, 'has_entity_tag', tag)` + +Returns `true` if the entity matches that entity tag, `false` if it doesn't, and `null` if the tag is not valid. ### `query(e, 'is_burning')` diff --git a/docs/scarpet/api/Inventories.md b/docs/scarpet/api/Inventories.md index 1ecc692b..ebe2ed7b 100644 --- a/docs/scarpet/api/Inventories.md +++ b/docs/scarpet/api/Inventories.md @@ -28,6 +28,14 @@ an inventory, all API functions typically do nothing and return null. Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item. +### item_list(tag?) + +With no arguments, returns a list of all items in the game. With an item tag provided, list items matching the tag, or `null` if tag is not valid. + +### item_tags(item, tag?) + +Returns list of tags the item belongs to, or, if tag is provided, `true` if an item maches the tag, `false` if it doesn't and `null` if that's not a valid tag + ### `stack_limit(item)` Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets), diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index e7f1f8f6..397c1760 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -52,6 +52,8 @@ inventory_remove inventory_set inventory_size item_category +item_list +item_tags last_tick_times light liquid diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 46f3a139..759e69d0 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -68,7 +68,7 @@ player spawn entity_id entity_list entity_area entity_selector query modify entity_event entity_load_handler entity_types - stack_limit item_category recipe_data crafting_remaining_item + item_list item_tags stack_limit item_category recipe_data crafting_remaining_item inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item diff --git a/src/main/java/carpet/script/api/Entities.java b/src/main/java/carpet/script/api/Entities.java index c5a137a9..ccf35938 100644 --- a/src/main/java/carpet/script/api/Entities.java +++ b/src/main/java/carpet/script/api/Entities.java @@ -23,6 +23,7 @@ import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.CompoundTag; import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; @@ -34,6 +35,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -183,8 +185,9 @@ public class Entities { expression.addLazyFunction("entity_list", 1, (c, t, lv) -> { String who = lv.get(0).evalValue(c).getString(); - EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who); - List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate); + ServerCommandSource source = ((CarpetContext)c).s; + EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, source.getMinecraftServer()); + List entityList = source.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate); Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList())); return (_c, _t ) -> retval; }); @@ -212,8 +215,8 @@ public class Entities { throw new InternalExpressionException("Range of 'entity_area' cannot come from a block argument"); Vec3d range = rangeLocator.vec; Box area = centerBox.expand(range.x, range.y, range.z); - EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who); - List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate); + EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, cc.s.getMinecraftServer()); + List entityList = cc.s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate); Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList())); return (_c, _t ) -> retval; }); @@ -238,7 +241,9 @@ public class Entities { Value v = lv.get(0).evalValue(c); if (!(v instanceof EntityValue)) throw new InternalExpressionException("First argument to query should be an entity"); - String what = lv.get(1).evalValue(c).getString(); + String what = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT); + if (what.equals("tags")) + c.host.issueDeprecation("'tags' for entity querying"); Value retval; if (lv.size()==2) retval = ((EntityValue) v).get(what, null); @@ -269,11 +274,13 @@ public class Entities { return (cc, tt) -> v; }); - expression.addFunction("entity_types", (lv) -> + expression.addLazyFunction("entity_types", -1, (c, t, lv) -> { + if (lv.size() > 1) throw new InternalExpressionException("'entity_types' requires one or no arguments"); - String desc = (lv.size() == 1)?lv.get(0).getString():"*"; - return EntityValue.getEntityDescriptor(desc).listValue; + String desc = (lv.size() == 1)?lv.get(0).evalValue(c).getString():"*"; + Value ret = EntityValue.getEntityDescriptor(desc, ((CarpetContext) c).s.getMinecraftServer()).listValue; + return (_c, _t) -> ret; }); expression.addLazyFunction("entity_load_handler", -1, (c, t, lv) -> @@ -285,7 +292,7 @@ public class Entities { ? ((ListValue) entityValue).getItems().stream().map(Value::getString).collect(Collectors.toList()) : Collections.singletonList(entityValue.getString()); Set> types = new HashSet<>(); - descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s).typeList)); + descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s, ((CarpetContext) c).s.getMinecraftServer()).typeList)); FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 1, true, false); CarpetEventServer events = ((CarpetScriptHost)c.host).getScriptServer().events; if (funArg.function == null) diff --git a/src/main/java/carpet/script/api/Inventories.java b/src/main/java/carpet/script/api/Inventories.java index b5f6bfb4..fb4983ae 100644 --- a/src/main/java/carpet/script/api/Inventories.java +++ b/src/main/java/carpet/script/api/Inventories.java @@ -12,6 +12,7 @@ import carpet.script.value.NullValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; +import carpet.script.value.ValueConversions; import com.google.common.collect.Sets; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.command.argument.ItemStackArgument; @@ -32,6 +33,7 @@ import net.minecraft.recipe.ShapedRecipe; import net.minecraft.recipe.ShapelessRecipe; import net.minecraft.recipe.SpecialCraftingRecipe; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.tag.TagManager; import net.minecraft.util.Identifier; import net.minecraft.util.InvalidIdentifierException; import net.minecraft.util.math.Vec3d; @@ -40,6 +42,7 @@ import net.minecraft.util.registry.Registry; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; public class Inventories { public static void apply(Expression expression) @@ -60,6 +63,43 @@ public class Inventories { return (_c, _t) -> res; }); + expression.addLazyFunction("item_list", -1, (c, t, lv) -> + { + if (lv.size() == 0) + { + Value res = ListValue.wrap(Registry.ITEM.getIds().stream().map(ValueConversions::of).collect(Collectors.toList())); + return (cc, tt) -> res; + } + CarpetContext cc = (CarpetContext)c; + TagManager tagManager = cc.s.getMinecraftServer().getTagManager(); + String tag = lv.get(0).evalValue(c).getString(); + net.minecraft.tag.Tag itemTag = tagManager.getItems().getTag(new Identifier(tag)); + if (itemTag == null) return LazyValue.NULL; + Value ret = ListValue.wrap(itemTag.values().stream().map(b -> ValueConversions.of(Registry.ITEM.getId(b))).collect(Collectors.toList())); + return (_c, _t) -> ret; + }); + + expression.addLazyFunction("item_tags", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext)c; + TagManager tagManager = cc.s.getMinecraftServer().getTagManager(); + if (lv.size() == 0) + { + Value ret = ListValue.wrap(tagManager.getItems().getTagIds().stream().map(ValueConversions::of).collect(Collectors.toList())); + return (_c, _t) -> ret; + } + Item item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString()).getItem(); + if (lv.size() == 1) + { + Value ret = ListValue.wrap(tagManager.getItems().getTagsFor(item).stream().map(ValueConversions::of).collect(Collectors.toList())); + return (_c, _t) -> ret; + } + String tag = lv.get(1).evalValue(c).getString(); + net.minecraft.tag.Tag itemTag = tagManager.getItems().getTag(new Identifier(tag)); + if (itemTag == null) return LazyValue.NULL; + return item.isIn(itemTag)?LazyValue.TRUE:LazyValue.FALSE; + }); + expression.addLazyFunction("recipe_data", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index c30ff0aa..89535a7c 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -223,10 +223,28 @@ public class EntityValue extends Value return entity.hashCode(); } - public static EntityClassDescriptor getEntityDescriptor(String who) + public static EntityClassDescriptor getEntityDescriptor(String who, MinecraftServer server) { EntityClassDescriptor eDesc = EntityClassDescriptor.byName.get(who); - if (eDesc == null) throw new InternalExpressionException(who+" is not a valid entity descriptor"); + if (eDesc == null) + { + boolean positive = true; + if (who.startsWith("!")) + { + positive = false; + who = who.substring(1); + } + net.minecraft.tag.Tag> eTag = server.getTagManager().getEntityTypes().getTag(new Identifier(who)); + if (eTag == null) throw new InternalExpressionException(who+" is not a valid entity descriptor"); + if (positive) + { + return new EntityClassDescriptor(null, e -> eTag.contains(e.getType()) && e.isAlive(), eTag.values().stream()); + } + else + { + return new EntityClassDescriptor(null, e -> !eTag.contains(e.getType()) && e.isAlive(), Registry.ENTITY_TYPE.stream().filter(et -> !eTag.contains(et))); + } + } return eDesc; //TODO add more here like search by tags, or type //if (who.startsWith('tag:')) @@ -401,8 +419,21 @@ public class EntityValue extends Value put("passengers", (e, a) -> ListValue.wrap(e.getPassengerList().stream().map(EntityValue::new).collect(Collectors.toList()))); put("mount", (e, a) -> (e.getVehicle()!=null)?new EntityValue(e.getVehicle()):Value.NULL); put("unmountable", (e, a) -> new NumericValue(((EntityInterface)e).isPermanentVehicle())); + // deprecated put("tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList()))); + + put("scoreboard_tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList()))); + put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTagsFor(e.getType()).stream().map(ValueConversions::of).collect(Collectors.toList()))); + // deprecated put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString()))); + + put("has_scoreboard_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString()))); + put("has_entity_tag", (e, a) -> { + net.minecraft.tag.Tag> tag = e.getServer().getTagManager().getEntityTypes().getTag(new Identifier(a.getString())); + if (tag == null) return Value.NULL; + return new NumericValue(e.getType().isIn(tag)); + }); + put("yaw", (e, a)-> new NumericValue(e.yaw)); put("head_yaw", (e, a)-> { if (e instanceof LivingEntity) diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 938acd20..877300dc 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -267,7 +267,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } } - private static Map itemCache = new HashMap<>(); + private static final Map itemCache = new HashMap<>(); public static ItemStackArgument parseItem(String itemString) {