mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-24 03:38:04 +00:00
merge
This commit is contained in:
commit
6a8cc01dc1
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -31,4 +31,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Grasscutter
|
name: Grasscutter
|
||||||
path: grasscutter-*-dev.jar
|
path: grasscutter-*.jar
|
||||||
|
@ -43,7 +43,7 @@ sourceCompatibility = JavaVersion.VERSION_17
|
|||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
||||||
group = 'xyz.grasscutters'
|
group = 'xyz.grasscutters'
|
||||||
version = '1.0.3-dev'
|
version = '1.1.0'
|
||||||
|
|
||||||
sourceCompatibility = 17
|
sourceCompatibility = 17
|
||||||
targetCompatibility = 17
|
targetCompatibility = 17
|
||||||
|
49
plugin-schema.json
Normal file
49
plugin-schema.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "JSON schema for a Grasscutter Plugin",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true,
|
||||||
|
"definitions": {
|
||||||
|
"plugin-name": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[A-Za-z\\d_.-]+$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [ "name", "description", "mainClass" ],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "The unique name of plugin.",
|
||||||
|
"$ref": "#/definitions/plugin-name"
|
||||||
|
},
|
||||||
|
"mainClass": {
|
||||||
|
"description": "The plugin's initial class file.",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?!org\\.bukkit\\.)([a-zA-Z_$][a-zA-Z\\d_$]*\\.)*[a-zA-Z_$][a-zA-Z\\d_$]*$"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"description": "A plugin revision identifier.",
|
||||||
|
"type": [ "string", "number" ]
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"description": "Human readable plugin summary.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"description": "The plugin author.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"authors": {
|
||||||
|
"description": "The plugin contributors.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"website": {
|
||||||
|
"title": "Website",
|
||||||
|
"description": "The URL to the plugin's site",
|
||||||
|
"type": "string",
|
||||||
|
"format": "uri"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
option csharp_namespace = "YSFreedom.Common.Protocol";
|
|
||||||
|
|
||||||
|
option java_package = "emu.grasscutter.net.proto";
|
||||||
|
|
||||||
message WorldPlayerReviveReq {
|
message WorldPlayerReviveReq {
|
||||||
enum CmdId {
|
enum CmdId {
|
||||||
|
@ -23,7 +23,7 @@ public final class Config {
|
|||||||
public GameServerOptions GameServer = new GameServerOptions();
|
public GameServerOptions GameServer = new GameServerOptions();
|
||||||
public DispatchServerOptions DispatchServer = new DispatchServerOptions();
|
public DispatchServerOptions DispatchServer = new DispatchServerOptions();
|
||||||
public Locale LocaleLanguage = Locale.getDefault();
|
public Locale LocaleLanguage = Locale.getDefault();
|
||||||
public Locale DefaultLanguage = Locale.US;
|
public Locale DefaultLanguage = Locale.ENGLISH;
|
||||||
|
|
||||||
public Boolean OpenStamina = true;
|
public Boolean OpenStamina = true;
|
||||||
public GameServerOptions getGameServerOptions() {
|
public GameServerOptions getGameServerOptions() {
|
||||||
|
@ -149,9 +149,10 @@ public final class Grasscutter {
|
|||||||
|
|
||||||
public static void loadLanguage() {
|
public static void loadLanguage() {
|
||||||
var locale = config.LocaleLanguage;
|
var locale = config.LocaleLanguage;
|
||||||
String languageTag = locale.toLanguageTag();
|
var languageTag = locale.toLanguageTag();
|
||||||
|
|
||||||
if (languageTag.equals("und")) {
|
if (languageTag.equals("und")) {
|
||||||
Grasscutter.getLogger().error("Illegal locale language, using en-US instead.");
|
Grasscutter.getLogger().error("Illegal locale language, using 'en-US' instead.");
|
||||||
language = Language.getLanguage("en-US");
|
language = Language.getLanguage("en-US");
|
||||||
} else {
|
} else {
|
||||||
language = Language.getLanguage(languageTag);
|
language = Language.getLanguage(languageTag);
|
||||||
|
@ -9,7 +9,7 @@ public @interface Command {
|
|||||||
|
|
||||||
String usage() default "No usage specified";
|
String usage() default "No usage specified";
|
||||||
|
|
||||||
String description() default "No description specified";
|
String description() default "commands.generic.no_description_specified";
|
||||||
|
|
||||||
String[] aliases() default {};
|
String[] aliases() default {};
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import emu.grasscutter.game.player.Player;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface CommandHandler {
|
public interface CommandHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to the target.
|
* Send a message to the target.
|
||||||
*
|
*
|
||||||
|
@ -79,6 +79,12 @@ public final class CommandMap {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Command> getAnnotationsAsList() { return new LinkedList<>(this.annotations.values()); }
|
||||||
|
|
||||||
|
public HashMap<String, Command> getAnnotations() {
|
||||||
|
return new LinkedHashMap<>(this.annotations);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all registered commands.
|
* Returns a list of all registered commands.
|
||||||
*
|
*
|
||||||
|
@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "account", usage = "account <create|delete> <username> [uid]", description = "Modify user accounts")
|
@Command(label = "account", usage = "account <create|delete> <username> [uid]", description = "commands.account.description")
|
||||||
public final class AccountCommand implements CommandHandler {
|
public final class AccountCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,8 +9,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "broadcast", usage = "broadcast <message>",
|
@Command(label = "broadcast", usage = "broadcast <message>", aliases = {"b"}, permission = "server.broadcast", description = "commands.broadcast.description")
|
||||||
description = "Sends a message to all the players", aliases = {"b"}, permission = "server.broadcast")
|
|
||||||
public final class BroadcastCommand implements CommandHandler {
|
public final class BroadcastCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,9 +8,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "changescene", usage = "changescene <scene id>",
|
@Command(label = "changescene", usage = "changescene <scene id>", aliases = {"scene"}, permission = "player.changescene", description = "commands.changescene.description")
|
||||||
description = "Changes your scene", aliases = {"scene"}, permission = "player.changescene")
|
|
||||||
public final class ChangeSceneCommand implements CommandHandler {
|
public final class ChangeSceneCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||||||
import static emu.grasscutter.utils.Language.translate;
|
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>", //Merged /clearartifacts and /clearweapons to /clear <args> [uid]
|
||||||
description = "Deletes unequipped unlocked items, including yellow rarity ones from your inventory",
|
description = "commands.clear.description",
|
||||||
aliases = {"clear"}, permission = "player.clearinv")
|
aliases = {"clear"}, permission = "player.clearinv")
|
||||||
|
|
||||||
public final class ClearCommand implements CommandHandler {
|
public final class ClearCommand implements CommandHandler {
|
||||||
|
@ -9,9 +9,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "coop", usage = "coop [host UID]",
|
@Command(label = "coop", usage = "coop [host UID]", permission = "server.coop", description = "commands.coop.description")
|
||||||
description = "Forces someone to join the world of others", permission = "server.coop")
|
|
||||||
public final class CoopCommand implements CommandHandler {
|
public final class CoopCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
|
@ -13,8 +13,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "drop", usage = "drop <itemId|itemName> [amount]",
|
@Command(label = "drop", usage = "drop <itemId|itemName> [amount]", aliases = {"d", "dropitem"}, permission = "server.drop", description = "commands.drop.description")
|
||||||
description = "Drops an item near you", aliases = {"d", "dropitem"}, permission = "server.drop")
|
|
||||||
public final class DropCommand implements CommandHandler {
|
public final class DropCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,9 +8,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "enterdungeon", usage = "enterdungeon <dungeon id>",
|
@Command(label = "enterdungeon", usage = "enterdungeon <dungeon id>", aliases = {"dungeon"}, permission = "player.enterdungeon", description = "commands.enter_dungeon.description")
|
||||||
description = "Enter a dungeon", aliases = {"dungeon"}, permission = "player.enterdungeon")
|
|
||||||
public final class EnterDungeonCommand implements CommandHandler {
|
public final class EnterDungeonCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
|
@ -15,8 +15,7 @@ import java.util.*;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "giveall", usage = "giveall [amount]",
|
@Command(label = "giveall", usage = "giveall [amount]", aliases = {"givea"}, permission = "player.giveall", threading = true, description = "commands.giveAll.description")
|
||||||
description = "Gives all items", aliases = {"givea"}, permission = "player.giveall", threading = true)
|
|
||||||
public final class GiveAllCommand implements CommandHandler {
|
public final class GiveAllCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,8 +16,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "giveart", usage = "giveart <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]", description = "Gives the player a specified artifact", aliases = {"gart"}, permission = "player.giveart")
|
@Command(label = "giveart", usage = "giveart <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]", aliases = {"gart"}, permission = "player.giveart", description = "commands.giveArtifact.description")
|
||||||
public final class GiveArtifactCommand implements CommandHandler {
|
public final class GiveArtifactCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
|
@ -12,8 +12,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "givechar", usage = "givechar <avatarId> [level]",
|
@Command(label = "givechar", usage = "givechar <avatarId> [level]", aliases = {"givec"}, permission = "player.givechar", description = "commands.giveChar.description")
|
||||||
description = "Gives the player a specified character", aliases = {"givec"}, permission = "player.givechar")
|
|
||||||
public final class GiveCharCommand implements CommandHandler {
|
public final class GiveCharCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -77,6 +76,6 @@ public final class GiveCharCommand implements CommandHandler {
|
|||||||
avatar.recalcStats();
|
avatar.recalcStats();
|
||||||
|
|
||||||
targetPlayer.addAvatar(avatar);
|
targetPlayer.addAvatar(avatar);
|
||||||
CommandHandler.sendMessage(sender, translate("commands.execution.giveChar.given", Integer.toString(avatarId), Integer.toString(level), Integer.toString(targetPlayer.getUid())));
|
CommandHandler.sendMessage(sender, translate("commands.giveChar.given", Integer.toString(avatarId), Integer.toString(level), Integer.toString(targetPlayer.getUid())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package emu.grasscutter.command.commands;
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.command.Command;
|
import emu.grasscutter.command.Command;
|
||||||
import emu.grasscutter.command.CommandHandler;
|
import emu.grasscutter.command.CommandHandler;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
@ -12,13 +11,13 @@ import emu.grasscutter.game.props.ActionReason;
|
|||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "give", usage = "give <itemId|itemName> [amount] [level]", description = "Gives an item to you or the specified player", aliases = {
|
@Command(label = "give", usage = "give <itemId|itemName> [amount] [level]", aliases = {
|
||||||
"g", "item", "giveitem"}, permission = "player.give")
|
"g", "item", "giveitem"}, permission = "player.give", description = "commands.give.description")
|
||||||
public final class GiveCommand implements CommandHandler {
|
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 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+)");
|
Pattern refineRegex = Pattern.compile("r(\\d+)");
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package emu.grasscutter.command.commands;
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.command.Command;
|
import emu.grasscutter.command.Command;
|
||||||
import emu.grasscutter.command.CommandHandler;
|
import emu.grasscutter.command.CommandHandler;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
@ -9,8 +8,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "godmode", usage = "godmode [on|off|toggle]",
|
@Command(label = "godmode", usage = "godmode [on|off|toggle]", permission = "player.godmode", description = "commands.godmode.description")
|
||||||
description = "Prevents you from taking damage. Defaults to toggle.", permission = "player.godmode")
|
|
||||||
public final class GodModeCommand implements CommandHandler {
|
public final class GodModeCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -11,9 +11,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "heal", usage = "heal|h", aliases = {"h"},
|
@Command(label = "heal", usage = "heal|h", aliases = {"h"}, permission = "player.heal", description = "commands.heal.description")
|
||||||
description = "Heal all characters in your current team.", permission = "player.heal")
|
|
||||||
public final class HealCommand implements CommandHandler {
|
public final class HealCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
|
@ -10,8 +10,7 @@ import java.util.*;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "help", usage = "help [command]",
|
@Command(label = "help", usage = "help [command]", description = "commands.help.description")
|
||||||
description = "Sends the help message or shows information about a specified command")
|
|
||||||
public final class HelpCommand implements CommandHandler {
|
public final class HelpCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -39,7 +38,7 @@ public final class HelpCommand implements CommandHandler {
|
|||||||
} else {
|
} else {
|
||||||
Command annotation = handler.getClass().getAnnotation(Command.class);
|
Command annotation = handler.getClass().getAnnotation(Command.class);
|
||||||
|
|
||||||
builder.append(" ").append(annotation.description()).append("\n");
|
builder.append(" ").append(translate(annotation.description())).append("\n");
|
||||||
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
||||||
if (annotation.aliases().length >= 1) {
|
if (annotation.aliases().length >= 1) {
|
||||||
builder.append("\n").append(translate("commands.help.aliases"));
|
builder.append("\n").append(translate("commands.help.aliases"));
|
||||||
@ -61,7 +60,7 @@ public final class HelpCommand implements CommandHandler {
|
|||||||
StringBuilder builder = new StringBuilder("\n" + translate("commands.help.available_commands") + "\n");
|
StringBuilder builder = new StringBuilder("\n" + translate("commands.help.available_commands") + "\n");
|
||||||
annotations.forEach(annotation -> {
|
annotations.forEach(annotation -> {
|
||||||
builder.append(annotation.label()).append("\n");
|
builder.append(annotation.label()).append("\n");
|
||||||
builder.append(" ").append(annotation.description()).append("\n");
|
builder.append(" ").append(translate(annotation.description())).append("\n");
|
||||||
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
||||||
if (annotation.aliases().length >= 1) {
|
if (annotation.aliases().length >= 1) {
|
||||||
builder.append("\n").append(translate("commands.help.aliases"));
|
builder.append("\n").append(translate("commands.help.aliases"));
|
||||||
@ -78,7 +77,7 @@ public final class HelpCommand implements CommandHandler {
|
|||||||
CommandHandler.sendMessage(player, translate("commands.help.available_commands"));
|
CommandHandler.sendMessage(player, translate("commands.help.available_commands"));
|
||||||
annotations.forEach(annotation -> {
|
annotations.forEach(annotation -> {
|
||||||
StringBuilder builder = new StringBuilder(annotation.label()).append("\n");
|
StringBuilder builder = new StringBuilder(annotation.label()).append("\n");
|
||||||
builder.append(" ").append(annotation.description()).append("\n");
|
builder.append(" ").append(translate(annotation.description())).append("\n");
|
||||||
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
||||||
if (annotation.aliases().length >= 1) {
|
if (annotation.aliases().length >= 1) {
|
||||||
builder.append("\n").append(translate("commands.help.aliases"));
|
builder.append("\n").append(translate("commands.help.aliases"));
|
||||||
|
@ -8,8 +8,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "kick", usage = "kick",
|
@Command(label = "kick", usage = "kick", permission = "server.kick", description = "commands.kick.description")
|
||||||
description = "Kicks the specified player from the server (WIP)", permission = "server.kick")
|
|
||||||
public final class KickCommand implements CommandHandler {
|
public final class KickCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -12,8 +12,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "killall", usage = "killall [sceneId]",
|
@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", description = "commands.kill.description")
|
||||||
description = "Kill all entities", permission = "server.killall")
|
|
||||||
public final class KillAllCommand implements CommandHandler {
|
public final class KillAllCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -13,8 +13,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"},
|
@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", description = "commands.list.description")
|
||||||
description = "Kills the players current character", permission = "player.killcharacter")
|
|
||||||
public final class KillCharacterCommand implements CommandHandler {
|
public final class KillCharacterCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -10,8 +10,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "list", usage = "list [uid]",
|
@Command(label = "list", usage = "list [uid]", aliases = {"players"}, description = "commands.list.description")
|
||||||
description = "List online players", aliases = {"players"})
|
|
||||||
public final class ListCommand implements CommandHandler {
|
public final class ListCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -10,8 +10,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "permission", usage = "permission <add|remove> <permission>",
|
@Command(label = "permission", usage = "permission <add|remove> <permission>", permission = "*", description = "commands.permission.description")
|
||||||
description = "Grants or removes a permission for a user", permission = "*")
|
|
||||||
public final class PermissionCommand implements CommandHandler {
|
public final class PermissionCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,8 +9,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "position", usage = "position", aliases = {"pos"},
|
@Command(label = "position", usage = "position", aliases = {"pos"}, description = "commands.position.description")
|
||||||
description = "Get coordinates.")
|
|
||||||
public final class PositionCommand implements CommandHandler {
|
public final class PositionCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,8 +9,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "reload", usage = "reload",
|
@Command(label = "reload", usage = "reload", permission = "server.reload", description = "commands.reload.description")
|
||||||
description = "Reload server config", permission = "server.reload")
|
|
||||||
public final class ReloadCommand implements CommandHandler {
|
public final class ReloadCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -11,8 +11,7 @@ import java.util.List;
|
|||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "resetconst", usage = "resetconst [all]",
|
@Command(label = "resetconst", usage = "resetconst [all]",
|
||||||
description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.",
|
aliases = {"resetconstellation"}, permission = "player.resetconstellation", description = "commands.resetConst.description")
|
||||||
aliases = {"resetconstellation"}, permission = "player.resetconstellation")
|
|
||||||
public final class ResetConstCommand implements CommandHandler {
|
public final class ResetConstCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,9 +9,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "resetshop", usage = "resetshop",
|
@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", description = "commands.status.description")
|
||||||
description = "Reset target player's shop refresh time.", permission = "server.resetshop")
|
|
||||||
public final class ResetShopLimitCommand implements CommandHandler {
|
public final class ResetShopLimitCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
|
@ -6,7 +6,9 @@ import emu.grasscutter.game.player.Player;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Command(label = "restart", usage = "restart - Restarts the current session")
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
|
@Command(label = "restart", usage = "restart", description = "commands.restart.description")
|
||||||
public final class RestartCommand implements CommandHandler {
|
public final class RestartCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -13,8 +13,7 @@ import java.util.List;
|
|||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
@Command(label = "sendmail", usage = "sendmail <userId|all|help> [templateId]",
|
@Command(label = "sendmail", usage = "sendmail <userId|all|help> [templateId]", permission = "server.sendmail", description = "commands.sendMail.description")
|
||||||
description = "Sends mail to the specified user. The usage of this command changes based on it's composition state.", permission = "server.sendmail")
|
|
||||||
public final class SendMailCommand implements CommandHandler {
|
public final class SendMailCommand implements CommandHandler {
|
||||||
|
|
||||||
// TODO: You should be able to do /sendmail and then just send subsequent messages until you finish
|
// TODO: You should be able to do /sendmail and then just send subsequent messages until you finish
|
||||||
@ -40,7 +39,7 @@ public final class SendMailCommand implements CommandHandler {
|
|||||||
MailBuilder mailBuilder;
|
MailBuilder mailBuilder;
|
||||||
switch (args.get(0).toLowerCase()) {
|
switch (args.get(0).toLowerCase()) {
|
||||||
case "help" -> {
|
case "help" -> {
|
||||||
CommandHandler.sendMessage(sender, this.getClass().getAnnotation(Command.class).description() + "\nUsage: " + this.getClass().getAnnotation(Command.class).usage());
|
CommandHandler.sendMessage(sender, translate(this.getClass().getAnnotation(Command.class).description()) + "\nUsage: " + this.getClass().getAnnotation(Command.class).usage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "all" -> mailBuilder = new MailBuilder(true, new Mail());
|
case "all" -> mailBuilder = new MailBuilder(true, new Mail());
|
||||||
|
@ -8,8 +8,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "say", usage = "say <message>", description = "Sends a message to a player as the server",
|
@Command(label = "say", usage = "say <message>",
|
||||||
aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage")
|
aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage", description = "commands.sendMessage.description")
|
||||||
public final class SendMessageCommand implements CommandHandler {
|
public final class SendMessageCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -12,8 +12,7 @@ import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
|
|||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "setfetterlevel", usage = "setfetterlevel <level>",
|
@Command(label = "setfetterlevel", usage = "setfetterlevel <level>",
|
||||||
description = "Sets your fetter level for your current active character",
|
aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel", description = "commands.setFetterLevel.description")
|
||||||
aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel")
|
|
||||||
public final class SetFetterLevelCommand implements CommandHandler {
|
public final class SetFetterLevelCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -15,8 +15,7 @@ import emu.grasscutter.utils.Language;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "setstats", usage = "setstats|stats <stat> <value>",
|
@Command(label = "setstats", usage = "setstats|stats <stat> <value>", aliases = {"stats"}, permission = "player.setstats", description = "commands.setStats.description")
|
||||||
description = "Set fight property for your current active character", aliases = {"stats"}, permission = "player.setstats")
|
|
||||||
public final class SetStatsCommand implements CommandHandler {
|
public final class SetStatsCommand implements CommandHandler {
|
||||||
static class Stat {
|
static class Stat {
|
||||||
String name;
|
String name;
|
||||||
|
@ -10,8 +10,7 @@ import java.util.List;
|
|||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "setworldlevel", usage = "setworldlevel <level>",
|
@Command(label = "setworldlevel", usage = "setworldlevel <level>",
|
||||||
description = "Sets your world level (Relog to see proper effects)",
|
aliases = {"setworldlvl"}, permission = "player.setworldlevel", description = "commands.setWorldLevel.description")
|
||||||
aliases = {"setworldlvl"}, permission = "player.setworldlevel")
|
|
||||||
public final class SetWorldLevelCommand implements CommandHandler {
|
public final class SetWorldLevelCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,8 +22,7 @@ import java.util.Random;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "spawn", usage = "spawn <entityId> [amount] [level(monster only)]",
|
@Command(label = "spawn", usage = "spawn <entityId> [amount] [level(monster only)]", permission = "server.spawn", description = "commands.spawn.description")
|
||||||
description = "Spawns an entity near you", permission = "server.spawn")
|
|
||||||
public final class SpawnCommand implements CommandHandler {
|
public final class SpawnCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,8 +9,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "stop", usage = "stop",
|
@Command(label = "stop", usage = "stop", permission = "server.stop", description = "commands.stop.description")
|
||||||
description = "Stops the server", permission = "server.stop")
|
|
||||||
public final class StopCommand implements CommandHandler {
|
public final class StopCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -14,8 +14,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "talent", usage = "talent <talentID> <value>",
|
@Command(label = "talent", usage = "talent <talentID> <value>", permission = "player.settalent", description = "commands.talent.description")
|
||||||
description = "Set talent level for your current active character", permission = "player.settalent")
|
|
||||||
public final class TalentCommand implements CommandHandler {
|
public final class TalentCommand implements CommandHandler {
|
||||||
private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) {
|
private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) {
|
||||||
int oldLevel = avatar.getSkillLevelMap().get(talentId);
|
int oldLevel = avatar.getSkillLevelMap().get(talentId);
|
||||||
|
@ -10,9 +10,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "tpall", usage = "tpall",
|
@Command(label = "tpall", usage = "tpall", permission = "player.tpall", description = "commands.teleportAll.description")
|
||||||
description = "Teleports all players in your world to your position", permission = "player.tpall")
|
|
||||||
public final class TeleportAllCommand implements CommandHandler {
|
public final class TeleportAllCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
|
@ -10,8 +10,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "teleport", usage = "teleport <x> <y> <z> [scene id]", aliases = {"tp"},
|
@Command(label = "teleport", usage = "teleport <x> <y> <z> [scene id]", aliases = {"tp"}, permission = "player.teleport", description = "commands.teleport.description")
|
||||||
description = "Change the player's position.", permission = "player.teleport")
|
|
||||||
public final class TeleportCommand implements CommandHandler {
|
public final class TeleportCommand implements CommandHandler {
|
||||||
|
|
||||||
private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later
|
private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later
|
||||||
|
@ -11,8 +11,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@Command(label = "weather", usage = "weather <weatherId> [climateId]",
|
@Command(label = "weather", usage = "weather <weatherId> [climateId]", aliases = {"w"}, permission = "player.weather", description = "commands.weather.description")
|
||||||
description = "Changes the weather.", aliases = {"w"}, permission = "player.weather")
|
|
||||||
public final class WeatherCommand implements CommandHandler {
|
public final class WeatherCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,7 +96,7 @@ public class GachaBanner {
|
|||||||
return toProto("");
|
return toProto("");
|
||||||
}
|
}
|
||||||
public GachaInfo toProto(String sessionKey) {
|
public GachaInfo toProto(String sessionKey) {
|
||||||
String record = "https://"
|
String record = "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://"
|
||||||
+ (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ?
|
+ (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ?
|
||||||
Grasscutter.getConfig().getDispatchOptions().Ip :
|
Grasscutter.getConfig().getDispatchOptions().Ip :
|
||||||
Grasscutter.getConfig().getDispatchOptions().PublicIp)
|
Grasscutter.getConfig().getDispatchOptions().PublicIp)
|
||||||
|
@ -1,442 +0,0 @@
|
|||||||
package emu.grasscutter.game.managers.MovementManager;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.game.entity.EntityAvatar;
|
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
|
||||||
import emu.grasscutter.game.player.Player;
|
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
|
||||||
import emu.grasscutter.game.props.LifeState;
|
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
|
||||||
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass;
|
|
||||||
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
|
||||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
|
||||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass;
|
|
||||||
import emu.grasscutter.server.game.GameSession;
|
|
||||||
import emu.grasscutter.server.packet.send.*;
|
|
||||||
import emu.grasscutter.utils.Position;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.lang.Math;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class MovementManager {
|
|
||||||
|
|
||||||
public HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>();
|
|
||||||
|
|
||||||
private enum ConsumptionType {
|
|
||||||
None(0),
|
|
||||||
|
|
||||||
// consume
|
|
||||||
CLIMB_START(-500),
|
|
||||||
CLIMBING(-150),
|
|
||||||
CLIMB_JUMP(-2500),
|
|
||||||
DASH(-1800),
|
|
||||||
SPRINT(-360),
|
|
||||||
FLY(-60),
|
|
||||||
SWIM_DASH_START(-200),
|
|
||||||
SWIM_DASH(-200),
|
|
||||||
SWIMMING(-80),
|
|
||||||
FIGHT(0),
|
|
||||||
|
|
||||||
// restore
|
|
||||||
STANDBY(500),
|
|
||||||
RUN(500),
|
|
||||||
WALK(500),
|
|
||||||
STANDBY_MOVE(500),
|
|
||||||
POWERED_FLY(500);
|
|
||||||
|
|
||||||
public final int amount;
|
|
||||||
ConsumptionType(int amount) {
|
|
||||||
this.amount = amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Consumption {
|
|
||||||
public ConsumptionType consumptionType;
|
|
||||||
public int amount;
|
|
||||||
public Consumption(ConsumptionType ct, int a) {
|
|
||||||
consumptionType = ct;
|
|
||||||
amount = a;
|
|
||||||
}
|
|
||||||
public Consumption(ConsumptionType ct) {
|
|
||||||
this(ct, ct.amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MotionState previousState = MotionState.MOTION_STANDBY;
|
|
||||||
private MotionState currentState = MotionState.MOTION_STANDBY;
|
|
||||||
private Position previousCoordinates = new Position(0, 0, 0);
|
|
||||||
private Position currentCoordinates = new Position(0, 0, 0);
|
|
||||||
|
|
||||||
private final Player player;
|
|
||||||
|
|
||||||
private float landSpeed = 0;
|
|
||||||
private long landTimeMillisecond = 0;
|
|
||||||
private Timer movementManagerTickTimer;
|
|
||||||
private GameSession cachedSession = null;
|
|
||||||
private GameEntity cachedEntity = null;
|
|
||||||
private int staminaRecoverDelay = 0;
|
|
||||||
private int skillCaster = 0;
|
|
||||||
private int skillCasting = 0;
|
|
||||||
|
|
||||||
public MovementManager(Player player) {
|
|
||||||
previousCoordinates.add(new Position(0,0,0));
|
|
||||||
this.player = player;
|
|
||||||
|
|
||||||
MotionStatesCategorized.put("SWIM", new HashSet<>(Arrays.asList(
|
|
||||||
MotionState.MOTION_SWIM_MOVE,
|
|
||||||
MotionState.MOTION_SWIM_IDLE,
|
|
||||||
MotionState.MOTION_SWIM_DASH,
|
|
||||||
MotionState.MOTION_SWIM_JUMP
|
|
||||||
)));
|
|
||||||
|
|
||||||
MotionStatesCategorized.put("STANDBY", new HashSet<>(Arrays.asList(
|
|
||||||
MotionState.MOTION_STANDBY,
|
|
||||||
MotionState.MOTION_STANDBY_MOVE,
|
|
||||||
MotionState.MOTION_DANGER_STANDBY,
|
|
||||||
MotionState.MOTION_DANGER_STANDBY_MOVE,
|
|
||||||
MotionState.MOTION_LADDER_TO_STANDBY,
|
|
||||||
MotionState.MOTION_JUMP_UP_WALL_FOR_STANDBY
|
|
||||||
)));
|
|
||||||
|
|
||||||
MotionStatesCategorized.put("CLIMB", new HashSet<>(Arrays.asList(
|
|
||||||
MotionState.MOTION_CLIMB,
|
|
||||||
MotionState.MOTION_CLIMB_JUMP,
|
|
||||||
MotionState.MOTION_STANDBY_TO_CLIMB,
|
|
||||||
MotionState.MOTION_LADDER_IDLE,
|
|
||||||
MotionState.MOTION_LADDER_MOVE,
|
|
||||||
MotionState.MOTION_LADDER_SLIP,
|
|
||||||
MotionState.MOTION_STANDBY_TO_LADDER
|
|
||||||
)));
|
|
||||||
|
|
||||||
MotionStatesCategorized.put("FLY", new HashSet<>(Arrays.asList(
|
|
||||||
MotionState.MOTION_FLY,
|
|
||||||
MotionState.MOTION_FLY_IDLE,
|
|
||||||
MotionState.MOTION_FLY_SLOW,
|
|
||||||
MotionState.MOTION_FLY_FAST,
|
|
||||||
MotionState.MOTION_POWERED_FLY
|
|
||||||
)));
|
|
||||||
|
|
||||||
MotionStatesCategorized.put("RUN", new HashSet<>(Arrays.asList(
|
|
||||||
MotionState.MOTION_DASH,
|
|
||||||
MotionState.MOTION_DANGER_DASH,
|
|
||||||
MotionState.MOTION_DASH_BEFORE_SHAKE,
|
|
||||||
MotionState.MOTION_RUN,
|
|
||||||
MotionState.MOTION_DANGER_RUN,
|
|
||||||
MotionState.MOTION_WALK,
|
|
||||||
MotionState.MOTION_DANGER_WALK
|
|
||||||
)));
|
|
||||||
|
|
||||||
MotionStatesCategorized.put("FIGHT", new HashSet<>(Arrays.asList(
|
|
||||||
MotionState.MOTION_FIGHT
|
|
||||||
)));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handle(GameSession session, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo, GameEntity entity) {
|
|
||||||
if (movementManagerTickTimer == null) {
|
|
||||||
movementManagerTickTimer = new Timer();
|
|
||||||
movementManagerTickTimer.scheduleAtFixedRate(new MotionManagerTick(), 0, 200);
|
|
||||||
}
|
|
||||||
// cache info for later use in tick
|
|
||||||
cachedSession = session;
|
|
||||||
cachedEntity = entity;
|
|
||||||
|
|
||||||
MotionInfo motionInfo = moveInfo.getMotionInfo();
|
|
||||||
moveEntity(entity, moveInfo);
|
|
||||||
VectorOuterClass.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;
|
|
||||||
}
|
|
||||||
currentState = motionInfo.getState();
|
|
||||||
Grasscutter.getLogger().debug("" + currentState + "\t" + (moveInfo.getIsReliable() ? "reliable" : ""));
|
|
||||||
handleFallOnGround(motionInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void resetTimer() {
|
|
||||||
Grasscutter.getLogger().debug("MovementManager ticker stopped");
|
|
||||||
movementManagerTickTimer.cancel();
|
|
||||||
movementManagerTickTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void moveEntity(GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
|
|
||||||
entity.getPosition().set(moveInfo.getMotionInfo().getPos());
|
|
||||||
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
|
|
||||||
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
|
|
||||||
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
|
|
||||||
entity.setMotionState(moveInfo.getMotionInfo().getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPlayerMoving() {
|
|
||||||
float diffX = currentCoordinates.getX() - previousCoordinates.getX();
|
|
||||||
float diffY = currentCoordinates.getY() - previousCoordinates.getY();
|
|
||||||
float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ();
|
|
||||||
// Grasscutter.getLogger().debug("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates + ", " + diffX + ", " + diffY + ", " + diffZ);
|
|
||||||
return Math.abs(diffX) > 0.2 || Math.abs(diffY) > 0.1 || Math.abs(diffZ) > 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getCurrentStamina() {
|
|
||||||
return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getMaximumStamina() {
|
|
||||||
return player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns new stamina
|
|
||||||
public int updateStamina(GameSession session, int amount) {
|
|
||||||
int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
|
||||||
if (amount == 0) {
|
|
||||||
return currentStamina;
|
|
||||||
}
|
|
||||||
int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
|
||||||
int newStamina = currentStamina + amount;
|
|
||||||
if (newStamina < 0) {
|
|
||||||
newStamina = 0;
|
|
||||||
}
|
|
||||||
if (newStamina > playerMaxStamina) {
|
|
||||||
newStamina = playerMaxStamina;
|
|
||||||
}
|
|
||||||
session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
|
||||||
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
|
||||||
return newStamina;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleFallOnGround(@NotNull MotionInfo motionInfo) {
|
|
||||||
MotionState state = motionInfo.getState();
|
|
||||||
// land speed and fall on ground event arrive in different packets
|
|
||||||
// cache land speed
|
|
||||||
if (state == MotionState.MOTION_LAND_SPEED) {
|
|
||||||
landSpeed = motionInfo.getSpeed().getY();
|
|
||||||
landTimeMillisecond = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
if (state == MotionState.MOTION_FALL_ON_GROUND) {
|
|
||||||
// if not received immediately after MOTION_LAND_SPEED, discard this packet.
|
|
||||||
// TODO: Test in high latency.
|
|
||||||
int maxDelay = 200;
|
|
||||||
if ((System.currentTimeMillis() - landTimeMillisecond) > maxDelay) {
|
|
||||||
Grasscutter.getLogger().debug("MOTION_FALL_ON_GROUND received after " + maxDelay + "ms, discard.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float currentHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
|
||||||
float maxHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
|
||||||
float damage = 0;
|
|
||||||
Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
|
|
||||||
if (landSpeed < -23.5) {
|
|
||||||
damage = (float)(maxHP * 0.33);
|
|
||||||
}
|
|
||||||
if (landSpeed < -25) {
|
|
||||||
damage = (float)(maxHP * 0.5);
|
|
||||||
}
|
|
||||||
if (landSpeed < -26.5) {
|
|
||||||
damage = (float)(maxHP * 0.66);
|
|
||||||
}
|
|
||||||
if (landSpeed < -28) {
|
|
||||||
damage = (maxHP * 1);
|
|
||||||
}
|
|
||||||
float newHP = currentHP - damage;
|
|
||||||
if (newHP < 0) {
|
|
||||||
newHP = 0;
|
|
||||||
}
|
|
||||||
Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
|
|
||||||
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
|
||||||
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
|
|
||||||
if (newHP == 0) {
|
|
||||||
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_FALL);
|
|
||||||
}
|
|
||||||
landSpeed = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDrowning() {
|
|
||||||
int stamina = getCurrentStamina();
|
|
||||||
if (stamina < 10) {
|
|
||||||
boolean isSwimming = MotionStatesCategorized.get("SWIM").contains(currentState);
|
|
||||||
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + isSwimming);
|
|
||||||
if (isSwimming && currentState != MotionState.MOTION_SWIM_IDLE) {
|
|
||||||
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_DRAWN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) {
|
|
||||||
cachedSession.send(new PacketAvatarLifeStateChangeNotify(
|
|
||||||
cachedSession.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(),
|
|
||||||
LifeState.LIFE_DEAD,
|
|
||||||
dieType
|
|
||||||
));
|
|
||||||
cachedSession.send(new PacketLifeStateChangeNotify(
|
|
||||||
cachedEntity,
|
|
||||||
LifeState.LIFE_DEAD,
|
|
||||||
dieType
|
|
||||||
));
|
|
||||||
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0);
|
|
||||||
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
|
|
||||||
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
|
|
||||||
session.getPlayer().getScene().removeEntity(entity);
|
|
||||||
((EntityAvatar)entity).onDeath(dieType, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MotionManagerTick extends TimerTask
|
|
||||||
{
|
|
||||||
public void run() {
|
|
||||||
if (Grasscutter.getConfig().OpenStamina) {
|
|
||||||
boolean moving = isPlayerMoving();
|
|
||||||
if (moving || (getCurrentStamina() < getMaximumStamina())) {
|
|
||||||
// Grasscutter.getLogger().debug("Player moving: " + moving + ", stamina full: " + (getCurrentStamina() >= getMaximumStamina()) + ", recalculate stamina");
|
|
||||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
|
||||||
|
|
||||||
// TODO: refactor these conditions.
|
|
||||||
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
|
|
||||||
consumption = getClimbConsumption();
|
|
||||||
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
|
|
||||||
consumption = getSwimConsumptions();
|
|
||||||
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
|
|
||||||
consumption = getRunWalkDashConsumption();
|
|
||||||
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
|
|
||||||
consumption = getFlyConsumption();
|
|
||||||
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
|
|
||||||
consumption = getStandConsumption();
|
|
||||||
} else if (MotionStatesCategorized.get("FIGHT").contains(currentState)) {
|
|
||||||
consumption = getFightConsumption();
|
|
||||||
}
|
|
||||||
|
|
||||||
// delay 2 seconds before start recovering - as official server does.
|
|
||||||
if (cachedSession != null) {
|
|
||||||
if (consumption.amount < 0) {
|
|
||||||
staminaRecoverDelay = 0;
|
|
||||||
}
|
|
||||||
if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) {
|
|
||||||
if (staminaRecoverDelay < 10) {
|
|
||||||
staminaRecoverDelay++;
|
|
||||||
consumption = new Consumption(ConsumptionType.None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Grasscutter.getLogger().debug(getCurrentStamina() + "/" + getMaximumStamina() + "\t" + currentState + "\t" + "isMoving: " + isPlayerMoving() + "\t(" + consumption.consumptionType + "," + consumption.amount + ")");
|
|
||||||
updateStamina(cachedSession, consumption.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tick triggered
|
|
||||||
handleDrowning();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
previousState = currentState;
|
|
||||||
previousCoordinates = new Position(currentCoordinates.getX(),
|
|
||||||
currentCoordinates.getY(), currentCoordinates.getZ());;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumption getClimbConsumption() {
|
|
||||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
|
||||||
if (currentState == MotionState.MOTION_CLIMB) {
|
|
||||||
consumption = new Consumption(ConsumptionType.CLIMBING);
|
|
||||||
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
|
|
||||||
consumption = new Consumption(ConsumptionType.CLIMB_START);
|
|
||||||
}
|
|
||||||
if (!isPlayerMoving()) {
|
|
||||||
consumption = new Consumption(ConsumptionType.None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentState == MotionState.MOTION_CLIMB_JUMP) {
|
|
||||||
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
|
|
||||||
consumption = new Consumption(ConsumptionType.CLIMB_JUMP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return consumption;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumption getSwimConsumptions() {
|
|
||||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
|
||||||
if (currentState == MotionState.MOTION_SWIM_MOVE) {
|
|
||||||
consumption = new Consumption(ConsumptionType.SWIMMING);
|
|
||||||
}
|
|
||||||
if (currentState == MotionState.MOTION_SWIM_DASH) {
|
|
||||||
consumption = new Consumption(ConsumptionType.SWIM_DASH_START);
|
|
||||||
if (previousState == MotionState.MOTION_SWIM_DASH) {
|
|
||||||
consumption = new Consumption(ConsumptionType.SWIM_DASH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return consumption;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumption getRunWalkDashConsumption() {
|
|
||||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
|
||||||
if (currentState == MotionState.MOTION_DASH_BEFORE_SHAKE) {
|
|
||||||
consumption = new Consumption(ConsumptionType.DASH);
|
|
||||||
if (previousState == MotionState.MOTION_DASH_BEFORE_SHAKE) {
|
|
||||||
// only charge once
|
|
||||||
consumption = new Consumption(ConsumptionType.SPRINT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentState == MotionState.MOTION_DASH) {
|
|
||||||
consumption = new Consumption(ConsumptionType.SPRINT);
|
|
||||||
}
|
|
||||||
if (currentState == MotionState.MOTION_RUN) {
|
|
||||||
consumption = new Consumption(ConsumptionType.RUN);
|
|
||||||
}
|
|
||||||
if (currentState == MotionState.MOTION_WALK) {
|
|
||||||
consumption = new Consumption(ConsumptionType.WALK);
|
|
||||||
}
|
|
||||||
return consumption;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumption getFlyConsumption() {
|
|
||||||
Consumption consumption = new Consumption(ConsumptionType.FLY);
|
|
||||||
HashMap<Integer, Float> glidingCostReduction = new HashMap<>() {{
|
|
||||||
put(212301, 0.8f); // Amber
|
|
||||||
put(222301, 0.8f); // Venti
|
|
||||||
}};
|
|
||||||
float reduction = 1;
|
|
||||||
for (EntityAvatar entity: cachedSession.getPlayer().getTeamManager().getActiveTeam()) {
|
|
||||||
for (int skillId: entity.getAvatar().getProudSkillList()) {
|
|
||||||
if (glidingCostReduction.containsKey(skillId)) {
|
|
||||||
reduction = glidingCostReduction.get(skillId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
consumption.amount *= reduction;
|
|
||||||
|
|
||||||
// POWERED_FLY, e.g. wind tunnel
|
|
||||||
if (currentState == MotionState.MOTION_POWERED_FLY) {
|
|
||||||
consumption = new Consumption(ConsumptionType.POWERED_FLY);
|
|
||||||
}
|
|
||||||
return consumption;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumption getStandConsumption() {
|
|
||||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
|
||||||
if (currentState == MotionState.MOTION_STANDBY) {
|
|
||||||
consumption = new Consumption(ConsumptionType.STANDBY);
|
|
||||||
}
|
|
||||||
if (currentState == MotionState.MOTION_STANDBY_MOVE) {
|
|
||||||
consumption = new Consumption(ConsumptionType.STANDBY_MOVE);
|
|
||||||
}
|
|
||||||
return consumption;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumption getFightConsumption() {
|
|
||||||
Consumption consumption = new Consumption(ConsumptionType.None);
|
|
||||||
HashMap<Integer, Integer> fightingCost = new HashMap<>() {{
|
|
||||||
put(10013, -1000); // Kamisato Ayaka
|
|
||||||
put(10413, -1000); // Mona
|
|
||||||
}};
|
|
||||||
if (fightingCost.containsKey(skillCasting)) {
|
|
||||||
consumption = new Consumption(ConsumptionType.FIGHT, fightingCost.get(skillCasting));
|
|
||||||
// only handle once, so reset.
|
|
||||||
skillCasting = 0;
|
|
||||||
skillCaster = 0;
|
|
||||||
}
|
|
||||||
return consumption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifySkill(int caster, int skillId) {
|
|
||||||
skillCaster = caster;
|
|
||||||
skillCasting = skillId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
|||||||
package emu.grasscutter.game.managers.SotSManager;
|
package emu.grasscutter.game.managers;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.entity.EntityAvatar;
|
import emu.grasscutter.game.entity.EntityAvatar;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
|
||||||
import emu.grasscutter.game.managers.MovementManager.MovementManager;
|
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.game.world.World;
|
|
||||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass;
|
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass;
|
||||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass;
|
import emu.grasscutter.net.proto.PropChangeReasonOuterClass;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
@ -29,6 +26,8 @@ public class SotSManager {
|
|||||||
private final Player player;
|
private final Player player;
|
||||||
private Timer autoRecoverTimer;
|
private Timer autoRecoverTimer;
|
||||||
|
|
||||||
|
public final static int GlobalMaximumSpringVolume = 8500000;
|
||||||
|
|
||||||
public SotSManager(Player player) {
|
public SotSManager(Player player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.managers.StaminaManager;
|
||||||
|
|
||||||
|
public class Consumption {
|
||||||
|
public ConsumptionType consumptionType;
|
||||||
|
public int amount;
|
||||||
|
|
||||||
|
public Consumption(ConsumptionType ct, int a) {
|
||||||
|
consumptionType = ct;
|
||||||
|
amount = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Consumption(ConsumptionType ct) {
|
||||||
|
this(ct, ct.amount);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package emu.grasscutter.game.managers.StaminaManager;
|
||||||
|
|
||||||
|
public enum ConsumptionType {
|
||||||
|
None(0),
|
||||||
|
|
||||||
|
// consume
|
||||||
|
CLIMB_START(-500),
|
||||||
|
CLIMBING(-150),
|
||||||
|
CLIMB_JUMP(-2500),
|
||||||
|
SPRINT(-1800),
|
||||||
|
DASH(-360),
|
||||||
|
FLY(-60),
|
||||||
|
SWIM_DASH_START(-200),
|
||||||
|
SWIM_DASH(-200),
|
||||||
|
SWIMMING(-80),
|
||||||
|
FIGHT(0),
|
||||||
|
|
||||||
|
// restore
|
||||||
|
STANDBY(500),
|
||||||
|
RUN(500),
|
||||||
|
WALK(500),
|
||||||
|
STANDBY_MOVE(500),
|
||||||
|
POWERED_FLY(500);
|
||||||
|
|
||||||
|
public final int amount;
|
||||||
|
|
||||||
|
ConsumptionType(int amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,357 @@
|
|||||||
|
package emu.grasscutter.game.managers.StaminaManager;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.entity.EntityAvatar;
|
||||||
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
|
import emu.grasscutter.game.props.LifeState;
|
||||||
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
|
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
|
||||||
|
import emu.grasscutter.net.proto.EvtDoSkillSuccNotifyOuterClass.EvtDoSkillSuccNotify;
|
||||||
|
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
||||||
|
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||||
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||||
|
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.*;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
|
import java.lang.Math;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class StaminaManager {
|
||||||
|
private final Player player;
|
||||||
|
private HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>();
|
||||||
|
|
||||||
|
public final static int GlobalMaximumStamina = 24000;
|
||||||
|
private Position currentCoordinates = new Position(0, 0, 0);
|
||||||
|
private Position previousCoordinates = new Position(0, 0, 0);
|
||||||
|
private MotionState currentState = MotionState.MOTION_STANDBY;
|
||||||
|
private MotionState previousState = MotionState.MOTION_STANDBY;
|
||||||
|
private final Timer sustainedStaminaHandlerTimer = new Timer();
|
||||||
|
private final SustainedStaminaHandler handleSustainedStamina = new SustainedStaminaHandler();
|
||||||
|
private boolean timerRunning = false;
|
||||||
|
private GameSession cachedSession = null;
|
||||||
|
private GameEntity cachedEntity = null;
|
||||||
|
private int staminaRecoverDelay = 0;
|
||||||
|
private boolean isInSkillMove = false;
|
||||||
|
public boolean getIsInSkillMove() {
|
||||||
|
return isInSkillMove;
|
||||||
|
}
|
||||||
|
public void setIsInSkillMove(boolean b) {
|
||||||
|
isInSkillMove = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StaminaManager(Player player) {
|
||||||
|
this.player = player;
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("SWIM", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_SWIM_MOVE,
|
||||||
|
MotionState.MOTION_SWIM_IDLE,
|
||||||
|
MotionState.MOTION_SWIM_DASH,
|
||||||
|
MotionState.MOTION_SWIM_JUMP
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("STANDBY", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_STANDBY,
|
||||||
|
MotionState.MOTION_STANDBY_MOVE,
|
||||||
|
MotionState.MOTION_DANGER_STANDBY,
|
||||||
|
MotionState.MOTION_DANGER_STANDBY_MOVE,
|
||||||
|
MotionState.MOTION_LADDER_TO_STANDBY,
|
||||||
|
MotionState.MOTION_JUMP_UP_WALL_FOR_STANDBY
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("CLIMB", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_CLIMB,
|
||||||
|
MotionState.MOTION_CLIMB_JUMP,
|
||||||
|
MotionState.MOTION_STANDBY_TO_CLIMB,
|
||||||
|
MotionState.MOTION_LADDER_IDLE,
|
||||||
|
MotionState.MOTION_LADDER_MOVE,
|
||||||
|
MotionState.MOTION_LADDER_SLIP,
|
||||||
|
MotionState.MOTION_STANDBY_TO_LADDER
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("FLY", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_FLY,
|
||||||
|
MotionState.MOTION_FLY_IDLE,
|
||||||
|
MotionState.MOTION_FLY_SLOW,
|
||||||
|
MotionState.MOTION_FLY_FAST,
|
||||||
|
MotionState.MOTION_POWERED_FLY
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("RUN", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_DASH,
|
||||||
|
MotionState.MOTION_DANGER_DASH,
|
||||||
|
MotionState.MOTION_DASH_BEFORE_SHAKE,
|
||||||
|
MotionState.MOTION_RUN,
|
||||||
|
MotionState.MOTION_DANGER_RUN,
|
||||||
|
MotionState.MOTION_WALK,
|
||||||
|
MotionState.MOTION_DANGER_WALK
|
||||||
|
)));
|
||||||
|
|
||||||
|
MotionStatesCategorized.put("FIGHT", new HashSet<>(Arrays.asList(
|
||||||
|
MotionState.MOTION_FIGHT
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPlayerMoving() {
|
||||||
|
float diffX = currentCoordinates.getX() - previousCoordinates.getX();
|
||||||
|
float diffY = currentCoordinates.getY() - previousCoordinates.getY();
|
||||||
|
float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ();
|
||||||
|
Grasscutter.getLogger().debug("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates +
|
||||||
|
", " + diffX + ", " + diffY + ", " + diffZ);
|
||||||
|
return Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.2 || Math.abs(diffZ) > 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns new stamina and sends PlayerPropNotify
|
||||||
|
public int updateStamina(GameSession session, Consumption consumption) {
|
||||||
|
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
|
if (consumption.amount == 0) {
|
||||||
|
return currentStamina;
|
||||||
|
}
|
||||||
|
int playerMaxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||||
|
Grasscutter.getLogger().debug(currentStamina + "/" + playerMaxStamina + "\t" + currentState + "\t" +
|
||||||
|
(isPlayerMoving() ? "moving" : " ") + "\t(" + consumption.consumptionType + "," +
|
||||||
|
consumption.amount + ")");
|
||||||
|
int newStamina = currentStamina + consumption.amount;
|
||||||
|
if (newStamina < 0) {
|
||||||
|
newStamina = 0;
|
||||||
|
}
|
||||||
|
if (newStamina > playerMaxStamina) {
|
||||||
|
newStamina = playerMaxStamina;
|
||||||
|
}
|
||||||
|
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
||||||
|
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
||||||
|
return newStamina;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kills avatar, removes entity and sends notification.
|
||||||
|
// TODO: Probably move this to Avatar class? since other components may also need to kill avatar.
|
||||||
|
public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) {
|
||||||
|
session.send(new PacketAvatarLifeStateChangeNotify(player.getTeamManager().getCurrentAvatarEntity().getAvatar(),
|
||||||
|
LifeState.LIFE_DEAD, dieType));
|
||||||
|
session.send(new PacketLifeStateChangeNotify(entity, LifeState.LIFE_DEAD, dieType));
|
||||||
|
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0);
|
||||||
|
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
|
||||||
|
player.getScene().removeEntity(entity);
|
||||||
|
((EntityAvatar)entity).onDeath(dieType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startSustainedStaminaHandler() {
|
||||||
|
if (!player.isPaused() && !timerRunning) {
|
||||||
|
timerRunning = true;
|
||||||
|
sustainedStaminaHandlerTimer.scheduleAtFixedRate(handleSustainedStamina, 0, 200);
|
||||||
|
// Grasscutter.getLogger().debug("[MovementManager] SustainedStaminaHandlerTimer started");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopSustainedStaminaHandler() {
|
||||||
|
if (timerRunning) {
|
||||||
|
timerRunning = false;
|
||||||
|
sustainedStaminaHandlerTimer.cancel();
|
||||||
|
// Grasscutter.getLogger().debug("[MovementManager] SustainedStaminaHandlerTimer stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
|
||||||
|
// External trigger handler
|
||||||
|
|
||||||
|
public void handleEvtDoSkillSuccNotify(GameSession session, EvtDoSkillSuccNotify notify) {
|
||||||
|
handleImmediateStamina(session, notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleCombatInvocationsNotify(GameSession session, EntityMoveInfo moveInfo, GameEntity entity) {
|
||||||
|
// cache info for later use in SustainedStaminaHandler tick
|
||||||
|
cachedSession = session;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startSustainedStaminaHandler();
|
||||||
|
handleImmediateStamina(session, motionInfo, motionState, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal handler
|
||||||
|
|
||||||
|
private void handleImmediateStamina(GameSession session, MotionInfo motionInfo, MotionState motionState,
|
||||||
|
GameEntity entity) {
|
||||||
|
switch (motionState) {
|
||||||
|
case MOTION_DASH_BEFORE_SHAKE:
|
||||||
|
if (previousState != MotionState.MOTION_DASH_BEFORE_SHAKE) {
|
||||||
|
updateStamina(session, new Consumption(ConsumptionType.SPRINT));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MOTION_CLIMB_JUMP:
|
||||||
|
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
|
||||||
|
updateStamina(session, new Consumption(ConsumptionType.CLIMB_JUMP));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MOTION_SWIM_DASH:
|
||||||
|
if (previousState != MotionState.MOTION_SWIM_DASH) {
|
||||||
|
updateStamina(session, new Consumption(ConsumptionType.SWIM_DASH_START));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleImmediateStamina(GameSession session, EvtDoSkillSuccNotify notify) {
|
||||||
|
Consumption consumption = getFightConsumption(notify.getSkillId());
|
||||||
|
updateStamina(session, consumption);
|
||||||
|
}
|
||||||
|
|
||||||
|
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().debug("Player moving: " + moving + ", stamina full: " +
|
||||||
|
(currentStamina >= maxStamina) + ", recalculate stamina");
|
||||||
|
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||||
|
if (!isInSkillMove) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 = new Consumption(ConsumptionType.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateStamina(cachedSession, consumption);
|
||||||
|
}
|
||||||
|
handleDrowning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousState = currentState;
|
||||||
|
previousCoordinates = new Position(
|
||||||
|
currentCoordinates.getX(),
|
||||||
|
currentCoordinates.getY(),
|
||||||
|
currentCoordinates.getZ()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDrowning() {
|
||||||
|
int stamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
|
if (stamina < 10) {
|
||||||
|
boolean isSwimming = MotionStatesCategorized.get("SWIM").contains(currentState);
|
||||||
|
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" +
|
||||||
|
player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + isSwimming);
|
||||||
|
if (isSwimming && currentState != MotionState.MOTION_SWIM_IDLE) {
|
||||||
|
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_DRAWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consumption Calculators
|
||||||
|
|
||||||
|
private Consumption getFightConsumption(int skillCasting) {
|
||||||
|
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||||
|
HashMap<Integer, Integer> fightingCost = new HashMap<>() {{
|
||||||
|
put(10013, -1000); // Kamisato Ayaka
|
||||||
|
put(10413, -1000); // Mona
|
||||||
|
}};
|
||||||
|
if (fightingCost.containsKey(skillCasting)) {
|
||||||
|
consumption = new Consumption(ConsumptionType.FIGHT, fightingCost.get(skillCasting));
|
||||||
|
}
|
||||||
|
return consumption;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumption getClimbSustainedConsumption() {
|
||||||
|
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||||
|
if (currentState == MotionState.MOTION_CLIMB && isPlayerMoving()) {
|
||||||
|
consumption = new Consumption(ConsumptionType.CLIMBING);
|
||||||
|
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
|
||||||
|
consumption = new Consumption(ConsumptionType.CLIMB_START);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumption;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumption getSwimSustainedConsumptions() {
|
||||||
|
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||||
|
if (currentState == MotionState.MOTION_SWIM_MOVE) {
|
||||||
|
consumption = new Consumption(ConsumptionType.SWIMMING);
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_SWIM_DASH) {
|
||||||
|
consumption = new Consumption(ConsumptionType.SWIM_DASH);
|
||||||
|
}
|
||||||
|
return consumption;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumption getRunWalkDashSustainedConsumption() {
|
||||||
|
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||||
|
if (currentState == MotionState.MOTION_DASH) {
|
||||||
|
consumption = new Consumption(ConsumptionType.DASH);
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_RUN) {
|
||||||
|
consumption = new Consumption(ConsumptionType.RUN);
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_WALK) {
|
||||||
|
consumption = new Consumption(ConsumptionType.WALK);
|
||||||
|
}
|
||||||
|
return consumption;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumption getFlySustainedConsumption() {
|
||||||
|
Consumption consumption = new Consumption(ConsumptionType.FLY);
|
||||||
|
HashMap<Integer, Float> glidingCostReduction = new HashMap<>() {{
|
||||||
|
put(212301, 0.8f); // Amber
|
||||||
|
put(222301, 0.8f); // Venti
|
||||||
|
}};
|
||||||
|
float reduction = 1;
|
||||||
|
for (EntityAvatar entity : cachedSession.getPlayer().getTeamManager().getActiveTeam()) {
|
||||||
|
for (int skillId : entity.getAvatar().getProudSkillList()) {
|
||||||
|
if (glidingCostReduction.containsKey(skillId)) {
|
||||||
|
reduction = glidingCostReduction.get(skillId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumption.amount *= reduction;
|
||||||
|
// POWERED_FLY, e.g. wind tunnel
|
||||||
|
if (currentState == MotionState.MOTION_POWERED_FLY) {
|
||||||
|
consumption = new Consumption(ConsumptionType.POWERED_FLY);
|
||||||
|
}
|
||||||
|
return consumption;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumption getStandSustainedConsumption() {
|
||||||
|
Consumption consumption = new Consumption(ConsumptionType.None);
|
||||||
|
if (currentState == MotionState.MOTION_STANDBY) {
|
||||||
|
consumption = new Consumption(ConsumptionType.STANDBY);
|
||||||
|
}
|
||||||
|
if (currentState == MotionState.MOTION_STANDBY_MOVE) {
|
||||||
|
consumption = new Consumption(ConsumptionType.STANDBY_MOVE);
|
||||||
|
}
|
||||||
|
return consumption;
|
||||||
|
}
|
||||||
|
}
|
@ -22,8 +22,8 @@ import emu.grasscutter.game.inventory.GameItem;
|
|||||||
import emu.grasscutter.game.inventory.Inventory;
|
import emu.grasscutter.game.inventory.Inventory;
|
||||||
import emu.grasscutter.game.mail.Mail;
|
import emu.grasscutter.game.mail.Mail;
|
||||||
import emu.grasscutter.game.mail.MailHandler;
|
import emu.grasscutter.game.mail.MailHandler;
|
||||||
import emu.grasscutter.game.managers.MovementManager.MovementManager;
|
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
|
||||||
import emu.grasscutter.game.managers.SotSManager.SotSManager;
|
import emu.grasscutter.game.managers.SotSManager;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.props.EntityType;
|
import emu.grasscutter.game.props.EntityType;
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
@ -62,9 +62,6 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||||||
@Entity(value = "players", useDiscriminator = false)
|
@Entity(value = "players", useDiscriminator = false)
|
||||||
public class Player {
|
public class Player {
|
||||||
|
|
||||||
@Transient private static int GlobalMaximumSpringVolume = 8500000;
|
|
||||||
@Transient private static int GlobalMaximumStamina = 24000;
|
|
||||||
|
|
||||||
@Id private int id;
|
@Id private int id;
|
||||||
@Indexed(options = @IndexOptions(unique = true)) private String accountId;
|
@Indexed(options = @IndexOptions(unique = true)) private String accountId;
|
||||||
|
|
||||||
@ -132,7 +129,7 @@ public class Player {
|
|||||||
@Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
@Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
||||||
|
|
||||||
private MapMarksManager mapMarksManager;
|
private MapMarksManager mapMarksManager;
|
||||||
@Transient private MovementManager movementManager;
|
@Transient private StaminaManager staminaManager;
|
||||||
|
|
||||||
private long springLastUsed;
|
private long springLastUsed;
|
||||||
|
|
||||||
@ -178,7 +175,7 @@ public class Player {
|
|||||||
this.expeditionInfo = new HashMap<>();
|
this.expeditionInfo = new HashMap<>();
|
||||||
this.messageHandler = null;
|
this.messageHandler = null;
|
||||||
this.mapMarksManager = new MapMarksManager();
|
this.mapMarksManager = new MapMarksManager();
|
||||||
this.movementManager = new MovementManager(this);
|
this.staminaManager = new StaminaManager(this);
|
||||||
this.sotsManager = new SotSManager(this);
|
this.sotsManager = new SotSManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +203,7 @@ public class Player {
|
|||||||
this.getRotation().set(0, 307, 0);
|
this.getRotation().set(0, 307, 0);
|
||||||
this.messageHandler = null;
|
this.messageHandler = null;
|
||||||
this.mapMarksManager = new MapMarksManager();
|
this.mapMarksManager = new MapMarksManager();
|
||||||
this.movementManager = new MovementManager(this);
|
this.staminaManager = new StaminaManager(this);
|
||||||
this.sotsManager = new SotSManager(this);
|
this.sotsManager = new SotSManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -875,11 +872,11 @@ public class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
|
getStaminaManager().stopSustainedStaminaHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUnpause() {
|
public void onUnpause() {
|
||||||
|
getStaminaManager().startSustainedStaminaHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(BasePacket packet) {
|
public void sendPacket(BasePacket packet) {
|
||||||
@ -1024,7 +1021,7 @@ public class Player {
|
|||||||
return mapMarksManager;
|
return mapMarksManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MovementManager getMovementManager() { return movementManager; }
|
public StaminaManager getStaminaManager() { return staminaManager; }
|
||||||
|
|
||||||
public SotSManager getSotSManager() { return sotsManager; }
|
public SotSManager getSotSManager() { return sotsManager; }
|
||||||
|
|
||||||
@ -1152,7 +1149,7 @@ public class Player {
|
|||||||
|
|
||||||
public void onLogout() {
|
public void onLogout() {
|
||||||
// stop stamina calculation
|
// stop stamina calculation
|
||||||
getMovementManager().resetTimer();
|
getStaminaManager().stopSustainedStaminaHandler();
|
||||||
|
|
||||||
// force to leave the dungeon
|
// force to leave the dungeon
|
||||||
if (getScene().getSceneType() == SceneType.SCENE_DUNGEON) {
|
if (getScene().getSceneType() == SceneType.SCENE_DUNGEON) {
|
||||||
@ -1214,7 +1211,7 @@ public class Player {
|
|||||||
} else if (prop == PlayerProperty.PROP_LAST_CHANGE_AVATAR_TIME) { // 10001
|
} else if (prop == PlayerProperty.PROP_LAST_CHANGE_AVATAR_TIME) { // 10001
|
||||||
// TODO: implement sanity check
|
// TODO: implement sanity check
|
||||||
} else if (prop == PlayerProperty.PROP_MAX_SPRING_VOLUME) { // 10002
|
} else if (prop == PlayerProperty.PROP_MAX_SPRING_VOLUME) { // 10002
|
||||||
if (!(value >= 0 && value <= GlobalMaximumSpringVolume)) { return false; }
|
if (!(value >= 0 && value <= getSotSManager().GlobalMaximumSpringVolume)) { return false; }
|
||||||
} else if (prop == PlayerProperty.PROP_CUR_SPRING_VOLUME) { // 10003
|
} else if (prop == PlayerProperty.PROP_CUR_SPRING_VOLUME) { // 10003
|
||||||
int playerMaximumSpringVolume = getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
|
int playerMaximumSpringVolume = getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
|
||||||
if (!(value >= 0 && value <= playerMaximumSpringVolume)) { return false; }
|
if (!(value >= 0 && value <= playerMaximumSpringVolume)) { return false; }
|
||||||
@ -1231,7 +1228,7 @@ public class Player {
|
|||||||
} else if (prop == PlayerProperty.PROP_IS_TRANSFERABLE) { // 10009
|
} else if (prop == PlayerProperty.PROP_IS_TRANSFERABLE) { // 10009
|
||||||
if (!(0 <= value && value <= 1)) { return false; }
|
if (!(0 <= value && value <= 1)) { return false; }
|
||||||
} else if (prop == PlayerProperty.PROP_MAX_STAMINA) { // 10010
|
} else if (prop == PlayerProperty.PROP_MAX_STAMINA) { // 10010
|
||||||
if (!(value >= 0 && value <= GlobalMaximumStamina)) { return false; }
|
if (!(value >= 0 && value <= getStaminaManager().GlobalMaximumStamina)) { return false; }
|
||||||
} else if (prop == PlayerProperty.PROP_CUR_PERSIST_STAMINA) { // 10011
|
} else if (prop == PlayerProperty.PROP_CUR_PERSIST_STAMINA) { // 10011
|
||||||
int playerMaximumStamina = getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
int playerMaximumStamina = getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||||
if (!(value >= 0 && value <= playerMaximumStamina)) { return false; }
|
if (!(value >= 0 && value <= playerMaximumStamina)) { return false; }
|
||||||
@ -1242,7 +1239,7 @@ public class Player {
|
|||||||
} else if (prop == PlayerProperty.PROP_PLAYER_EXP) { // 10014
|
} else if (prop == PlayerProperty.PROP_PLAYER_EXP) { // 10014
|
||||||
if (!(0 <= value)) { return false; }
|
if (!(0 <= value)) { return false; }
|
||||||
} else if (prop == PlayerProperty.PROP_PLAYER_HCOIN) { // 10015
|
} else if (prop == PlayerProperty.PROP_PLAYER_HCOIN) { // 10015
|
||||||
// see 10015
|
// see PlayerProperty.PROP_PLAYER_HCOIN comments
|
||||||
} else if (prop == PlayerProperty.PROP_PLAYER_SCOIN) { // 10016
|
} else if (prop == PlayerProperty.PROP_PLAYER_SCOIN) { // 10016
|
||||||
// See 10015
|
// See 10015
|
||||||
} else if (prop == PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE) { // 10017
|
} else if (prop == PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE) { // 10017
|
||||||
|
@ -557,7 +557,7 @@ public class TeamManager {
|
|||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
player.getMovementManager().resetTimer(); // prevent drowning immediately after respawn
|
player.getStaminaManager().stopSustainedStaminaHandler(); // prevent drowning immediately after respawn
|
||||||
|
|
||||||
// Revive all team members
|
// Revive all team members
|
||||||
for (EntityAvatar entity : getActiveTeam()) {
|
for (EntityAvatar entity : getActiveTeam()) {
|
||||||
|
@ -4,12 +4,12 @@ import emu.grasscutter.Grasscutter;
|
|||||||
import emu.grasscutter.server.event.Event;
|
import emu.grasscutter.server.event.Event;
|
||||||
import emu.grasscutter.server.event.EventHandler;
|
import emu.grasscutter.server.event.EventHandler;
|
||||||
import emu.grasscutter.server.event.HandlerPriority;
|
import emu.grasscutter.server.event.HandlerPriority;
|
||||||
import emu.grasscutter.utils.EventConsumer;
|
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -48,11 +48,22 @@ public final class PluginManager {
|
|||||||
.filter(file -> file.getName().endsWith(".jar"))
|
.filter(file -> file.getName().endsWith(".jar"))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
URL[] pluginNames = new URL[plugins.size()];
|
||||||
|
plugins.forEach(plugin -> {
|
||||||
|
try {
|
||||||
|
pluginNames[plugins.indexOf(plugin)] = plugin.toURI().toURL();
|
||||||
|
} catch (MalformedURLException exception) {
|
||||||
|
Grasscutter.getLogger().warn("Unable to load plugin.", exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
URLClassLoader classLoader = new URLClassLoader(pluginNames);
|
||||||
|
|
||||||
plugins.forEach(plugin -> {
|
plugins.forEach(plugin -> {
|
||||||
try {
|
try {
|
||||||
URL url = plugin.toURI().toURL();
|
URL url = plugin.toURI().toURL();
|
||||||
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
|
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
|
||||||
URL configFile = loader.findResource("plugin.json");
|
URL configFile = loader.findResource("plugin.json"); // Find the plugin.json file for each plugin.
|
||||||
InputStreamReader fileReader = new InputStreamReader(configFile.openStream());
|
InputStreamReader fileReader = new InputStreamReader(configFile.openStream());
|
||||||
|
|
||||||
PluginConfig pluginConfig = Grasscutter.getGsonFactory().fromJson(fileReader, PluginConfig.class);
|
PluginConfig pluginConfig = Grasscutter.getGsonFactory().fromJson(fileReader, PluginConfig.class);
|
||||||
@ -68,10 +79,10 @@ public final class PluginManager {
|
|||||||
JarEntry entry = entries.nextElement();
|
JarEntry entry = entries.nextElement();
|
||||||
if(entry.isDirectory() || !entry.getName().endsWith(".class") || entry.getName().contains("module-info")) continue;
|
if(entry.isDirectory() || !entry.getName().endsWith(".class") || entry.getName().contains("module-info")) continue;
|
||||||
String className = entry.getName().replace(".class", "").replace("/", ".");
|
String className = entry.getName().replace(".class", "").replace("/", ".");
|
||||||
loader.loadClass(className);
|
classLoader.loadClass(className); // Use the same class loader for ALL plugins.
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> pluginClass = loader.loadClass(pluginConfig.mainClass);
|
Class<?> pluginClass = classLoader.loadClass(pluginConfig.mainClass);
|
||||||
Plugin pluginInstance = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
|
Plugin pluginInstance = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
|
||||||
this.loadPlugin(pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig), loader);
|
this.loadPlugin(pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig), loader);
|
||||||
|
|
||||||
@ -156,6 +167,10 @@ public final class PluginManager {
|
|||||||
.toList().forEach(handler -> this.invokeHandler(event, handler));
|
.toList().forEach(handler -> this.invokeHandler(event, handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Plugin getPlugin(String name) {
|
||||||
|
return this.plugins.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs logic checks then invokes the provided event handler.
|
* Performs logic checks then invokes the provided event handler.
|
||||||
* @param event The event passed through to the handler.
|
* @param event The event passed through to the handler.
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
|
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
|
||||||
@ -8,11 +10,19 @@ import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
|||||||
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
|
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
|
||||||
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
||||||
|
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||||
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.CombatInvocationsNotify)
|
@Opcodes(PacketOpcodes.CombatInvocationsNotify)
|
||||||
public class HandlerCombatInvocationsNotify extends PacketHandler {
|
public class HandlerCombatInvocationsNotify extends PacketHandler {
|
||||||
|
|
||||||
|
private float cachedLandingSpeed = 0;
|
||||||
|
private long cachedLandingTimeMillisecond = 0;
|
||||||
|
private boolean monitorLandingEvent = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload);
|
CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload);
|
||||||
@ -28,7 +38,33 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
|
|||||||
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
|
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
|
||||||
GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
|
GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
session.getPlayer().getMovementManager().handle(session, moveInfo, entity);
|
// Move player
|
||||||
|
MotionInfo motionInfo = moveInfo.getMotionInfo();
|
||||||
|
entity.getPosition().set(motionInfo.getPos());
|
||||||
|
entity.getRotation().set(motionInfo.getRot());
|
||||||
|
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
|
||||||
|
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
|
||||||
|
MotionState motionState = motionInfo.getState();
|
||||||
|
entity.setMotionState(motionState);
|
||||||
|
|
||||||
|
session.getPlayer().getStaminaManager().handleCombatInvocationsNotify(session, moveInfo, entity);
|
||||||
|
|
||||||
|
// TODO: handle MOTION_FIGHT landing
|
||||||
|
// For plunge attacks, LAND_SPEED is always -30 and is not useful.
|
||||||
|
// May need the height when starting plunge attack.
|
||||||
|
|
||||||
|
if (monitorLandingEvent) {
|
||||||
|
if (motionState == MotionState.MOTION_FALL_ON_GROUND) {
|
||||||
|
monitorLandingEvent = false;
|
||||||
|
handleFallOnGround(session, entity, motionState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (motionState == MotionState.MOTION_LAND_SPEED) {
|
||||||
|
// MOTION_LAND_SPEED and MOTION_FALL_ON_GROUND arrive in different packet. Cache land speed for later use.
|
||||||
|
cachedLandingSpeed = motionInfo.getSpeed().getY();
|
||||||
|
cachedLandingTimeMillisecond = System.currentTimeMillis();
|
||||||
|
monitorLandingEvent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -47,5 +83,39 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) {
|
||||||
|
// If not received immediately after MOTION_LAND_SPEED, discard this packet.
|
||||||
|
int maxDelay = 200;
|
||||||
|
long actualDelay = System.currentTimeMillis() - cachedLandingTimeMillisecond;
|
||||||
|
Grasscutter.getLogger().debug("MOTION_FALL_ON_GROUND received after " + actualDelay + "/" + maxDelay + "ms." + (actualDelay > maxDelay ? " Discard" : ""));
|
||||||
|
if (actualDelay > maxDelay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||||
|
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
|
float damage = 0;
|
||||||
|
if (cachedLandingSpeed < -23.5) {
|
||||||
|
damage = (float) (maxHP * 0.33);
|
||||||
|
}
|
||||||
|
if (cachedLandingSpeed < -25) {
|
||||||
|
damage = (float) (maxHP * 0.5);
|
||||||
|
}
|
||||||
|
if (cachedLandingSpeed < -26.5) {
|
||||||
|
damage = (float) (maxHP * 0.66);
|
||||||
|
}
|
||||||
|
if (cachedLandingSpeed < -28) {
|
||||||
|
damage = (maxHP * 1);
|
||||||
|
}
|
||||||
|
float newHP = currentHP - damage;
|
||||||
|
if (newHP < 0) {
|
||||||
|
newHP = 0;
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().debug(currentHP + "/" + maxHP + "\t" + "\tDamage: " + damage + "\tnewHP: " + newHP);
|
||||||
|
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
|
||||||
|
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
if (newHP == 0) {
|
||||||
|
session.getPlayer().getStaminaManager().killAvatar(session, entity, PlayerDieTypeOuterClass.PlayerDieType.PLAYER_DIE_FALL);
|
||||||
|
}
|
||||||
|
cachedLandingSpeed = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,11 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
import emu.grasscutter.game.managers.SotSManager.SotSManager;
|
import emu.grasscutter.game.managers.SotSManager;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
|
||||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.EnterTransPointRegionNotify)
|
@Opcodes(PacketOpcodes.EnterTransPointRegionNotify)
|
||||||
public class HandlerEnterTransPointRegionNotify extends PacketHandler {
|
public class HandlerEnterTransPointRegionNotify extends PacketHandler {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
@ -15,10 +14,7 @@ public class HandlerEvtDoSkillSuccNotify extends PacketHandler {
|
|||||||
EvtDoSkillSuccNotify notify = EvtDoSkillSuccNotify.parseFrom(payload);
|
EvtDoSkillSuccNotify notify = EvtDoSkillSuccNotify.parseFrom(payload);
|
||||||
// TODO: Will be used for deducting stamina for charged skills.
|
// TODO: Will be used for deducting stamina for charged skills.
|
||||||
|
|
||||||
int caster = notify.getCasterId();
|
session.getPlayer().getStaminaManager().handleEvtDoSkillSuccNotify(session, notify);
|
||||||
int skillId = notify.getSkillId();
|
|
||||||
|
|
||||||
session.getPlayer().getMovementManager().notifySkill(caster, skillId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package emu.grasscutter.server.packet.recv;
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
import emu.grasscutter.game.managers.SotSManager.SotSManager;
|
import emu.grasscutter.game.managers.SotSManager;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
@ -13,16 +13,16 @@ import java.io.PrintWriter;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import emu.grasscutter.GameConstants;
|
import emu.grasscutter.GameConstants;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.command.CommandMap;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.ResourceLoader;
|
import emu.grasscutter.data.ResourceLoader;
|
||||||
import emu.grasscutter.data.def.AvatarData;
|
import emu.grasscutter.data.def.AvatarData;
|
||||||
@ -31,6 +31,8 @@ import emu.grasscutter.data.def.MonsterData;
|
|||||||
import emu.grasscutter.data.def.SceneData;
|
import emu.grasscutter.data.def.SceneData;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
public final class Tools {
|
public final class Tools {
|
||||||
public static void createGmHandbook() throws Exception {
|
public static void createGmHandbook() throws Exception {
|
||||||
ToolsWithLanguageOption.createGmHandbook(getLanguageOption());
|
ToolsWithLanguageOption.createGmHandbook(getLanguageOption());
|
||||||
@ -112,6 +114,19 @@ final class ToolsWithLanguageOption {
|
|||||||
writer.println("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
|
writer.println("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
|
||||||
writer.println("// Created " + dtf.format(now) + System.lineSeparator() + System.lineSeparator());
|
writer.println("// Created " + dtf.format(now) + System.lineSeparator() + System.lineSeparator());
|
||||||
|
|
||||||
|
CommandMap cmdMap = new CommandMap(true);
|
||||||
|
List<Command> cmdList = new ArrayList<>(cmdMap.getAnnotationsAsList());
|
||||||
|
|
||||||
|
writer.println("// Commands");
|
||||||
|
for (Command cmd : cmdList) {
|
||||||
|
String cmdName = cmd.label();
|
||||||
|
while (cmdName.length() <= 15) {
|
||||||
|
cmdName = " " + cmdName;
|
||||||
|
}
|
||||||
|
writer.println(cmdName + " : " + translate(cmd.description()));
|
||||||
|
}
|
||||||
|
writer.println();
|
||||||
|
|
||||||
list = new ArrayList<>(GameData.getAvatarDataMap().keySet());
|
list = new ArrayList<>(GameData.getAvatarDataMap().keySet());
|
||||||
Collections.sort(list);
|
Collections.sort(list);
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ public final class Language {
|
|||||||
* @return A language instance.
|
* @return A language instance.
|
||||||
*/
|
*/
|
||||||
public static Language getLanguage(String langCode) {
|
public static Language getLanguage(String langCode) {
|
||||||
return new Language(langCode + ".json");
|
return new Language(langCode + ".json", Grasscutter.getConfig().DefaultLanguage.toLanguageTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,6 +30,7 @@ public final class Language {
|
|||||||
*/
|
*/
|
||||||
public static String translate(String key, Object... args) {
|
public static String translate(String key, Object... args) {
|
||||||
String translated = Grasscutter.getLanguage().get(key);
|
String translated = Grasscutter.getLanguage().get(key);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return translated.formatted(args);
|
return translated.formatted(args);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
@ -38,48 +39,27 @@ public final class Language {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* creates a language instance.
|
|
||||||
* @param fileName The name of the language file.
|
|
||||||
*/
|
|
||||||
private Language(String fileName) {
|
|
||||||
@Nullable JsonObject languageData = null;
|
|
||||||
|
|
||||||
languageData = loadLanguage(fileName);
|
|
||||||
|
|
||||||
if (languageData == null) {
|
|
||||||
Grasscutter.getLogger().info("Now switch to default language");
|
|
||||||
languageData = loadDefaultLanguage();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert languageData != null : "languageData is null";
|
|
||||||
this.languageData = languageData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load default language file and creates a language instance.
|
|
||||||
* @return language data
|
|
||||||
*/
|
|
||||||
private JsonObject loadDefaultLanguage() {
|
|
||||||
var fileName = Grasscutter.getConfig().DefaultLanguage.toLanguageTag() + ".json";
|
|
||||||
return loadLanguage(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a file and creates a language instance.
|
* Reads a file and creates a language instance.
|
||||||
* @param fileName The name of the language file.
|
* @param fileName The name of the language file.
|
||||||
* @return language data
|
|
||||||
*/
|
*/
|
||||||
private JsonObject loadLanguage(String fileName) {
|
private Language(String fileName, String fallback) {
|
||||||
@Nullable JsonObject languageData = null;
|
@Nullable JsonObject languageData = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream file = Grasscutter.class.getResourceAsStream("/languages/" + fileName);
|
InputStream file = Grasscutter.class.getResourceAsStream("/languages/" + fileName);
|
||||||
languageData = Grasscutter.getGsonFactory().fromJson(Utils.readFromInputStream(file), JsonObject.class);
|
String translationContents = Utils.readFromInputStream(file);
|
||||||
|
if(translationContents.equals("empty")) {
|
||||||
|
file = Grasscutter.class.getResourceAsStream("/languages/" + fallback);
|
||||||
|
translationContents = Utils.readFromInputStream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
languageData = Grasscutter.getGsonFactory().fromJson(translationContents, JsonObject.class);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
Grasscutter.getLogger().warn("Failed to load language file: " + fileName);
|
Grasscutter.getLogger().warn("Failed to load language file: " + fileName, exception);
|
||||||
}
|
}
|
||||||
return languageData;
|
|
||||||
|
this.languageData = languageData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package emu.grasscutter.utils;
|
package emu.grasscutter.utils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
@ -17,6 +18,8 @@ import io.netty.buffer.Unpooled;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
@SuppressWarnings({"UnusedReturnValue", "BooleanMethodIsAlwaysInverted"})
|
@SuppressWarnings({"UnusedReturnValue", "BooleanMethodIsAlwaysInverted"})
|
||||||
@ -252,14 +255,18 @@ public final class Utils {
|
|||||||
* @param stream The input stream.
|
* @param stream The input stream.
|
||||||
* @return The string.
|
* @return The string.
|
||||||
*/
|
*/
|
||||||
public static String readFromInputStream(InputStream stream) {
|
public static String readFromInputStream(@Nullable InputStream stream) {
|
||||||
|
if(stream == null) return "empty";
|
||||||
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream,"UTF-8"))) {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||||
String line; while ((line = reader.readLine()) != null) {
|
String line; while ((line = reader.readLine()) != null) {
|
||||||
stringBuilder.append(line);
|
stringBuilder.append(line);
|
||||||
} stream.close();
|
} stream.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Grasscutter.getLogger().warn("Failed to read from input stream.");
|
Grasscutter.getLogger().warn("Failed to read from input stream.");
|
||||||
|
} catch (NullPointerException ignored) {
|
||||||
|
return "empty";
|
||||||
} return stringBuilder.toString();
|
} return stringBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
"console_execute_error": "This command can only be run from the console.",
|
"console_execute_error": "This command can only be run from the console.",
|
||||||
"player_execute_error": "Run this command in-game.",
|
"player_execute_error": "Run this command in-game.",
|
||||||
"command_exist_error": "No command found.",
|
"command_exist_error": "No command found.",
|
||||||
|
"no_description_specified": "No description specified",
|
||||||
"invalid": {
|
"invalid": {
|
||||||
"amount": "Invalid amount.",
|
"amount": "Invalid amount.",
|
||||||
"artifactId": "Invalid artifactId.",
|
"artifactId": "Invalid artifactId.",
|
||||||
@ -95,17 +96,20 @@
|
|||||||
"create": "Account created with UID %s.",
|
"create": "Account created with UID %s.",
|
||||||
"delete": "Account deleted.",
|
"delete": "Account deleted.",
|
||||||
"no_account": "Account not found.",
|
"no_account": "Account not found.",
|
||||||
"command_usage": "Usage: account <create|delete> <username> [uid]"
|
"command_usage": "Usage: account <create|delete> <username> [uid]",
|
||||||
|
"description": "Modify user accounts"
|
||||||
},
|
},
|
||||||
"broadcast": {
|
"broadcast": {
|
||||||
"command_usage": "Usage: broadcast <message>",
|
"command_usage": "Usage: broadcast <message>",
|
||||||
"message_sent": "Message sent."
|
"message_sent": "Message sent.",
|
||||||
|
"description": "Sends a message to all the players"
|
||||||
},
|
},
|
||||||
"changescene": {
|
"changescene": {
|
||||||
"usage": "Usage: changescene <sceneId>",
|
"usage": "Usage: changescene <sceneId>",
|
||||||
"already_in_scene": "You are already in that scene.",
|
"already_in_scene": "You are already in that scene.",
|
||||||
"success": "Changed to scene %s.",
|
"success": "Changed to scene %s.",
|
||||||
"exists_error": "The specified scene does not exist."
|
"exists_error": "The specified scene does not exist.",
|
||||||
|
"description": "Changes your scene"
|
||||||
},
|
},
|
||||||
"clear": {
|
"clear": {
|
||||||
"command_usage": "Usage: clear <all|wp|art|mat>",
|
"command_usage": "Usage: clear <all|wp|art|mat>",
|
||||||
@ -115,35 +119,41 @@
|
|||||||
"furniture": "Cleared furniture for %s.",
|
"furniture": "Cleared furniture for %s.",
|
||||||
"displays": "Cleared displays for %s.",
|
"displays": "Cleared displays for %s.",
|
||||||
"virtuals": "Cleared virtuals for %s.",
|
"virtuals": "Cleared virtuals for %s.",
|
||||||
"everything": "Cleared everything for %s."
|
"everything": "Cleared everything for %s.",
|
||||||
|
"description": "Deletes unequipped unlocked items, including yellow rarity ones from your inventory"
|
||||||
},
|
},
|
||||||
"coop": {
|
"coop": {
|
||||||
"usage": "Usage: coop <playerId> <target playerId>",
|
"usage": "Usage: coop <playerId> <target playerId>",
|
||||||
"success": "Summoned %s to %s's world."
|
"success": "Summoned %s to %s's world.",
|
||||||
|
"description": "Forces someone to join the world of others"
|
||||||
},
|
},
|
||||||
"enter_dungeon": {
|
"enter_dungeon": {
|
||||||
"usage": "Usage: enterdungeon <dungeon id>",
|
"usage": "Usage: enterdungeon <dungeon id>",
|
||||||
"changed": "Changed to dungeon %s",
|
"changed": "Changed to dungeon %s",
|
||||||
"not_found_error": "Dungeon does not exist",
|
"not_found_error": "Dungeon does not exist",
|
||||||
"in_dungeon_error": "You are already in that dungeon"
|
"in_dungeon_error": "You are already in that dungeon",
|
||||||
|
"description": "Enter a dungeon"
|
||||||
},
|
},
|
||||||
"giveAll": {
|
"giveAll": {
|
||||||
"usage": "Usage: giveall [player] [amount]",
|
"usage": "Usage: giveall [player] [amount]",
|
||||||
"started": "Receiving all items...",
|
"started": "Receiving all items...",
|
||||||
"success": "Successfully gave all items to %s.",
|
"success": "Successfully gave all items to %s.",
|
||||||
"invalid_amount_or_playerId": "Invalid amount or player ID."
|
"invalid_amount_or_playerId": "Invalid amount or player ID.",
|
||||||
|
"description": "Gives all items"
|
||||||
},
|
},
|
||||||
"giveArtifact": {
|
"giveArtifact": {
|
||||||
"usage": "Usage: giveart|gart [player] <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]",
|
"usage": "Usage: giveart|gart [player] <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]",
|
||||||
"id_error": "Invalid artifact ID.",
|
"id_error": "Invalid artifact ID.",
|
||||||
"success": "Given %s to %s."
|
"success": "Given %s to %s.",
|
||||||
|
"description": "Gives the player a specified artifact"
|
||||||
},
|
},
|
||||||
"giveChar": {
|
"giveChar": {
|
||||||
"usage": "Usage: givechar <player> <itemId|itemName> [amount]",
|
"usage": "Usage: givechar <player> <itemId|itemName> [amount]",
|
||||||
"given": "Given %s with level %s to %s.",
|
"given": "Given %s with level %s to %s.",
|
||||||
"invalid_avatar_id": "Invalid avatar id.",
|
"invalid_avatar_id": "Invalid avatar id.",
|
||||||
"invalid_avatar_level": "Invalid avatar level.",
|
"invalid_avatar_level": "Invalid avatar level.",
|
||||||
"invalid_avatar_or_player_id": "Invalid avatar or player ID."
|
"invalid_avatar_or_player_id": "Invalid avatar or player ID.",
|
||||||
|
"description": "Gives the player a specified character"
|
||||||
},
|
},
|
||||||
"give": {
|
"give": {
|
||||||
"usage": "Usage: give <player> <itemId|itemName> [amount] [level]",
|
"usage": "Usage: give <player> <itemId|itemName> [amount] [level]",
|
||||||
@ -151,29 +161,36 @@
|
|||||||
"refinement_must_between_1_and_5": "Refinement must be between 1 and 5.",
|
"refinement_must_between_1_and_5": "Refinement must be between 1 and 5.",
|
||||||
"given": "Given %s of %s to %s.",
|
"given": "Given %s of %s to %s.",
|
||||||
"given_with_level_and_refinement": "Given %s with level %s, refinement %s %s times to %s",
|
"given_with_level_and_refinement": "Given %s with level %s, refinement %s %s times to %s",
|
||||||
"given_level": "Given %s with level %s %s times to %s"
|
"given_level": "Given %s with level %s %s times to %s",
|
||||||
|
"description": "Gives an item to you or the specified player"
|
||||||
},
|
},
|
||||||
"godmode": {
|
"godmode": {
|
||||||
"success": "Godmode is now %s for %s."
|
"success": "Godmode is now %s for %s.",
|
||||||
|
"description": "Prevents you from taking damage. Defaults to toggle."
|
||||||
},
|
},
|
||||||
"heal": {
|
"heal": {
|
||||||
"success": "All characters have been healed."
|
"success": "All characters have been healed.",
|
||||||
|
"description": "Heal all characters in your current team."
|
||||||
},
|
},
|
||||||
"kick": {
|
"kick": {
|
||||||
"player_kick_player": "Player [%s:%s] has kicked player [%s:%s]",
|
"player_kick_player": "Player [%s:%s] has kicked player [%s:%s]",
|
||||||
"server_kick_player": "Kicking player [%s:%s]"
|
"server_kick_player": "Kicking player [%s:%s]",
|
||||||
|
"description": "Kicks the specified player from the server (WIP)"
|
||||||
},
|
},
|
||||||
"kill": {
|
"kill": {
|
||||||
"usage": "Usage: killall [playerUid] [sceneId]",
|
"usage": "Usage: killall [playerUid] [sceneId]",
|
||||||
"scene_not_found_in_player_world": "Scene not found in player world",
|
"scene_not_found_in_player_world": "Scene not found in player world",
|
||||||
"kill_monsters_in_scene": "Killing %s monsters in scene %s"
|
"kill_monsters_in_scene": "Killing %s monsters in scene %s",
|
||||||
|
"description": "Kill all entities"
|
||||||
},
|
},
|
||||||
"killCharacter": {
|
"killCharacter": {
|
||||||
"usage": "Usage: /killcharacter [playerId]",
|
"usage": "Usage: /killcharacter [playerId]",
|
||||||
"success": "Killed %s's current character."
|
"success": "Killed %s's current character.",
|
||||||
|
"description": "Kills the players current character"
|
||||||
},
|
},
|
||||||
"list": {
|
"list": {
|
||||||
"success": "There are %s player(s) online:"
|
"success": "There are %s player(s) online:",
|
||||||
|
"description": "List online players"
|
||||||
},
|
},
|
||||||
"permission": {
|
"permission": {
|
||||||
"usage": "Usage: permission <add|remove> <username> <permission>",
|
"usage": "Usage: permission <add|remove> <username> <permission>",
|
||||||
@ -181,21 +198,26 @@
|
|||||||
"has_error": "They already have this permission!",
|
"has_error": "They already have this permission!",
|
||||||
"remove": "Permission removed.",
|
"remove": "Permission removed.",
|
||||||
"not_have_error": "They don't have this permission!",
|
"not_have_error": "They don't have this permission!",
|
||||||
"account_error": "The account cannot be found."
|
"account_error": "The account cannot be found.",
|
||||||
|
"description": "Grants or removes a permission for a user"
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"success": "Coordinates: %.3f, %.3f, %.3f\nScene id: %d"
|
"success": "Coordinates: %s, %s, %s\nScene id: %s",
|
||||||
|
"description": "Get coordinates."
|
||||||
},
|
},
|
||||||
"reload": {
|
"reload": {
|
||||||
"reload_start": "Reloading config.",
|
"reload_start": "Reloading config.",
|
||||||
"reload_done": "Reload complete."
|
"reload_done": "Reload complete.",
|
||||||
|
"description": "Reload server config"
|
||||||
},
|
},
|
||||||
"resetConst": {
|
"resetConst": {
|
||||||
"reset_all": "Reset all avatars' constellations.",
|
"reset_all": "Reset all avatars' constellations.",
|
||||||
"success": "Constellations for %s have been reset. Please relog to see changes."
|
"success": "Constellations for %s have been reset. Please relog to see changes.",
|
||||||
|
"description": "Resets the constellation level on your current active character, will need to relog after using the command to see any changes."
|
||||||
},
|
},
|
||||||
"resetShopLimit": {
|
"resetShopLimit": {
|
||||||
"usage": "Usage: /resetshop <player id>"
|
"usage": "Usage: /resetshop <player id>",
|
||||||
|
"description": "Reset target player's shop refresh time."
|
||||||
},
|
},
|
||||||
"sendMail": {
|
"sendMail": {
|
||||||
"usage": "Usage: give [player] <itemId|itemName> [amount]",
|
"usage": "Usage: give [player] <itemId|itemName> [amount]",
|
||||||
@ -217,17 +239,20 @@
|
|||||||
"message": "<message>",
|
"message": "<message>",
|
||||||
"sender": "<sender>",
|
"sender": "<sender>",
|
||||||
"arguments": "<itemId|itemName|finish> [amount] [level]",
|
"arguments": "<itemId|itemName|finish> [amount] [level]",
|
||||||
"error": "ERROR: invalid construction stage %s. Check console for stacktrace."
|
"error": "ERROR: invalid construction stage %s. Check console for stacktrace.",
|
||||||
|
"description": "Sends mail to the specified user. The usage of this command changes based on it's composition state."
|
||||||
},
|
},
|
||||||
"sendMessage": {
|
"sendMessage": {
|
||||||
"usage": "Usage: sendmessage <player> <message>",
|
"usage": "Usage: sendmessage <player> <message>",
|
||||||
"success": "Message sent."
|
"success": "Message sent.",
|
||||||
|
"description": "Sends a message to a player as the server"
|
||||||
},
|
},
|
||||||
"setFetterLevel": {
|
"setFetterLevel": {
|
||||||
"usage": "Usage: setfetterlevel <level>",
|
"usage": "Usage: setfetterlevel <level>",
|
||||||
"range_error": "Fetter level must be between 0 and 10.",
|
"range_error": "Fetter level must be between 0 and 10.",
|
||||||
"success": "Fetter level set to %s",
|
"success": "Fetter level set to %s",
|
||||||
"level_error": "Invalid fetter level."
|
"level_error": "Invalid fetter level.",
|
||||||
|
"description": "Sets your fetter level for your current active character"
|
||||||
},
|
},
|
||||||
"setStats": {
|
"setStats": {
|
||||||
"usage_console": "Usage: setstats|stats @<UID> <stat> <value>",
|
"usage_console": "Usage: setstats|stats @<UID> <stat> <value>",
|
||||||
@ -238,20 +263,24 @@
|
|||||||
"player_error": "Player not found or offline.",
|
"player_error": "Player not found or offline.",
|
||||||
"set_self": "%s set to %s.",
|
"set_self": "%s set to %s.",
|
||||||
"set_for_uid": "%s for %s set to %s.",
|
"set_for_uid": "%s for %s set to %s.",
|
||||||
"set_max_hp": "MAX HP set to %s."
|
"set_max_hp": "MAX HP set to %s.",
|
||||||
|
"description": "Set fight property for your current active character"
|
||||||
},
|
},
|
||||||
"setWorldLevel": {
|
"setWorldLevel": {
|
||||||
"usage": "Usage: setworldlevel <level>",
|
"usage": "Usage: setworldlevel <level>",
|
||||||
"value_error": "World level must be between 0-8",
|
"value_error": "World level must be between 0-8",
|
||||||
"success": "World level set to %s.",
|
"success": "World level set to %s.",
|
||||||
"invalid_world_level": "Invalid world level."
|
"invalid_world_level": "Invalid world level.",
|
||||||
|
"description": "Sets your world level (Relog to see proper effects)"
|
||||||
},
|
},
|
||||||
"spawn": {
|
"spawn": {
|
||||||
"usage": "Usage: spawn <entityId> [amount] [level(monster only)]",
|
"usage": "Usage: spawn <entityId> [amount] [level(monster only)]",
|
||||||
"success": "Spawned %s of %s."
|
"success": "Spawned %s of %s.",
|
||||||
|
"description": "Spawns an entity near you"
|
||||||
},
|
},
|
||||||
"stop": {
|
"stop": {
|
||||||
"success": "Server shutting down..."
|
"success": "Server shutting down...",
|
||||||
|
"description": "Stops the server"
|
||||||
},
|
},
|
||||||
"talent": {
|
"talent": {
|
||||||
"usage_1": "To set talent level: /talent set <talentID> <value>",
|
"usage_1": "To set talent level: /talent set <talentID> <value>",
|
||||||
@ -267,18 +296,21 @@
|
|||||||
"invalid_level": "Invalid talent level.",
|
"invalid_level": "Invalid talent level.",
|
||||||
"normal_attack_id": "Normal Attack ID %s.",
|
"normal_attack_id": "Normal Attack ID %s.",
|
||||||
"e_skill_id": "E skill ID %s.",
|
"e_skill_id": "E skill ID %s.",
|
||||||
"q_skill_id": "Q skill ID %s."
|
"q_skill_id": "Q skill ID %s.",
|
||||||
|
"description": "Set talent level for your current active character"
|
||||||
},
|
},
|
||||||
"teleportAll": {
|
"teleportAll": {
|
||||||
"success": "Summoned all players to your location.",
|
"success": "Summoned all players to your location.",
|
||||||
"error": "You only can use this command in MP mode."
|
"error": "You only can use this command in MP mode.",
|
||||||
|
"description": "Teleports all players in your world to your position"
|
||||||
},
|
},
|
||||||
"teleport": {
|
"teleport": {
|
||||||
"usage_server": "Usage: /tp @<player id> <x> <y> <z> [scene id]",
|
"usage_server": "Usage: /tp @<player id> <x> <y> <z> [scene id]",
|
||||||
"usage": "Usage: /tp [@<player id>] <x> <y> <z> [scene id]",
|
"usage": "Usage: /tp [@<player id>] <x> <y> <z> [scene id]",
|
||||||
"specify_player_id": "You must specify a player id.",
|
"specify_player_id": "You must specify a player id.",
|
||||||
"invalid_position": "Invalid position.",
|
"invalid_position": "Invalid position.",
|
||||||
"success": "Teleported %s to %s, %s, %s in scene %s"
|
"success": "Teleported %s to %s, %s, %s in scene %s",
|
||||||
|
"description": "Change the player's position."
|
||||||
},
|
},
|
||||||
"tower": {
|
"tower": {
|
||||||
"unlock_done": "Abyss Corridor's Floors are all unlocked now."
|
"unlock_done": "Abyss Corridor's Floors are all unlocked now."
|
||||||
@ -286,16 +318,22 @@
|
|||||||
"weather": {
|
"weather": {
|
||||||
"usage": "Usage: weather <weatherId> [climateId]",
|
"usage": "Usage: weather <weatherId> [climateId]",
|
||||||
"success": "Changed weather to %s with climate %s",
|
"success": "Changed weather to %s with climate %s",
|
||||||
"invalid_id": "Invalid ID."
|
"invalid_id": "Invalid ID.",
|
||||||
|
"description": "Changes the weather."
|
||||||
},
|
},
|
||||||
"drop": {
|
"drop": {
|
||||||
"command_usage": "Usage: drop <itemId|itemName> [amount]",
|
"command_usage": "Usage: drop <itemId|itemName> [amount]",
|
||||||
"success": "Dropped %s of %s."
|
"success": "Dropped %s of %s.",
|
||||||
|
"description": "Drops an item near you"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"usage": "Usage: ",
|
"usage": "Usage: ",
|
||||||
"aliases": "Aliases: ",
|
"aliases": "Aliases: ",
|
||||||
"available_commands": "Available commands: "
|
"available_commands": "Available commands: ",
|
||||||
|
"description": "Sends the help message or shows information about a specified command"
|
||||||
|
},
|
||||||
|
"restart": {
|
||||||
|
"description": "Restarts the current session"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
298
src/main/resources/languages/pl-PL.json
Normal file
298
src/main/resources/languages/pl-PL.json
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
{
|
||||||
|
"messages": {
|
||||||
|
"game": {
|
||||||
|
"port_bind": "Serwer gry uruchomiony na porcie: %s",
|
||||||
|
"connect": "Klient połączył się z %s",
|
||||||
|
"disconnect": "Klient rozłączył się z %s",
|
||||||
|
"game_update_error": "Wystąpił błąd podczas aktualizacji gry.",
|
||||||
|
"command_error": "Błąd komendy:"
|
||||||
|
},
|
||||||
|
"dispatch": {
|
||||||
|
"port_bind": "[Dispatch] Serwer dispatch wystartował na porcie %s",
|
||||||
|
"request": "[Dispatch] Klient %s %s zapytanie: %s",
|
||||||
|
"keystore": {
|
||||||
|
"general_error": "[Dispatch] Błąd łądowania keystore!",
|
||||||
|
"password_error": "[Dispatch] Nie można załadować keystore. Próba z domyślnym hasłem keystore...",
|
||||||
|
"no_keystore_error": "[Dispatch] Brak certyfikatu SSL! Przejście na serwer HTTP.",
|
||||||
|
"default_password": "[Dispatch] Domyślne hasło keystore zadziałało. Rozważ ustawienie go na 123456 w pliku config.json."
|
||||||
|
},
|
||||||
|
"no_commands_error": "Komendy nie są wspierane w trybie DISPATCH_ONLY.",
|
||||||
|
"unhandled_request_error": "[Dispatch] Potencjalnie niepodtrzymane %s zapytanie: %s",
|
||||||
|
"account": {
|
||||||
|
"login_attempt": "[Dispatch] Klient %s próbuje się zalogować",
|
||||||
|
"login_success": "[Dispatch] Klient %s zalogował się jako %s",
|
||||||
|
"login_token_attempt": "[Dispatch] Klient %s próbuje się zalogować poprzez token",
|
||||||
|
"login_token_error": "[Dispatch] Klient %s nie mógł się zalogować poprzez token",
|
||||||
|
"login_token_success": "[Dispatch] Klient %s zalogował się poprzez token jako %s",
|
||||||
|
"combo_token_success": "[Dispatch] Klient %s pomyślnie wymienił combo token",
|
||||||
|
"combo_token_error": "[Dispatch] Klient %s nie wymienił combo token'u",
|
||||||
|
"account_login_create_success": "[Dispatch] Klient %s nie mógł się zalogować: Konto %s stworzone",
|
||||||
|
"account_login_create_error": "[Dispatch] Klient %s nie mógł się zalogować: Tworzenie konta nie powiodło się",
|
||||||
|
"account_login_exist_error": "[Dispatch] Klient %s nie mógł się zalogować: Nie znaleziono konta",
|
||||||
|
"account_cache_error": "Błąd pamięci cache konta gry",
|
||||||
|
"session_key_error": "Błędny klucz sesji.",
|
||||||
|
"username_error": "Nazwa użytkownika nie znaleziona.",
|
||||||
|
"username_create_error": "Nazwa użytkownika nie znaleziona, tworzenie nie powiodło się."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"free_software": "Grasscutter to DARMOWE oprogramowanie. Jeżeli ktoś Ci je sprzedał, to zostałeś oscamowany. Strona domowa: https://github.com/Grasscutters/Grasscutter",
|
||||||
|
"starting": "Uruchamianie Grasscutter...",
|
||||||
|
"shutdown": "Wyłączanie...",
|
||||||
|
"done": "Gotowe! Wpisz \"help\" aby uzyskać pomoc",
|
||||||
|
"error": "Wystąpił błąd.",
|
||||||
|
"welcome": "Witamy w Grasscutter",
|
||||||
|
"run_mode_error": "Błędny tryb pracy serwera: %s.",
|
||||||
|
"run_mode_help": "Tryb pracy serwera musi być ustawiony na 'HYBRID', 'DISPATCH_ONLY', lub 'GAME_ONLY'. Nie można wystartować Grasscutter...",
|
||||||
|
"create_resources": "Tworzenie folderu resources...",
|
||||||
|
"resources_error": "Umieść kopię 'BinOutput' i 'ExcelBinOutput' w folderze resources."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"generic": {
|
||||||
|
"not_specified": "Nie podano komendy.",
|
||||||
|
"unknown_command": "Nieznana komenda: %s",
|
||||||
|
"permission_error": "Nie masz uprawnień do tej komendy.",
|
||||||
|
"console_execute_error": "Tą komende można wywołać tylko z konsoli.",
|
||||||
|
"player_execute_error": "Wywołaj tą komendę w grze.",
|
||||||
|
"command_exist_error": "Nie znaleziono komendy.",
|
||||||
|
"invalid": {
|
||||||
|
"amount": "Błędna ilość.",
|
||||||
|
"artifactId": "Błędne ID artefaktu.",
|
||||||
|
"avatarId": "Błędne id postaci.",
|
||||||
|
"avatarLevel": "Błędny poziom postaci.",
|
||||||
|
"entityId": "Błędne id obiektu.",
|
||||||
|
"id przedmiotu": "Błędne id przedmiotu.",
|
||||||
|
"itemLevel": "Błędny poziom przedmiotu.",
|
||||||
|
"itemRefinement": "Błędne ulepszenie.",
|
||||||
|
"playerId": "Błędne playerId.",
|
||||||
|
"uid": "Błędne UID."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"execution": {
|
||||||
|
"uid_error": "Błędne UID.",
|
||||||
|
"player_exist_error": "Gracz nie znaleziony.",
|
||||||
|
"player_offline_error": "Gracz nie jest online.",
|
||||||
|
"item_id_error": "Błędne ID przedmiotu.",
|
||||||
|
"item_player_exist_error": "Błędny przedmiot lub UID.",
|
||||||
|
"entity_id_error": "Błędne ID obiektu.",
|
||||||
|
"player_exist_offline_error": "Gracz nie znaleziony lub jest offline.",
|
||||||
|
"argument_error": "Błędne argumenty.",
|
||||||
|
"clear_target": "Cel wyczyszczony.",
|
||||||
|
"set_target": "Następne komendy będą celować w @%s.",
|
||||||
|
"need_target": "Ta komenda wymaga docelowego UID. Dodaj argument <@UID> lub ustaw stały cel poleceniem /target @UID."
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"enabled": "Włączone",
|
||||||
|
"disabled": "Wyłączone",
|
||||||
|
"help": "Pomoc",
|
||||||
|
"success": "Sukces"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"modify": "Modyfikuj konta użytkowników",
|
||||||
|
"invalid": "Błędne UID.",
|
||||||
|
"exists": "Konto już istnieje.",
|
||||||
|
"create": "Stworzono konto z UID %s.",
|
||||||
|
"delete": "Konto usunięte.",
|
||||||
|
"no_account": "Nie znaleziono konta.",
|
||||||
|
"command_usage": "Użycie: account <create|delete> <nazwa> [uid]"
|
||||||
|
},
|
||||||
|
"broadcast": {
|
||||||
|
"command_usage": "Użycie: broadcast <wiadomość>",
|
||||||
|
"message_sent": "Wiadomość wysłana."
|
||||||
|
},
|
||||||
|
"changescene": {
|
||||||
|
"usage": "Użycie: changescene <id sceny>",
|
||||||
|
"already_in_scene": "Już jesteś na tej scenie.",
|
||||||
|
"success": "Zmieniono scene na %s.",
|
||||||
|
"exists_error": "Ta scena nie istenieje."
|
||||||
|
},
|
||||||
|
"clear": {
|
||||||
|
"command_usage": "Użycie: clear <all|wp|art|mat>",
|
||||||
|
"weapons": "Wyczyszczono bronie dla %s.",
|
||||||
|
"artifacts": "Wyczyszczono artefakty dla %s.",
|
||||||
|
"materials": "Wyczyszczono materiały dla %s.",
|
||||||
|
"furniture": "Wyczyszczono meble dla %s.",
|
||||||
|
"displays": "Wyczyszczono displays dla %s.",
|
||||||
|
"virtuals": "Wyczyszczono virtuals dla %s.",
|
||||||
|
"everything": "Wyczyszczono wszystko dla %s."
|
||||||
|
},
|
||||||
|
"coop": {
|
||||||
|
"usage": "Użycie: coop <id gracza> <id gracza docelowego>",
|
||||||
|
"success": "Przyzwano %s do świata %s."
|
||||||
|
},
|
||||||
|
"enter_dungeon": {
|
||||||
|
"usage": "Użycie: enterdungeon <ID lochu>",
|
||||||
|
"changed": "Zmieniono loch na %s",
|
||||||
|
"not_found_error": "Ten loch nie istnieje",
|
||||||
|
"in_dungeon_error": "Już jesteś w tym lochu"
|
||||||
|
},
|
||||||
|
"giveAll": {
|
||||||
|
"usage": "Użycie: giveall [gracz] [ilość]",
|
||||||
|
"started": "Dodawanie wszystkich przedmiotów...",
|
||||||
|
"success": "Pomyślnie dodano wszystkie przedmioty dla %s.",
|
||||||
|
"invalid_amount_or_playerId": "Błędna ilość lub ID gracza."
|
||||||
|
},
|
||||||
|
"giveArtifact": {
|
||||||
|
"usage": "Użycie: giveart|gart [gracz] <id artefaktu> <mainPropId> [<appendPropId>[,<razy>]]... [poziom]",
|
||||||
|
"id_error": "Błędne ID artefaktu.",
|
||||||
|
"success": "Dano %s dla %s."
|
||||||
|
},
|
||||||
|
"giveChar": {
|
||||||
|
"usage": "Użycie: givechar <gracz> <id przedmiotu | nazwa przedmiotu> [ilość]",
|
||||||
|
"given": "Dano %s z poziomem %s dla %s.",
|
||||||
|
"invalid_avatar_id": "Błędne ID postaci.",
|
||||||
|
"invalid_avatar_level": "Błędny poziom postaci.",
|
||||||
|
"invalid_avatar_or_player_id": "Błędne ID postaci lub gracza."
|
||||||
|
},
|
||||||
|
"give": {
|
||||||
|
"usage": "Użycie: give <gracz> <id przedmiotu | nazwa przedmiotu> [ilość] [poziom]",
|
||||||
|
"refinement_only_applicable_weapons": "Ulepszenie można zastosować tylko dla broni.",
|
||||||
|
"refinement_must_between_1_and_5": "Ulepszenie musi być pomiędzy 1, a 5.",
|
||||||
|
"given": "Dano %s %s dla %s.",
|
||||||
|
"given_with_level_and_refinement": "Dano %s z poziomem %s, ulepszeniem %s %s razy dla %s",
|
||||||
|
"given_level": "Dano %s z poziomem %s %s razy dla %s"
|
||||||
|
},
|
||||||
|
"godmode": {
|
||||||
|
"success": "Godmode jest teraz %s dla %s."
|
||||||
|
},
|
||||||
|
"heal": {
|
||||||
|
"success": "Wszystkie postacie zostały wyleczone."
|
||||||
|
},
|
||||||
|
"kick": {
|
||||||
|
"player_kick_player": "Gracz [%s:%s] wyrzucił gracza [%s:%s]",
|
||||||
|
"server_kick_player": "Wyrzucono gracza [%s:%s]"
|
||||||
|
},
|
||||||
|
"kill": {
|
||||||
|
"usage": "Użycie: killall [UID gracza] [ID sceny]",
|
||||||
|
"scene_not_found_in_player_world": "Scena nie znaleziona w świecie gracza",
|
||||||
|
"kill_monsters_in_scene": "Zabito %s potworów w scenie %s"
|
||||||
|
},
|
||||||
|
"killCharacter": {
|
||||||
|
"usage": "Użycie: /killcharacter [ID gracza]",
|
||||||
|
"success": "Zabito aktualną postać gracza %s."
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"success": "Teraz jest %s gracz(y) online:"
|
||||||
|
},
|
||||||
|
"permission": {
|
||||||
|
"usage": "Użycie: permission <add|remove> <nazwa gracza> <uprawnienie>",
|
||||||
|
"add": "Dodano uprawnienie",
|
||||||
|
"has_error": "To konto już ma to uprawnienie!",
|
||||||
|
"remove": "Usunięto uprawnienie.",
|
||||||
|
"not_have_error": "To konto nie ma tych uprawnień!",
|
||||||
|
"account_error": "Konto nie może zostać znalezione."
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"success": "Koordynaty: %.3f, %.3f, %.3f\nID sceny: %d"
|
||||||
|
},
|
||||||
|
"reload": {
|
||||||
|
"reload_start": "Ponowne ładowanie konfiguracji.",
|
||||||
|
"reload_done": "Ponowne ładowanie zakończone."
|
||||||
|
},
|
||||||
|
"resetConst": {
|
||||||
|
"reset_all": "Resetuj konstelacje wszystkich postaci.",
|
||||||
|
"success": "Konstelacje dla %s zostały zresetowane. Proszę zalogować się ponownie aby zobaczyć zmiany."
|
||||||
|
},
|
||||||
|
"resetShopLimit": {
|
||||||
|
"usage": "Użycie: /resetshop <ID gracza>"
|
||||||
|
},
|
||||||
|
"sendMail": {
|
||||||
|
"usage": "Użycie: `/sendmail <ID gracza | all | help> [id szablonu]`",
|
||||||
|
"user_not_exist": "Gracz o ID '%s' nie istnieje",
|
||||||
|
"start_composition": "Komponowanie wiadomości.\nProszę użyj `/sendmail <tytuł>` aby kontynuować.\nMożesz użyć `/sendmail stop` w dowolnym momencie",
|
||||||
|
"templates": "Szablony zostaną zaimplementowane niedługo...",
|
||||||
|
"invalid_arguments": "Błędne argumenty.\nUżycie `/sendmail <ID gracza | all | help> [id szablonu]`",
|
||||||
|
"send_cancel": "Anulowano wysyłanie wiadomości",
|
||||||
|
"send_done": "Wysłano wiadomość do gracza %s!",
|
||||||
|
"send_all_done": "Wysłano wiadomośc do wszystkich graczy!",
|
||||||
|
"not_composition_end": "Komponowanie nie jest na ostatnim etapie.\nProszę użyj `/sendmail %s` lub `/sendmail stop` aby anulować",
|
||||||
|
"please_use": "Proszę użyj `/sendmail %s`",
|
||||||
|
"set_title": "Tytuł wiadomości to teraz: '%s'.\nUżyj '/sendmail <treść>' aby kontynuować.",
|
||||||
|
"set_contents": "Treść wiadomości to teraz '%s'.\nUżyj '/sendmail <nadawca>' aby kontynuować.",
|
||||||
|
"set_message_sender": "Nadawca wiadomości to teraz '%s'.\nUżyj '/sendmail <id przedmiotu | nazwa przedmiotu | zakończ> [ilość] [poziom]' aby kontynuować.",
|
||||||
|
"send": "Załączono %s %s (poziom %s) do wiadomości.\nDodaj więcej przedmiotów lub użyj `/sendmail finish` aby wysłać wiadomość.",
|
||||||
|
"invalid_arguments_please_use": "Błędne argumenty \nProszę użyj `/sendmail %s`",
|
||||||
|
"title": "<tytuł>",
|
||||||
|
"message": "<wiadomość>",
|
||||||
|
"sender": "<nadawca>",
|
||||||
|
"arguments": "<id przedmiotu | nazwa przedmiotu | zakończ> [ilość] [poziom]",
|
||||||
|
"error": "BŁĄD: niepoprawny etap konstrukcji: %s. Sprawdź konsolę aby dowiedzieć się więcej."
|
||||||
|
},
|
||||||
|
"sendMessage": {
|
||||||
|
"usage": "Użycie: /sendmessage <player> <message>",
|
||||||
|
"success": "Wiadomość wysłana."
|
||||||
|
},
|
||||||
|
"setFetterLevel": {
|
||||||
|
"usage": "Użycie: setfetterlevel <poziom>",
|
||||||
|
"range_error": "Poziom przyjaźni musi być pomiędzy 0,a 10.",
|
||||||
|
"success": "Poziom przyjaźni ustawiono na: %s",
|
||||||
|
"level_error": "Błędny poziom przyjaźni."
|
||||||
|
},
|
||||||
|
"setStats": {
|
||||||
|
"usage_console": "Użycie: setstats|stats @<UID> <statystyka> <wartość>",
|
||||||
|
"usage_ingame": "Użycie: setstats|stats [@UID] <statystyka> <wartość>",
|
||||||
|
"help_message": "\n\tWartości dla Statystyka: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Bonus DMG żywiołu: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) RES na żywioł: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys\n",
|
||||||
|
"value_error": "Błędna wartość statystyki.",
|
||||||
|
"uid_error": "Błędne UID.",
|
||||||
|
"player_error": "Gracza nie znaleziono lub jest offline.",
|
||||||
|
"set_self": "%s ustawiono na %s.",
|
||||||
|
"set_for_uid": "%s dla %s ustawiono na %s.",
|
||||||
|
"set_max_hp": "Maksymalne HP ustawione na %s."
|
||||||
|
},
|
||||||
|
"setWorldLevel": {
|
||||||
|
"usage": "Użycie: setworldlevel <poziom>",
|
||||||
|
"value_error": "Poziom świata musi być pomiędzy 0, a 8",
|
||||||
|
"success": "Ustawiono poziom świata na: %s.",
|
||||||
|
"invalid_world_level": "Invalid world level."
|
||||||
|
},
|
||||||
|
"spawn": {
|
||||||
|
"usage": "Użycie: /spawn <id obiektu> [ilość] [poziom(tylko potwory)]",
|
||||||
|
"success": "Stworzono %s %s."
|
||||||
|
},
|
||||||
|
"stop": {
|
||||||
|
"success": "Serwer wyłącza się..."
|
||||||
|
},
|
||||||
|
"talent": {
|
||||||
|
"usage_1": "Aby ustawić poziom talentu: /talent set <ID talentu> <wartość>",
|
||||||
|
"usage_2": "Inny sposób na ustawienie poziomu talentu: /talent <n lub e lub q> <wartość>",
|
||||||
|
"usage_3": "Aby uzyskać ID talentu: /talent getid",
|
||||||
|
"lower_16": "Błędny poziom talentu. Poziom powinien być mniejszy niż 16",
|
||||||
|
"set_id": "Ustawiono talent na %s.",
|
||||||
|
"set_atk": "Ustawiono talent Atak Podstawowy na poziom %s.",
|
||||||
|
"set_e": "Ustawiono poziom talentu E na %s.",
|
||||||
|
"set_q": "Ustawiono poziom talentu Q na %s.",
|
||||||
|
"invalid_skill_id": "Błędne ID umiejętności.",
|
||||||
|
"set_this": "Ustawiono ten talent na poziom %s.",
|
||||||
|
"invalid_level": "Błędny poziom talentu.",
|
||||||
|
"normal_attack_id": "ID podstawowego ataku: %s.",
|
||||||
|
"e_skill_id": "ID umiejętności E: %s.",
|
||||||
|
"q_skill_id": "ID umiejętności Q: %s."
|
||||||
|
},
|
||||||
|
"teleportAll": {
|
||||||
|
"success": "Przyzwano wszystkich graczy do Ciebie.",
|
||||||
|
"error": "Możesz użyć tej komendy wyłącznie w trybie MP."
|
||||||
|
},
|
||||||
|
"teleport": {
|
||||||
|
"usage_server": "Użycie: /tp @<ID gracza> <x> <y> <z> [ID sceny]",
|
||||||
|
"usage": "Użycie: /tp [@<ID gracza>] <x> <y> <z> [ID sceny]",
|
||||||
|
"specify_player_id": "Musisz określić ID gracza.",
|
||||||
|
"invalid_position": "Błędna pozycja.",
|
||||||
|
"success": "Przeteleportowano %s do %s, %s, %s w scenie %s"
|
||||||
|
},
|
||||||
|
"weather": {
|
||||||
|
"usage": "Użycie: weather <ID pogody> [ID klimatu]",
|
||||||
|
"success": "Zmieniono pogodę na %s z klimatem %s",
|
||||||
|
"invalid_id": "Błędne ID."
|
||||||
|
},
|
||||||
|
"drop": {
|
||||||
|
"command_usage": "Użycie: drop <ID przedmiotu|nazwa przedmiotu> [ilość]",
|
||||||
|
"success": "Wyrzucono %s of %s."
|
||||||
|
},
|
||||||
|
"help": {
|
||||||
|
"usage": "Użycie: ",
|
||||||
|
"aliases": "Aliasy: ",
|
||||||
|
"available_commands": "Dostępne komendy: "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -36,12 +36,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"free_software": "Grasscutter 是免费开源软件。如果你是付费购买的,那已经被骗了。Github:https://github.com/Grasscutters/Grasscutter",
|
"free_software": "Grasscutter 是免费开源软件,遵循Apache-2.0 license。如果您是付费购买的,那您已经被骗了。项目地址:Github:https://github.com/Grasscutters/Grasscutter",
|
||||||
"starting": "正在启动 Grasscutter...",
|
"starting": "正在启动 Grasscutter...",
|
||||||
"shutdown": "正在关闭...",
|
"shutdown": "正在关闭...",
|
||||||
"done": "加載完成!输入 \"help\" 查看命令列表",
|
"done": "加载完成!输入 \"help\" 查看命令列表",
|
||||||
"error": "发生了一个错误。",
|
"error": "发生了一个错误。",
|
||||||
"welcome": "欢迎使用 Grasscutter",
|
"welcome": "欢迎使用 Grasscutter!珍惜这段美妙的旅途吧!",
|
||||||
"run_mode_error": "无效的服务器运行模式: %s。",
|
"run_mode_error": "无效的服务器运行模式: %s。",
|
||||||
"run_mode_help": "服务器运行模式必须为 HYBRID、DISPATCH_ONLY 或 GAME_ONLY。Grasscutter 启动失败...",
|
"run_mode_help": "服务器运行模式必须为 HYBRID、DISPATCH_ONLY 或 GAME_ONLY。Grasscutter 启动失败...",
|
||||||
"create_resources": "正在创建 resources 目录...",
|
"create_resources": "正在创建 resources 目录...",
|
||||||
@ -52,12 +52,12 @@
|
|||||||
"generic": {
|
"generic": {
|
||||||
"not_specified": "没有指定命令。",
|
"not_specified": "没有指定命令。",
|
||||||
"unknown_command": "未知的命令:%s",
|
"unknown_command": "未知的命令:%s",
|
||||||
"permission_error": "您没有执行此命令的权限。",
|
"permission_error": "哼哼哼!您没有执行此命令的权限!请联系服务器管理员解决!",
|
||||||
"console_execute_error": "此命令只能在服务器控制台执行。",
|
"console_execute_error": "此命令只能在服务器控制台执行呐~",
|
||||||
"player_execute_error": "此命令只能在游戏内执行。",
|
"player_execute_error": "此命令只能在游戏内执行哦~",
|
||||||
"command_exist_error": "找不到命令。",
|
"command_exist_error": "这条命令……好像找不到呢?。",
|
||||||
"invalid": {
|
"invalid": {
|
||||||
"amount": "无效的 数量.",
|
"amount": "无效的数量.",
|
||||||
"artifactId": "无效的圣遗物ID。",
|
"artifactId": "无效的圣遗物ID。",
|
||||||
"avatarId": "无效的角色ID。",
|
"avatarId": "无效的角色ID。",
|
||||||
"avatarLevel": "无效的角色等級。",
|
"avatarLevel": "无效的角色等級。",
|
||||||
@ -119,7 +119,7 @@
|
|||||||
},
|
},
|
||||||
"coop": {
|
"coop": {
|
||||||
"usage": "用法:coop <playerId> <target playerId>",
|
"usage": "用法:coop <playerId> <target playerId>",
|
||||||
"success": "已召唤 %s 到 %s的世界"
|
"success": "已强制召唤 %s 到 %s的世界"
|
||||||
},
|
},
|
||||||
"enter_dungeon": {
|
"enter_dungeon": {
|
||||||
"usage": "用法:enterdungeon <dungeon id>",
|
"usage": "用法:enterdungeon <dungeon id>",
|
||||||
@ -140,21 +140,21 @@
|
|||||||
},
|
},
|
||||||
"giveChar": {
|
"giveChar": {
|
||||||
"usage": "用法:givechar <player> <itemId|itemName> [amount]",
|
"usage": "用法:givechar <player> <itemId|itemName> [amount]",
|
||||||
"given": "Given %s with level %s to %s.",
|
"given": "给予角色 %s 等级 %s 向UID %s.",
|
||||||
"invalid_avatar_id": "无效的角色ID。",
|
"invalid_avatar_id": "无效的角色ID。",
|
||||||
"invalid_avatar_level": "无效的角色等級。.",
|
"invalid_avatar_level": "无效的角色等級。.",
|
||||||
"invalid_avatar_or_player_id": "无效的角色ID/玩家ID。"
|
"invalid_avatar_or_player_id": "无效的角色ID/玩家ID。"
|
||||||
},
|
},
|
||||||
"give": {
|
"give": {
|
||||||
"usage": "用法:give <player> <itemId|itemName> [amount] [level] [refinement]",
|
"usage": "用法:give <player> <itemId|itemName> [amount] [level] [refinement]",
|
||||||
"refinement_only_applicable_weapons": "精炼等级参数仅在武器上可用",
|
"refinement_only_applicable_weapons": "精炼等阶参数仅在给予武器时可用",
|
||||||
"refinement_must_between_1_and_5": "精炼等级必须在 1 到 5 之间。",
|
"refinement_must_between_1_and_5": "精炼等阶必须在 1 到 5 之间。",
|
||||||
"given": "已将 %s 个 %s 给予 %s。",
|
"given": "已将 %s 个 %s 给予 %s。",
|
||||||
"given_with_level_and_refinement": "已将 %s [等級%s, 精炼%s] %s个给予 %s",
|
"given_with_level_and_refinement": "已将 %s [等級%s, 精炼%s] %s个给予 %s",
|
||||||
"given_level": "已将 %s 等级 %s %s 个给予 %s"
|
"given_level": "已将 %s 等级 %s %s 个给予UID %s"
|
||||||
},
|
},
|
||||||
"godmode": {
|
"godmode": {
|
||||||
"success": "上帝模式被设置为 %s 。 [用户:%s]"
|
"success": "上帝模式已被设置为 %s 。 [用户:%s]"
|
||||||
},
|
},
|
||||||
"heal": {
|
"heal": {
|
||||||
"success": "所有角色已被治疗。"
|
"success": "所有角色已被治疗。"
|
||||||
@ -173,7 +173,7 @@
|
|||||||
"success": "已杀死 %s 目前使用的角色。"
|
"success": "已杀死 %s 目前使用的角色。"
|
||||||
},
|
},
|
||||||
"list": {
|
"list": {
|
||||||
"message": "目前在线人数:%s"
|
"success": "目前在线人数:%s"
|
||||||
},
|
},
|
||||||
"permission": {
|
"permission": {
|
||||||
"usage": "用法:permission <add|remove> <username> <permission>",
|
"usage": "用法:permission <add|remove> <username> <permission>",
|
||||||
@ -188,7 +188,7 @@
|
|||||||
},
|
},
|
||||||
"reload": {
|
"reload": {
|
||||||
"reload_start": "正在重载配置文件和数据。",
|
"reload_start": "正在重载配置文件和数据。",
|
||||||
"reload_done": "重装完毕。"
|
"reload_done": "重载完毕。"
|
||||||
},
|
},
|
||||||
"resetConst": {
|
"resetConst": {
|
||||||
"reset_all": "重置所有角色的命座。",
|
"reset_all": "重置所有角色的命座。",
|
||||||
@ -204,7 +204,7 @@
|
|||||||
"templates": "邮件模板尚未实装...",
|
"templates": "邮件模板尚未实装...",
|
||||||
"invalid_arguments": "无效的参数。\n指令使用方法 `/sendmail <userId|all|help> [templateId]`",
|
"invalid_arguments": "无效的参数。\n指令使用方法 `/sendmail <userId|all|help> [templateId]`",
|
||||||
"send_cancel": "取消发送邮件",
|
"send_cancel": "取消发送邮件",
|
||||||
"send_done": "已将邮件给 %s!",
|
"send_done": "已将邮件发送给 %s!",
|
||||||
"send_all_done": "邮件已发送给所有人!",
|
"send_all_done": "邮件已发送给所有人!",
|
||||||
"not_composition_end": "现在邮件发送未到最后阶段。\n请使用 `/sendmail %s` 继续发送邮件,或使用 `/sendmail stop` 来停止发送邮件。",
|
"not_composition_end": "现在邮件发送未到最后阶段。\n请使用 `/sendmail %s` 继续发送邮件,或使用 `/sendmail stop` 来停止发送邮件。",
|
||||||
"please_use": "请使用 `/sendmail %s`",
|
"please_use": "请使用 `/sendmail %s`",
|
||||||
@ -257,21 +257,21 @@
|
|||||||
"usage_1": "设置天赋等级:/talent set <talentID> <value>",
|
"usage_1": "设置天赋等级:/talent set <talentID> <value>",
|
||||||
"usage_2": "另一种设置天赋等级的命令使用方法:/talent <n or e or q> <value>",
|
"usage_2": "另一种设置天赋等级的命令使用方法:/talent <n or e or q> <value>",
|
||||||
"usage_3": "获取天赋ID指令用法:/talent getid",
|
"usage_3": "获取天赋ID指令用法:/talent getid",
|
||||||
"lower_16": "无效的天赋等级,天赋等级应低于16。",
|
"lower_16": "无效的天赋等级,天赋等级应小于等于15。",
|
||||||
"set_id": "将天赋等级设为 %s。",
|
"set_id": "将天赋等级设为 %s。",
|
||||||
"set_atk": "将普通攻击等级设为 %s。",
|
"set_atk": "将普通攻击等级设为 %s。",
|
||||||
"set_e": "设定天赋E等级为 %s。",
|
"set_e": "设定元素战技等级为 %s。",
|
||||||
"set_q": "设定天赋Q等级为 %s。",
|
"set_q": "设定元素爆发等级为 %s。",
|
||||||
"invalid_skill_id": "无效的技能ID。",
|
"invalid_skill_id": "无效的技能ID。",
|
||||||
"set_this": "将天赋等级设为 %s。",
|
"set_this": "将天赋等级设为 %s。",
|
||||||
"invalid_level": "无效的天赋等级。",
|
"invalid_level": "无效的天赋等级。",
|
||||||
"normal_attack_id": "普通攻击的 ID 为 %s。",
|
"normal_attack_id": "普通攻击的 ID 为 %s。",
|
||||||
"e_skill_id": "E技能ID %s。",
|
"e_skill_id": "元素战技ID %s。",
|
||||||
"q_skill_id": "Q技能ID %s。"
|
"q_skill_id": "元素爆发ID %s。"
|
||||||
},
|
},
|
||||||
"teleportAll": {
|
"teleportAll": {
|
||||||
"success": "已将全部玩家传送到你的位置",
|
"success": "已将全部玩家传送到你的位置",
|
||||||
"error": "命令仅限多人游戏使用。"
|
"error": "命令仅限处于多人游戏状态下使用。"
|
||||||
},
|
},
|
||||||
"teleport": {
|
"teleport": {
|
||||||
"usage_server": "用法:/tp @<player id> <x> <y> <z> [scene id]",
|
"usage_server": "用法:/tp @<player id> <x> <y> <z> [scene id]",
|
||||||
@ -282,12 +282,12 @@
|
|||||||
},
|
},
|
||||||
"weather": {
|
"weather": {
|
||||||
"usage": "用法:weather <weatherId> [climateId]",
|
"usage": "用法:weather <weatherId> [climateId]",
|
||||||
"success": "已将当前天气设定为 %s,气候则为 %s。",
|
"success": "已将当前天气设定为 %s,气候为 %s。",
|
||||||
"invalid_id": "无效的ID。"
|
"invalid_id": "无效的天气ID。"
|
||||||
},
|
},
|
||||||
"drop": {
|
"drop": {
|
||||||
"command_usage": "用法:drop <itemId|itemName> [amount]",
|
"command_usage": "用法:drop <itemId|itemName> [amount]",
|
||||||
"success": "已將 %s x %s 丟在附近。"
|
"success": "已将 %s x %s 丟在附近。"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"usage": "用法:",
|
"usage": "用法:",
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
"player_execute_error": "請在遊戲裡使用這條指令。",
|
"player_execute_error": "請在遊戲裡使用這條指令。",
|
||||||
"command_exist_error": "找不到指令。",
|
"command_exist_error": "找不到指令。",
|
||||||
"invalid": {
|
"invalid": {
|
||||||
"amount": "無效的 數量.",
|
"amount": "無效的數量。",
|
||||||
"artifactId": "無效的聖遺物ID。",
|
"artifactId": "無效的聖遺物ID。",
|
||||||
"avatarId": "無效的角色ID。",
|
"avatarId": "無效的角色ID。",
|
||||||
"avatarLevel": "無效的角色等級。",
|
"avatarLevel": "無效的角色等級。",
|
||||||
@ -119,7 +119,7 @@
|
|||||||
},
|
},
|
||||||
"coop": {
|
"coop": {
|
||||||
"usage": "用法:coop <playerId> <target playerId>",
|
"usage": "用法:coop <playerId> <target playerId>",
|
||||||
"success": "Summoned %s to %s's world."
|
"success": "召喚了 %s 到 %s 的世界。"
|
||||||
},
|
},
|
||||||
"enter_dungeon": {
|
"enter_dungeon": {
|
||||||
"usage": "用法:enterdungeon <dungeon id>",
|
"usage": "用法:enterdungeon <dungeon id>",
|
||||||
@ -140,7 +140,7 @@
|
|||||||
},
|
},
|
||||||
"giveChar": {
|
"giveChar": {
|
||||||
"usage": "用法:givechar <player> <itemId|itemName> [amount]",
|
"usage": "用法:givechar <player> <itemId|itemName> [amount]",
|
||||||
"given": "Given %s with level %s to %s.",
|
"given": "已將 %s 等級 %s 給予 %s。",
|
||||||
"invalid_avatar_id": "無效的角色ID。",
|
"invalid_avatar_id": "無效的角色ID。",
|
||||||
"invalid_avatar_level": "無效的角色等級。.",
|
"invalid_avatar_level": "無效的角色等級。.",
|
||||||
"invalid_avatar_or_player_id": "無效的角色ID/玩家ID。"
|
"invalid_avatar_or_player_id": "無效的角色ID/玩家ID。"
|
||||||
@ -173,7 +173,7 @@
|
|||||||
"success": "已殺死 %s 目前的場上角色。"
|
"success": "已殺死 %s 目前的場上角色。"
|
||||||
},
|
},
|
||||||
"list": {
|
"list": {
|
||||||
"message": "目前總線上人數:%s"
|
"success": "目前總線上人數:%s"
|
||||||
},
|
},
|
||||||
"permission": {
|
"permission": {
|
||||||
"usage": "用法:permission <add|remove> <username> <permission>",
|
"usage": "用法:permission <add|remove> <username> <permission>",
|
||||||
@ -270,7 +270,7 @@
|
|||||||
"q_skill_id": "Q技能ID %s。"
|
"q_skill_id": "Q技能ID %s。"
|
||||||
},
|
},
|
||||||
"teleportAll": {
|
"teleportAll": {
|
||||||
"success": "Summoned all players to your location.",
|
"success": "召喚了所有玩家到你的位置上。",
|
||||||
"error": "此指令僅可在多人遊戲下可用。"
|
"error": "此指令僅可在多人遊戲下可用。"
|
||||||
},
|
},
|
||||||
"teleport": {
|
"teleport": {
|
||||||
|
Loading…
Reference in New Issue
Block a user