Skip to content
Snippets Groups Projects
Commit 711ae2f5 authored by Magix's avatar Magix Committed by GitHub
Browse files

Merge branch 'development' into development

parents 79babcc5 38fd7bb7
Branches
Tags
No related merge requests found
Showing
with 440 additions and 679 deletions
...@@ -9,6 +9,8 @@ import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify; ...@@ -9,6 +9,8 @@ import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify;
import java.util.List; import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "weather", usage = "weather <weatherId> [climateId]", @Command(label = "weather", usage = "weather <weatherId> [climateId]",
description = "Changes the weather.", aliases = {"w"}, permission = "player.weather") description = "Changes the weather.", aliases = {"w"}, permission = "player.weather")
public final class WeatherCommand implements CommandHandler { public final class WeatherCommand implements CommandHandler {
...@@ -16,7 +18,7 @@ public final class WeatherCommand implements CommandHandler { ...@@ -16,7 +18,7 @@ public final class WeatherCommand 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) {
CommandHandler.sendMessage(sender, Grasscutter.getLanguage().Target_needed); CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
return; return;
} }
...@@ -27,17 +29,17 @@ public final class WeatherCommand implements CommandHandler { ...@@ -27,17 +29,17 @@ public final class WeatherCommand implements CommandHandler {
try { try {
climateId = Integer.parseInt(args.get(1)); climateId = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, Grasscutter.getLanguage().Weather_invalid_id); CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id"));
} }
case 1: case 1:
try { try {
weatherId = Integer.parseInt(args.get(0)); weatherId = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, Grasscutter.getLanguage().Weather_invalid_id); CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id"));
} }
break; break;
default: default:
CommandHandler.sendMessage(sender, Grasscutter.getLanguage().Weather_usage); CommandHandler.sendMessage(sender, translate("commands.weather.usage"));
return; return;
} }
...@@ -46,7 +48,6 @@ public final class WeatherCommand implements CommandHandler { ...@@ -46,7 +48,6 @@ public final class WeatherCommand implements CommandHandler {
targetPlayer.getScene().setWeather(weatherId); targetPlayer.getScene().setWeather(weatherId);
targetPlayer.getScene().setClimate(climate); targetPlayer.getScene().setClimate(climate);
targetPlayer.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(targetPlayer)); targetPlayer.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(targetPlayer));
CommandHandler.sendMessage(sender, Grasscutter.getLanguage().Weather_message.replace("{weatherId}", Integer.toString(weatherId)).replace("{climateId}", Integer.toString(climateId))); CommandHandler.sendMessage(sender, translate("commands.weather.success", Integer.toString(weatherId), Integer.toString(climateId)));
} }
} }
...@@ -7,6 +7,7 @@ import java.util.Map.Entry; ...@@ -7,6 +7,7 @@ import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.google.gson.Gson;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import org.reflections.Reflections; import org.reflections.Reflections;
...@@ -120,14 +121,15 @@ public class ResourceLoader { ...@@ -120,14 +121,15 @@ public class ResourceLoader {
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception { protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
try (FileReader fileReader = new FileReader(Grasscutter.getConfig().RESOURCE_FOLDER + "ExcelBinOutput/" + fileName)) { FileReader fileReader = new FileReader(Grasscutter.getConfig().RESOURCE_FOLDER + "ExcelBinOutput/" + fileName);
List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType()); Gson gson = Grasscutter.getGsonFactory();
List list = gson.fromJson(fileReader, List.class);
for (Object o : list) {
GameResource res = (GameResource) o; for (Object o : list) {
res.onLoad(); Map<String, Object> tempMap = Utils.switchPropertiesUpperLowerCase((Map<String, Object>) o, c);
map.put(res.getId(), res); GameResource res = gson.fromJson(gson.toJson(tempMap), TypeToken.get(c).getType());
} res.onLoad();
map.put(res.getId(), res);
} }
} }
......
package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
import emu.grasscutter.utils.Utils;
public class BasicDungeonSettleListener implements DungeonSettleListener {
@Override
public void onDungeonSettle(Scene scene) {
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge()));
}
}
package emu.grasscutter.game.dungeons; package emu.grasscutter.game.dungeons;
import java.util.ArrayList;
import java.util.List;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.def.DungeonData; import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify; import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify; import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.List;
public class DungeonChallenge { public class DungeonChallenge {
private final Scene scene; private final Scene scene;
private final SceneGroup group; private final SceneGroup group;
...@@ -40,7 +32,7 @@ public class DungeonChallenge { ...@@ -40,7 +32,7 @@ public class DungeonChallenge {
private int score; private int score;
private int objective = 0; private int objective = 0;
private IntSet rewardedPlayers; private IntSet rewardedPlayers;
public DungeonChallenge(Scene scene, SceneGroup group) { public DungeonChallenge(Scene scene, SceneGroup group) {
this.scene = scene; this.scene = scene;
this.group = group; this.group = group;
...@@ -129,8 +121,7 @@ public class DungeonChallenge { ...@@ -129,8 +121,7 @@ public class DungeonChallenge {
} }
private void settle() { private void settle() {
getScene().setAutoCloseTime(Utils.getCurrentSeconds() + 1000); getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
getScene().broadcastPacket(new PacketDungeonSettleNotify(this));
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0)); getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0));
} }
......
...@@ -14,9 +14,11 @@ import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp; ...@@ -14,9 +14,11 @@ import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp; import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import java.util.List;
public class DungeonManager { public class DungeonManager {
private final GameServer server; private final GameServer server;
private static final BasicDungeonSettleListener basicDungeonSettleObserver = new BasicDungeonSettleListener();
public DungeonManager(GameServer server) { public DungeonManager(GameServer server) {
this.server = server; this.server = server;
} }
...@@ -49,13 +51,32 @@ public class DungeonManager { ...@@ -49,13 +51,32 @@ public class DungeonManager {
int sceneId = data.getSceneId(); int sceneId = data.getSceneId();
player.getScene().setPrevScene(sceneId); player.getScene().setPrevScene(sceneId);
player.getWorld().transferPlayerToScene(player, sceneId, data); if(player.getWorld().transferPlayerToScene(player, sceneId, data)){
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
}
player.getScene().setPrevScenePoint(pointId); player.getScene().setPrevScenePoint(pointId);
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId)); player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
return true; return true;
} }
/**
* used in tower dungeons handoff
*/
public boolean handoffDungeon(Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (data == null) {
return false;
}
Grasscutter.getLogger().info(player.getNickname() + " is trying to enter tower dungeon " + dungeonId);
if(player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)){
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
}
return true;
}
public void exitDungeon(Player player) { public void exitDungeon(Player player) {
if (player.getScene().getSceneType() != SceneType.SCENE_DUNGEON) { if (player.getScene().getSceneType() != SceneType.SCENE_DUNGEON) {
return; return;
...@@ -77,6 +98,8 @@ public class DungeonManager { ...@@ -77,6 +98,8 @@ public class DungeonManager {
} }
// clean temp team if it has // clean temp team if it has
player.getTeamManager().cleanTemporaryTeam(); player.getTeamManager().cleanTemporaryTeam();
player.getTowerManager().clearEntry();
// Transfer player back to world // Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos); player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp)); player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
......
package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.world.Scene;
public interface DungeonSettleListener {
void onDungeonSettle(Scene scene);
}
package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
import emu.grasscutter.server.packet.send.PacketTowerFloorRecordChangeNotify;
import emu.grasscutter.utils.Utils;
public class TowerDungeonSettleListener implements DungeonSettleListener {
@Override
public void onDungeonSettle(Scene scene) {
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
var towerManager = scene.getPlayers().get(0).getTowerManager();
towerManager.notifyCurLevelRecordChangeWhenDone();
scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify(towerManager.getCurrentFloorId()));
scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge(),
true,
towerManager.hasNextLevel(),
towerManager.getNextFloorId()
));
}
}
package emu.grasscutter.game.expedition;
import dev.morphia.annotations.Entity;
@Entity
public class ExpeditionInfo {
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getExpId() {
return expId;
}
public void setExpId(int expId) {
this.expId = expId;
}
public int getHourTime() {
return hourTime;
}
public void setHourTime(int hourTime) {
this.hourTime = hourTime;
}
public int getStartTime() {
return startTime;
}
public void setStartTime(int startTime) {
this.startTime = startTime;
}
private int state;
private int expId;
private int hourTime;
private int startTime;
}
package emu.grasscutter.game.expedition;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.game.GameServer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileReader;
import java.util.Collection;
import java.util.List;
public class ExpeditionManager {
public GameServer getGameServer() {
return gameServer;
}
private final GameServer gameServer;
public Int2ObjectMap<List<ExpeditionRewardDataList>> getExpeditionRewardDataList() { return expeditionRewardData; }
private final Int2ObjectMap<List<ExpeditionRewardDataList>> expeditionRewardData;
public ExpeditionManager(GameServer gameServer) {
this.gameServer = gameServer;
this.expeditionRewardData = new Int2ObjectOpenHashMap<>();
this.load();
}
public synchronized void load() {
try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "ExpeditionReward.json")) {
getExpeditionRewardDataList().clear();
List<ExpeditionRewardInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ExpeditionRewardInfo.class).getType());
if(banners.size() > 0) {
for (ExpeditionRewardInfo di : banners) {
getExpeditionRewardDataList().put(di.getExpId(), di.getExpeditionRewardDataList());
}
Grasscutter.getLogger().info("Expedition reward successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load expedition reward. Expedition reward size is 0.");
}
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load expedition reward.", e);
}
}
}
package emu.grasscutter.game.expedition;
public class ExpeditionRewardData {
private int itemId;
private int minCount;
private int maxCount;
public int getItemId() {
return itemId;
}
public int getMinCount() { return minCount; }
public int getMaxCount() {
return maxCount;
}
}
package emu.grasscutter.game.expedition;
import java.util.List;
public class ExpeditionRewardDataList {
public int getHourTime() {
return hourTime;
}
public List<ExpeditionRewardData> getExpeditionRewardData() {
return expeditionRewardData;
}
private int hourTime;
private List<ExpeditionRewardData> expeditionRewardData;
}
package emu.grasscutter.game.expedition;
import java.util.List;
public class ExpeditionRewardInfo {
public int getExpId() {
return expId;
}
public List<ExpeditionRewardDataList> getExpeditionRewardDataList() {
return expeditionRewardDataList;
}
private int expId;
private List<ExpeditionRewardDataList> expeditionRewardDataList;
}
...@@ -60,6 +60,7 @@ public class MovementManager { ...@@ -60,6 +60,7 @@ public class MovementManager {
private final Player player; private final Player player;
private float landSpeed = 0; private float landSpeed = 0;
private long landTimeMillisecond = 0;
private Timer movementManagerTickTimer; private Timer movementManagerTickTimer;
private GameSession cachedSession = null; private GameSession cachedSession = null;
private GameEntity cachedEntity = null; private GameEntity cachedEntity = null;
...@@ -192,12 +193,20 @@ public class MovementManager { ...@@ -192,12 +193,20 @@ public class MovementManager {
// cache land speed // cache land speed
if (state == MotionState.MOTION_LAND_SPEED) { if (state == MotionState.MOTION_LAND_SPEED) {
landSpeed = motionInfo.getSpeed().getY(); landSpeed = motionInfo.getSpeed().getY();
landTimeMillisecond = System.currentTimeMillis();
} }
if (state == MotionState.MOTION_FALL_ON_GROUND) { 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 currentHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); float maxHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float damage = 0; float damage = 0;
// Grasscutter.getLogger().debug("LandSpeed: " + landSpeed); Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
if (landSpeed < -23.5) { if (landSpeed < -23.5) {
damage = (float)(maxHP * 0.33); damage = (float)(maxHP * 0.33);
} }
...@@ -214,7 +223,7 @@ public class MovementManager { ...@@ -214,7 +223,7 @@ public class MovementManager {
if (newHP < 0) { if (newHP < 0) {
newHP = 0; newHP = 0;
} }
// Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP); Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP); cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP)); cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
if (newHP == 0) { if (newHP == 0) {
...@@ -259,7 +268,7 @@ public class MovementManager { ...@@ -259,7 +268,7 @@ public class MovementManager {
if (Grasscutter.getConfig().OpenStamina) { if (Grasscutter.getConfig().OpenStamina) {
boolean moving = isPlayerMoving(); boolean moving = isPlayerMoving();
if (moving || (getCurrentStamina() < getMaximumStamina())) { if (moving || (getCurrentStamina() < getMaximumStamina())) {
Grasscutter.getLogger().debug("Player moving: " + moving + ", stamina full: " + (getCurrentStamina() >= getMaximumStamina()) + ", recalculate stamina"); // Grasscutter.getLogger().debug("Player moving: " + moving + ", stamina full: " + (getCurrentStamina() >= getMaximumStamina()) + ", recalculate stamina");
Consumption consumption = Consumption.None; Consumption consumption = Consumption.None;
// TODO: refactor these conditions. // TODO: refactor these conditions.
...@@ -297,14 +306,16 @@ public class MovementManager { ...@@ -297,14 +306,16 @@ public class MovementManager {
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) { } else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
// RUN, DASH and WALK // RUN, DASH and WALK
// DASH // DASH
if (currentState == MotionState.MOTION_DASH) { if (currentState == MotionState.MOTION_DASH_BEFORE_SHAKE) {
if (previousState == MotionState.MOTION_DASH) { consumption = Consumption.DASH;
if (previousState == MotionState.MOTION_DASH_BEFORE_SHAKE) {
// only charge once
consumption = Consumption.SPRINT; consumption = Consumption.SPRINT;
} else {
// cost more to start dashing
consumption = Consumption.DASH;
} }
} }
if (currentState == MotionState.MOTION_DASH) {
consumption = Consumption.SPRINT;
}
// RUN // RUN
if (currentState == MotionState.MOTION_RUN) { if (currentState == MotionState.MOTION_RUN) {
consumption = Consumption.RUN; consumption = Consumption.RUN;
...@@ -338,14 +349,13 @@ public class MovementManager { ...@@ -338,14 +349,13 @@ public class MovementManager {
staminaRecoverDelay = 0; staminaRecoverDelay = 0;
} }
if (consumption.amount > 0) { if (consumption.amount > 0) {
if (staminaRecoverDelay < 5) { if (staminaRecoverDelay < 10) {
staminaRecoverDelay++; staminaRecoverDelay++;
consumption = Consumption.None; consumption = Consumption.None;
} }
} }
int newStamina = updateStamina(cachedSession, consumption.amount); int newStamina = updateStamina(cachedSession, consumption.amount);
cachedSession.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA)); cachedSession.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + "isMoving: " + isPlayerMoving() + "\t" + consumption + "(" + consumption.amount + ")"); Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + "isMoving: " + isPlayerMoving() + "\t" + consumption + "(" + consumption.amount + ")");
} }
} }
......
...@@ -2,9 +2,13 @@ package emu.grasscutter.game.managers.SotSManager; ...@@ -2,9 +2,13 @@ package emu.grasscutter.game.managers.SotSManager;
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.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;
...@@ -14,6 +18,8 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotif ...@@ -14,6 +18,8 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotif
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.List; import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
// Statue of the Seven Manager // Statue of the Seven Manager
public class SotSManager { public class SotSManager {
...@@ -21,6 +27,7 @@ public class SotSManager { ...@@ -21,6 +27,7 @@ public class SotSManager {
// NOTE: Spring volume balance *1 = fight prop HP *100 // NOTE: Spring volume balance *1 = fight prop HP *100
private final Player player; private final Player player;
private Timer autoRecoverTimer;
public SotSManager(Player player) { public SotSManager(Player player) {
this.player = player; this.player = player;
...@@ -46,26 +53,44 @@ public class SotSManager { ...@@ -46,26 +53,44 @@ public class SotSManager {
public void autoRevive(GameSession session) { public void autoRevive(GameSession session) {
player.getTeamManager().getActiveTeam().forEach(entity -> { player.getTeamManager().getActiveTeam().forEach(entity -> {
boolean isAlive = entity.isAlive(); boolean isAlive = entity.isAlive();
float currentHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
// Grasscutter.getLogger().debug("" + entity.getAvatar().getAvatarData().getName() + "\t" + currentHP + "/" + maxHP + "\t" + (isAlive ? "ALIVE":"DEAD"));
float newHP = (float)(maxHP * 0.3);
if (currentHP < newHP) {
updateAvatarCurHP(session, entity, newHP);
}
if (!isAlive) { if (!isAlive) {
float maxHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float newHP = (float)(maxHP * 0.3);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
} }
}); });
} }
public void scheduleAutoRecover(GameSession session) { public void scheduleAutoRecover(GameSession session) {
// TODO: play audio effects? possibly client side? - client automatically plays. if (autoRecoverTimer == null) {
// delay 2.5 seconds autoRecoverTimer = new Timer();
new Thread(() -> { autoRecoverTimer.schedule(new AutoRecoverTimerTick(session), 2500);
try { }
Thread.sleep(2500); }
autoRecover(session);
} catch (Exception e) { public void cancelAutoRecover() {
Grasscutter.getLogger().error(e.getMessage()); if (autoRecoverTimer != null) {
} autoRecoverTimer.cancel();
}).start(); autoRecoverTimer = null;
}
}
private class AutoRecoverTimerTick extends TimerTask
{
private GameSession session;
public AutoRecoverTimerTick(GameSession session) {
this.session = session;
}
public void run() {
autoRecover(session);
cancelAutoRecover();
}
} }
public void refillSpringVolume() { public void refillSpringVolume() {
...@@ -124,24 +149,27 @@ public class SotSManager { ...@@ -124,24 +149,27 @@ public class SotSManager {
float newHP = currentHP + needSV / 100; // convert SV to HP float newHP = currentHP + needSV / 100; // convert SV to HP
// TODO: Figure out why client shows current HP instead of added HP. updateAvatarCurHP(session, entity, newHP);
// Say an avatar had 12000 and now has 14000, it should show "2000".
// The client always show "+14000" which is incorrect.
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
session.send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
newHP, List.of(3), PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_STATUE_RECOVER,
ChangeHpReasonOuterClass.ChangeHpReason.ChangeHpAddStatue));
session.send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
Avatar avatar = entity.getAvatar();
avatar.setCurrentHp(newHP);
session.send(new PacketAvatarFightPropUpdateNotify(avatar, FightProperty.FIGHT_PROP_CUR_HP));
player.save();
} }
}); });
} }
} }
private void updateAvatarCurHP(GameSession session, EntityAvatar entity, float newHP) {
// TODO: Figure out why client shows current HP instead of added HP.
// Say an avatar had 12000 and now has 14000, it should show "2000".
// The client always show "+14000" which is incorrect.
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
session.send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
newHP, List.of(3), PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_STATUE_RECOVER,
ChangeHpReasonOuterClass.ChangeHpReason.ChangeHpAddStatue));
session.send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
Avatar avatar = entity.getAvatar();
avatar.setCurrentHp(newHP);
session.send(new PacketAvatarFightPropUpdateNotify(avatar, FightProperty.FIGHT_PROP_CUR_HP));
player.save();
}
} }
...@@ -14,6 +14,7 @@ import emu.grasscutter.game.avatar.AvatarStorage; ...@@ -14,6 +14,7 @@ import emu.grasscutter.game.avatar.AvatarStorage;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.expedition.ExpeditionInfo;
import emu.grasscutter.game.friends.FriendsList; import emu.grasscutter.game.friends.FriendsList;
import emu.grasscutter.game.friends.PlayerProfile; import emu.grasscutter.game.friends.PlayerProfile;
import emu.grasscutter.game.gacha.PlayerGachaInfo; import emu.grasscutter.game.gacha.PlayerGachaInfo;
...@@ -26,6 +27,7 @@ import emu.grasscutter.game.managers.SotSManager.SotSManager; ...@@ -26,6 +27,7 @@ import emu.grasscutter.game.managers.SotSManager.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;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.managers.MapMarkManager.*; import emu.grasscutter.game.managers.MapMarkManager.*;
import emu.grasscutter.game.tower.TowerManager; import emu.grasscutter.game.tower.TowerManager;
...@@ -50,6 +52,7 @@ import emu.grasscutter.server.packet.send.*; ...@@ -50,6 +52,7 @@ import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.DateHelper; import emu.grasscutter.utils.DateHelper;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.MessageHandler; import emu.grasscutter.utils.MessageHandler;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
...@@ -101,6 +104,7 @@ public class Player { ...@@ -101,6 +104,7 @@ public class Player {
private ArrayList<AvatarProfileData> shownAvatars; private ArrayList<AvatarProfileData> shownAvatars;
private Set<Integer> rewardedLevels; private Set<Integer> rewardedLevels;
private ArrayList<ShopLimit> shopLimit; private ArrayList<ShopLimit> shopLimit;
private Map<Long, ExpeditionInfo> expeditionInfo;
private int sceneId; private int sceneId;
private int regionId; private int regionId;
...@@ -170,6 +174,7 @@ public class Player { ...@@ -170,6 +174,7 @@ public class Player {
this.moonCardGetTimes = new HashSet<>(); this.moonCardGetTimes = new HashSet<>();
this.shopLimit = new ArrayList<>(); this.shopLimit = new ArrayList<>();
this.expeditionInfo = new HashMap<>();
this.messageHandler = null; this.messageHandler = null;
this.mapMarksManager = new MapMarksManager(); this.mapMarksManager = new MapMarksManager();
this.movementManager = new MovementManager(this); this.movementManager = new MovementManager(this);
...@@ -673,6 +678,28 @@ public class Player { ...@@ -673,6 +678,28 @@ public class Player {
session.send(new PacketCardProductRewardNotify(getMoonCardRemainDays())); session.send(new PacketCardProductRewardNotify(getMoonCardRemainDays()));
} }
public Map<Long, ExpeditionInfo> getExpeditionInfo() {
return expeditionInfo;
}
public void addExpeditionInfo(long avaterGuid, int expId, int hourTime, int startTime){
ExpeditionInfo exp = new ExpeditionInfo();
exp.setExpId(expId);
exp.setHourTime(hourTime);
exp.setState(1);
exp.setStartTime(startTime);
expeditionInfo.put(avaterGuid, exp);
}
public void removeExpeditionInfo(long avaterGuid){
expeditionInfo.remove(avaterGuid);
}
public ExpeditionInfo getExpeditionInfo(long avaterGuid){
return expeditionInfo.get(avaterGuid);
}
public List<ShopLimit> getShopLimit() { public List<ShopLimit> getShopLimit() {
return shopLimit; return shopLimit;
} }
...@@ -1029,6 +1056,22 @@ public class Player { ...@@ -1029,6 +1056,22 @@ public class Player {
this.resetSendPlayerLocTime(); this.resetSendPlayerLocTime();
} }
} }
// Expedition
var timeNow = Utils.getCurrentSeconds();
var needNotify = false;
for (Long key : expeditionInfo.keySet()) {
ExpeditionInfo e = expeditionInfo.get(key);
if(e.getState() == 1){
if(timeNow - e.getStartTime() >= e.getHourTime() * 60 * 60){
e.setState(2);
needNotify = true;
}
}
}
if(needNotify){
this.save();
this.sendPacket(new PacketAvatarExpeditionDataNotify(this));
}
} }
...@@ -1041,6 +1084,7 @@ public class Player { ...@@ -1041,6 +1084,7 @@ public class Player {
@PostLoad @PostLoad
private void onLoad() { private void onLoad() {
this.getTeamManager().setPlayer(this); this.getTeamManager().setPlayer(this);
this.getTowerManager().setPlayer(this);
} }
public void save() { public void save() {
...@@ -1110,6 +1154,10 @@ public class Player { ...@@ -1110,6 +1154,10 @@ public class Player {
} }
public void onLogout() { public void onLogout() {
// force to leave the dungeon
if(getScene().getSceneType() == SceneType.SCENE_DUNGEON){
this.getServer().getDungeonManager().exitDungeon(this);
}
// Leave world // Leave world
if (this.getWorld() != null) { if (this.getWorld() != null) {
this.getWorld().removePlayer(this); this.getWorld().removePlayer(this);
......
...@@ -4,13 +4,7 @@ import com.google.gson.reflect.TypeToken; ...@@ -4,13 +4,7 @@ import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.data.def.ShopGoodsData; import emu.grasscutter.data.def.ShopGoodsData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.managers.InventoryManager;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.ItemParamOuterClass;
import emu.grasscutter.net.proto.ShopGoodsOuterClass;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
...@@ -54,9 +48,9 @@ public class ShopManager { ...@@ -54,9 +48,9 @@ public class ShopManager {
public static int getShopNextRefreshTime(ShopInfo shopInfo) { public static int getShopNextRefreshTime(ShopInfo shopInfo) {
return switch (shopInfo.getShopRefreshType()) { return switch (shopInfo.getShopRefreshType()) {
case SHOP_REFRESH_DAILY -> Utils.GetNextTimestampOfThisHour(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam()); case SHOP_REFRESH_DAILY -> Utils.getNextTimestampOfThisHour(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
case SHOP_REFRESH_WEEKLY -> Utils.GetNextTimestampOfThisHourInNextWeek(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam()); case SHOP_REFRESH_WEEKLY -> Utils.getNextTimestampOfThisHourInNextWeek(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
case SHOP_REFRESH_MONTHLY -> Utils.GetNextTimestampOfThisHourInNextMonth(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam()); case SHOP_REFRESH_MONTHLY -> Utils.getNextTimestampOfThisHourInNextMonth(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
default -> 0; default -> 0;
}; };
} }
......
...@@ -3,40 +3,100 @@ package emu.grasscutter.game.tower; ...@@ -3,40 +3,100 @@ package emu.grasscutter.game.tower;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.TowerLevelData;
import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.dungeons.TowerDungeonSettleListener;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify;
import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp; import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp;
import java.util.List; import java.util.List;
@Entity @Entity
public class TowerManager { public class TowerManager {
@Transient private final Player player; @Transient private Player player;
public TowerManager(Player player) { public TowerManager(Player player) {
this.player = player; this.player = player;
} }
public void setPlayer(Player player) {
this.player = player;
}
private int currentFloorId;
private int currentLevel; private int currentLevel;
private int currentFloor; @Transient
private int currentLevelId;
@Transient
private int entryScene;
public int getCurrentFloorId() {
return currentFloorId;
}
private static final List<DungeonSettleListener> towerDungeonSettleListener = List.of(new TowerDungeonSettleListener());
public void teamSelect(int floor, List<List<Long>> towerTeams) { public void teamSelect(int floor, List<List<Long>> towerTeams) {
var floorData = GameData.getTowerFloorDataMap().get(floor); var floorData = GameData.getTowerFloorDataMap().get(floor);
this.currentFloor = floorData.getFloorId(); this.currentFloorId = floorData.getFloorId();
this.currentLevel = floorData.getLevelId(); this.currentLevel = 0;
this.currentLevelId = GameData.getTowerLevelDataMap().values().stream()
.filter(x -> x.getLevelId() == floorData.getLevelId() && x.getLevelIndex() == 1)
.findFirst()
.map(TowerLevelData::getID)
.orElse(0);
if (entryScene == 0){
entryScene = player.getSceneId();
}
player.getTeamManager().setupTemporaryTeam(towerTeams); player.getTeamManager().setupTemporaryTeam(towerTeams);
} }
public void enterLevel(int enterPointId) { public void enterLevel(int enterPointId) {
var levelData = GameData.getTowerLevelDataMap().get(currentLevel); var levelData = GameData.getTowerLevelDataMap().get(currentLevelId + currentLevel);
this.currentLevel++;
var id = levelData.getDungeonId(); var id = levelData.getDungeonId();
notifyCurLevelRecordChange();
// use team user choose // use team user choose
player.getTeamManager().useTemporaryTeam(0); player.getTeamManager().useTemporaryTeam(0);
player.getServer().getDungeonManager() player.getServer().getDungeonManager().handoffDungeon(player, id,
.enterDungeon(player, enterPointId, id); towerDungeonSettleListener);
// make sure user can exit dungeon correctly
player.getScene().setPrevScene(entryScene);
player.getScene().setPrevScenePoint(enterPointId);
player.getSession().send(new PacketTowerEnterLevelRsp(currentFloorId, currentLevel));
}
public void notifyCurLevelRecordChange(){
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel));
}
public void notifyCurLevelRecordChangeWhenDone(){
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel + 1));
}
public boolean hasNextLevel(){
return this.currentLevel < 3;
}
public int getNextFloorId() {
if(hasNextLevel()){
return 0;
}
this.currentFloorId++;
return this.currentFloorId;
}
player.getSession().send(new PacketTowerEnterLevelRsp(currentFloor, currentLevel)); public void clearEntry() {
this.entryScene = 0;
} }
} }
package emu.grasscutter.game.world; package emu.grasscutter.game.world;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.def.DungeonData; import emu.grasscutter.data.def.DungeonData;
...@@ -8,6 +7,7 @@ import emu.grasscutter.data.def.MonsterData; ...@@ -8,6 +7,7 @@ import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.data.def.WorldLevelData; import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.dungeons.DungeonChallenge; import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamInfo; import emu.grasscutter.game.player.TeamInfo;
...@@ -20,11 +20,8 @@ import emu.grasscutter.net.packet.BasePacket; ...@@ -20,11 +20,8 @@ import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.SceneScriptManager; import emu.grasscutter.scripts.SceneScriptManager;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneBlock; import emu.grasscutter.scripts.data.SceneBlock;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify; import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
...@@ -56,6 +53,7 @@ public class Scene { ...@@ -56,6 +53,7 @@ public class Scene {
private SceneScriptManager scriptManager; private SceneScriptManager scriptManager;
private DungeonChallenge challenge; private DungeonChallenge challenge;
private List<DungeonSettleListener> dungeonSettleListeners;
private DungeonData dungeonData; private DungeonData dungeonData;
private int prevScene; // Id of the previous scene private int prevScene; // Id of the previous scene
private int prevScenePoint; private int prevScenePoint;
...@@ -205,6 +203,17 @@ public class Scene { ...@@ -205,6 +203,17 @@ public class Scene {
this.challenge = challenge; this.challenge = challenge;
} }
public void addDungeonSettleObserver(DungeonSettleListener dungeonSettleListener){
if(dungeonSettleListeners == null){
dungeonSettleListeners = new ArrayList<>();
}
dungeonSettleListeners.add(dungeonSettleListener);
}
public List<DungeonSettleListener> getDungeonSettleObservers() {
return dungeonSettleListeners;
}
public boolean isInScene(GameEntity entity) { public boolean isInScene(GameEntity entity) {
return this.entities.containsKey(entity.getId()); return this.entities.containsKey(entity.getId());
} }
......
package emu.grasscutter.languages;
public final class CNLanguage {
public String An_error_occurred_during_game_update = "游戏更新时发生了错误.";
public String Starting_Grasscutter = "正在开启Grasscutter...";
public String Invalid_server_run_mode = "无效的服务器运行模式. ";
public String Server_run_mode = "服务器运行模式必须为以下几种之一: 'HYBRID'(混合模式), 'DISPATCH_ONLY'(仅dispatch模式), 或 'GAME_ONLY'(仅游戏模式). 无法启动Grasscutter...";
public String Shutting_down = "正在停止....";
public String Start_done = "加载完成!需要指令帮助请输入 \"help\"";
public String Dispatch_mode_not_support_command = "仅dispatch模式无法使用指令。";
public String Command_error = "命令错误:";
public String Error = "出现错误.";
public String Grasscutter_is_free = "Grasscutter是免费软件,如果你是花钱买到的,你大概被骗了。主页: https://github.com/Grasscutters/Grasscutter";
public String Game_start_port = "游戏服务器已在端口 {port} 上开启。";
public String Client_connect = "来自 {address} 的客户端已连接。";
public String Client_disconnect = "来自 {address} 的客户端已断开。";
public String Client_request = "[Dispatch] 客户端 {ip} 请求: {method} {url}";
public String Not_load_keystore = "[Dispatch] 无法加载证书,正在尝试默认密码...";
public String Use_default_keystore = "[Dispatch] 成功使用默认密码加载证书. 请考虑将config.json中的KeystorePassword项改为123456.";
public String Load_keystore_error = "[Dispatch] 加载证书时出现错误!";
public String Not_find_ssl_cert = "[Dispatch] 未找到SSL证书,正在回滚至HTTP模式。";
public String Welcome = "欢迎使用Grasscutter";
public String Potential_unhandled_request = "[Dispatch] 潜在的未处理请求: {method} {url}";
public String Client_login_token = "[Dispatch] 客户端 {ip} 正在尝试使用token登录...";
public String Client_token_login_failed = "[Dispatch] 客户端 {ip} 使用token登录失败。";
public String Client_login_in_token = "[Dispatch] 客户端 {ip} 使用token以 {uid} 的身份登录。";
public String Game_account_cache_error = "游戏账户缓存出现错误。";
public String Wrong_session_key = "会话密钥错误。";
public String Client_exchange_combo_token = "[Dispatch] 客户端 {ip} 成功交换token。";
public String Client_failed_exchange_combo_token = "[Dispatch] 客户端 {ip} 交换token失败。";
public String Dispatch_start_server_port = "[Dispatch] Dispatch服务器已在端口 {port} 上开启。";
public String Client_failed_login_account_create = "[Dispatch] 客户端 {ip} 登录失败: 已创建UID为 {uid} 的账户。";
public String Client_failed_login_account_create_failed = "[Dispatch] 客户端 {ip} 登录失败: 创建账户失败。";
public String Client_failed_login_account_no_found = "[Dispatch] 客户端 {ip} 登录失败: 未找到帐户。";
public String Client_login = "[Dispatch] 客户端 {ip} 以 {uid} 的身份登录。";
public String Username_not_found = "未找到此用户名.";
public String Username_not_found_create_failed = "未找到此用户名, 创建失败。.";
// Command
public String No_command_specified = "未指定命令.";
public String Unknown_command = "未知命令: ";
public String You_not_permission_run_command = "你没有权限运行此命令.";
public String This_command_can_only_run_from_console = "此命令只能在控制台运行.";
public String Run_this_command_in_game = "请在游戏内运行此命令.";
public String Invalid_playerId = "无效的玩家ID.";
public String Player_not_found = "未找到此玩家.";
public String Player_is_offline = "此玩家已离线.";
public String Invalid_amount = "无效的数量.";
public String Invalid_arguments = "无效的命令参数.";
public String Invalid_artifact_id = "无效的圣遗物ID.";
public String Invalid_avatar_id = "无效的角色ID.";
public String Invalid_avatar_level = "无效的角色等级.";
public String Invalid_entity_id = "无效的物品ID.";
public String Invalid_item_id = "无效的物品ID.";
public String Invalid_item_level = "无效的物品等级.";
public String Invalid_item_refinement = "无效的精炼等级.";
public String Invalid_UID = "无效的UID.";
public String Enabled = "启用";
public String Disabled = "禁用";
public String No_command_found = "未找到命令.";
public String Help = "帮助";
public String Player_not_found_or_offline = "此玩家不存在或已离线.";
public String Success = "成功";
public String Target_cleared = "已清除选择目标";
public String Target_set = "接下来的命令将默认以 @{uid} 为目标。输入命令时不必继续携带UID参数。";
public String Target_needed = "此命令需要指定一个目标用户. 输入命令时携带 <@UID> 参数或使用 /target @UID 来指定一个默认目标用户.";
// Help
public String Help_usage = " 用法: ";
public String Help_aliases = " 别名: ";
public String Help_available_command = " 可用命令:";
// Account
public String Modify_user_account = "修改用户帐户";
public String Account_exists = "账户已存在.";
public String Account_create_UID = "UID为 {uid} 的账户已创建.";
public String Account_delete = "已删除账户.";
public String Account_not_find = "账户不存在.";
public String Account_command_usage = "用法: account <create(创建)|delete(删除)> <用户名> [uid]";
// Broadcast
public String Broadcast_command_usage = "用法: broadcast <消息>";
public String Broadcast_message_sent = "消息已发送.";
// ChangeScene
public String Change_screen_usage = "用法: changescene <场景id>";
public String Change_screen_you_in_that_screen = "你已经在此场景中了";
public String Change_screen = "切换到场景 ";
public String Change_screen_not_exist = "此场景不存在。";
// Cleart_or_playerId
public String Clear_weapons = "已清除 {name} 的武器.";
public String Clear_artifacts = "已清除 {name} 的圣遗物 .";
public String Clear_materials = "已清除 {name} 的材料.";
public String Clear_furniture = "已清除 {name} 的摆设.";
public String Clear_displays = "已清除 {name} 的displays.";
public String Clear_virtuals = "已清除 {name} 的virtuals.";
public String Clear_everything = "已清除 {name} 的所有物品.";
// Coop
public String Coop_usage = "用法: coop <房主的UID>";
public String Coop_success = "已将{target}拉进{host}的世界.";
// Drop
public String Drop_usage = "用法: drop <物品ID|物品名> [数量]";
public String Drop_dropped_of = "已在地上丢弃 {amount} 个 {item}.";
// EnterDungeon
public String EnterDungeon_usage = "用法: enterdungeon <副本 id>";
public String EnterDungeon_changed_to_dungeon = "已进入副本 ";
public String EnterDungeon_dungeon_not_found = "副本不存在";
public String EnterDungeon_you_in_that_dungeon = "你已经在此副本中了。";
// GiveAll
public String GiveAll_usage = "用法: giveall [数量]";
public String GiveAll_item = "正在给予所有物品...";
public String GiveAll_done = "完成。";
// GiveArtifact
public String GiveArtifact_usage = "用法: giveart|gart [玩家] <圣遗物Id> <主词条Id> [<副词条Id>[,<被强化次数>]]... [等级]";
public String GiveArtifact_given = "已将 {itemId} 给予 {target}.";
// GiveChar
public String GiveChar_usage = "用法: givechar <p玩家> <角色Id|角色名> [等级]";
public String GiveChar_given = "将等级为 {level} 的 {avatarId} 给予 {target}.";
// Give
public String Give_usage = "用法: give [玩家名] <物品ID|物品名> [数量] [等级] ";
public String Give_refinement_only_applicable_weapons = "精炼只对武器有效。";
public String Give_refinement_must_between_1_and_5 = "精炼等级必须在1和5之间。";
public String Give_given = "已将 {amount} 个 {item} 给与 {target}.";
public String Give_given_with_level_and_refinement = "已将 {amount} 个等级为 {lvl}, 精炼 {refinement} 的 {item} 给予 {target}.";
public String Give_given_level = "已将 {amount} 个等级为 {lvl} 的 {item} 给与 {target}.";
// GodMode
public String Godmode_usage = "用法: godmode [on|off|toggle]";
public String Godmode_status = "设置 {name} 的无敌模式为: {status} ";
// Heal
public String Heal_message = "所有角色已被治疗。";
// Kick
public String Kick_player_kick_player = "玩家 [{sendUid}:{sendName}] 已踢出 [{kickUid}:{kickName}]";
public String Kick_server_player = "正在踢出玩家 [{kickUid}:{kickName}]";
// Kill
public String Kill_usage = "用法: killall [玩家UID] [场景ID]";
public String Kill_scene_not_found_in_player_world = "未在玩家世界中找到此场景";
public String Kill_kill_monsters_in_scene = "已杀死场景 {id} 中的 {size} 只怪物。 ";
// KillCharacter
public String KillCharacter_usage = "用法: /killcharacter [玩家Id]";
public String KillCharacter_kill_current_character = "已干掉 {name} 当前的场上角色.";
// List
public String List_message = "现有 {size} 名玩家在线:";
// Permission
public String Permission_usage = "用法: permission <add|remove> <权限名>";
public String Permission_add = "权限已添加。";
public String Permission_have_permission = "此玩家已拥有此权限!";
public String Permission_remove = "权限已移除。";
public String Permission_not_have_permission = "此玩家未拥有此权限!";
// Position
public String Position_message = "坐标: {x},{y},{z}\n场景: {id}";
// Reload
public String Reload_reload_start = "正在重新加载配置.";
public String Reload_reload_done = "完成.";
// ResetConst
public String ResetConst_reset_all = "重置你所有角色的命座。";
public String ResetConst_reset_all_done = "{name} 的命座已被重置。请重新登录。";
// ResetShopLimit
public String ResetShopLimit_usage = "用法: /resetshop <玩家id>";
// SendMail
public String SendMail_usage = "用法: give [player] <itemId|itemName> [amount]";
public String SendMail_user_not_exist = "不存在id为 '{id}' 的用户。";
public String SendMail_start_composition = "开始编辑邮件的组成部分.\n请使用 `/sendmail <标题>` 以继续.\n你可以在任何时候使用`/sendmail stop` 来停止编辑.";
public String SendMail_templates = "很快就会有邮件模板了.......";
public String SendMail_invalid_arguments = "无效的参数.\n用法: `/sendmail <用户Id|all|help> [模板Id]``";
public String SendMail_send_cancel = "已取消发送邮件。";
public String SendMail_send_done = "已向 {name} 发送邮件!";
public String SendMail_send_all_done = "已向所有玩家发送邮件!";
public String SendMail_not_composition_end = "邮件组成部分编辑尚未结束.\n请使用 `/sendmail {args}` 或 `/sendmail stop` 来停止编辑";
public String SendMail_Please_use = "请使用 `/sendmail {args}`";
public String SendMail_set_title = "邮件标题已设为 '{title}'.\n使用 '/sendmail <邮件正文>' 以继续.";
public String SendMail_set_contents = "邮件的正文如下:\n '{contents}'\n使用 '/sendmail <发送者署名>' 以继续.";
public String SendMail_set_message_sender = "邮件的发送者已设为 '{send}'.\n使用 '/sendmail <物品Id|物品名|finish(结束编辑并发送)> [数量] [等级]";
public String SendMail_send = "已将 {amount} 个 {item} (等级 {lvl}) 作为邮件附件.\n你可以继续添加附件,也可以使用 `/sendmail finish` 来停止编辑并发送邮件.";
public String SendMail_invalid_arguments_please_use = "无效的参数 \n 请使用 `/sendmail {args}`";
public String SendMail_title = "<标题>";
public String SendMail_message = "<正文>";
public String SendMail_sender = "<发送者>";
public String SendMail_arguments = "<物品Id|物品名|finish(结束编辑并发送)> [数量] [等级]";
public String SendMail_error = "错误:无效的编写阶段 {stage}. 需要stacktrace请看服务器命令行.";
// SendMessage
public String SendMessage_usage = "用法: sendmessage <玩家名> <消息>";
public String SenaMessage_message_sent = "已发送.";
// SetFetterLevel
public String SetFetterLevel_usage = "用法: setfetterlevel <等级>";
public String SetFetterLevel_fetter_level_must_between_0_and_10 = "设置的好感等级必须位于 0 和 10 之间。";
public String SetFetterLevel_fetter_set_level = "好感等级已设置为 {level}";
public String SetFetterLevel_invalid_fetter_level = "无效的好感等级。";
// SetStats
public String SetStats_usage = "用法: setstats|stats <stat> <value>";
public String SetStats_setstats_help_message = "用法: /setstats|stats <hp(生命值) | mhp(最大生命值) | def(防御力) | atk(攻击) | em(元素精通) | crate(暴击率) | cdmg(暴击伤害)> <数值> 基本属性(整数)";
public String SetStats_stats_help_message = "用法: /stats <er(元素充能) | epyro(火伤) | ecryo(冰伤) | ehydro(水伤) | eanemo(风伤) | egeo(岩伤) | edend(草伤) | eelec(雷伤) | ephys(物伤)> <数值> 元素属性(百分比)";
public String SetStats_set_max_hp = "最大生命值已设为 {int}.";
public String SetStats_set_max_hp_error = "无效的生命数值.";
public String SetStats_set_hp = "生命设置为 {int}.";
public String SetStats_set_hp_error = "无效的生命数值.";
public String SetStats_set_def = "防御力设置为 {int}.";
public String SetStats_set_def_error = "无效的防御力数值.";
public String SetStats_set_atk = "攻击力设置为 {int}.";
public String SetStats_set_atk_error = "无效的攻击力数值.";
public String SetStats_set_em = "元素精通设置为 {int}.";
public String SetStats_set_em_error = "无效的元素精通数值.";
public String SetStats_set_er = "元素充能设置为 {int}%.";
public String SetStats_set_er_error = "无效的元素充能数值.";
public String SetStats_set_cr = "暴击率设置为 {int}%.";
public String SetStats_set_cr_error = "无效的暴击率数值.";
public String SetStats_set_cd = "暴击伤害设置为 {int}%.";
public String SetStats_set_cd_error = "无效的暴击伤害数值.";
public String SetStats_set_pdb = "火伤设置为 {int}%.";
public String SetStats_set_pdb_error = "无效的火伤数值.";
public String SetStats_set_cdb = "冰伤设置为 {int}%.";
public String SetStats_set_cdb_error = "无效的冰伤数值.";
public String SetStats_set_hdb = "水伤设置为 {int}%.";
public String SetStats_set_hdb_error = "无效的水伤数值.";
public String SetStats_set_adb = "风伤设置为 {int}%.";
public String SetStats_set_adb_error = "无效的风伤数值.";
public String SetStats_set_gdb = "岩伤设置为 {int}%.";
public String SetStats_set_gdb_error = "无效的岩伤数值.";
public String SetStats_set_edb = "雷伤设置为 {int}%.";
public String SetStats_set_edb_error = "无效的雷伤数值.";
public String SetStats_set_physdb = "物伤设置为 {int}%.";
public String SetStats_set_physdb_error = "无效的物伤数值.";
public String SetStats_set_ddb = "草伤设置为 {int}%.";
public String SetStats_set_ddb_error = "无效的草伤数值.";
// SetWorldLevel
public String SetWorldLevel_usage = "用法: setworldlevel <level>";
public String SetWorldLevel_world_level_must_between_0_and_8 = "世界等级必须在0-8之间。";
public String SetWorldLevel_set_world_level = "世界等级已设置为 {level}.";
public String SetWorldLevel_invalid_world_level = "无效的世界等级.";
// Spawn
public String Spawn_usage = "用法: spawn <实体ID|实体名> [数量] [等级(仅限怪物)]";
public String Spawn_message = "已生成 {amount} 个 {id}.";
// Stop
public String Stop_message = "正在关闭服务器...";
// Talent
public String Talent_usage_1 = "设置技能等级: /talent set <技能ID> <数值>";
public String Talent_usage_2 = "另一种方式: /talent <n 或 e 或 q> <数值>";
public String Talent_usage_3 = "获取技能ID: /talent getid";
public String Talent_lower_16 = "技能等级应低于16。";
public String Talent_set_atk = "设置普通攻击等级为 {level}.";
public String Talent_set_e = "设置元素战技(e技能)等级为 {level}.";
public String Talent_set_q = "设置元素爆发(q技能)等级为 {level}.";
public String Talent_invalid_skill_id = "无效的技能ID。";
public String Talent_set_this = "技能等级已设为 {level}.";
public String Talent_set_id = "将技能 {id} 的等级设为 {level}.";
public String Talent_invalid_talent_level = "无效的技能等级。";
public String Talent_normal_attack_id = "普通攻击技能ID {id}.";
public String Talent_e_skill_id = "元素战技(e技能)ID {id}.";
public String Talent_q_skill_id = "元素爆发(q技能)ID {id}.";
// TeleportAll
public String TeleportAll_message = "此命令仅在多人游戏下可用。";
// Teleport
public String Teleport_usage_server = "用法: /tp <x> <y> <z> [场景ID]";
public String Teleport_invalid_position = "无效的位置。";
public String Teleport_message = "已将 {name} 传送到场景 {id} ,坐标 {x},{y},{z}";
// Weather
public String Weather_usage = "用法: weather <天气ID> [气候ID]";
public String Weather_message = "已修改天气为 {weatherId} 气候为 {climateId}";
public String Weather_invalid_id = "无效的ID。";
}
package emu.grasscutter.languages;
public final class Language {
public String An_error_occurred_during_game_update = "An error occurred during game update.";
public String Starting_Grasscutter = "Starting Grasscutter...";
public String Invalid_server_run_mode = "Invalid server run mode.";
public String Server_run_mode = "Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter...";
public String Shutting_down = "Shutting down...";
public String Start_done = "Done! For help, type \"help\"";
public String Dispatch_mode_not_support_command = "Commands are not supported in dispatch only mode.";
public String Command_error = "Command error:";
public String Error = "An error occurred.";
public String Grasscutter_is_free = "Grasscutter is FREE software. If you have paid for this, you may have been scammed. Homepage: https://github.com/Grasscutters/Grasscutter";
public String Game_start_port = "Game Server started on port {port}";
public String Client_connect = "Client connected from {address}";
public String Client_disconnect = "Client disconnected from {address}";
public String Client_request = "[Dispatch] Client {ip} {method} request: {url}";
public String Not_load_keystore = "[Dispatch] Unable to load keystore. Trying default keystore password...";
public String Use_default_keystore = "[Dispatch] The default keystore password was loaded successfully. Please consider setting the password to 123456 in config.json.";
public String Load_keystore_error = "[Dispatch] Error while loading keystore!";
public String Not_find_ssl_cert = "[Dispatch] No SSL cert found! Falling back to HTTP server.";
public String Welcome = "Welcome to Grasscutter";
public String Potential_unhandled_request = "[Dispatch] Potential unhandled {method} request: {url}";
public String Client_try_login = "[Dispatch] Client {ip} is trying to log in";
public String Client_login_token = "[Dispatch] Client {ip} is trying to log in via token";
public String Client_token_login_failed = "[Dispatch] Client {ip} failed to log in via token";
public String Client_login_in_token = "[Dispatch] Client {ip} logged in via token as {uid}";
public String Game_account_cache_error = "Game account cache information error";
public String Wrong_session_key = "Wrong session key.";
public String Client_exchange_combo_token = "[Dispatch] Client {ip} succeed to exchange combo token";
public String Client_failed_exchange_combo_token = "[Dispatch] Client {ip} failed to exchange combo token";
public String Dispatch_start_server_port = "[Dispatch] Dispatch server started on port {port}";
public String Client_failed_login_account_create = "[Dispatch] Client {ip} failed to log in: Account {uid} created";
public String Client_failed_login_account_create_failed = "[Dispatch] Client {ip} failed to log in: Account create failed";
public String Client_failed_login_account_no_found = "[Dispatch] Client {ip} failed to log in: Account no found";
public String Client_login = "[Dispatch] Client {ip} logged in as {uid}";
public String Username_not_found = "Username not found.";
public String Username_not_found_create_failed = "Username not found, create failed.";
public String Create_resources_folder = "Creating resources folder...";
public String Place_copy = "Place a copy of 'BinOutput' and 'ExcelBinOutput' in the resources folder.";
// Command
public String No_command_specified = "No command specified.";
public String Unknown_command = "Unknown command: ";
public String You_not_permission_run_command = "You do not have permission to run this command.";
public String This_command_can_only_run_from_console = "This command can only be run from the console.";
public String Run_this_command_in_game = "Run this command in-game.";
public String Invalid_amount = "Invalid amount.";
public String Invalid_arguments = "Invalid arguments.";
public String Invalid_artifact_id = "Invalid artifact ID.";
public String Invalid_avatar_id = "Invalid avatar id.";
public String Invalid_avatar_level = "Invalid avatar level.";
public String Invalid_entity_id = "Invalid entity id.";
public String Invalid_item_id = "Invalid item id.";
public String Invalid_item_level = "Invalid item level.";
public String Invalid_item_refinement = "Invalid item refinement level.";
public String Invalid_playerId = "Invalid playerId.";
public String Invalid_UID = "Invalid UID.";
public String Player_not_found = "Player not found.";
public String Player_is_offline = "Player is offline.";
public String Enabled = "enabled";
public String Disabled = "disabled";
public String No_command_found = "No command found.";
public String Help = "Help";
public String Player_not_found_or_offline = "Player not found or offline.";
public String Success = "Success";
public String Target_cleared = "Target cleared.";
public String Target_set = "Subsequent commands will target @{uid} by default.";
public String Target_needed = "This command requires a target UID. Add a <@UID> argument or set a persistent target with /target @UID.";
// Help
public String Help_usage = " Usage: ";
public String Help_aliases = " Aliases: ";
public String Help_available_command = "Available commands:";
// Account
public String Modify_user_account = "Modify user accounts";
public String Account_exists = "Account already exists.";
public String Account_create_UID = "Account created with UID {uid}.";
public String Account_delete = "Account deleted.";
public String Account_not_find = "Account not found.";
public String Account_command_usage = "Usage: account <create|delete> <username> [uid]";
// Broadcast
public String Broadcast_command_usage = "Usage: broadcast <message>";
public String Broadcast_message_sent = "Message sent.";
// ChangeScene
public String Change_screen_usage = "Usage: changescene <scene id>";
public String Change_screen_you_in_that_screen = "You are already in that scene";
public String Change_screen = "Changed to scene ";
public String Change_screen_not_exist = "Scene does not exist";
// Clear
public String Clear_usage = "Usage: clear <all|wp|art|mat>";
public String Clear_weapons = "Cleared weapons for {name} .";
public String Clear_artifacts = "Cleared artifacts for {name} .";
public String Clear_materials = "Cleared materials for {name} .";
public String Clear_furniture = "Cleared furniture for {name} .";
public String Clear_displays = "Cleared displays for {name} .";
public String Clear_virtuals = "Cleared virtuals for {name} .";
public String Clear_everything = "Cleared everything for {name} .";
// Coop
public String Coop_usage = "Usage: coop <host UID>";
public String Coop_success = "Summoned {target} to {host}'s world.";
// Drop
public String Drop_usage = "Usage: drop <itemId|itemName> [amount]";
public String Drop_dropped_of = "Dropped {amount} of {item}.";
// EnterDungeon
public String EnterDungeon_usage = "Usage: enterdungeon <dungeon id>";
public String EnterDungeon_changed_to_dungeon = "Changed to dungeon ";
public String EnterDungeon_dungeon_not_found = "Dungeon does not exist";
public String EnterDungeon_you_in_that_dungeon = "You are already in that dungeon";
// GiveAll
public String GiveAll_usage = "Usage: giveall [amount]";
public String GiveAll_item = "Giving all items...";
public String GiveAll_done = "Giving all items done";
// GiveArtifact
public String GiveArtifact_usage = "Usage: giveart|gart [player] <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]";
public String GiveArtifact_given = "Given {itemId} to {target}.";
// GiveChar
public String GiveChar_usage = "Usage: givechar <player> <itemId|itemName> [amount]";
public String GiveChar_given = "Given {avatarId} with level {level} to {target}.";
// Give
public String Give_usage = "Usage: give <player> <itemId|itemName> [amount] [level]";
public String Give_refinement_only_applicable_weapons = "Refinement is only applicable to weapons.";
public String Give_refinement_must_between_1_and_5 = "Refinement must be between 1 and 5.";
public String Give_given = "Given {amount} of {item} to {target}.";
public String Give_given_with_level_and_refinement = "Given {item} with level {lvl}, refinement {refinement} {amount} times to {target}";
public String Give_given_level = "Given {item} with level {lvl} {amount} times to {target}";
// GodMode
public String Godmode_usage = "Usage: godmode [on|off|toggle]";
public String Godmode_status = "Godmode is now {status} for {name}.";
// Heal
public String Heal_message = "All characters have been healed.";
// Kick
public String Kick_player_kick_player = "Player [{sendUid}:{sendName}] has kicked player [{kickUid}:{kickName}]";
public String Kick_server_player = "Kicking player [{kickUid}:{kickName}]";
// Kill
public String Kill_usage = "Usage: killall [playerUid] [sceneId]";
public String Kill_scene_not_found_in_player_world = "Scene not found in player world";
public String Kill_kill_monsters_in_scene = "Killing {size} monsters in scene {id}";
// KillCharacter
public String KillCharacter_usage = "Usage: /killcharacter [playerId]";
public String KillCharacter_kill_current_character = "Killed {name} current character.";
// List
public String List_message = "There are {size} player(s) online:";
// Permission
public String Permission_usage = "Usage: permission <add|remove> <permission>";
public String Permission_add = "Permission added.";
public String Permission_have_permission = "They already have this permission!";
public String Permission_remove = "Permission removed.";
public String Permission_not_have_permission = "They don't have this permission!";
// Position
public String Position_message = "Coord: {x}, {y}, {z}\nScene id: {id}";
// Reload
public String Reload_reload_start = "Reloading config.";
public String Reload_reload_done = "Reload complete.";
// ResetConst
public String ResetConst_reset_all = "Reset all avatars' constellations.";
public String ResetConst_reset_all_done = "Constellations for {name} have been reset. Please relog to see changes.";
// ResetShopLimit
public String ResetShopLimit_usage = "Usage: /resetshop <player id>";
// SendMail
public String SendMail_usage = "Usage: give [player] <itemId|itemName> [amount]";
public String SendMail_user_not_exist = "The user with an id of '{id}' does not exist";
public String SendMail_start_composition = "Starting composition of message.\nPlease use `/sendmail <title>` to continue.\nYou can use `/sendmail stop` at any time";
public String SendMail_templates = "Mail templates coming soon implemented...";
public String SendMail_invalid_arguments = "Invalid arguments.\nUsage `/sendmail <userId|all|help> [templateId]`";
public String SendMail_send_cancel = "Message sending cancelled";
public String SendMail_send_done = "Message sent to user {name}!";
public String SendMail_send_all_done = "Message sent to all users!";
public String SendMail_not_composition_end = "Message composition not at final stage.\nPlease use `/sendmail {args}` or `/sendmail stop` to cancel";
public String SendMail_please_use = "Please use `/sendmail {args}`";
public String SendMail_set_title = "Message title set as '{title}'.\nUse '/sendmail <content>' to continue.";
public String SendMail_set_contents = "Message contents set as '{contents}'.\nUse '/sendmail <sender>' to continue.";
public String SendMail_set_message_sender = "Message sender set as '{send}'.\nUse '/sendmail <itemId|itemName|finish> [amount] [level]' to continue.";
public String SendMail_send = "Attached {amount} of {item} (level {lvl}) to the message.\nContinue adding more items or use `/sendmail finish` to send the message.";
public String SendMail_invalid_arguments_please_use = "Invalid arguments \n Please use `/sendmail {args}`";
public String SendMail_title = "<title>";
public String SendMail_message = "<message>";
public String SendMail_sender = "<sender>";
public String SendMail_arguments = "<itemId|itemName|finish> [amount] [level]";
public String SendMail_error = "ERROR: invalid construction stage {stage}. Check console for stacktrace.";
// SendMessage
public String SendMessage_usage = "Usage: sendmessage <player> <message>";
public String SenaMessage_message_sent = "Message sent.";
// SetFetterLevel
public String SetFetterLevel_usage = "Usage: setfetterlevel <level>";
public String SetFetterLevel_fetter_level_must_between_0_and_10 = "Fetter level must be between 0 and 10.";
public String SetFetterLevel_fetter_set_level = "Fetter level set to {level}";
public String SetFetterLevel_invalid_fetter_level = "Invalid fetter level.";
// SetStats
public String SetStats_usage_console = "Usage: setstats|stats @<UID> <stat> <value>";
public String SetStats_usage_ingame = "Usage: setstats|stats [@UID] <stat> <value>";
public String SetStats_help_message = """
\n\tValues for <stat>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi
\t(cont.) Elemental DMG Bonus: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys
\t(cont.) Elemental RES: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys
""";
public String SetStats_value_error = "Invalid stat value.";
public String SetStats_set_self = "{name} set to {value}.";
public String SetStats_set_for_uid = "{name} for {uid} set to {value}.";
public String Stats_FIGHT_PROP_MAX_HP = "Max HP";
public String Stats_FIGHT_PROP_CUR_HP = "Current HP";
public String Stats_FIGHT_PROP_CUR_ATTACK = "ATK";
public String Stats_FIGHT_PROP_BASE_ATTACK = "Base ATK";
public String Stats_FIGHT_PROP_DEFENSE = "DEF";
public String Stats_FIGHT_PROP_ELEMENT_MASTERY = "Elemental Mastery";
public String Stats_FIGHT_PROP_CHARGE_EFFICIENCY = "Energy Recharge";
public String Stats_FIGHT_PROP_CRITICAL = "Crit Rate";
public String Stats_FIGHT_PROP_CRITICAL_HURT = "Crit DMG";
public String Stats_FIGHT_PROP_ADD_HURT = "DMG Bonus";
public String Stats_FIGHT_PROP_WIND_ADD_HURT = "Anemo DMG Bonus";
public String Stats_FIGHT_PROP_ICE_ADD_HURT = "Cryo DMG Bonus";
public String Stats_FIGHT_PROP_GRASS_ADD_HURT = "Dendro DMG Bonus";
public String Stats_FIGHT_PROP_ELEC_ADD_HURT = "Electro DMG Bonus";
public String Stats_FIGHT_PROP_ROCK_ADD_HURT = "Geo DMG Bonus";
public String Stats_FIGHT_PROP_WATER_ADD_HURT = "Hydro DMG Bonus";
public String Stats_FIGHT_PROP_FIRE_ADD_HURT = "Pyro DMG Bonus";
public String Stats_FIGHT_PROP_PHYSICAL_ADD_HURT = "Physical DMG Bonus";
public String Stats_FIGHT_PROP_SUB_HURT = "DMG Reduction";
public String Stats_FIGHT_PROP_WIND_SUB_HURT = "Anemo RES";
public String Stats_FIGHT_PROP_ICE_SUB_HURT = "Cryo RES";
public String Stats_FIGHT_PROP_GRASS_SUB_HURT = "Dendro RES";
public String Stats_FIGHT_PROP_ELEC_SUB_HURT = "Electro RES";
public String Stats_FIGHT_PROP_ROCK_SUB_HURT = "Geo RES";
public String Stats_FIGHT_PROP_WATER_SUB_HURT = "Hydro RES";
public String Stats_FIGHT_PROP_FIRE_SUB_HURT = "Pyro RES";
public String Stats_FIGHT_PROP_PHYSICAL_SUB_HURT = "Physical RES";
public String Stats_FIGHT_PROP_SKILL_CD_MINUS_RATIO = "Cooldown Reduction";
public String Stats_FIGHT_PROP_HEAL_ADD = "Healing Bonus";
public String Stats_FIGHT_PROP_HEALED_ADD = "Incoming Healing Bonus";
public String Stats_FIGHT_PROP_SHIELD_COST_MINUS_RATIO = "Shield Strength";
public String Stats_FIGHT_PROP_DEFENCE_IGNORE_RATIO = "DEF Ignore";
// SetWorldLevel
public String SetWorldLevel_usage = "Usage: setworldlevel <level>";
public String SetWorldLevel_world_level_must_between_0_and_8 = "World level must be between 0-8";
public String SetWorldLevel_set_world_level = "World level set to {level}.";
public String SetWorldLevel_invalid_world_level = "Invalid world level.";
// Spawn
public String Spawn_usage = "Usage: spawn <entityId> [amount] [level(monster only)]";
public String Spawn_message = "Spawned {amount} of {id}.";
// Stop
public String Stop_message = "Server shutting down...";
// Talent
public String Talent_usage_1 = "To set talent level: /talent set <talentID> <value>";
public String Talent_usage_2 = "Another way to set talent level: /talent <n or e or q> <value>";
public String Talent_usage_3 = "To get talent ID: /talent getid";
public String Talent_lower_16 = "Invalid talent level. Level should be lower than 16";
public String Talent_set_id = "Set talent {id} to {level}.";
public String Talent_set_atk = "Set talent Normal ATK to {level}.";
public String Talent_set_e = "Set talent E to {level}.";
public String Talent_set_q = "Set talent Q to {level}.";
public String Talent_invalid_skill_id = "Invalid skill ID.";
public String Talent_set_this = "Set this talent to {level}.";
public String Talent_invalid_talent_level = "Invalid talent level.";
public String Talent_normal_attack_id = "Normal Attack ID {id}.";
public String Talent_e_skill_id = "E skill ID {id}.";
public String Talent_q_skill_id = "Q skill ID {id}.";
// TeleportAll
public String TeleportAll_message = "You only can use this command in MP mode.";
// Teleport
public String Teleport_usage = "Usage: /tp <x> <y> <z> [scene id]";
public String Teleport_invalid_position = "Invalid position.";
public String Teleport_message = "Teleported {name} to {x},{y},{z} in scene {id}";
// Weather
public String Weather_usage = "Usage: weather <weatherId> [climateId]";
public String Weather_message = "Changed weather to {weatherId} with climate {climateId}";
public String Weather_invalid_id = "Invalid ID.";
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment