diff --git a/common/src/main/java/org/figuramc/figura/lua/api/HostAPI.java b/common/src/main/java/org/figuramc/figura/lua/api/HostAPI.java index 39ebf16af..d3921e2b8 100644 --- a/common/src/main/java/org/figuramc/figura/lua/api/HostAPI.java +++ b/common/src/main/java/org/figuramc/figura/lua/api/HostAPI.java @@ -12,13 +12,21 @@ import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.NonNullList; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.protocol.game.ServerboundClientCommandPacket; +import net.minecraft.stats.Stat; +import net.minecraft.stats.Stats; import net.minecraft.world.InteractionHand; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.SlotAccess; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; import org.figuramc.figura.FiguraMod; import org.figuramc.figura.avatar.Avatar; import org.figuramc.figura.avatar.AvatarManager; @@ -28,6 +36,7 @@ import org.figuramc.figura.lua.LuaWhitelist; import org.figuramc.figura.lua.api.entity.EntityAPI; import org.figuramc.figura.lua.api.world.ItemStackAPI; +import org.figuramc.figura.lua.api.world.WorldAPI; import org.figuramc.figura.lua.docs.LuaFieldDoc; import org.figuramc.figura.lua.docs.LuaMethodDoc; import org.figuramc.figura.lua.docs.LuaMethodOverload; @@ -532,6 +541,136 @@ public List> getStatusEffects() { return list; } + @LuaWhitelist + @LuaMethodDoc( + value = "host.update_statistics", + aliases = "updateStats" + ) + public void updateStatistics() { + this.minecraft.getConnection().send(new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.REQUEST_STATS)); // in theory, this cant nullptr; if it does, ill fix later + } + @LuaWhitelist + public void updateStats() {updateStatistics();} + + @LuaWhitelist + @LuaMethodDoc( + value = "host.get_general_statistics", + aliases = "getGeneralStats" + ) + public Map getGeneralStatistics() { + Map map = new HashMap<>(); + LocalPlayer player = this.minecraft.player; + if (player == null || !isHost()) return map; + for (Stat stat : Stats.CUSTOM) { + String name = stat.getName().replace("minecraft.custom:minecraft.",""); // turn registry to translation key + String formatted = stat.format(player.getStats().getValue(stat)).replaceAll("[^\\d.]", ""); // remove things that aren't digits or periods + double finalValue = Double.parseDouble(formatted); + map.put(name, finalValue); + } + return map; + } + @LuaWhitelist + public Map getGeneralStats() { return getGeneralStatistics(); } + + @LuaWhitelist + @LuaMethodDoc( + value = "host.get_item_statistic", + aliases = "getItemStat", + overloads = { + @LuaMethodOverload( + argumentTypes = ItemStackAPI.class, + argumentNames = "item" + ), + @LuaMethodOverload( + argumentTypes = {String.class}, + argumentNames = "id" + ) + } + ) + public Map getItemStatistic(Object id) { + Map map = new HashMap<>(); + LocalPlayer player = this.minecraft.player; + if (player == null || !isHost()) return map; + String itemKey; + ItemStackAPI stack; + if (id instanceof String) { + stack = WorldAPI.newItem(id.toString(), 1, null); // this handles errors for me and im lazy + itemKey = id.toString(); + } else { + stack = (ItemStackAPI) id; + itemKey = BuiltInRegistries.ITEM.getKey(stack.itemStack.getItem()).toString(); + } + + int timesMined = 0; // init to 0 for if it's not a block + int timesBroken; + int timesCrafted; + int timesUsed; + int timesPickedUp; + int timesDropped; + if (stack.itemStack.getItem() instanceof BlockItem) { + Block block = stack.getBlockstate().blockState.getBlock(); + Stat stat = Stats.BLOCK_MINED.get(block); + timesMined = player.getStats().getValue(stat); + } + + Stat brokenStat = Stats.ITEM_BROKEN.get(stack.itemStack.getItem()); + timesBroken = player.getStats().getValue(brokenStat); + + Stat craftedStat = Stats.ITEM_CRAFTED.get(stack.itemStack.getItem()); + timesCrafted = player.getStats().getValue(craftedStat); + + Stat usedStat = Stats.ITEM_USED.get(stack.itemStack.getItem()); + timesUsed = player.getStats().getValue(usedStat); + + Stat pickedUpStat = Stats.ITEM_PICKED_UP.get(stack.itemStack.getItem()); + timesPickedUp = player.getStats().getValue(pickedUpStat); + + Stat droppedStat = Stats.ITEM_DROPPED.get(stack.itemStack.getItem()); + timesDropped = player.getStats().getValue(droppedStat); + + + map.put("mined", timesMined); + map.put("crafted", timesCrafted); + map.put("used", timesUsed); + map.put("picked_up", timesPickedUp); + map.put("broken", timesBroken); + map.put("dropped", timesDropped); + return map; + } + @LuaWhitelist + public Map getItemStat(Object id) { return getItemStatistic(id); } + + @LuaWhitelist + @LuaMethodDoc( + value = "host.get_entity_statistic", + aliases = "get_entity_stat", + overloads = @LuaMethodOverload( + argumentTypes = String.class, + argumentNames = "type" + ) + ) + public Map getEntityStatistic(String type) { + Map map = new HashMap<>(); + LocalPlayer player = this.minecraft.player; + if (player == null || !isHost()) return map; + Optional> entity = EntityType.byString(type); + if (entity.isPresent()) { + Stat> killedStat = Stats.ENTITY_KILLED.get(entity.get()); + Stat> killedByStat = Stats.ENTITY_KILLED_BY.get(entity.get()); + + int timesKilled = player.getStats().getValue(killedStat); + int timesKilledBy = player.getStats().getValue(killedByStat); + + map.put("killed", timesKilled); + map.put("killed_by", timesKilledBy); + } + else { + throw new LuaError("Invalid entity type " + type); + } + return map; + } + @LuaWhitelist + public Map getEntityStat(String type) { return getEntityStatistic(type); } @LuaWhitelist @LuaMethodDoc("host.get_clipboard") 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 e4b27a47b..1541ddeeb 100644 --- a/common/src/main/resources/assets/figura/lang/en_us.json +++ b/common/src/main/resources/assets/figura/lang/en_us.json @@ -1088,6 +1088,10 @@ "figura.docs.host.append_chat_history": "Appends the message on the recent chat history", "figura.docs.host.get_chat_message": "Returns a table with information about a chat message\nTakes an index, where 1 means the last message on chat", "figura.docs.host.set_chat_message": "Modifies a chat message with the given text\nTakes an index, were 1 means the last message on chat\nSetting the message to nil will effectively remove it from the chat", + "figura.docs.host.update_statistics": "Updates the player's statistics as if the statistics screen was opened", + "figura.docs.host.get_general_statistics": "Returns a table with general stats about the player\nThe table contains all values in the \"general\" section", + "figura.docs.host.get_item_statistic": "Returns a table with the item's stats in the \"items\" section\nTakes an ItemStack or a string with the item's id", + "figura.docs.host.get_entity_statistic": "Returns a table with the entity's stats in the \"mobs\" section\nTakes a string with the entity's type", "figura.docs.host.swing_arm": "Animates swinging the player's arm\nIf the boolean is true, then the offhand is the one that swings", "figura.docs.host.is_first_person": "Returns true if the camera is in first person mode", "figura.docs.host.is_camera_backwards": "Returns true if the camera is facing backward",