11 Commits

17 changed files with 316 additions and 68 deletions

Binary file not shown.

View File

@ -12,7 +12,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", permissionTargeted = "server.killall.others", description = "commands.kill.description")
@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", permissionTargeted = "server.killall.others", description = "commands.killall.description")
public final class KillAllCommand implements CommandHandler {
@Override
@ -26,14 +26,14 @@ public final class KillAllCommand implements CommandHandler {
scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0)));
break;
default:
CommandHandler.sendMessage(sender, translate(sender, "commands.kill.usage"));
CommandHandler.sendMessage(sender, translate(sender, "commands.killall.usage"));
return;
}
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
}
if (scene == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.kill.scene_not_found_in_player_world"));
CommandHandler.sendMessage(sender, translate(sender, "commands.killall.scene_not_found_in_player_world"));
return;
}
@ -43,6 +43,6 @@ public final class KillAllCommand implements CommandHandler {
.filter(entity -> entity instanceof EntityMonster)
.toList();
toKill.forEach(entity -> sceneF.killEntity(entity, 0));
CommandHandler.sendMessage(sender, translate(sender, "commands.kill.kill_monsters_in_scene", Integer.toString(toKill.size()), Integer.toString(scene.getId())));
CommandHandler.sendMessage(sender, translate(sender, "commands.killall.kill_monsters_in_scene", Integer.toString(toKill.size()), Integer.toString(scene.getId())));
}
}

View File

@ -13,11 +13,16 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", permissionTargeted = "player.killcharacter.others", description = "commands.killCharacter.description")
@Command(label = "killcharacter", usage = "killcharacter [playerID]", aliases = {"suicide", "kill"}, permission = "player.killcharacter", permissionTargeted = "player.killcharacter.others", description = "commands.killCharacter.description")
public final class KillCharacterCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
CommandHandler.sendMessage(sender, translate(sender, "commands.killCharacter.usage"));
return;
}
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
// Packets

View File

@ -21,6 +21,6 @@ public final class ResetShopLimitCommand implements CommandHandler {
targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0));
targetPlayer.save();
CommandHandler.sendMessage(sender, translate(sender, "commands.status.success"));
CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.success"));
}
}

View File

@ -1,14 +1,23 @@
package emu.grasscutter.game.ability;
import java.util.Optional;
import com.google.protobuf.InvalidProtocolBufferException;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.data.custom.AbilityModifierEntry;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
@ -18,6 +27,7 @@ import emu.grasscutter.net.proto.AbilityMixinCostStaminaOuterClass.AbilityMixinC
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
public class AbilityManager {
private Player player;
@ -31,7 +41,7 @@ public class AbilityManager {
}
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
//System.out.println(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
// Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
switch (invoke.getArgumentType()) {
case ABILITY_META_OVERRIDE_PARAM:
handleOverrideParam(invoke);
@ -139,20 +149,7 @@ public class AbilityManager {
}
private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
AbilityActionGenerateElemBall action = AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData());
if (action == null) {
return;
}
ItemData itemData = GameData.getItemDataMap().get(2024);
if (itemData == null) {
return; // Should never happen
}
EntityItem energyBall = new EntityItem(getPlayer().getScene(), getPlayer(), itemData, new Position(action.getPos()), 1);
energyBall.getRotation().set(action.getRot());
getPlayer().getScene().addEntity(energyBall);
this.player.getEnergyManager().handleGenerateElemBall(invoke);
}
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {

View File

@ -62,6 +62,8 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static emu.grasscutter.Configuration.GAME_OPTIONS;
@Entity(value = "avatars", useDiscriminator = false)
public class Avatar {
@Id private ObjectId id;
@ -493,6 +495,9 @@ public class Avatar {
// Get hp percent, set to 100% if none
float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
// Store current energy value for later
float currentEnergy = (this.getSkillDepot() != null) ? this.getFightProperty(this.getSkillDepot().getElementType().getCurEnergyProp()) : 0f;
// Clear properties
this.getFightProperties().clear();
@ -511,10 +516,16 @@ public class Avatar {
}
// Set energy usage
if (data.getSkillDepot() != null && data.getSkillDepot().getEnergySkillData() != null) {
ElementType element = data.getSkillDepot().getElementType();
this.setFightProperty(element.getMaxEnergyProp(), data.getSkillDepot().getEnergySkillData().getCostElemVal());
this.setFightProperty((element.getMaxEnergyProp().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal());
if (this.getSkillDepot() != null && this.getSkillDepot().getEnergySkillData() != null) {
ElementType element = this.getSkillDepot().getElementType();
this.setFightProperty(element.getMaxEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal());
if (GAME_OPTIONS.energyUsage) {
this.setFightProperty(element.getCurEnergyProp(), currentEnergy);
}
else {
this.setFightProperty(element.getCurEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal());
}
}
// Artifacts

View File

@ -12,7 +12,6 @@ import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlock;
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
@ -32,7 +31,6 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.utils.Utils;
@ -128,19 +126,42 @@ public class EntityAvatar extends GameEntity {
return healed;
}
public void addEnergy(float amount) {
FightProperty curEnergyProp = getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
public void clearEnergy(PropChangeReason reason) {
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
this.setFightProperty(curEnergyProp, 0);
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, 0f, reason));
}
public void addEnergy(float amount, PropChangeReason reason) {
this.addEnergy(amount, reason, false);
}
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
// Get current and maximum energy for this avatar.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
float curEnergy = this.getFightProperty(curEnergyProp);
float maxEnergy = this.getFightProperty(maxEnergyProp);
// Get energy recharge.
float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
// Scale amount by energy recharge, if the amount is not flat.
if (!isFlat) {
amount *= energyRecharge;
}
// Determine the new energy value.
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
// Set energy and notify.
if (newEnergy != curEnergy) {
setFightProperty(curEnergyProp, newEnergy);
this.setFightProperty(curEnergyProp, newEnergy);
getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(getAvatar(), curEnergyProp));
getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, PropChangeReason.PROP_CHANGE_ENERGY_BALL));
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
}
}

