diff --git a/common/src/main/java/org/figuramc/figura/lua/api/ClientAPI.java b/common/src/main/java/org/figuramc/figura/lua/api/ClientAPI.java index a4f91665d..f1618af08 100644 --- a/common/src/main/java/org/figuramc/figura/lua/api/ClientAPI.java +++ b/common/src/main/java/org/figuramc/figura/lua/api/ClientAPI.java @@ -11,7 +11,9 @@ import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.server.IntegratedServer; +import net.minecraft.core.Registry; import net.minecraft.core.UUIDUtil; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.repository.Pack; @@ -43,6 +45,7 @@ import java.text.SimpleDateFormat; import java.util.*; import java.util.function.Supplier; +import java.util.stream.Collectors; @LuaWhitelist @LuaTypeDoc( @@ -698,6 +701,25 @@ public static String getTranslatedString(@LuaNotNil String text, LuaValue args) return component.getString(); } + @LuaWhitelist + @LuaMethodDoc( + overloads = { + @LuaMethodOverload(argumentTypes = String.class, argumentNames = "registryName"), + }, + value = "client.get_registry" + ) + public static List getRegistry(@LuaNotNil String registryName) { + Registry registry = BuiltInRegistries.REGISTRY.get(new ResourceLocation(registryName)); + + if (registry != null) { + return registry.keySet().stream() + .map(ResourceLocation::toString) + .collect(Collectors.toList()); + } else { + throw new LuaError("Registry " + registryName + " does not exist"); + } + } + @LuaWhitelist @LuaMethodDoc( overloads = { diff --git a/common/src/main/java/org/figuramc/figura/lua/api/world/WorldAPI.java b/common/src/main/java/org/figuramc/figura/lua/api/world/WorldAPI.java index df60494a6..a1101099c 100644 --- a/common/src/main/java/org/figuramc/figura/lua/api/world/WorldAPI.java +++ b/common/src/main/java/org/figuramc/figura/lua/api/world/WorldAPI.java @@ -13,6 +13,7 @@ import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.Heightmap; import org.figuramc.figura.avatar.Avatar; import org.figuramc.figura.avatar.AvatarManager; import org.figuramc.figura.lua.LuaNotNil; @@ -23,6 +24,7 @@ import org.figuramc.figura.lua.docs.LuaMethodDoc; import org.figuramc.figura.lua.docs.LuaMethodOverload; import org.figuramc.figura.lua.docs.LuaTypeDoc; +import org.figuramc.figura.math.vector.FiguraVec2; import org.figuramc.figura.math.vector.FiguraVec3; import org.figuramc.figura.utils.EntityUtils; import org.figuramc.figura.utils.LuaUtils; @@ -329,6 +331,38 @@ public static Integer getBlockLightLevel(Object x, Double y, Double z) { return world.getBrightness(LightLayer.BLOCK, blockPos); } + @LuaWhitelist + @LuaMethodDoc( + overloads = { + @LuaMethodOverload( + argumentTypes = {FiguraVec2.class, String.class}, + argumentNames = {"pos", "heightmap"} + ), + @LuaMethodOverload( + argumentTypes = {Double.class, Double.class, String.class}, + argumentNames = {"x", "z", "heightmap"} + ) + }, + value = "world.get_height" + ) + public static Integer getHeight(Object x, Double z, String heightmap) { + FiguraVec2 pos = LuaUtils.parseVec2("getHeight", x, z); + Level world = getCurrentWorld(); + + BlockPos blockPos = new BlockPos((int) pos.x(), 0, (int) pos.y()); + if (world.getChunkAt(blockPos) == null) + return null; + + Heightmap.Types heightmapType; + try { + heightmapType = heightmap != null ? Heightmap.Types.valueOf(heightmap.toUpperCase()) : Heightmap.Types.MOTION_BLOCKING; + } catch (IllegalArgumentException e) { + throw new LuaError("Invalid heightmap type provided"); + } + + return world.getHeight(heightmapType, (int) pos.x(), (int) pos.y()); + } + @LuaWhitelist @LuaMethodDoc( overloads = { diff --git a/common/src/main/java/org/figuramc/figura/lua/docs/FiguraListDocs.java b/common/src/main/java/org/figuramc/figura/lua/docs/FiguraListDocs.java index ebe2b0d9a..cda977c73 100644 --- a/common/src/main/java/org/figuramc/figura/lua/docs/FiguraListDocs.java +++ b/common/src/main/java/org/figuramc/figura/lua/docs/FiguraListDocs.java @@ -5,6 +5,7 @@ import com.google.gson.JsonObject; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.ChatFormatting; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.locale.Language; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -15,6 +16,7 @@ import net.minecraft.world.item.UseAnim; import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.levelgen.Heightmap; import org.figuramc.figura.FiguraMod; import org.figuramc.figura.animation.Animation; import org.figuramc.figura.mixin.input.KeyMappingAccessor; @@ -108,6 +110,14 @@ public class FiguraListDocs { for (ClipContext.Fluid value : ClipContext.Fluid.values()) add(value.name()); }}; + private static final LinkedHashSet HEIGHTMAP_TYPE = new LinkedHashSet<>() {{ + for (Heightmap.Types value : Heightmap.Types.values()) + add(value.name()); + }}; + private static final LinkedHashSet REGISTRIES = new LinkedHashSet<>() {{ + for (ResourceLocation resourceLocation : BuiltInRegistries.REGISTRY.keySet()) + add(resourceLocation.getPath()); + }}; private enum ListDoc { KEYBINDS(() -> FiguraListDocs.KEYBINDS, "Keybinds", "keybinds", 2), @@ -126,7 +136,9 @@ private enum ListDoc { RENDER_MODES(() -> FiguraListDocs.RENDER_MODES, "RenderModes", "render_modes", 1), STRING_ENCODINGS(() -> FiguraListDocs.STRING_ENCODINGS, "StringEncodings", "string_encodings", 1), BLOCK_RAYCAST_TYPE(() -> FiguraListDocs.BLOCK_RAYCAST_TYPE, "BlockRaycastTypes", "block_raycast_types", 1), - FLUID_RAYCAST_TYPE(() -> FiguraListDocs.FLUID_RAYCAST_TYPE, "FluidRaycastTypes", "fluid_raycast_types", 1); + FLUID_RAYCAST_TYPE(() -> FiguraListDocs.FLUID_RAYCAST_TYPE, "FluidRaycastTypes", "fluid_raycast_types", 1), + HEIGHTMAP_TYPE(() -> FiguraListDocs.HEIGHTMAP_TYPE, "HeightmapTypes", "heightmap_types", 1), + REGISTRIES(() -> FiguraListDocs.REGISTRIES, "Registries", "registries", 1); private final Supplier supplier; private final String name, id; diff --git a/common/src/main/resources/assets/figura/lang/en_us.json b/common/src/main/resources/assets/figura/lang/en_us.json index b6340a81b..120fed627 100644 --- a/common/src/main/resources/assets/figura/lang/en_us.json +++ b/common/src/main/resources/assets/figura/lang/en_us.json @@ -633,6 +633,8 @@ "figura.docs.enum.string_encodings": "List of valid string encodings\nUsed within Buffers", "figura.docs.enum.block_raycast_types": "List of valid BlockRaycastTypes\nUsed to determine how raycast.block handles blocks", "figura.docs.enum.fluid_raycast_types": "List of valid FluidRaycastTypes\nUsed to determine how raycast.block handles fluids", + "figura.docs.enum.heightmap_types": "List of valid HeightmapTypes\nUsed in world.getHeight to select the type of heightmap", + "figura.docs.enum.registries": "A list of valid registries.\nUsed in client.getRegistry to select the type of registry", "figura.docs.globals": "Documentation for the various things Figura adds to the global lua state", "figura.docs.globals.vec": "An alias for \"vectors.vec\"", "figura.docs.globals.require": "The require() function takes the name of one of your scripts, without the .lua extension\nIf this script has not been already run before, it will run that script and return the value that script returns\nIf it has been run before, then it will not run the file again, but it will return the same thing as the first time\nIf a required script has no returns, then require() will return true\nIf the name you give isn't any of your scripts, it will error\nScripts can be accessed relative to the executing script using `./` and `../`", @@ -925,6 +927,7 @@ "figura.docs.client.get_camera_entity": "Returns the entity the camera is currently targeting, so returns the entity you are currently spectating, including yourself", "figura.docs.client.get_server_data": "Returns a table with information on the currently connected server (also for singleplayer worlds)", "figura.docs.client.get_date": "Returns a table with information about the client's current time", + "figura.docs.client.get_registry": "Returns a list of all values in the specified registry\nSee the `registries` enum for a list of valid registries", "figura.docs.client.get_frame_time": "Returns the current fraction between the last tick and the next tick\nThis is the value used as \"delta\" in the RENDER event", "figura.docs.client.get_actionbar": "Returns the current actionbar text, or nil if the action bar isn't visible", "figura.docs.client.get_title": "Returns the current title text, or nil if the title isn't visible", @@ -1652,6 +1655,7 @@ "figura.docs.world.get_sky_light_level": "Gets the skylight level of the block at the given position", "figura.docs.world.get_block_light_level": "Gets the block light level of the block at the given position", "figura.docs.world.is_open_sky": "Gets whether or not the sky is open at the given position", + "figura.docs.world.get_height": "Returns the highest point at the given position according to the provided heightmap\nDefaults to MOTION_BLOCKING if no heightmap is provided", "figura.docs.world.get_dimension": "Gets the dimension name of this world", "figura.docs.world.get_entity": "Returns an EntityAPI object from this UUID's entity, or nil if no entity was found", "figura.docs.world.get_players": "Returns a table containing instances of Player for all players in the world\nThe players are indexed by their names",