diff --git a/.gitignore b/.gitignore index 35ee889c5..9fa6c9427 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,6 @@ language/ languages/ gacha-mapping.js data/gacha_mappings.js + +# macOS +.DS_Store diff --git a/src/main/java/emu/grasscutter/command/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java index a183c6ac3..2ff105b31 100644 --- a/src/main/java/emu/grasscutter/command/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -150,7 +150,7 @@ public final class CommandMap { int uid = Integer.parseInt(targetUidStr); targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid); if (targetPlayer == null) { - CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error")); + CommandHandler.sendMessage(player, translate("commands.execution.player_exist_offline_error")); } else { targetPlayerIds.put(playerId, uid); CommandHandler.sendMessage(player, translate("commands.execution.set_target", targetUidStr)); @@ -178,7 +178,7 @@ public final class CommandMap { int uid = Integer.parseInt(arg); targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid); if (targetPlayer == null) { - CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error")); + CommandHandler.sendMessage(player, translate("commands.execution.player_exist_offline_error")); return; } break; @@ -194,7 +194,7 @@ public final class CommandMap { if (targetPlayerIds.containsKey(playerId)) { targetPlayer = Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.get(playerId)); // We check every time in case the target goes offline after being targeted if (targetPlayer == null) { - CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error")); + CommandHandler.sendMessage(player, translate("commands.execution.player_exist_offline_error")); return; } } else { diff --git a/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java index 594eb27c6..59706dd96 100644 --- a/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java @@ -8,7 +8,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "changescene", usage = "changescene ", aliases = {"scene"}, permission = "player.changescene", description = "commands.changescene.description") +@Command(label = "changescene", usage = "changescene ", aliases = {"scene"}, permission = "player.changescene", permissionTargeted = "player.changescene.others", description = "commands.changescene.description") public final class ChangeSceneCommand implements CommandHandler { @Override @@ -31,11 +31,12 @@ public final class ChangeSceneCommand implements CommandHandler { } boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, targetPlayer.getPos()); - CommandHandler.sendMessage(sender, translate("commands.changescene.result", Integer.toString(sceneId))); - if (!result) { CommandHandler.sendMessage(sender, translate("commands.changescene.exists_error")); + return; } + + CommandHandler.sendMessage(sender, translate("commands.changescene.success", Integer.toString(sceneId))); } catch (Exception e) { CommandHandler.sendMessage(sender, translate("commands.execution.argument_error")); } diff --git a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java index 38f78e638..ab0ff0eb1 100644 --- a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java @@ -14,7 +14,7 @@ import static emu.grasscutter.utils.Language.translate; @Command(label = "clear", usage = "clear ", //Merged /clearartifacts and /clearweapons to /clear [uid] description = "commands.clear.description", - aliases = {"clear"}, permission = "player.clearinv") + aliases = {"clear"}, permission = "player.clearinv", permissionTargeted = "player.clearinv.others") public final class ClearCommand implements CommandHandler { diff --git a/src/main/java/emu/grasscutter/command/commands/CoopCommand.java b/src/main/java/emu/grasscutter/command/commands/CoopCommand.java index cf7d4fc82..a86472978 100644 --- a/src/main/java/emu/grasscutter/command/commands/CoopCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/CoopCommand.java @@ -9,7 +9,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "coop", usage = "coop [host UID]", permission = "server.coop", description = "commands.coop.description") +@Command(label = "coop", usage = "coop [host UID]", permission = "server.coop", permissionTargeted = "server.coop.others", description = "commands.coop.description") public final class CoopCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/DropCommand.java b/src/main/java/emu/grasscutter/command/commands/DropCommand.java index 10c306cad..e2579a7be 100644 --- a/src/main/java/emu/grasscutter/command/commands/DropCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/DropCommand.java @@ -13,7 +13,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "drop", usage = "drop [amount]", aliases = {"d", "dropitem"}, permission = "server.drop", description = "commands.drop.description") +@Command(label = "drop", usage = "drop [amount]", aliases = {"d", "dropitem"}, permission = "server.drop", permissionTargeted = "server.drop.others", description = "commands.drop.description") public final class DropCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java b/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java index 8534c034f..c4e37a93e 100644 --- a/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java @@ -8,7 +8,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "enterdungeon", usage = "enterdungeon ", aliases = {"dungeon"}, permission = "player.enterdungeon", description = "commands.enter_dungeon.description") +@Command(label = "enterdungeon", usage = "enterdungeon ", aliases = {"dungeon"}, permission = "player.enterdungeon", permissionTargeted = "player.enterdungeon.others", description = "commands.enter_dungeon.description") public final class EnterDungeonCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java index c94d67129..6b9104626 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java @@ -15,7 +15,7 @@ import java.util.*; import static emu.grasscutter.utils.Language.translate; -@Command(label = "giveall", usage = "giveall [amount]", aliases = {"givea"}, permission = "player.giveall", threading = true, description = "commands.giveAll.description") +@Command(label = "giveall", usage = "giveall [amount]", aliases = {"givea"}, permission = "player.giveall", permissionTargeted = "player.giveall.others", threading = true, description = "commands.giveAll.description") public final class GiveAllCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java index cf479ed57..25503dbd3 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java @@ -19,7 +19,7 @@ import static java.util.Map.entry; import static emu.grasscutter.utils.Language.translate; -@Command(label = "giveart", usage = "giveart [[,]]... [level]", aliases = {"gart"}, permission = "player.giveart", description = "commands.giveArtifact.description") +@Command(label = "giveart", usage = "giveart [[,]]... [level]", aliases = {"gart"}, permission = "player.giveart", permissionTargeted = "player.giveart.others", description = "commands.giveArtifact.description") public final class GiveArtifactCommand implements CommandHandler { private static final Map> mainPropMap = Map.ofEntries( entry("hp", Map.ofEntries(entry(EquipType.EQUIP_BRACER, 14001))), diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java index 5c6bad0d2..af789f394 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java @@ -12,7 +12,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "givechar", usage = "givechar [level]", aliases = {"givec"}, permission = "player.givechar", description = "commands.giveChar.description") +@Command(label = "givechar", usage = "givechar [level]", aliases = {"givec"}, permission = "player.givechar", permissionTargeted = "player.givechar.others", description = "commands.giveChar.description") public final class GiveCharCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index 19a9a8d26..25500f36f 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -17,7 +17,7 @@ import java.util.regex.Pattern; import static emu.grasscutter.utils.Language.translate; @Command(label = "give", usage = "give [amount] [level]", aliases = { - "g", "item", "giveitem"}, permission = "player.give", description = "commands.give.description") + "g", "item", "giveitem"}, permission = "player.give", permissionTargeted = "player.give.others", description = "commands.give.description") public final class GiveCommand implements CommandHandler { Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java is a joke of a proglang that doesn't have raw string literals Pattern refineRegex = Pattern.compile("r(\\d+)"); diff --git a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java index bf2a00c9f..98a375838 100644 --- a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java @@ -8,7 +8,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "godmode", usage = "godmode [on|off|toggle]", permission = "player.godmode", description = "commands.godmode.description") +@Command(label = "godmode", usage = "godmode [on|off|toggle]", permission = "player.godmode", permissionTargeted = "player.godmode.others", description = "commands.godmode.description") public final class GodModeCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/HealCommand.java b/src/main/java/emu/grasscutter/command/commands/HealCommand.java index 440db0a49..78ff14405 100644 --- a/src/main/java/emu/grasscutter/command/commands/HealCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/HealCommand.java @@ -11,7 +11,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "heal", usage = "heal|h", aliases = {"h"}, permission = "player.heal", description = "commands.heal.description") +@Command(label = "heal", usage = "heal|h", aliases = {"h"}, permission = "player.heal", permissionTargeted = "player.heal.others", description = "commands.heal.description") public final class HealCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java b/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java index da9ac7b5e..853395fe6 100644 --- a/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java @@ -12,7 +12,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", description = "commands.kill.description") +@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", permissionTargeted = "server.killall.others", description = "commands.kill.description") public final class KillAllCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java index 3eda6f7e7..7cff601c4 100644 --- a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java @@ -13,7 +13,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", description = "commands.list.description") +@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", permissionTargeted = "player.killcharacter.others", description = "commands.list.description") public final class KillCharacterCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java index 3a77cee4d..e8229eaf5 100644 --- a/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java @@ -11,7 +11,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @Command(label = "resetconst", usage = "resetconst [all]", - aliases = {"resetconstellation"}, permission = "player.resetconstellation", description = "commands.resetConst.description") + aliases = {"resetconstellation"}, permission = "player.resetconstellation", permissionTargeted = "player.resetconstellation.others", description = "commands.resetConst.description") public final class ResetConstCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java b/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java index 7aa84ff6a..bf5e2a4a6 100644 --- a/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java @@ -9,7 +9,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", description = "commands.status.description") +@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", permissionTargeted = "server.resetshop.others", description = "commands.resetshop.description") public final class ResetShopLimitCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java b/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java index 18d6264db..befbf4f00 100644 --- a/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java @@ -9,7 +9,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @Command(label = "say", usage = "say ", - aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage", description = "commands.sendMessage.description") + aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage", permissionTargeted = "server.sendmessage.others", description = "commands.sendMessage.description") public final class SendMessageCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java index ca5a3cb43..cf356d06b 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java @@ -12,7 +12,7 @@ import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify; import static emu.grasscutter.utils.Language.translate; @Command(label = "setfetterlevel", usage = "setfetterlevel ", - aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel", description = "commands.setFetterLevel.description") + aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel", permissionTargeted = "player.setfetterlevel.others", description = "commands.setFetterLevel.description") public final class SetFetterLevelCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index c7ed78a58..a0572d2c2 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -15,7 +15,7 @@ import emu.grasscutter.utils.Language; import static emu.grasscutter.utils.Language.translate; -@Command(label = "setstats", usage = "setstats|stats ", aliases = {"stats"}, permission = "player.setstats", description = "commands.setStats.description") +@Command(label = "setstats", usage = "setstats|stats ", aliases = {"stats"}, permission = "player.setstats", permissionTargeted = "player.setstats.others", description = "commands.setStats.description") public final class SetStatsCommand implements CommandHandler { static class Stat { String name; @@ -175,7 +175,7 @@ public final class SetStatsCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - String syntax = sender == null ? translate("commands.setStats.usage_console") : translate("commands.setStats.ingame"); + String syntax = sender == null ? translate("commands.setStats.usage_console") : translate("commands.setStats.usage_ingame"); String usage = syntax + translate("commands.setStats.help_message"); String statStr; String valueStr; diff --git a/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java index 41b959336..aa773159e 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java @@ -10,7 +10,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @Command(label = "setworldlevel", usage = "setworldlevel ", - aliases = {"setworldlvl"}, permission = "player.setworldlevel", description = "commands.setWorldLevel.description") + aliases = {"setworldlvl"}, permission = "player.setworldlevel", permissionTargeted = "player.setworldlevel.others", description = "commands.setWorldLevel.description") public final class SetWorldLevelCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java index 7f0c704c6..e3193c638 100644 --- a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java @@ -22,7 +22,7 @@ import java.util.Random; import static emu.grasscutter.utils.Language.translate; -@Command(label = "spawn", usage = "spawn [amount] [level(monster only)]", permission = "server.spawn", description = "commands.spawn.description") +@Command(label = "spawn", usage = "spawn [amount] [level(monster only)]", permission = "server.spawn", permissionTargeted = "server.spawn.others", description = "commands.spawn.description") public final class SpawnCommand implements CommandHandler { @Override @@ -46,13 +46,13 @@ public final class SpawnCommand implements CommandHandler { try { amount = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.error.amount")); + CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount")); } // Fallthrough case 1: try { id = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId")); + CommandHandler.sendMessage(sender, translate("commands.generic.invalid.entityId")); } break; default: @@ -64,7 +64,7 @@ public final class SpawnCommand implements CommandHandler { GadgetData gadgetData = GameData.getGadgetDataMap().get(id); ItemData itemData = GameData.getItemDataMap().get(id); if (monsterData == null && gadgetData == null && itemData == null) { - CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId")); + CommandHandler.sendMessage(sender, translate("commands.generic.invalid.entityId")); return; } Scene scene = targetPlayer.getScene(); diff --git a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java index 1540a81f8..40ac11b50 100644 --- a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java @@ -14,7 +14,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "talent", usage = "talent ", permission = "player.settalent", description = "commands.talent.description") +@Command(label = "talent", usage = "talent ", permission = "player.settalent", permissionTargeted = "player.settalent.others", description = "commands.talent.description") public final class TalentCommand implements CommandHandler { private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) { int oldLevel = avatar.getSkillLevelMap().get(talentId); @@ -24,7 +24,7 @@ public final class TalentCommand implements CommandHandler { } // Upgrade skill - avatar.getSkillLevelMap().put(talentLevel, talentLevel); + avatar.getSkillLevelMap().put(talentId, talentLevel); avatar.save(); // Packet diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java index 175f69b81..bfa0ac821 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java @@ -10,7 +10,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "tpall", usage = "tpall", permission = "player.tpall", description = "commands.teleportAll.description") +@Command(label = "tpall", usage = "tpall", permission = "player.tpall", permissionTargeted = "player.tpall.others", description = "commands.teleportAll.description") public final class TeleportAllCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java index 0d15b55af..8a9fb9948 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java @@ -10,7 +10,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "teleport", usage = "teleport [scene id]", aliases = {"tp"}, permission = "player.teleport", description = "commands.teleport.description") +@Command(label = "teleport", usage = "teleport [scene id]", aliases = {"tp"}, permission = "player.teleport", permissionTargeted = "player.teleport.others", description = "commands.teleport.description") public final class TeleportCommand implements CommandHandler { private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later diff --git a/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java b/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java index e0fce695c..bd7b8bc1f 100644 --- a/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java @@ -10,7 +10,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @Command(label = "unlocktower", usage = "unlocktower", aliases = {"ut"}, - description = "Unlock all levels of tower", permission = "player.tower") + description = "commands.unlocktower.description", permission = "player.tower") public class UnlockTowerCommand implements CommandHandler { @Override @@ -21,7 +21,7 @@ public class UnlockTowerCommand implements CommandHandler { unlockFloor(sender, sender.getServer().getTowerScheduleManager() .getScheduleFloors()); - CommandHandler.sendMessage(sender, translate("commands.tower.unlock_done")); + CommandHandler.sendMessage(sender, translate("commands.unlocktower.success")); } public void unlockFloor(Player player, List floors){ diff --git a/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java index dd0002790..0edbd8482 100644 --- a/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java @@ -11,7 +11,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "weather", usage = "weather [climateId]", aliases = {"w"}, permission = "player.weather", description = "commands.weather.description") +@Command(label = "weather", usage = "weather [climateId]", aliases = {"w"}, permission = "player.weather", permissionTargeted = "player.weather.others", description = "commands.weather.description") public final class WeatherCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 5b8523ec3..7e8baa291 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -1,6 +1,7 @@ package emu.grasscutter.game; import dev.morphia.annotations.*; +import emu.grasscutter.Grasscutter; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Utils; @@ -107,11 +108,41 @@ public class Account { this.permissions.add(permission); return true; } + public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) { + String[] wildcardParts = wildcard.split("\\."); + if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission + return false; + } + for (int i=0; i= (permissionParts.length-1)) { + return true; + } + break; + default: // This layer isn't a wildcard, it needs to match exactly + if (!wildcardParts[i].equals(permissionParts[i])) { + return false; + } + } + } + // At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **). + return (wildcardParts.length == permissionParts.length); + } + public boolean hasPermission(String permission) { - return this.permissions.contains(permission) || - this.permissions.contains("*") || - (this.permissions.contains("player") || this.permissions.contains("player.*")) && permission.startsWith("player.") || - (this.permissions.contains("server") || this.permissions.contains("server.*")) && permission.startsWith("server."); + if (this.permissions.contains(permission) || this.permissions.contains("*")) { + return true; + } + String[] permissionParts = permission.split("\\."); + for (String p : this.permissions) { + if (permissionMatchesWildcard(p, permissionParts)) { + return true; + } + } + return false; } public boolean removePermission(String permission) { diff --git a/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java b/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java index c480d047f..3f212ce4a 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java +++ b/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java @@ -9,6 +9,10 @@ public class TowerDungeonSettleListener implements DungeonSettleListener { @Override public void onDungeonSettle(Scene scene) { + if(scene.getScriptManager().getVariables().containsKey("stage") + && scene.getScriptManager().getVariables().get("stage") == 1){ + return; + } scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000); var towerManager = scene.getPlayers().get(0).getTowerManager(); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index 0ae6f356b..f8a96a808 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -117,7 +117,9 @@ public class EntityMonster extends GameEntity { this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); } if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) { - getScene().getScriptManager().onMonsterDie(); + if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){ + getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this); + } getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null); } if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) { diff --git a/src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java b/src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java index 72b91c055..1156993b1 100644 --- a/src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java +++ b/src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java @@ -190,14 +190,17 @@ public class StaminaManager { // Returns new stamina and sends PlayerPropNotify public int setStamina(GameSession session, String reason, int newStamina) { - // set stamina - player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina); - session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA)); - // notify updated - for (Map.Entry listener : afterUpdateStaminaListeners.entrySet()) { - listener.getValue().onAfterUpdateStamina(reason, newStamina); + if (Grasscutter.getConfig().OpenStamina) { + // set stamina + player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina); + session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA)); + // notify updated + for (Map.Entry listener : afterUpdateStaminaListeners.entrySet()) { + listener.getValue().onAfterUpdateStamina(reason, newStamina); + } + return newStamina; } - return newStamina; + return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); } // Kills avatar, removes entity and sends notification. @@ -243,15 +246,16 @@ public class StaminaManager { cachedEntity = entity; MotionInfo motionInfo = moveInfo.getMotionInfo(); MotionState motionState = motionInfo.getState(); - boolean isReliable = moveInfo.getIsReliable(); - Grasscutter.getLogger().trace("" + motionState + "\t" + (isReliable ? "reliable" : "")); - if (isReliable) { - currentState = motionState; - Vector posVector = motionInfo.getPos(); - Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ()); - if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) { - currentCoordinates = newPos; - } + int notifyEntityId = entity.getId(); + int currentAvatarEntityId = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId(); + if (notifyEntityId != currentAvatarEntityId) { + return; + } + currentState = motionState; + Vector posVector = motionInfo.getPos(); + Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ()); + if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) { + currentCoordinates = newPos; } startSustainedStaminaHandler(); handleImmediateStamina(session, motionInfo, motionState, entity); @@ -287,50 +291,48 @@ public class StaminaManager { private class SustainedStaminaHandler extends TimerTask { public void run() { - if (Grasscutter.getConfig().OpenStamina) { - boolean moving = isPlayerMoving(); - int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); - int maxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA); - if (moving || (currentStamina < maxStamina)) { - Grasscutter.getLogger().trace("Player moving: " + moving + ", stamina full: " + - (currentStamina >= maxStamina) + ", recalculate stamina"); + boolean moving = isPlayerMoving(); + int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); + int maxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA); + if (moving || (currentStamina < maxStamina)) { + Grasscutter.getLogger().trace("Player moving: " + moving + ", stamina full: " + + (currentStamina >= maxStamina) + ", recalculate stamina"); - Consumption consumption = new Consumption(ConsumptionType.None); - if (MotionStatesCategorized.get("CLIMB").contains(currentState)) { - consumption = getClimbSustainedConsumption(); - } else if (MotionStatesCategorized.get("SWIM").contains((currentState))) { - consumption = getSwimSustainedConsumptions(); - } else if (MotionStatesCategorized.get("RUN").contains(currentState)) { - consumption = getRunWalkDashSustainedConsumption(); - } else if (MotionStatesCategorized.get("FLY").contains(currentState)) { - consumption = getFlySustainedConsumption(); - } else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) { - consumption = getStandSustainedConsumption(); - } + Consumption consumption = new Consumption(ConsumptionType.None); + if (MotionStatesCategorized.get("CLIMB").contains(currentState)) { + consumption = getClimbSustainedConsumption(); + } else if (MotionStatesCategorized.get("SWIM").contains((currentState))) { + consumption = getSwimSustainedConsumptions(); + } else if (MotionStatesCategorized.get("RUN").contains(currentState)) { + consumption = getRunWalkDashSustainedConsumption(); + } else if (MotionStatesCategorized.get("FLY").contains(currentState)) { + consumption = getFlySustainedConsumption(); + } else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) { + consumption = getStandSustainedConsumption(); + } - /* - TODO: Reductions that apply to all motion types: - Elemental Resonance - Wind: -15% - Skills - Diona E: -10% while shield lasts - Barbara E: -12% while lasts - */ - if (cachedSession != null) { - if (consumption.amount < 0) { - staminaRecoverDelay = 0; - } - if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) { - // For POWERED_FLY recover immediately - things like Amber's gliding exam may require this. - if (staminaRecoverDelay < 10) { - // For others recover after 2 seconds (10 ticks) - as official server does. - staminaRecoverDelay++; - consumption.amount = 0; - Grasscutter.getLogger().trace("[StaminaManager] Delaying recovery: " + staminaRecoverDelay); - } - } - updateStaminaRelative(cachedSession, consumption); + /* + TODO: Reductions that apply to all motion types: + Elemental Resonance + Wind: -15% + Skills + Diona E: -10% while shield lasts + Barbara E: -12% while lasts + */ + if (cachedSession != null) { + if (consumption.amount < 0) { + staminaRecoverDelay = 0; } + if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) { + // For POWERED_FLY recover immediately - things like Amber's gliding exam may require this. + if (staminaRecoverDelay < 10) { + // For others recover after 2 seconds (10 ticks) - as official server does. + staminaRecoverDelay++; + consumption.amount = 0; + Grasscutter.getLogger().trace("[StaminaManager] Delaying recovery: " + staminaRecoverDelay); + } + } + updateStaminaRelative(cachedSession, consumption); } } previousState = currentState; diff --git a/src/main/java/emu/grasscutter/game/tower/TowerManager.java b/src/main/java/emu/grasscutter/game/tower/TowerManager.java index 9346ffead..cac848ea6 100644 --- a/src/main/java/emu/grasscutter/game/tower/TowerManager.java +++ b/src/main/java/emu/grasscutter/game/tower/TowerManager.java @@ -7,10 +7,7 @@ import emu.grasscutter.data.def.TowerLevelData; import emu.grasscutter.game.dungeons.DungeonSettleListener; import emu.grasscutter.game.dungeons.TowerDungeonSettleListener; import emu.grasscutter.game.player.Player; -import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify; -import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify; -import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp; -import emu.grasscutter.server.packet.send.PacketTowerLevelStarCondNotify; +import emu.grasscutter.server.packet.send.*; import java.util.HashMap; import java.util.List; @@ -152,4 +149,10 @@ public class TowerManager { return recordMap.get(player.getServer().getTowerScheduleManager().getLastEntranceFloor()) .getStarCount() >= 6; } + + public void mirrorTeamSetUp(int teamId) { + // use team user choose + player.getTeamManager().useTemporaryTeam(teamId); + player.sendPacket(new PacketTowerMiddleLevelChangeTeamNotify()); + } } diff --git a/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java b/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java index 33f5c158d..952acd806 100644 --- a/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java +++ b/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java @@ -49,6 +49,7 @@ public class TowerScheduleManager { public int getNextFloorId(int floorId){ var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId(); + var scheduleFloors = getScheduleFloors(); var nextId = 0; // find in entrance floors first for(int i=0;i variables; - private Bindings bindings; private SceneConfig config; private List blocks; private boolean isInit; - - private final Int2ObjectOpenHashMap> triggers; + /** + * SceneTrigger Set + */ + private final Map triggers; + /** + * current triggers controlled by RefreshGroup + */ + private final Int2ObjectOpenHashMap> currentTriggers; private final Int2ObjectOpenHashMap regions; + private Map sceneGroups; private SceneGroup currentGroup; - private AtomicInteger monsterAlive; - private AtomicInteger monsterTideCount; - private int monsterSceneLimit; - private ConcurrentLinkedQueue monsterOrders; + private ScriptMonsterTideService scriptMonsterTideService; + private ScriptMonsterSpawnService scriptMonsterSpawnService; public SceneScriptManager(Scene scene) { this.scene = scene; this.scriptLib = new ScriptLib(this); this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib); - this.triggers = new Int2ObjectOpenHashMap<>(); + this.triggers = new HashMap<>(); + this.currentTriggers = new Int2ObjectOpenHashMap<>(); + this.regions = new Int2ObjectOpenHashMap<>(); this.variables = new HashMap<>(); - + this.sceneGroups = new HashMap<>(); + this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this); + // TEMPORARY if (this.getScene().getId() < 10) { return; @@ -103,17 +108,35 @@ public class SceneScriptManager { } public Set getTriggersByEvent(int eventId) { - return triggers.computeIfAbsent(eventId, e -> new HashSet<>()); + return currentTriggers.computeIfAbsent(eventId, e -> new HashSet<>()); } - public void registerTrigger(SceneTrigger trigger) { + this.triggers.put(trigger.name, trigger); getTriggersByEvent(trigger.event).add(trigger); } public void deregisterTrigger(SceneTrigger trigger) { + this.triggers.remove(trigger.name); getTriggersByEvent(trigger.event).remove(trigger); } - + public void resetTriggers(List triggerNames) { + for(var name : triggerNames){ + var instance = triggers.get(name); + this.currentTriggers.get(instance.event).clear(); + this.currentTriggers.get(instance.event).add(instance); + } + } + public void refreshGroup(SceneGroup group, int suiteIndex){ + var suite = group.getSuiteByIndex(suiteIndex); + if(suite == null){ + return; + } + if(suite.triggers.size() > 0){ + resetTriggers(suite.triggers); + } + spawnMonstersInGroup(group, suite); + spawnGadgetsInGroup(group, suite); + } public SceneRegion getRegionById(int id) { return regions.get(id); } @@ -263,6 +286,7 @@ public class SceneScriptManager { } } } + this.sceneGroups.put(group.id, group); } catch (ScriptException e) { Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e); } @@ -322,96 +346,36 @@ public class SceneScriptManager { this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId())); } } - + public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) { var suite = group.getSuiteByIndex(suiteIndex); if(suite == null){ return; } - if(suite.sceneMonsters.size() > 0){ - this.currentGroup = group; - this.monsterSceneLimit = 0; - suite.sceneMonsters.forEach(mob -> spawnMonstersInGroup(group, mob)); + spawnMonstersInGroup(group, suite); + } + public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) { + if(suite == null || suite.sceneMonsters.size() <= 0){ + return; } + this.currentGroup = group; + suite.sceneMonsters.forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob)); } public void spawnMonstersInGroup(SceneGroup group) { this.currentGroup = group; - this.monsterSceneLimit = 0; - group.monsters.values().forEach(mob -> spawnMonstersInGroup(group, mob)); + group.monsters.values().forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob)); } - public void spawnMonstersInGroup(SceneGroup group,Integer[] ordersConfigId, int tideCount, int sceneLimit) { + + public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) { this.currentGroup = group; - this.monsterSceneLimit = sceneLimit; - this.monsterTideCount = new AtomicInteger(tideCount); - this.monsterAlive = new AtomicInteger(0); - this.monsterOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId)); + this.scriptMonsterTideService = + new ScriptMonsterTideService(this, group, tideCount, sceneLimit, ordersConfigId); - // add the last turn - group.monsters.keySet().stream() - .filter(i -> !this.monsterOrders.contains(i)) - .forEach(this.monsterOrders::add); - for (int i = 0; i < sceneLimit; i++) { - spawnMonstersInGroup(group, group.monsters.get(this.monsterOrders.poll())); - } } - public void spawnMonstersInGroup(SceneGroup group, SceneMonster monster) { - if(monster == null){ - return; - } - if(this.monsterSceneLimit > 0){ - this.monsterTideCount.decrementAndGet(); - this.monsterAlive.incrementAndGet(); - } - - MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); - - if (data == null) { - return; - } - - // Calculate level - int level = monster.level; - - if (getScene().getDungeonData() != null) { - level = getScene().getDungeonData().getShowLevel(); - } else if (getScene().getWorld().getWorldLevel() > 0) { - WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getScene().getWorld().getWorldLevel()); - - if (worldLevelData != null) { - level = worldLevelData.getMonsterLevel(); - } - } - - // Spawn mob - EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level); - entity.getRotation().set(monster.rot); - entity.setGroupId(group.id); - entity.setConfigId(monster.config_id); - - getScene().addEntity(entity); - - callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); - } - - public void onMonsterDie(){ - if(this.monsterSceneLimit <= 0){ - return; - } - if(this.monsterAlive.decrementAndGet() >= this.monsterSceneLimit) { - // maybe not happen - return; - } - if(this.monsterTideCount.get() > 0){ - // add more - spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll())); - }else if(this.monsterAlive.get() == 0){ - // spawn the last turn of monsters - //callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs()); - while(!this.monsterOrders.isEmpty()){ - spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll())); - } - } + public void spawnMonstersByConfigId(int configId, int delayTime) { + // TODO delay + this.scriptMonsterSpawnService.spawnMonster(this.currentGroup.id, this.currentGroup.monsters.get(configId)); } // Events @@ -432,17 +396,35 @@ public class SceneScriptManager { args = CoerceJavaToLua.coerce(params); } - ret = condition.call(this.getScriptLibLua(), args); + ret = safetyCall(trigger.condition, condition, args); } - if (ret.checkboolean() == true) { + if (ret.isboolean() && ret.checkboolean()) { LuaValue action = (LuaValue) this.getBindings().get(trigger.action); - action.call(this.getScriptLibLua(), LuaValue.NIL); + var arg = new ScriptArgs(); + arg.param2 = 100; + var args = CoerceJavaToLua.coerce(arg); + safetyCall(trigger.action, action, args); } + //TODO some ret may not bool } } -// public LuaValue safetyCall(){ -// -// } + public LuaValue safetyCall(String name, LuaValue func, LuaValue args){ + try{ + return func.call(this.getScriptLibLua(), args); + }catch (LuaError error){ + ScriptLib.logger.error("[LUA] call trigger failed {},{}",name,args,error); + return LuaValue.valueOf(-1); + } + } + + public ScriptMonsterTideService getScriptMonsterTideService() { + return scriptMonsterTideService; + } + + public ScriptMonsterSpawnService getScriptMonsterSpawnService() { + return scriptMonsterSpawnService; + } + } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 1c4bbd0f2..0d686dd5a 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -28,7 +28,17 @@ public class ScriptLib { public SceneScriptManager getSceneScriptManager() { return sceneScriptManager; } - + + private String printTable(LuaTable table){ + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for(var meta : table.keys()){ + sb.append(meta).append(":").append(table.get(meta)).append(","); + } + sb.append("}"); + return sb.toString(); + } + public int SetGadgetStateByConfigId(int configId, int gadgetState) { logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}", configId,gadgetState); @@ -123,7 +133,7 @@ public class ScriptLib { return 1; } - this.getSceneScriptManager().spawnMonstersInGroup(group, ordersConfigId, tideCount, sceneLimit); + this.getSceneScriptManager().startMonsterTideInGroup(group, ordersConfigId, tideCount, sceneLimit); return 0; } @@ -204,10 +214,13 @@ public class ScriptLib { getSceneScriptManager().getVariables().put(var, getSceneScriptManager().getVariables().get(var) + value); return LuaValue.ZERO; } - + + /** + * Set the actions and triggers to designated group + */ public int RefreshGroup(LuaTable table) { logger.debug("[LUA] Call RefreshGroup with {}", - table); + printTable(table)); // Kill and Respawn? int groupId = table.get("group_id").toint(); int suite = table.get("suite").toint(); @@ -218,8 +231,7 @@ public class ScriptLib { return 1; } - this.getSceneScriptManager().spawnMonstersInGroup(group, suite); - this.getSceneScriptManager().spawnGadgetsInGroup(group, suite); + getSceneScriptManager().refreshGroup(group, suite); return 0; } @@ -260,7 +272,7 @@ public class ScriptLib { public int SetMonsterBattleByGroup(int var1, int var2, int var3){ logger.debug("[LUA] Call SetMonsterBattleByGroup with {},{},{}", var1,var2,var3); - + // TODO return 0; } @@ -270,7 +282,7 @@ public class ScriptLib { return 0; } - // 8-1 + public int GetGroupVariableValueByGroup(String name, int groupId){ logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}", name,groupId); @@ -288,7 +300,7 @@ public class ScriptLib { public int KillEntityByConfigId(LuaTable table){ logger.debug("[LUA] Call KillEntityByConfigId with {}", - table); + printTable(table)); var configId = table.get("config_id"); if(configId == LuaValue.NIL){ return 1; @@ -306,6 +318,26 @@ public class ScriptLib { logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}", key,value,groupId); + getSceneScriptManager().getVariables().put(key, value); + return 0; + } + + public int CreateMonster(LuaTable table){ + logger.debug("[LUA] Call CreateMonster with {}", + printTable(table)); + var configId = table.get("config_id").toint(); + var delayTime = table.get("delay_time").toint(); + + getSceneScriptManager().spawnMonstersByConfigId(configId, delayTime); + return 0; + } + + public int TowerMirrorTeamSetUp(int team, int var1) { + logger.debug("[LUA] Call TowerMirrorTeamSetUp with {},{}", + team,var1); + + getSceneScriptManager().getScene().getPlayers().get(0).getTowerManager().mirrorTeamSetUp(team-1); + return 0; } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java b/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java index a1603b1e6..a627f67c4 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java @@ -7,4 +7,18 @@ public class SceneTrigger { public String source; public String condition; public String action; + + @Override + public boolean equals(Object obj) { + if(obj instanceof SceneTrigger sceneTrigger){ + return this.name.equals(sceneTrigger.name); + } + return super.equals(obj); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + } diff --git a/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterSpawnService.java b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterSpawnService.java new file mode 100644 index 000000000..0d6baf25d --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterSpawnService.java @@ -0,0 +1,73 @@ +package emu.grasscutter.scripts.service; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.data.def.WorldLevelData; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.scripts.SceneScriptManager; +import emu.grasscutter.scripts.constants.EventType; +import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.ScriptArgs; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class ScriptMonsterSpawnService { + + private final SceneScriptManager sceneScriptManager; + private final List> onMonsterCreatedListener = new ArrayList<>(); + + private final List> onMonsterDeadListener = new ArrayList<>(); + + public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){ + this.sceneScriptManager = sceneScriptManager; + } + + public void addMonsterCreatedListener(Consumer consumer){ + onMonsterCreatedListener.add(consumer); + } + public void addMonsterDeadListener(Consumer consumer){ + onMonsterDeadListener.add(consumer); + } + + public void onMonsterDead(EntityMonster entityMonster){ + onMonsterDeadListener.forEach(l -> l.accept(entityMonster)); + } + public void spawnMonster(int groupId, SceneMonster monster) { + if(monster == null){ + return; + } + + MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); + + if (data == null) { + return; + } + + // Calculate level + int level = monster.level; + + if (sceneScriptManager.getScene().getDungeonData() != null) { + level = sceneScriptManager.getScene().getDungeonData().getShowLevel(); + } else if (sceneScriptManager.getScene().getWorld().getWorldLevel() > 0) { + WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(sceneScriptManager.getScene().getWorld().getWorldLevel()); + + if (worldLevelData != null) { + level = worldLevelData.getMonsterLevel(); + } + } + + // Spawn mob + EntityMonster entity = new EntityMonster(sceneScriptManager.getScene(), data, monster.pos, level); + entity.getRotation().set(monster.rot); + entity.setGroupId(groupId); + entity.setConfigId(monster.config_id); + + onMonsterCreatedListener.forEach(action -> action.accept(entity)); + + sceneScriptManager.getScene().addEntity(entity); + + sceneScriptManager.callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); + } +} diff --git a/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterTideService.java b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterTideService.java new file mode 100644 index 000000000..3cf186188 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterTideService.java @@ -0,0 +1,74 @@ +package emu.grasscutter.scripts.service; + +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.scripts.SceneScriptManager; +import emu.grasscutter.scripts.constants.EventType; +import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.ScriptArgs; + +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class ScriptMonsterTideService { + private final SceneScriptManager sceneScriptManager; + private final SceneGroup currentGroup; + private final AtomicInteger monsterAlive; + private final AtomicInteger monsterTideCount; + private final AtomicInteger monsterKillCount; + private final int monsterSceneLimit; + private final ConcurrentLinkedQueue monsterConfigOrders; + + public ScriptMonsterTideService(SceneScriptManager sceneScriptManager, + SceneGroup group, int tideCount, int monsterSceneLimit, Integer[] ordersConfigId){ + this.sceneScriptManager = sceneScriptManager; + this.currentGroup = group; + this.monsterSceneLimit = monsterSceneLimit; + this.monsterTideCount = new AtomicInteger(tideCount); + this.monsterKillCount = new AtomicInteger(0); + this.monsterAlive = new AtomicInteger(0); + this.monsterConfigOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId)); + + this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterCreatedListener(this::onMonsterCreated); + this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(this::onMonsterDead); + // spawn the first turn + for (int i = 0; i < this.monsterSceneLimit; i++) { + this.sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(group.id, getNextMonster()); + } + } + + public void onMonsterCreated(EntityMonster entityMonster){ + if(this.monsterSceneLimit > 0){ + this.monsterTideCount.decrementAndGet(); + this.monsterAlive.incrementAndGet(); + } + } + + public SceneMonster getNextMonster(){ + var nextId = this.monsterConfigOrders.poll(); + if(currentGroup.monsters.containsKey(nextId)){ + return currentGroup.monsters.get(nextId); + } + // TODO some monster config_id do not exist in groups, so temporarily set it to the first + return currentGroup.monsters.values().stream().findFirst().orElse(null); + } + + public void onMonsterDead(EntityMonster entityMonster){ + if(this.monsterSceneLimit <= 0){ + return; + } + if(this.monsterAlive.decrementAndGet() >= this.monsterSceneLimit) { + // maybe not happen + return; + } + this.monsterKillCount.incrementAndGet(); + if(this.monsterTideCount.get() > 0){ + // add more + this.sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(this.currentGroup.id, getNextMonster()); + } + // spawn the last turn of monsters + // fix the 5-2 + this.sceneScriptManager.callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs(this.monsterKillCount.get())); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerMiddleLevelChangeTeamNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerMiddleLevelChangeTeamNotify.java new file mode 100644 index 000000000..f778c68aa --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerMiddleLevelChangeTeamNotify.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.TowerMiddleLevelChangeTeamNotifyOuterClass; + +public class PacketTowerMiddleLevelChangeTeamNotify extends BasePacket { + + public PacketTowerMiddleLevelChangeTeamNotify() { + super(PacketOpcodes.TowerMiddleLevelChangeTeamNotify); + + TowerMiddleLevelChangeTeamNotifyOuterClass.TowerMiddleLevelChangeTeamNotify proto = + TowerMiddleLevelChangeTeamNotifyOuterClass.TowerMiddleLevelChangeTeamNotify.newBuilder() + .build(); + + this.setData(proto); + } +} diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index 4ec17a214..9e7271ea3 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -334,6 +334,13 @@ }, "restart": { "description": "Restarts the current session" + }, + "unlocktower": { + "success": "unlock done", + "description": "Unlock all levels of tower" + }, + "resetshop": { + "description": "reset shop" } } } diff --git a/src/main/resources/languages/pl-PL.json b/src/main/resources/languages/pl-PL.json index e9ff74e25..8f76d8951 100644 --- a/src/main/resources/languages/pl-PL.json +++ b/src/main/resources/languages/pl-PL.json @@ -184,7 +184,7 @@ "account_error": "Konto nie może zostać znalezione." }, "position": { - "success": "Koordynaty: %.3f, %.3f, %.3f\nID sceny: %d" + "success": "Koordynaty: %s, %s, %s\nID sceny: %s" }, "reload": { "reload_start": "Ponowne ładowanie konfiguracji.", @@ -293,6 +293,13 @@ "usage": "Użycie: ", "aliases": "Aliasy: ", "available_commands": "Dostępne komendy: " + }, + "unlocktower": { + "success": "odblokować gotowe", + "description": "Odblokuj głęboką spiralę" + }, + "resetshop": { + "description": "zresetuj sklep" } } } \ No newline at end of file diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 89d1d6a90..f9e750438 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -201,7 +201,7 @@ "description": "给予或移除指定玩家的权限。" }, "position": { - "success": "坐标:%.3f, %.3f, %.3f\n场景ID:%d", + "success": "坐标:%s, %s, %s\n场景ID:%s", "description": "获取所在位置。" }, "reload": { @@ -249,7 +249,7 @@ "setFetterLevel": { "usage": "用法:setfetterlevel ", "range_error": "好感度等级必须在 0 到 10 之间。", - "fetter_set_level": "好感度已设置为 %s 级", + "success": "好感度已设置为 %s 级", "level_error": "无效的好感度等级。", "description": "设置当前角色的好感度等级。" }, @@ -330,6 +330,13 @@ }, "restart": { "description": "重新启动服务器。" + }, + "unlocktower": { + "success": "解锁完成。", + "description": "解锁深境螺旋的所有层" + }, + "resetshop": { + "description": "重置商店时间" } } } diff --git a/src/main/resources/languages/zh-TW.json b/src/main/resources/languages/zh-TW.json index 7a7f5c100..9c3c99686 100644 --- a/src/main/resources/languages/zh-TW.json +++ b/src/main/resources/languages/zh-TW.json @@ -184,7 +184,7 @@ "account_error": "The account cannot be found." }, "position": { - "success": "坐標:%.3f, %.3f, %.3f\n場景ID:%d" + "success": "坐標:%s, %s, %s\n場景ID:%s" }, "reload": { "reload_start": "正在重新加載設定檔。", @@ -226,7 +226,7 @@ "setFetterLevel": { "usage": "用法:setfetterlevel ", "range_error": "好感度必須在 0 到 10 之間。", - "fetter_set_level": "好感等級已設定為 %s", + "success": "好感等級已設定為 %s", "level_error": "無效的好感度。" }, "setStats": { @@ -293,6 +293,13 @@ "usage": "用法:", "aliases": "別名:", "available_commands": "可用指令:" + }, + "unlocktower": { + "success": "解鎖完成。", + "description": "解鎖所有級別的深境螺旋" + }, + "resetshop": { + "description": "重置商店時間" } } }