diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 8e80f1c2..e959efd9 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -29,21 +29,46 @@ but obviously using UUIDs takes more memory and compute. Returns global lists of entities in the current dimension of a specified type. Currently the following selectors are available: -* `*`: all entites -* `living` -* `items` -* `players` -* `!players` +* `*`: all entities, even `!valid` +* `valid` - all entities that are not dead (health > 0). All main categories below also return only +entities in the `valid` category. +* `living` - all entities that resemble a creature of any sort +* `projectile` - all entities that are not living that can be throw or projected +* `undead`, `arthropod`, `aquatic`, `regular`, `illager` - all entities that belong to any of these groups. Every +living entity belongs to one and only one of these. +* `monster`, `creature`, `ambient`, `water_creature`, `water_ambient`, `misc` - another categorization of +living entites based on their spawn group. +* 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`, `evoker_fangs`, +`experience_bottle`, `experience_orb`, `eye_of_ender`, `falling_block`, `fireball`, `firework_rocket`, +`fishing_bobber`, `fox`, `furnace_minecart`, `ghast`, `giant`, `guardian`, `hopper_minecart`, `horse`, `husk`, +`illusioner`, `iron_golem`, `item`, `item_frame`, `leash_knot`, `lightning_bolt`, `llama`, `llama_spit`, +`magma_cube`, `minecart`, `mooshroom`, `mule`, `ocelot`, `painting`, `panda`, `parrot`, `phantom`, `pig`, `pillager`, +`player`, `polar_bear`, `potion`, `pufferfish`, `rabbit`, `ravager`, `salmon`, `sheep`, `shulker`, `shulker_bullet`, +`silverfish`, `skeleton`, `skeleton_horse`, `slime`, `small_fireball`, `snow_golem`, `snowball`, `spawner_minecart`, +`spectral_arrow`, `spider`, `squid`, `stray`, `tnt`, `tnt_minecart`, `trader_llama`, `trident`, `tropical_fish`, +`turtle`, `vex`, `villager`, `vindicator`, `wandering_trader`, `witch`, `wither`, `wither_skeleton`, `wither_skull`, +`wolf`, `zombie`, `zombie_horse`, `zombie_pigman`, `zombie_villager` + +All categories can be preceeded with `'!'` which will fetch all entites that are valid (health > 0) but not belonging to +that group. Calls to `entity_list` always fetch entites from the current world that the script executes. ### `entity_area(type, cx, cy, cz, dx, dy, dz)` Returns entities of a specified type in an area centered on `cx, cy, cz` and at most `dx, dy, dz` blocks away from the center point. Uses the same selectors as `entities_list`. +entity_area is simpler than `entity_selector` and runs about 20% faster, but is limited to predefined selectors and +cuboid search area. + ### `entity_selector(selector)` Returns entities satisifying given vanilla entity selector. Most complex among all the methods of selecting entities, -but the most capable. Selectors are cached so it should be as fast as other methods of selecting entities. +but the most capable. Selectors are cached so it should be as fast as other methods of selecting entities. Unlike other +entities fetching / filtering method, this one doesn't guarantee to return entities from current dimension, since +selectors can return any loaded entity in the world. ### `spawn(name, pos, nbt?)` diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 5c87cfb1..5eef1f07 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -12,10 +12,14 @@ import carpet.script.CarpetContext; import carpet.script.EntityEventsGroup; import carpet.script.argument.Vector3Argument; import carpet.script.exception.InternalExpressionException; +import com.google.common.collect.ImmutableMap; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.entity.EntityCategory; +import net.minecraft.entity.EntityGroup; import net.minecraft.entity.ai.brain.MemoryModuleType; import net.minecraft.entity.ai.pathing.Path; +import net.minecraft.entity.projectile.Projectile; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket; @@ -191,14 +195,47 @@ public class EntityValue extends Value return res; //TODO add more here like search by tags, or type //if (who.startsWith('tag:')) } + + private static Map entityGroupMap = ImmutableMap.of( + "undead", EntityGroup.UNDEAD, + "arthropod", EntityGroup.ARTHROPOD, + "aquatic", EntityGroup.AQUATIC, + "regular", EntityGroup.DEFAULT, + "illager", EntityGroup.ILLAGER + ); + private static final Map, Predicate>> entityPredicates = new HashMap, Predicate>>() {{ - put("*", Pair.of(null, EntityPredicates.VALID_ENTITY)); + put("*", Pair.of(null, e -> true)); + put("valid", Pair.of(null, EntityPredicates.VALID_ENTITY)); + put("!valid", Pair.of(null, e -> !e.isAlive())); + put("living", Pair.of(null, (e) -> e instanceof LivingEntity && e.isAlive())); - put("items", Pair.of(EntityType.ITEM, EntityPredicates.VALID_ENTITY)); - put("players", Pair.of(EntityType.PLAYER, EntityPredicates.VALID_ENTITY)); - put("!players", Pair.of(null, (e) -> !(e instanceof PlayerEntity) )); + put("!living", Pair.of(null, (e) -> !(e instanceof LivingEntity) && e.isAlive())); + + put("projectile", Pair.of(null, (e) -> e instanceof Projectile && e.isAlive())); + put("!projectile", Pair.of(null, (e) -> !(e instanceof Projectile) && e.isAlive())); + + for (String groupStr : entityGroupMap.keySet()) + { + EntityGroup group = entityGroupMap.get(groupStr); + put( groupStr, Pair.of(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() == group && e.isAlive()))); + put("!"+groupStr, Pair.of(null, e -> ((e instanceof LivingEntity) && ((LivingEntity) e).getGroup() != group && e.isAlive()))); + } + for (Identifier typeId : Registry.ENTITY_TYPE.getIds()) + { + EntityType type = Registry.ENTITY_TYPE.get(typeId); + String mobType = ValueConversions.simplify(typeId); + put( mobType, Pair.of(type, EntityPredicates.VALID_ENTITY)); + put("!"+mobType, Pair.of(null, (e) -> e.getType() != type && e.isAlive())); + } + for (EntityCategory catId : EntityCategory.values()) + { + String catStr = catId.getName(); + put( catStr, Pair.of(null, e -> (e.getType().getCategory() == catId) && e.isAlive())); + put("!"+catStr, Pair.of(null, e -> (e.getType().getCategory() != catId) && e.isAlive())); + } }}; public Value get(String what, Value arg) { diff --git a/src/main/java/carpet/script/value/ValueConversions.java b/src/main/java/carpet/script/value/ValueConversions.java index eaa16379..d8471ff4 100644 --- a/src/main/java/carpet/script/value/ValueConversions.java +++ b/src/main/java/carpet/script/value/ValueConversions.java @@ -45,6 +45,16 @@ public class ValueConversions return new StringValue(id.getPath()); return new StringValue(id.toString()); } + + public static String simplify(Identifier id) + { + if (id == null) // should be Value.NULL + return ""; + if (id.getNamespace().equals("minecraft")) + return id.getPath(); + return id.toString(); + } + public static Value ofGlobalPos(GlobalPos pos) { return ListValue.of(