View File

@ -182,7 +182,7 @@ public class Inventory implements Iterable<GameItem> {
this.addVirtualItem(item.getItemId(), item.getCount());
return item;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_ADSORBATE) {
player.getTeamManager().addEnergyToTeam(item);
this.player.getEnergyManager().handlePickupElemBall(item);
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) {
// Get avatar id

View File

@ -0,0 +1,221 @@
package emu.grasscutter.game.managers;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.utils.Position;
import static emu.grasscutter.Configuration.GAME_OPTIONS;
import java.util.Optional;
import com.google.protobuf.InvalidProtocolBufferException;
public class EnergyManager {
private final Player player;
public EnergyManager(Player player) {
this.player = player;
}
public Player getPlayer() {
return this.player;
}
/**********
Particle creation for elemental skills.
**********/
private int getCastingAvatarIdForElemBall(int invokeEntityId) {
// To determine the avatar that has cast the skill that caused the energy particle to be generated,
// we have to look at the entity that has invoked the ability. This can either be that avatar directly,
// or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar
// that cast the skill.
int res = 0;
// Try to get the invoking entity from the scene.
GameEntity entity = player.getScene().getEntityById(invokeEntityId);
// If this entity is null, or not an `EntityClientGadget`, we assume that we are directly
// looking at the casting avatar (the null case will happen if the avatar was switched out
// between casting the skill and the particle being generated).
if (!(entity instanceof EntityClientGadget)) {
res = invokeEntityId;
}
// If the entity is a `EntityClientGadget`, we need to "walk up" the owner hierarchy,
// until the owner is no longer a gadget. This should then be the ID of the casting avatar.
else {
while (entity instanceof EntityClientGadget gadget) {
res = gadget.getOwnerEntityId();
entity = player.getScene().getEntityById(gadget.getOwnerEntityId());
}
}
return res;
}
public void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
// ToDo:
// This is also called when a weapon like Favonius Warbow etc. creates energy through its passive.
// We are not handling this correctly at the moment.
// Get action info.
AbilityActionGenerateElemBall action = AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData());
if (action == null) {
return;
}
// Determine the element of the energy particle that we have to generate.
// In case we can't, we default to an elementless particle.
// The element is the element of the avatar that has cast the ability.
// We can get that from the avatar's skill depot.
int itemId = 2024;
// Try to fetch the avatar from the player's party and determine their element.
// ToDo: Does this work in co-op?
int avatarId = getCastingAvatarIdForElemBall(invoke.getEntityId());
Optional<EntityAvatar> avatarEntity = player.getTeamManager().getActiveTeam()
.stream()
.filter(character -> character.getId() == avatarId)
.findFirst();
if (avatarEntity.isPresent()) {
Avatar avatar = avatarEntity.get().getAvatar();
if (avatar != null) {
AvatarSkillDepotData skillDepotData = avatar.getSkillDepot();
if (skillDepotData != null) {
ElementType element = skillDepotData.getElementType();
// If we found the element, we use it to deterine the ID of the
// energy particle that we have to generate.
if (element != null) {
itemId = switch (element) {
case Fire -> 2017;
case Water -> 2018;
case Grass -> 2019;
case Electric -> 2020;
case Wind -> 2021;
case Ice -> 2022;
case Rock -> 2023;
default -> 2024;
};
}
}
}
}
// Get the item data for an energy particle of the correct element.
ItemData itemData = GameData.getItemDataMap().get(itemId);
if (itemData == null) {
return; // Should never happen
}
// Generate entity.
EntityItem energyBall = new EntityItem(getPlayer().getScene(), getPlayer(), itemData, new Position(action.getPos()), 1);
energyBall.getRotation().set(action.getRot());
this.getPlayer().getScene().addEntity(energyBall);
}
/**********
Pickup of elemental particles and orbs.
**********/
public void handlePickupElemBall(GameItem elemBall) {
// Check if the item is indeed an energy particle/orb.
if (elemBall.getItemId() < 2001 ||elemBall.getItemId() > 2024) {
return;
}
// Determine the base amount of energy given by the particle/orb.
// Particles have a base amount of 1.0, and orbs a base amount of 3.0.
float baseEnergy = (elemBall.getItemId() <= 2008) ? 3.0f : 1.0f;
// Add energy to every team member.
for (int i = 0; i < this.player.getTeamManager().getActiveTeam().size(); i++) {
EntityAvatar entity = this.player.getTeamManager().getActiveTeam().get(i);
// On-field vs off-field multiplier.
// The on-field character gets no penalty.
// Off-field characters get a penalty depending on the team size, as follows:
// - 2 character team: 0.8
// - 3 character team: 0.7
// - 4 character team: 0.6
// - etc.
// We set a lower bound of 0.1 here, to avoid gaining no or negative energy.
float offFieldPenalty =
(this.player.getTeamManager().getCurrentCharacterIndex() == i)
? 1.0f
: 1.0f - this.player.getTeamManager().getActiveTeam().size() * 0.1f;
offFieldPenalty = Math.max(offFieldPenalty, 0.1f);
// Same element/neutral bonus.
// Same-element characters get a bonus of *3, while different-element characters get no bonus at all.
// For neutral particles/orbs, the multiplier is always *2.
if (entity.getAvatar().getSkillDepot() == null) {
continue;
}
ElementType avatarElement = entity.getAvatar().getSkillDepot().getElementType();
ElementType ballElement = switch (elemBall.getItemId()) {
case 2001, 2017 -> ElementType.Fire;
case 2002, 2018 -> ElementType.Water;
case 2003, 2019 -> ElementType.Grass;
case 2004, 2020 -> ElementType.Electric;
case 2005, 2021 -> ElementType.Wind;
case 2006, 2022 -> ElementType.Ice;
case 2007, 2023 -> ElementType.Rock;
default -> null;
};
float elementBonus = (ballElement == null) ? 2.0f : (avatarElement == ballElement) ? 3.0f : 1.0f;
// Add the energy.
entity.addEnergy(baseEnergy * elementBonus * offFieldPenalty, PropChangeReason.PROP_CHANGE_ENERGY_BALL);
}
}
/**********
Energy logic related to using skills.
**********/
private void handleBurstCast(Avatar avatar, int skillId) {
// Don't do anything if energy usage is disabled.
if (!GAME_OPTIONS.energyUsage) {
return;
}
// If the cast skill was a burst, consume energy.
if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) {
avatar.getAsEntity().clearEnergy(PropChangeReason.PROP_CHANGE_ABILITY);
}
}
public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) {
// Determine the entity that has cast the skill. Cancel if we can't find that avatar.
Optional<EntityAvatar> caster = this.player.getTeamManager().getActiveTeam().stream()
.filter(character -> character.getId() == casterId)
.findFirst();
if (caster.isEmpty()) {
return;
}
Avatar avatar = caster.get().getAvatar();
// Handle elemental burst.
this.handleBurstCast(avatar, skillId);
}
}

