diff --git a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java index d28957450..041c29009 100644 --- a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java @@ -1,6 +1,5 @@ package emu.grasscutter.command.commands; -import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.inventory.GameItem; @@ -9,97 +8,113 @@ import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; -import static emu.grasscutter.utils.Language.translate; - -@Command(label = "clear", usage = "clear ", //Merged /clearartifacts and /clearweapons to /clear [uid] +@Command(label = "clear", usage = "clear [lv] [r] [*]", description = "commands.clear.description", aliases = {"clear"}, permission = "player.clearinv", permissionTargeted = "player.clearinv.others") public final class ClearCommand implements CommandHandler { + private static Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java doesn't have raw string literals :( + private static Pattern refineRegex = Pattern.compile("r(\\d+)"); + private static Pattern rankRegex = Pattern.compile("(\\d+)\\*"); + + private static int matchIntOrNeg(Pattern pattern, String arg) { + Matcher match = pattern.matcher(arg); + if (match.find()) { + return Integer.parseInt(match.group(1)); // This should be exception-safe as only \d+ can be passed to it (i.e. non-empty string of pure digits) + } + return -1; + } + + private static class ClearItemParameters { + public int lvl = 1; + public int refinement = 1; + public int rank = 4; + }; + + private Stream getOther(ItemType type, Inventory playerInventory, ClearItemParameters param) { + return playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == type) + .filter(item -> item.getItemData().getRankLevel() <= param.rank) + .filter(item -> !item.isLocked() && !item.isEquipped()); + } + + private Stream getWeapons(Inventory playerInventory, ClearItemParameters param) { + return getOther(ItemType.ITEM_WEAPON, playerInventory, param) + .filter(item -> item.getLevel() <= param.lvl) + .filter(item -> item.getRefinement() < param.refinement); + } + + private Stream getRelics(Inventory playerInventory, ClearItemParameters param) { + return getOther(ItemType.ITEM_RELIQUARY, playerInventory, param) + .filter(item -> item.getLevel() <= param.lvl + 1); + } @Override public void execute(Player sender, Player targetPlayer, List args) { + Inventory playerInventory = targetPlayer.getInventory(); + ClearItemParameters param = new ClearItemParameters(); + + // Extract any tagged arguments (e.g. "lv90", "x100", "r5") + for (int i = args.size() - 1; i >= 0; i--) { // Reverse iteration as we are deleting elements + String arg = args.get(i).toLowerCase(); + boolean deleteArg = false; + int argNum; + // Note that a single argument can actually match all of these, e.g. "lv90r5*" + if ((argNum = matchIntOrNeg(lvlRegex, arg)) != -1) { + param.lvl = argNum; + deleteArg = true; + } + if ((argNum = matchIntOrNeg(refineRegex, arg)) != -1) { + param.refinement = argNum; + deleteArg = true; + } + if ((argNum = matchIntOrNeg(rankRegex, arg)) != -1) { + param.rank = argNum; + deleteArg = true; + } + if (deleteArg) { + args.remove(i); + } + } + if (args.size() < 1) { - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.command_usage")); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.command_usage"); return; } - Inventory playerInventory = targetPlayer.getInventory(); - List toDelete = null; - + + String playerString = targetPlayer.getNickname(); // Should probably be UID instead but whatever switch (args.get(0)) { case "wp" -> { - toDelete = playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_WEAPON) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .toList(); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.weapons", targetPlayer.getNickname())); + playerInventory.removeItems(getWeapons(playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString); } case "art" -> { - toDelete = playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) - .filter(item -> item.getLevel() == 1 && item.getExp() == 0) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .toList(); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.artifacts", targetPlayer.getNickname())); + playerInventory.removeItems(getRelics(playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString); } case "mat" -> { - toDelete = playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_MATERIAL) - .filter(item -> item.getLevel() == 1 && item.getExp() == 0) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .toList(); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.materials", targetPlayer.getNickname())); + playerInventory.removeItems(getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString); } case "all" -> { - toDelete = playerInventory.getItems().values().stream() - .filter(item1 -> item1.getItemType() == ItemType.ITEM_RELIQUARY) - .filter(item1 -> item1.getLevel() == 1 && item1.getExp() == 0) - .filter(item1 -> !item1.isLocked() && !item1.isEquipped()) - .toList(); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.artifacts", targetPlayer.getNickname())); - playerInventory.removeItems(toDelete); - - toDelete = playerInventory.getItems().values().stream() - .filter(item2 -> item2.getItemType() == ItemType.ITEM_MATERIAL) - .filter(item2 -> !item2.isLocked() && !item2.isEquipped()) - .toList(); - playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.materials", targetPlayer.getNickname())); - - toDelete = playerInventory.getItems().values().stream() - .filter(item3 -> item3.getItemType() == ItemType.ITEM_WEAPON) - .filter(item3 -> item3.getLevel() == 1 && item3.getExp() == 0) - .filter(item3 -> !item3.isLocked() && !item3.isEquipped()) - .toList(); - playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.weapons", targetPlayer.getNickname())); - - toDelete = playerInventory.getItems().values().stream() - .filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE) - .filter(item4 -> !item4.isLocked() && !item4.isEquipped()) - .toList(); - playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.furniture", targetPlayer.getNickname())); - - toDelete = playerInventory.getItems().values().stream() - .filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY) - .filter(item5 -> !item5.isLocked() && !item5.isEquipped()) - .toList(); - playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.displays", targetPlayer.getNickname())); - - toDelete = playerInventory.getItems().values().stream() - .filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL) - .filter(item6 -> !item6.isLocked() && !item6.isEquipped()) - .toList(); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.virtuals", targetPlayer.getNickname())); - CommandHandler.sendMessage(sender, translate(sender, "commands.clear.everything", targetPlayer.getNickname())); + playerInventory.removeItems(getRelics(playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString); + playerInventory.removeItems(getWeapons(playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString); + playerInventory.removeItems(getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString); + playerInventory.removeItems(getOther(ItemType.ITEM_FURNITURE, playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.furniture", playerString); + playerInventory.removeItems(getOther(ItemType.ITEM_DISPLAY, playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.displays", playerString); + playerInventory.removeItems(getOther(ItemType.ITEM_VIRTUAL, playerInventory, param).toList()); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.virtuals", playerString); + CommandHandler.sendTranslatedMessage(sender, "commands.clear.everything", playerString); } } - - if (toDelete != null) { - playerInventory.removeItems(toDelete); - } } } diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index d2629d525..6dc5a0c43 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -126,7 +126,7 @@ "description": "Send announcement to all online players, or manage server's announcement" }, "clear": { - "command_usage": "Usage: clear ", + "command_usage": "Usage: clear [lv] [r] [*]", "weapons": "Cleared weapons for %s.", "artifacts": "Cleared artifacts for %s.", "materials": "Cleared materials for %s.", @@ -134,7 +134,7 @@ "displays": "Cleared displays for %s.", "virtuals": "Cleared virtuals for %s.", "everything": "Cleared everything for %s.", - "description": "Deletes unequipped unlocked items, including yellow rarity ones from your inventory" + "description": "Deletes unequipped unlocked items from your inventory. Defaults to 4* level 1 refinement 1 or lower, but can be set higher." }, "coop": { "usage": "Usage: coop [host UID]", diff --git a/src/main/resources/languages/es-ES.json b/src/main/resources/languages/es-ES.json index c11240783..4e807991c 100644 --- a/src/main/resources/languages/es-ES.json +++ b/src/main/resources/languages/es-ES.json @@ -118,7 +118,7 @@ "description": "Modifica las cuentas de usuario" }, "clear": { - "command_usage": "Uso: clear ", + "command_usage": "Uso: clear [lv] [r] [*]", "weapons": "Eliminadas las armas para %s.", "artifacts": "Eliminados los artefactos para %s.", "materials": "Eliminados los materiales para %s.", diff --git a/src/main/resources/languages/fr-FR.json b/src/main/resources/languages/fr-FR.json index 91391520e..75cf5133a 100644 --- a/src/main/resources/languages/fr-FR.json +++ b/src/main/resources/languages/fr-FR.json @@ -119,7 +119,7 @@ "description": "Modifie les comptes utilisateurs" }, "clear": { - "command_usage": "Usage: clear ", + "command_usage": "Usage: clear [lv] [r] [*]", "weapons": "Les armes de %s ont été supprimés.", "artifacts": "Les artéfacts de %s ont été supprimés.", "materials": "Les matériaux de %s ont été supprimés.", diff --git a/src/main/resources/languages/pl-PL.json b/src/main/resources/languages/pl-PL.json index 3a0509ecd..edc444652 100644 --- a/src/main/resources/languages/pl-PL.json +++ b/src/main/resources/languages/pl-PL.json @@ -111,7 +111,7 @@ "command_usage": "Użycie: account [uid]" }, "clear": { - "command_usage": "Użycie: clear ", + "command_usage": "Użycie: clear [lv] [r] [*]", "weapons": "Wyczyszczono bronie dla %s.", "artifacts": "Wyczyszczono artefakty dla %s.", "materials": "Wyczyszczono materiały dla %s.", diff --git a/src/main/resources/languages/ro-RO.json b/src/main/resources/languages/ro-RO.json index 6e35cb99a..09a34f571 100644 --- a/src/main/resources/languages/ro-RO.json +++ b/src/main/resources/languages/ro-RO.json @@ -118,7 +118,7 @@ "description": "Modificați conturile de utilizator" }, "clear": { - "command_usage": "Utilizare: clear ", + "command_usage": "Utilizare: clear [lv] [r] [*]", "weapons": "Arme șterse pentru %s.", "artifacts": "Artefacte șterse pentru %s.", "materials": "Materiale șterse pentru %s.", diff --git a/src/main/resources/languages/ru-RU.json b/src/main/resources/languages/ru-RU.json index 6e75e4b43..b1117acc7 100644 --- a/src/main/resources/languages/ru-RU.json +++ b/src/main/resources/languages/ru-RU.json @@ -131,7 +131,7 @@ "description": "Изменяет текущую сцену" }, "clear": { - "command_usage": "Применение: clear ", + "command_usage": "Применение: clear [lv] [r] [*]", "weapons": "Удалены оружия у %s.", "artifacts": "Удалены артефакты у %s.", "materials": "Удалены материалы у %s.", diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index c2a2eee6a..9eaf00a9d 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -126,7 +126,7 @@ "description": "向所有在线玩家发送公告,或者管理服务器的公告" }, "clear": { - "command_usage": "用法:clear \nall: 所有, wp: 武器, art: 圣遗物, mat: 材料", + "command_usage": "用法:clear [lv] [r] [*]\nall: 所有, wp: 武器, art: 圣遗物, mat: 材料", "weapons": "已清除 %s 的武器。", "artifacts": "已清除 %s 的圣遗物。", "materials": "已清除 %s 的材料。", diff --git a/src/main/resources/languages/zh-TW.json b/src/main/resources/languages/zh-TW.json index da4b1e018..1d2dbb570 100644 --- a/src/main/resources/languages/zh-TW.json +++ b/src/main/resources/languages/zh-TW.json @@ -117,7 +117,7 @@ "description": "建立或刪除帳號。" }, "clear": { - "command_usage": "用法: clear ", + "command_usage": "用法: clear [lv] [r] [*]", "weapons": "已將 %s 的武器清空。", "artifacts": "已將 %s 的聖遺物清空。", "materials": "已將 %s 的材料清空。",