From bb84432b4ca24fe3b77e5aa44844b7a1af230e23 Mon Sep 17 00:00:00 2001
From: AnimeGitB <AnimeGitB@bigblueball.in>
Date: Sat, 9 Jul 2022 19:56:39 +0930
Subject: [PATCH] Add maximum level/refinement/rank to clear command

---
 .../command/commands/ClearCommand.java        | 159 ++++++++++--------
 src/main/resources/languages/en-US.json       |   4 +-
 src/main/resources/languages/es-ES.json       |   2 +-
 src/main/resources/languages/fr-FR.json       |   2 +-
 src/main/resources/languages/pl-PL.json       |   2 +-
 src/main/resources/languages/ro-RO.json       |   2 +-
 src/main/resources/languages/ru-RU.json       |   2 +-
 src/main/resources/languages/zh-CN.json       |   2 +-
 src/main/resources/languages/zh-TW.json       |   2 +-
 9 files changed, 96 insertions(+), 81 deletions(-)

diff --git a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java
index d2895745..041c2900 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 <all|wp|art|mat>", //Merged /clearartifacts and /clearweapons to /clear <args> [uid]
+@Command(label = "clear", usage = "clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
         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<GameItem> 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<GameItem> 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<GameItem> 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<String> 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<GameItem> 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 d2629d52..6dc5a0c4 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 <all|wp|art|mat>",
+      "command_usage": "Usage: clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
       "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 c1124078..4e807991 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 <all|wp|art|mat>",
+      "command_usage": "Uso: clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
       "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 91391520..75cf5133 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 <all|wp|art|mat>",
+        "command_usage": "Usage: clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
         "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 3a0509ec..edc44465 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 <create|delete> <nazwa> [uid]"
     },
     "clear": {
-      "command_usage": "U偶ycie: clear <all|wp|art|mat>",
+      "command_usage": "U偶ycie: clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
       "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 6e35cb99..09a34f57 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 <all|wp|art|mat>",
+      "command_usage": "Utilizare: clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
       "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 6e75e4b4..b1117acc 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 <all|wp|art|mat>",
+      "command_usage": "袩褉懈屑械薪械薪懈械: clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
       "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 c2a2eee6..9eaf00a9 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": "鐢ㄦ硶锛歝lear <all|wp|art|mat>\nall: 鎵€鏈�, wp: 姝﹀櫒, art: 鍦i仐鐗�, mat: 鏉愭枡",
+      "command_usage": "鐢ㄦ硶锛歝lear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]\nall: 鎵€鏈�, wp: 姝﹀櫒, art: 鍦i仐鐗�, 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 da4b1e01..1d2dbb57 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 <all|wp|art|mat>",
+      "command_usage": "鐢ㄦ硶锛� clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
       "weapons": "宸插皣 %s 鐨勬鍣ㄦ竻绌恒€�",
       "artifacts": "宸插皣 %s 鐨勮仏閬虹墿娓呯┖銆�",
       "materials": "宸插皣 %s 鐨勬潗鏂欐竻绌恒€�",
-- 
GitLab