View File

@ -24,6 +24,7 @@ import emu.grasscutter.game.inventory.Inventory;
import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
import emu.grasscutter.game.managers.EnergyManager;
import emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityType;
@ -144,6 +145,7 @@ public class Player {
@Transient private MapMarksManager mapMarksManager;
@Transient private StaminaManager staminaManager;
@Transient private EnergyManager energyManager;
private long springLastUsed;
private HashMap<String, MapMark> mapMarks;
@ -194,6 +196,7 @@ public class Player {
this.mapMarksManager = new MapMarksManager(this);
this.staminaManager = new StaminaManager(this);
this.sotsManager = new SotSManager(this);
this.energyManager = new EnergyManager(this);
}
// On player creation
@ -222,6 +225,7 @@ public class Player {
this.mapMarksManager = new MapMarksManager(this);
this.staminaManager = new StaminaManager(this);
this.sotsManager = new SotSManager(this);
this.energyManager = new EnergyManager(this);
}
public int getUid() {
@ -1088,6 +1092,10 @@ public class Player {
public SotSManager getSotSManager() { return sotsManager; }
public EnergyManager getEnergyManager() {
return this.energyManager;
}
public AbilityManager getAbilityManager() {
return abilityManager;
}

View File

@ -9,7 +9,6 @@ import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.props.FightProperty;
@ -582,24 +581,6 @@ public class TeamManager {
getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp));
}
public synchronized void addEnergyToTeam(GameItem energyBall) {
// TODO
float baseEnergy = 2;
for (int i = 0; i < getActiveTeam().size(); i++) {
EntityAvatar entity = getActiveTeam().get(i);
float energyGain = baseEnergy;
// Active character gets full hp
if (getCurrentCharacterIndex() != i) {
energyGain *= Math.max(1.0 - (getActiveTeam().size() * .1f), .6f);
}
entity.addEnergy(energyGain);
}
}
public void saveAvatars() {
// Save all avatars from active team
for (EntityAvatar entity : getActiveTeam()) {

View File

@ -4,9 +4,7 @@ import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.EvtDoSkillSuccNotifyOuterClass.EvtDoSkillSuccNotify;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.utils.Position;
@Opcodes(PacketOpcodes.EvtDoSkillSuccNotify)
public class HandlerEvtDoSkillSuccNotify extends PacketHandler {
@ -16,8 +14,8 @@ public class HandlerEvtDoSkillSuccNotify extends PacketHandler {
EvtDoSkillSuccNotify notify = EvtDoSkillSuccNotify.parseFrom(payload);
int skillId = notify.getSkillId();
int casterId = notify.getCasterId();
Vector forwardVector = notify.getForward();
Position forward = new Position(forwardVector.getX(), forwardVector.getY(), forwardVector.getZ());
session.getPlayer().getStaminaManager().handleEvtDoSkillSuccNotify(session, skillId, casterId);
session.getPlayer().getEnergyManager().handleEvtDoSkillSuccNotify(session, skillId, casterId);
}
}

View File

@ -176,6 +176,7 @@ public class ConfigContainer {
public boolean watchGachaConfig = false;
public boolean enableShopItems = true;
public boolean staminaUsage = true;
public boolean energyUsage = false;
public Rates rates = new Rates();
public static class InventoryLimits {

View File

@ -192,7 +192,7 @@
"server_kick_player": "Kicking player [%s:%s]...",
"description": "Kicks the specified player from the server (WIP)"
},
"kill": {
"killall": {
"usage": "Usage: killall [playerUID] [sceneID]",
"scene_not_found_in_player_world": "Scene not found in player world.",
"kill_monsters_in_scene": "Killing %s monsters in scene %s.",
@ -246,6 +246,7 @@
},
"resetShopLimit": {
"usage": "Usage: /resetshop <player ID>",
"success": "Reset complete.",
"description": "Reset target player's shop refresh time"
},
"sendMail": {

View File

@ -170,7 +170,7 @@
"player_kick_player": "Gracz [%s:%s] wyrzucił gracza [%s:%s]",
"server_kick_player": "Wyrzucono gracza [%s:%s]"
},
"kill": {
"killall": {
"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"
@ -202,7 +202,9 @@
"success": "Konstelacje dla %s zostały zresetowane. Proszę zalogować się ponownie aby zobaczyć zmiany."
},
"resetShopLimit": {
"usage": "Użycie: /resetshop <ID gracza>"
"usage": "Użycie: /resetshop <ID gracza>",
"success": "Reset complete.",
"description": "Reset target player's shop refresh time"
},
"sendMail": {
"usage": "Użycie: `/sendmail <ID gracza | all | help> [id szablonu]`",

View File

@ -19,7 +19,7 @@
"authentication": {
"default_unable_to_verify": "[Authentication] 称为 verifyUser 的方法在默认验证程序中不可用"
},
"no_commands_error": "此命令不适用于 Dispatch-only 模式",
"no_commands_error": " Dispatch 模式不支持使用命令",
"unhandled_request_error": "[Dispatch] 潜在的未处理请求:%s %s",
"account": {
"login_attempt": "[Dispatch] 客户端 %s 正在尝试登录",
@ -49,7 +49,7 @@
"error": "发生了一个错误。",
"welcome": "欢迎使用 Grasscutter珍惜这段美妙的旅途吧",
"run_mode_error": "无效的服务器运行模式:%s。",
"run_mode_help": "服务器运行模式必须为 HYBRIDDISPATCH_ONLY 或 GAME_ONLY。Grasscutter 启动失败...",
"run_mode_help": "服务器运行模式必须为 'HYBRID'(混合)、'DISPATCH_ONLY'(仅 Dispatch)'GAME_ONLY'(仅游戏)。Grasscutter 启动失败...",
"create_resources": "正在创建 resources 目录...",
"resources_error": "请将 BinOutput 和 ExcelBinOutput 复制到 resources 目录。",
"version": "Grasscutter 版本:%s-%s"
@ -192,7 +192,7 @@
"server_kick_player": "正在踢出玩家 [%s:%s]...",
"description": "从服务器内踢出指定玩家"
},
"kill": {
"killall": {
"usage": "用法killall [玩家UID] [场景ID]",
"scene_not_found_in_player_world": "未在玩家世界中找到此场景。",
"kill_monsters_in_scene": "已杀死场景 %s 中的 %s 个怪物。",
@ -201,7 +201,7 @@
"killCharacter": {
"usage": "用法killcharacter [玩家ID]",
"success": "已杀死 %s 当前角色。",
"description": "杀死当前角色"
"description": "杀死玩家当前角色"
},
"language": {
"current_language": "当前语言是:%s",
@ -246,6 +246,7 @@
},
"resetShopLimit": {
"usage": "用法resetshop <玩家ID>",
"success": "重置完成。",
"description": "重置所选玩家的商店刷新时间"
},
"sendMail": {
@ -411,7 +412,7 @@
"index": {
"title": "文档",
"handbook": "GM Handbook",
"gacha_mapping": "祈愿映射 JSON"
"gacha_mapping": "祈愿物品映射JSON"
}
}
}

View File

@ -191,7 +191,7 @@
"server_kick_player": "正在踢出玩家 [%s:%s]",
"description": "從伺服器內踢出指定玩家。"
},
"kill": {
"killall": {
"usage": "用法killall [playerUid] [sceneId]",
"scene_not_found_in_player_world": "未在玩家世界中找到此場景",
"kill_monsters_in_scene": "已殺死 %s 個怪物。 [場景ID %s]",
@ -250,6 +250,7 @@
},
"resetShopLimit": {
"usage": "用法:/resetshop <player id>",
"success": "重置完成。",
"description": "重置所選玩家的商店刷新時間。"
},
"sendMail": {