mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-22 18:56:15 +00:00
Remove GiveAll, GiveArt, GiveChar commands
This commit is contained in:
parent
6fd1ce813c
commit
baafb4104c
@ -1,184 +0,0 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AvatarData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "giveall", usage = "giveall [amount]", aliases = {"givea"}, permission = "player.giveall", permissionTargeted = "player.giveall.others", threading = true, description = "commands.giveAll.description")
|
||||
public final class GiveAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int amount = 99999;
|
||||
|
||||
switch (args.size()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: // [amount]
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default: // invalid
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.giveAll.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.giveAllItems(targetPlayer, amount);
|
||||
CommandHandler.sendMessage(sender, translate(targetPlayer, "commands.giveAll.success", targetPlayer.getNickname()));
|
||||
}
|
||||
|
||||
public void giveAllItems(Player player, int amount) {
|
||||
CommandHandler.sendMessage(player, translate(player, "commands.giveAll.started"));
|
||||
|
||||
for (AvatarData avatarData: GameData.getAvatarDataMap().values()) {
|
||||
//Exclude test avatar
|
||||
if (isTestAvatar(avatarData.getId())) continue;
|
||||
|
||||
Avatar avatar = new Avatar(avatarData);
|
||||
avatar.setLevel(90);
|
||||
avatar.setPromoteLevel(6);
|
||||
|
||||
// Add constellations.
|
||||
int talentBase = switch (avatar.getAvatarId()) {
|
||||
case 10000005 -> 70;
|
||||
case 10000006 -> 40;
|
||||
default -> (avatar.getAvatarId()-10000000)*10;
|
||||
};
|
||||
|
||||
for(int i = 1;i <= 6;++i){
|
||||
avatar.getTalentIdList().add(talentBase + i);
|
||||
}
|
||||
|
||||
// Handle skill depot for traveller.
|
||||
if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(504));
|
||||
}
|
||||
else if(avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(704));
|
||||
}
|
||||
|
||||
// This will handle stats and talents
|
||||
avatar.recalcStats();
|
||||
// Don't try to add each avatar to the current team
|
||||
player.addAvatar(avatar, false);
|
||||
}
|
||||
|
||||
//some test items
|
||||
List<GameItem> itemList = new ArrayList<>();
|
||||
for (ItemData itemdata: GameData.getItemDataMap().values()) {
|
||||
//Exclude test item
|
||||
if (isTestItem(itemdata.getId())) continue;
|
||||
|
||||
if (itemdata.isEquip()) {
|
||||
if (itemdata.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
GameItem item = new GameItem(itemdata);
|
||||
item.setLevel(90);
|
||||
item.setPromoteLevel(6);
|
||||
item.setRefinement(4);
|
||||
itemList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameItem item = new GameItem(itemdata);
|
||||
item.setCount(amount);
|
||||
itemList.add(item);
|
||||
}
|
||||
}
|
||||
int packetNum = 10;
|
||||
int itemLength = itemList.size();
|
||||
int number = itemLength / packetNum;
|
||||
int remainder = itemLength % packetNum;
|
||||
int offset = 0;
|
||||
for (int i = 0; i < packetNum; ++i) {
|
||||
if (remainder > 0) {
|
||||
player.getInventory().addItems(itemList.subList(i * number + offset, (i + 1) * number + offset + 1));
|
||||
--remainder;
|
||||
++offset;
|
||||
}
|
||||
else {
|
||||
player.getInventory().addItems(itemList.subList(i * number + offset, (i + 1) * number + offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTestAvatar(int avatarId) {
|
||||
return avatarId < 10000002 || avatarId >= 11000000;
|
||||
}
|
||||
|
||||
public boolean isTestItem(int itemId) {
|
||||
for (Range range: testItemRanges) {
|
||||
if (range.check(itemId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return testItemsList.contains(itemId);
|
||||
}
|
||||
|
||||
static class Range {
|
||||
private final int min, max;
|
||||
|
||||
public Range(int min, int max) {
|
||||
if(min > max){
|
||||
min ^= max;
|
||||
max ^= min;
|
||||
min ^= max;
|
||||
}
|
||||
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public boolean check(int value) {
|
||||
return value >= this.min && value <= this.max;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Range[] testItemRanges = new Range[] {
|
||||
new Range(106, 139),
|
||||
new Range(1000, 1099),
|
||||
new Range(2001, 3022),
|
||||
new Range(23300, 23340),
|
||||
new Range(23383, 23385),
|
||||
new Range(78310, 78554),
|
||||
new Range(99310, 99554),
|
||||
new Range(100001, 100187),
|
||||
new Range(100210, 100214),
|
||||
new Range(100303, 100398),
|
||||
new Range(100414, 100425),
|
||||
new Range(100454, 103008),
|
||||
new Range(109000, 109492),
|
||||
new Range(115001, 118004),
|
||||
new Range(141001, 141072),
|
||||
new Range(220050, 221016),
|
||||
};
|
||||
private static final Integer[] testItemsIds = new Integer[] {
|
||||
210, 211, 314, 315, 317, 1005, 1007, 1105, 1107, 1201, 1202,10366,
|
||||
101212, 11411, 11506, 11507, 11508, 12505, 12506, 12508, 12509, 13503,
|
||||
13506, 14411, 14503, 14505, 14508, 15504, 15505, 15506,
|
||||
20001, 10002, 10003, 10004, 10005, 10006, 10008,100231,100232,100431,
|
||||
101689,105001,105004, 106000,106001,108000,110000
|
||||
};
|
||||
|
||||
private static final Collection<Integer> testItemsList = Arrays.asList(testItemsIds);
|
||||
|
||||
}
|
||||
|
@ -1,208 +0,0 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.inventory.EquipType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import static java.util.Map.entry;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "giveart", usage = "giveart <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]", aliases = {"gart"}, permission = "player.giveart", permissionTargeted = "player.giveart.others", description = "commands.giveArtifact.description")
|
||||
public final class GiveArtifactCommand implements CommandHandler {
|
||||
private static final Map<String, Map<EquipType, Integer>> mainPropMap = Map.ofEntries(
|
||||
entry("hp", Map.ofEntries(entry(EquipType.EQUIP_BRACER, 14001))),
|
||||
entry("hp%", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10980), entry(EquipType.EQUIP_RING, 50980), entry(EquipType.EQUIP_DRESS, 30980))),
|
||||
entry("atk", Map.ofEntries(entry(EquipType.EQUIP_NECKLACE, 12001))),
|
||||
entry("atk%", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10990), entry(EquipType.EQUIP_RING, 50990), entry(EquipType.EQUIP_DRESS, 30990))),
|
||||
entry("def%", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10970), entry(EquipType.EQUIP_RING, 50970), entry(EquipType.EQUIP_DRESS, 30970))),
|
||||
entry("er", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10960))),
|
||||
entry("em", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10950), entry(EquipType.EQUIP_RING, 50880), entry(EquipType.EQUIP_DRESS, 30930))),
|
||||
entry("hb", Map.ofEntries(entry(EquipType.EQUIP_DRESS, 30940))),
|
||||
entry("cdmg", Map.ofEntries(entry(EquipType.EQUIP_DRESS, 30950))),
|
||||
entry("cr", Map.ofEntries(entry(EquipType.EQUIP_DRESS, 30960))),
|
||||
entry("phys%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50890))),
|
||||
entry("dendro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50900))),
|
||||
entry("geo%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50910))),
|
||||
entry("anemo%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50920))),
|
||||
entry("hydro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50930))),
|
||||
entry("cryo%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50940))),
|
||||
entry("electro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50950))),
|
||||
entry("pyro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50960)))
|
||||
);
|
||||
private static final Map<String, String> appendPropMap = Map.ofEntries(
|
||||
entry("hp", "0102"),
|
||||
entry("hp%", "0103"),
|
||||
entry("atk", "0105"),
|
||||
entry("atk%", "0106"),
|
||||
entry("def", "0108"),
|
||||
entry("def%", "0109"),
|
||||
entry("er", "0123"),
|
||||
entry("em", "0124"),
|
||||
entry("cr", "0120"),
|
||||
entry("cdmg", "0122")
|
||||
);
|
||||
|
||||
private int getAppendPropId(String substatText, ItemData itemData) {
|
||||
int res;
|
||||
|
||||
// If the given substat text is an integer, we just use that
|
||||
// as the append prop ID.
|
||||
try {
|
||||
res = Integer.parseInt(substatText);
|
||||
return res;
|
||||
}
|
||||
catch (NumberFormatException ignores) {
|
||||
// No need to handle this here. We just continue with the
|
||||
// possibility of the argument being a substat string.
|
||||
}
|
||||
|
||||
// If the argument was not an integer, we try to determine
|
||||
// the append prop ID from the given text + artifact information.
|
||||
// A substat string has the format `substat_tier`, with the
|
||||
// `_tier` part being optional.
|
||||
String[] substatArgs = substatText.split("_");
|
||||
String substatType;
|
||||
int substatTier;
|
||||
|
||||
if (substatArgs.length == 1) {
|
||||
substatType = substatArgs[0];
|
||||
substatTier =
|
||||
itemData.getRankLevel() == 1 ? 2
|
||||
: itemData.getRankLevel() == 2 ? 3
|
||||
: 4;
|
||||
}
|
||||
else if (substatArgs.length == 2) {
|
||||
substatType = substatArgs[0];
|
||||
substatTier = Integer.parseInt(substatArgs[1]);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// Check if the specified tier is legal for the artifact rarity.
|
||||
if (substatTier < 1 || substatTier > 4) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (itemData.getRankLevel() == 1 && substatTier > 2) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (itemData.getRankLevel() == 2 && substatTier > 3) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// Check if the given substat type string is a legal stat.
|
||||
if (!appendPropMap.containsKey(substatType)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// Build the append prop ID.
|
||||
return Integer.parseInt(Integer.toString(itemData.getRankLevel()) + appendPropMap.get(substatType) + Integer.toString(substatTier));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
// Sanity check
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the artifact piece ID from the arguments.
|
||||
int itemId;
|
||||
try {
|
||||
itemId = Integer.parseInt(args.remove(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.id_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if (itemData.getItemType() != ItemType.ITEM_RELIQUARY) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.id_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the main stat from the arguments.
|
||||
// If the given argument is an integer, we use that.
|
||||
// If not, we check if the argument string is in the main prop map.
|
||||
String mainPropIdString = args.remove(0);
|
||||
int mainPropId;
|
||||
|
||||
try {
|
||||
mainPropId = Integer.parseInt(mainPropIdString);
|
||||
} catch (NumberFormatException ignored) {
|
||||
mainPropId = -1;
|
||||
}
|
||||
|
||||
if (mainPropMap.containsKey(mainPropIdString) && mainPropMap.get(mainPropIdString).containsKey(itemData.getEquipType())) {
|
||||
mainPropId = mainPropMap.get(mainPropIdString).get(itemData.getEquipType());
|
||||
}
|
||||
|
||||
if (mainPropId == -1) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the level from the arguments.
|
||||
int level = 1;
|
||||
try {
|
||||
int last = Integer.parseInt(args.get(args.size()-1));
|
||||
if (last > 0 && last < 22) { // Luckily appendPropIds aren't in the range of [1,21]
|
||||
level = last;
|
||||
args.remove(args.size()-1);
|
||||
}
|
||||
} catch (NumberFormatException ignored) { // Could be a stat,times string so no need to panic
|
||||
}
|
||||
|
||||
// Get substats.
|
||||
ArrayList<Integer> appendPropIdList = new ArrayList<>();
|
||||
try {
|
||||
// Every remaining argument is a substat.
|
||||
args.forEach(it -> {
|
||||
// The substat syntax permits specifying a number of rolls for the given
|
||||
// substat. Split the string into stat and number if that is the case here.
|
||||
String[] arr;
|
||||
int n = 1;
|
||||
if ((arr = it.split(",")).length == 2) {
|
||||
it = arr[0];
|
||||
n = Integer.parseInt(arr[1]);
|
||||
if (n > 200) {
|
||||
n = 200;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the substat ID.
|
||||
int appendPropId = getAppendPropId(it, itemData);
|
||||
|
||||
// Add the current substat.
|
||||
appendPropIdList.addAll(Collections.nCopies(n, appendPropId));
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create item for the artifact.
|
||||
GameItem item = new GameItem(itemData);
|
||||
item.setLevel(level);
|
||||
item.setMainPropId(mainPropId);
|
||||
item.getAppendPropIdList().clear();
|
||||
item.getAppendPropIdList().addAll(appendPropIdList);
|
||||
targetPlayer.getInventory().addItem(item, ActionReason.SubfieldDrop);
|
||||
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.success", Integer.toString(itemId), Integer.toString(targetPlayer.getUid())));
|
||||
}
|
||||
}
|
||||
|
@ -1,86 +0,0 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AvatarData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "givechar", usage = "givechar <avatarId> [level]", aliases = {"givec"}, permission = "player.givechar", permissionTargeted = "player.givechar.others", description = "commands.giveChar.description")
|
||||
public final class GiveCharCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int avatarId;
|
||||
int level = 1;
|
||||
|
||||
switch (args.size()) {
|
||||
case 2:
|
||||
try {
|
||||
level = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from avatar name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarLevel"));
|
||||
return;
|
||||
} // Cheeky fall-through to parse first argument too
|
||||
case 1:
|
||||
try {
|
||||
avatarId = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from avatar name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarId"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.giveChar.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
|
||||
if (avatarData == null) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarId"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check level.
|
||||
if (level > 90) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarLevel"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate ascension level.
|
||||
int ascension;
|
||||
if (level <= 40) {
|
||||
ascension = (int) Math.ceil(level / 20f) - 1;
|
||||
} else {
|
||||
ascension = (int) Math.ceil(level / 10f) - 3;
|
||||
ascension = Math.min(ascension, 6);
|
||||
}
|
||||
|
||||
Avatar avatar = new Avatar(avatarId);
|
||||
avatar.setLevel(level);
|
||||
avatar.setPromoteLevel(ascension);
|
||||
|
||||
// Handle skill depot for traveller.
|
||||
if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(504));
|
||||
}
|
||||
else if(avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(704));
|
||||
}
|
||||
|
||||
// This will handle stats and talents
|
||||
avatar.recalcStats();
|
||||
|
||||
targetPlayer.addAvatar(avatar);
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.giveChar.given", Integer.toString(avatarId), Integer.toString(level), Integer.toString(targetPlayer.getUid())));
|
||||
}
|
||||
}
|
@ -1,29 +1,37 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameDepot;
|
||||
import emu.grasscutter.data.excels.AvatarData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.data.excels.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.excels.ReliquaryMainPropData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.utils.SparseSet;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "give", usage = "give <itemId|itemName> [amount] [level]", aliases = {
|
||||
@Command(label = "give", usage = "give <itemId|avatarId|\"all\"|\"weapons\"|\"mats\"|\"avatars\"> [lv<level>] [r<refinement>] [x<amount>] | give <artifactId> [lv<level>] [x<amount>] [mainPropId] [<appendPropId>[,<times>]]...", aliases = {
|
||||
"g", "item", "giveitem"}, permission = "player.give", permissionTargeted = "player.give.others", description = "commands.give.description")
|
||||
public final class GiveCommand implements CommandHandler {
|
||||
Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java is a joke of a proglang that doesn't have raw string literals
|
||||
Pattern refineRegex = Pattern.compile("r(\\d+)");
|
||||
Pattern amountRegex = Pattern.compile("((?<=x)\\d+|\\d+(?=x)(?!x\\d))");
|
||||
private static Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java doesn't have raw string literals :(
|
||||
private static Pattern refineRegex = Pattern.compile("r(\\d+)");
|
||||
private static Pattern constellationRegex = Pattern.compile("c(\\d+)");
|
||||
private static Pattern amountRegex = Pattern.compile("((?<=x)\\d+|\\d+(?=x)(?!x\\d))");
|
||||
|
||||
private int matchIntOrNeg(Pattern pattern, String arg) {
|
||||
private static int matchIntOrNeg(Pattern pattern, String arg) {
|
||||
Matcher match = pattern.matcher(arg);
|
||||
if (match.find()) {
|
||||
return Integer.parseInt(match.group(1)); // This should be exception-safe as only \d+ can be passed to it (i.e. non-empty string of pure digits)
|
||||
@ -31,27 +39,50 @@ public final class GiveCommand implements CommandHandler {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int item;
|
||||
int lvl = 1;
|
||||
int amount = 1;
|
||||
int refinement = 0;
|
||||
private static enum GiveAllType {
|
||||
NONE,
|
||||
ALL,
|
||||
WEAPONS,
|
||||
MATS,
|
||||
AVATARS
|
||||
}
|
||||
|
||||
for (int i = args.size()-1; i>=0; i--) { // Reverse iteration as we are deleting elements
|
||||
private static class GiveItemParameters {
|
||||
public int id;
|
||||
public int lvl = 0;
|
||||
public int amount = 1;
|
||||
public int refinement = 1;
|
||||
public int constellation = -1;
|
||||
public int mainPropId = -1;
|
||||
public List<Integer> appendPropIdList;
|
||||
public ItemData data;
|
||||
public AvatarData avatarData;
|
||||
public GiveAllType giveAllType = GiveAllType.NONE;
|
||||
};
|
||||
|
||||
private static GiveItemParameters parseArgs(Player sender, List<String> args) throws IllegalArgumentException {
|
||||
GiveItemParameters param = new GiveItemParameters();
|
||||
|
||||
// Extract any tagged arguments (e.g. "lv90", "x100", "r5")
|
||||
for (int i = args.size() - 1; i >= 0; i--) { // Reverse iteration as we are deleting elements
|
||||
String arg = args.get(i).toLowerCase();
|
||||
boolean deleteArg = false;
|
||||
int argNum;
|
||||
// Note that a single argument can actually match all of these, e.g. "lv90r5x100"
|
||||
if ((argNum = matchIntOrNeg(lvlRegex, arg)) != -1) {
|
||||
lvl = argNum;
|
||||
param.lvl = argNum;
|
||||
deleteArg = true;
|
||||
}
|
||||
if ((argNum = matchIntOrNeg(refineRegex, arg)) != -1) {
|
||||
refinement = argNum;
|
||||
param.refinement = argNum;
|
||||
deleteArg = true;
|
||||
}
|
||||
if ((argNum = matchIntOrNeg(constellationRegex, arg)) != -1) {
|
||||
param.constellation = argNum;
|
||||
deleteArg = true;
|
||||
}
|
||||
if ((argNum = matchIntOrNeg(amountRegex, arg)) != -1) {
|
||||
amount = argNum;
|
||||
param.amount = argNum;
|
||||
deleteArg = true;
|
||||
}
|
||||
if (deleteArg) {
|
||||
@ -59,112 +90,387 @@ public final class GiveCommand implements CommandHandler {
|
||||
}
|
||||
}
|
||||
|
||||
switch (args.size()) {
|
||||
case 4: // <itemId|itemName> [amount] [level] [refinement]
|
||||
try {
|
||||
refinement = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemRefinement"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 3: // <itemId|itemName> [amount] [level]
|
||||
try {
|
||||
lvl = Integer.parseInt(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemLevel"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 2: // <itemId|itemName> [amount]
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 1: // <itemId|itemName>
|
||||
try {
|
||||
item = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from item name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
// At this point, first remaining argument MUST be itemId/avatarId
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.usage"); // Reachable if someone does `/give lv90` or similar
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
String id = args.remove(0);
|
||||
boolean isRelic = false;
|
||||
|
||||
switch (id) {
|
||||
case "all":
|
||||
param.giveAllType = GiveAllType.ALL;
|
||||
break;
|
||||
default: // *No args*
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.give.usage"));
|
||||
return;
|
||||
case "weapons":
|
||||
param.giveAllType = GiveAllType.WEAPONS;
|
||||
break;
|
||||
case "mats":
|
||||
param.giveAllType = GiveAllType.MATS;
|
||||
break;
|
||||
case "avatars":
|
||||
param.giveAllType = GiveAllType.AVATARS;
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
param.id = Integer.parseInt(id);
|
||||
param.data = GameData.getItemDataMap().get(param.id);
|
||||
if ((param.id > 10_000_000) && (param.id < 12_000_000))
|
||||
param.avatarData = GameData.getAvatarDataMap().get(param.id);
|
||||
else if ((param.id > 1000) && (param.id < 1100))
|
||||
param.avatarData = GameData.getAvatarDataMap().get(param.id - 1000 + 10_000_000);
|
||||
isRelic = ((param.data != null) && (param.data.getItemType() == ItemType.ITEM_RELIQUARY));
|
||||
} catch (NumberFormatException e) {
|
||||
// TODO: Parse from item name using GM Handbook.
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.itemId");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
ItemData itemData = GameData.getItemDataMap().get(item);
|
||||
if (itemData == null) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemId"));
|
||||
if (param.amount < 1) param.amount = 1;
|
||||
if (param.refinement < 1) param.refinement = 1;
|
||||
if (param.refinement > 5) param.refinement = 5;
|
||||
if (isRelic) {
|
||||
// Input 0-20 to match game, instead of 1-21 which is the real level
|
||||
if (param.lvl < 0) param.lvl = 0;
|
||||
if (param.lvl > 20) param.lvl = 20;
|
||||
param.lvl += 1;
|
||||
if (illegalRelicIds.contains(param.id))
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.illegal_relic");
|
||||
} else {
|
||||
// Suitable for Avatars and Weapons
|
||||
if (param.lvl < 1) param.lvl = 1;
|
||||
if (param.lvl > 90) param.lvl = 90;
|
||||
}
|
||||
|
||||
if (isRelic && !args.isEmpty()) {
|
||||
try {
|
||||
parseRelicArgs(param, args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.usage_relic");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 1) { // *No args*
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.usage");
|
||||
return;
|
||||
}
|
||||
if (refinement != 0) {
|
||||
if (itemData.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
if (refinement < 1 || refinement > 5) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.give.refinement_must_between_1_and_5"));
|
||||
try {
|
||||
GiveItemParameters param = parseArgs(sender, args);
|
||||
|
||||
switch (param.giveAllType) {
|
||||
case ALL:
|
||||
giveAll(targetPlayer, param);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.giveall_success");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.give.refinement_only_applicable_weapons"));
|
||||
case WEAPONS:
|
||||
giveAllWeapons(targetPlayer, param);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.giveall_success");
|
||||
return;
|
||||
case MATS:
|
||||
giveAllMats(targetPlayer, param);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.giveall_success");
|
||||
return;
|
||||
case AVATARS:
|
||||
giveAllAvatars(targetPlayer, param);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.giveall_success");
|
||||
return;
|
||||
case NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if this is an avatar
|
||||
if (param.avatarData != null) {
|
||||
Avatar avatar = makeAvatar(param);
|
||||
targetPlayer.addAvatar(avatar);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.given_avatar", Integer.toString(param.id), Integer.toString(param.lvl), Integer.toString(targetPlayer.getUid()));
|
||||
return;
|
||||
}
|
||||
// If it's not an avatar, it needs to be a valid item
|
||||
if (param.data == null) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.itemId");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (param.data.getItemType()) {
|
||||
case ITEM_WEAPON:
|
||||
targetPlayer.getInventory().addItems(makeUnstackableItems(param), ActionReason.SubfieldDrop);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.given_with_level_and_refinement", Integer.toString(param.id), Integer.toString(param.lvl), Integer.toString(param.refinement), Integer.toString(param.amount), Integer.toString(targetPlayer.getUid()));
|
||||
return;
|
||||
case ITEM_RELIQUARY:
|
||||
targetPlayer.getInventory().addItems(makeArtifacts(param), ActionReason.SubfieldDrop);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.given_level", Integer.toString(param.id), Integer.toString(param.lvl), Integer.toString(param.amount), Integer.toString(targetPlayer.getUid()));
|
||||
//CommandHandler.sendTranslatedMessage(sender, "commands.giveArtifact.success", Integer.toString(param.id), Integer.toString(targetPlayer.getUid()));
|
||||
return;
|
||||
default:
|
||||
targetPlayer.getInventory().addItem(new GameItem(param.data, param.amount), ActionReason.SubfieldDrop);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.given", Integer.toString(param.amount), Integer.toString(param.id), Integer.toString(targetPlayer.getUid()));
|
||||
return;
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.item(targetPlayer, itemData, amount, lvl, refinement);
|
||||
private static Avatar makeAvatar(GiveItemParameters param) {
|
||||
return makeAvatar(param.avatarData, param.lvl, Avatar.getMinPromoteLevel(param.lvl), 0);
|
||||
}
|
||||
|
||||
if (!itemData.isEquip()) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.give.given", Integer.toString(amount), Integer.toString(item), Integer.toString(targetPlayer.getUid())));
|
||||
} else if (itemData.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.give.given_with_level_and_refinement", Integer.toString(item), Integer.toString(lvl), Integer.toString(refinement), Integer.toString(amount), Integer.toString(targetPlayer.getUid())));
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.give.given_level", Integer.toString(item), Integer.toString(lvl), Integer.toString(amount), Integer.toString(targetPlayer.getUid())));
|
||||
private static Avatar makeAvatar(AvatarData avatarData, int level, int promoteLevel, int constellation) {
|
||||
// Calculate ascension level.
|
||||
Avatar avatar = new Avatar(avatarData);
|
||||
avatar.setLevel(level);
|
||||
avatar.setPromoteLevel(promoteLevel);
|
||||
|
||||
// Add constellations.
|
||||
int talentBase = switch (avatar.getAvatarId()) {
|
||||
case 10000005 -> 70;
|
||||
case 10000006 -> 40;
|
||||
default -> (avatar.getAvatarId() - 10000000) * 10;
|
||||
};
|
||||
|
||||
for (int i = 1; i <= constellation; i++) {
|
||||
avatar.getTalentIdList().add(talentBase + i);
|
||||
}
|
||||
|
||||
// Main character needs skill depot manually added.
|
||||
if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(504));
|
||||
}
|
||||
else if(avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(704));
|
||||
}
|
||||
|
||||
avatar.recalcStats();
|
||||
|
||||
return avatar;
|
||||
}
|
||||
|
||||
private static void giveAllAvatars(Player player, GiveItemParameters param) {
|
||||
int promoteLevel = Avatar.getMinPromoteLevel(param.lvl);
|
||||
if (param.constellation < 0) {
|
||||
param.constellation = 6;
|
||||
}
|
||||
for (AvatarData avatarData : GameData.getAvatarDataMap().values()) {
|
||||
// Exclude test avatars
|
||||
int id = avatarData.getId();
|
||||
if (id < 10000002 || id >= 11000000) continue;
|
||||
|
||||
// Don't try to add each avatar to the current team
|
||||
player.addAvatar(makeAvatar(avatarData, param.lvl, promoteLevel, param.constellation), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void item(Player player, ItemData itemData, int amount, int lvl, int refinement) {
|
||||
if (itemData.isEquip()) {
|
||||
List<GameItem> items = new LinkedList<>();
|
||||
for (int i = 0; i < amount; i++) {
|
||||
GameItem item = new GameItem(itemData);
|
||||
if (item.isEquipped()) {
|
||||
// check item max level
|
||||
if (item.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
if (lvl > 90) lvl = 90;
|
||||
} else {
|
||||
if (lvl > 21) lvl = 21;
|
||||
}
|
||||
}
|
||||
item.setCount(amount);
|
||||
item.setLevel(lvl);
|
||||
if (lvl > 80) {
|
||||
item.setPromoteLevel(6);
|
||||
} else if (lvl > 70) {
|
||||
item.setPromoteLevel(5);
|
||||
} else if (lvl > 60) {
|
||||
item.setPromoteLevel(4);
|
||||
} else if (lvl > 50) {
|
||||
item.setPromoteLevel(3);
|
||||
} else if (lvl > 40) {
|
||||
item.setPromoteLevel(2);
|
||||
} else if (lvl > 20) {
|
||||
item.setPromoteLevel(1);
|
||||
}
|
||||
if (item.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
if (refinement > 0) {
|
||||
item.setRefinement(refinement - 1);
|
||||
} else {
|
||||
item.setRefinement(0);
|
||||
}
|
||||
}
|
||||
items.add(item);
|
||||
private static List<GameItem> makeUnstackableItems(GiveItemParameters param) {
|
||||
int promoteLevel = GameItem.getMinPromoteLevel(param.lvl);
|
||||
int totalExp = 0;
|
||||
if (param.data.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
int rankLevel = param.data.getRankLevel();
|
||||
for (int i = 1; i < param.lvl; i++)
|
||||
totalExp += GameData.getWeaponExpRequired(rankLevel, i);
|
||||
}
|
||||
|
||||
List<GameItem> items = new ArrayList<>(param.amount);
|
||||
for (int i = 0; i < param.amount; i++) {
|
||||
GameItem item = new GameItem(param.data);
|
||||
item.setLevel(param.lvl);
|
||||
if (item.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
item.setPromoteLevel(promoteLevel);
|
||||
item.setTotalExp(totalExp);
|
||||
item.setRefinement(param.refinement - 1); // Actual refinement data is 0..4 not 1..5
|
||||
}
|
||||
player.getInventory().addItems(items, ActionReason.SubfieldDrop);
|
||||
} else {
|
||||
GameItem item = new GameItem(itemData);
|
||||
item.setCount(amount);
|
||||
player.getInventory().addItem(item, ActionReason.SubfieldDrop);
|
||||
items.add(item);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private static List<GameItem> makeArtifacts(GiveItemParameters param) {
|
||||
param.lvl = Math.min(param.lvl, param.data.getMaxLevel());
|
||||
int rank = param.data.getRankLevel();
|
||||
int totalExp = 0;
|
||||
for (int i = 1; i < param.lvl; i++)
|
||||
totalExp += GameData.getRelicExpRequired(rank, i);
|
||||
|
||||
List<GameItem> items = new ArrayList<>(param.amount);
|
||||
for (int i = 0; i < param.amount; i++) {
|
||||
// Create item for the artifact.
|
||||
GameItem item = new GameItem(param.data);
|
||||
item.setLevel(param.lvl);
|
||||
item.setTotalExp(totalExp);
|
||||
int numAffixes = param.data.getAppendPropNum() + (param.lvl-1)/4;
|
||||
if (param.mainPropId > 0) // Keep random mainProp if we didn't specify one
|
||||
item.setMainPropId(param.mainPropId);
|
||||
if (param.appendPropIdList != null) {
|
||||
item.getAppendPropIdList().clear();
|
||||
item.getAppendPropIdList().addAll(param.appendPropIdList);
|
||||
}
|
||||
// If we didn't include enough substats, top them up to the appropriate level at random
|
||||
item.addAppendProps(numAffixes - item.getAppendPropIdList().size());
|
||||
items.add(item);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private static int getArtifactMainProp(ItemData itemData, FightProperty prop) throws IllegalArgumentException {
|
||||
if (prop != FightProperty.FIGHT_PROP_NONE)
|
||||
for (ReliquaryMainPropData data : GameDepot.getRelicMainPropList(itemData.getMainPropDepotId()))
|
||||
if (data.getWeight() > 0 && data.getFightProp() == prop)
|
||||
return data.getId();
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private static List<Integer> getArtifactAffixes(ItemData itemData, FightProperty prop) throws IllegalArgumentException {
|
||||
if (prop == FightProperty.FIGHT_PROP_NONE) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
List<Integer> affixes = new ArrayList<>();
|
||||
for (ReliquaryAffixData data : GameDepot.getRelicAffixList(itemData.getAppendPropDepotId())) {
|
||||
if (data.getWeight() > 0 && data.getFightProp() == prop) {
|
||||
affixes.add(data.getId());
|
||||
}
|
||||
}
|
||||
return affixes;
|
||||
}
|
||||
|
||||
private static int getAppendPropId(String substatText, ItemData itemData) throws IllegalArgumentException {
|
||||
// If the given substat text is an integer, we just use that as the append prop ID.
|
||||
try {
|
||||
return Integer.parseInt(substatText);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// If the argument was not an integer, we try to determine
|
||||
// the append prop ID from the given text + artifact information.
|
||||
// A substat string has the format `substat_tier`, with the
|
||||
// `_tier` part being optional, defaulting to the maximum.
|
||||
String[] substatArgs = substatText.split("_");
|
||||
String substatType = substatArgs[0];
|
||||
|
||||
int substatTier = 4;
|
||||
if (substatArgs.length > 1) {
|
||||
substatTier = Integer.parseInt(substatArgs[1]);
|
||||
}
|
||||
|
||||
List<Integer> substats = getArtifactAffixes(itemData, FightProperty.getPropByShortName(substatType));
|
||||
|
||||
if (substats.isEmpty()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
substatTier -= 1; // 1-indexed to 0-indexed
|
||||
substatTier = Math.min(Math.max(0, substatTier), substats.size() - 1);
|
||||
return substats.get(substatTier);
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseRelicArgs(GiveItemParameters param, List<String> args) throws IllegalArgumentException {
|
||||
// Get the main stat from the arguments.
|
||||
// If the given argument is an integer, we use that.
|
||||
// If not, we check if the argument string is in the main prop map.
|
||||
String mainPropIdString = args.remove(0);
|
||||
|
||||
try {
|
||||
param.mainPropId = Integer.parseInt(mainPropIdString);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// This can in turn throw an exception which we don't want to catch here.
|
||||
param.mainPropId = getArtifactMainProp(param.data, FightProperty.getPropByShortName(mainPropIdString));
|
||||
}
|
||||
|
||||
// Get substats.
|
||||
param.appendPropIdList = new ArrayList<>();
|
||||
// Every remaining argument is a substat.
|
||||
for (String prop : args) {
|
||||
// The substat syntax permits specifying a number of rolls for the given
|
||||
// substat. Split the string into stat and number if that is the case here.
|
||||
String[] arr = prop.split(",");
|
||||
prop = arr[0];
|
||||
int n = 1;
|
||||
if (arr.length > 1) {
|
||||
n = Math.min(Integer.parseInt(arr[1]), 200);
|
||||
}
|
||||
|
||||
// Determine the substat ID.
|
||||
int appendPropId = getAppendPropId(prop, param.data);
|
||||
|
||||
// Add the current substat.
|
||||
for (int i = 0; i < n; i++) {
|
||||
param.appendPropIdList.add(appendPropId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void addItemsChunked(Player player, List<GameItem> items, int packetSize) {
|
||||
// Send the items in multiple packets
|
||||
int lastIdx = items.size() - 1;
|
||||
for (int i = 0; i <= lastIdx; i += packetSize) {
|
||||
player.getInventory().addItems(items.subList(i, Math.min(i + packetSize, lastIdx)));
|
||||
}
|
||||
}
|
||||
|
||||
private static void giveAllMats(Player player, GiveItemParameters param) {
|
||||
List<GameItem> itemList = new ArrayList<>();
|
||||
for (ItemData itemdata : GameData.getItemDataMap().values()) {
|
||||
int id = itemdata.getId();
|
||||
if (id < 100_000) continue; // Nothing meaningful below this
|
||||
if (illegalItemIds.contains(id)) continue;
|
||||
if (itemdata.isEquip()) continue;
|
||||
|
||||
GameItem item = new GameItem(itemdata);
|
||||
item.setCount(param.amount);
|
||||
itemList.add(item);
|
||||
}
|
||||
|
||||
addItemsChunked(player, itemList, 100);
|
||||
}
|
||||
|
||||
private static void giveAllWeapons(Player player, GiveItemParameters param) {
|
||||
int promoteLevel = GameItem.getMinPromoteLevel(param.lvl);
|
||||
int quantity = Math.min(param.amount, 5);
|
||||
int refinement = param.refinement - 1;
|
||||
|
||||
List<GameItem> itemList = new ArrayList<>();
|
||||
for (ItemData itemdata : GameData.getItemDataMap().values()) {
|
||||
int id = itemdata.getId();
|
||||
if (id < 11100 || id > 16000) continue; // All extant weapons are within this range
|
||||
if (illegalWeaponIds.contains(id)) continue;
|
||||
if (!itemdata.isEquip()) continue;
|
||||
if (itemdata.getItemType() != ItemType.ITEM_WEAPON) continue;
|
||||
|
||||
for (int i = 0; i < quantity; i++) {
|
||||
GameItem item = new GameItem(itemdata);
|
||||
item.setLevel(param.lvl);
|
||||
item.setPromoteLevel(promoteLevel);
|
||||
item.setRefinement(refinement);
|
||||
itemList.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
addItemsChunked(player, itemList, 100);
|
||||
}
|
||||
|
||||
private static void giveAll(Player player, GiveItemParameters param) {
|
||||
giveAllAvatars(player, param);
|
||||
giveAllMats(player, param);
|
||||
giveAllWeapons(player, param);
|
||||
}
|
||||
|
||||
private static final SparseSet illegalWeaponIds = new SparseSet("""
|
||||
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
|
||||
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
|
||||
""");
|
||||
|
||||
private static final SparseSet illegalRelicIds = new SparseSet("""
|
||||
20001, 23300-23340, 23383-23385, 78310-78554, 99310-99554
|
||||
""");
|
||||
|
||||
private static final SparseSet illegalItemIds = new SparseSet("""
|
||||
100086, 100087, 100100-101000, 101106-101110, 101306, 101500-104000,
|
||||
105001, 105004, 106000-107000, 107011, 108000, 109000-110000,
|
||||
115000-130000, 200200-200899, 220050, 220054
|
||||
""");
|
||||
}
|
||||
|
@ -4,15 +4,12 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.utils.Language;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "setstats", usage = "setstats|stats <stat> <value>", aliases = {"stats"}, permission = "player.setstats", permissionTargeted = "player.setstats.others", description = "commands.setStats.description")
|
||||
@ -20,157 +17,50 @@ public final class SetStatsCommand implements CommandHandler {
|
||||
static class Stat {
|
||||
String name;
|
||||
FightProperty prop;
|
||||
boolean percent;
|
||||
|
||||
public Stat(String name, FightProperty prop, boolean percent) {
|
||||
public Stat(FightProperty prop) {
|
||||
this.name = prop.toString();
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public Stat(String name, FightProperty prop) {
|
||||
this.name = name;
|
||||
this.prop = prop;
|
||||
this.percent = percent;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Stat> stats = new HashMap<>();
|
||||
Map<String, Stat> stats;
|
||||
|
||||
public SetStatsCommand() {
|
||||
// Default stats
|
||||
stats.put("maxhp", new Stat(FightProperty.FIGHT_PROP_MAX_HP.toString(), FightProperty.FIGHT_PROP_MAX_HP, false));
|
||||
stats.put("hp", new Stat(FightProperty.FIGHT_PROP_CUR_HP.toString(), FightProperty.FIGHT_PROP_CUR_HP, false));
|
||||
stats.put("atk", new Stat(FightProperty.FIGHT_PROP_CUR_ATTACK.toString(), FightProperty.FIGHT_PROP_CUR_ATTACK, false));
|
||||
stats.put("atkb", new Stat(FightProperty.FIGHT_PROP_BASE_ATTACK.toString(), FightProperty.FIGHT_PROP_BASE_ATTACK, false)); // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff.
|
||||
stats.put("def", new Stat(FightProperty.FIGHT_PROP_DEFENSE.toString(), FightProperty.FIGHT_PROP_DEFENSE, false));
|
||||
stats.put("em", new Stat(FightProperty.FIGHT_PROP_ELEMENT_MASTERY.toString(), FightProperty.FIGHT_PROP_ELEMENT_MASTERY, false));
|
||||
stats.put("er", new Stat(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY.toString(), FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, true));
|
||||
stats.put("crate", new Stat(FightProperty.FIGHT_PROP_CRITICAL.toString(), FightProperty.FIGHT_PROP_CRITICAL, true));
|
||||
stats.put("cdmg", new Stat(FightProperty.FIGHT_PROP_CRITICAL_HURT.toString(), FightProperty.FIGHT_PROP_CRITICAL_HURT, true));
|
||||
stats.put("dmg", new Stat(FightProperty.FIGHT_PROP_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ADD_HURT, true)); // This seems to get reset after attacks
|
||||
stats.put("eanemo", new Stat(FightProperty.FIGHT_PROP_WIND_ADD_HURT.toString(), FightProperty.FIGHT_PROP_WIND_ADD_HURT, true));
|
||||
stats.put("ecryo", new Stat(FightProperty.FIGHT_PROP_ICE_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ICE_ADD_HURT, true));
|
||||
stats.put("edendro", new Stat(FightProperty.FIGHT_PROP_GRASS_ADD_HURT.toString(), FightProperty.FIGHT_PROP_GRASS_ADD_HURT, true));
|
||||
stats.put("eelectro", new Stat(FightProperty.FIGHT_PROP_ELEC_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ELEC_ADD_HURT, true));
|
||||
stats.put("egeo", new Stat(FightProperty.FIGHT_PROP_ROCK_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ROCK_ADD_HURT, true));
|
||||
stats.put("ehydro", new Stat(FightProperty.FIGHT_PROP_WATER_ADD_HURT.toString(), FightProperty.FIGHT_PROP_WATER_ADD_HURT, true));
|
||||
stats.put("epyro", new Stat(FightProperty.FIGHT_PROP_FIRE_ADD_HURT.toString(), FightProperty.FIGHT_PROP_FIRE_ADD_HURT, true));
|
||||
stats.put("ephys", new Stat(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT.toString(), FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, true));
|
||||
stats.put("resall", new Stat(FightProperty.FIGHT_PROP_SUB_HURT.toString(), FightProperty.FIGHT_PROP_SUB_HURT, true)); // This seems to get reset after attacks
|
||||
stats.put("resanemo", new Stat(FightProperty.FIGHT_PROP_WIND_SUB_HURT.toString(), FightProperty.FIGHT_PROP_WIND_SUB_HURT, true));
|
||||
stats.put("rescryo", new Stat(FightProperty.FIGHT_PROP_ICE_SUB_HURT.toString(), FightProperty.FIGHT_PROP_ICE_SUB_HURT, true));
|
||||
stats.put("resdendro", new Stat(FightProperty.FIGHT_PROP_GRASS_SUB_HURT.toString(), FightProperty.FIGHT_PROP_GRASS_SUB_HURT, true));
|
||||
stats.put("reselectro", new Stat(FightProperty.FIGHT_PROP_ELEC_SUB_HURT.toString(), FightProperty.FIGHT_PROP_ELEC_SUB_HURT, true));
|
||||
stats.put("resgeo", new Stat(FightProperty.FIGHT_PROP_ROCK_SUB_HURT.toString(), FightProperty.FIGHT_PROP_ROCK_SUB_HURT, true));
|
||||
stats.put("reshydro", new Stat(FightProperty.FIGHT_PROP_WATER_SUB_HURT.toString(), FightProperty.FIGHT_PROP_WATER_SUB_HURT, true));
|
||||
stats.put("respyro", new Stat(FightProperty.FIGHT_PROP_FIRE_SUB_HURT.toString(), FightProperty.FIGHT_PROP_FIRE_SUB_HURT, true));
|
||||
stats.put("resphys", new Stat(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT.toString(), FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, true));
|
||||
stats.put("cdr", new Stat(FightProperty.FIGHT_PROP_SKILL_CD_MINUS_RATIO.toString(), FightProperty.FIGHT_PROP_SKILL_CD_MINUS_RATIO, true));
|
||||
stats.put("heal", new Stat(FightProperty.FIGHT_PROP_HEAL_ADD.toString(), FightProperty.FIGHT_PROP_HEAL_ADD, true));
|
||||
stats.put("heali", new Stat(FightProperty.FIGHT_PROP_HEALED_ADD.toString(), FightProperty.FIGHT_PROP_HEALED_ADD, true));
|
||||
stats.put("shield", new Stat(FightProperty.FIGHT_PROP_SHIELD_COST_MINUS_RATIO.toString(), FightProperty.FIGHT_PROP_SHIELD_COST_MINUS_RATIO, true));
|
||||
stats.put("defi", new Stat(FightProperty.FIGHT_PROP_DEFENCE_IGNORE_RATIO.toString(), FightProperty.FIGHT_PROP_DEFENCE_IGNORE_RATIO, true));
|
||||
// Compatibility aliases
|
||||
stats.put("mhp", stats.get("maxhp"));
|
||||
stats.put("cr", stats.get("crate"));
|
||||
stats.put("cd", stats.get("cdmg"));
|
||||
stats.put("edend", stats.get("edendro"));
|
||||
stats.put("eelec", stats.get("eelectro"));
|
||||
stats.put("ethunder", stats.get("eelectro"));
|
||||
|
||||
this.stats = new HashMap<>();
|
||||
for (String key : FightProperty.getShortNames()) {
|
||||
this.stats.put(key, new Stat(FightProperty.getPropByShortName(key)));
|
||||
}
|
||||
// Full FightProperty enum that won't be advertised but can be used by devs
|
||||
// They have a prefix to avoid the "hp" clash
|
||||
stats.put("_none", new Stat("NONE", FightProperty.FIGHT_PROP_NONE, true));
|
||||
stats.put("_base_hp", new Stat("BASE_HP", FightProperty.FIGHT_PROP_BASE_HP, false));
|
||||
stats.put("_hp", new Stat("HP", FightProperty.FIGHT_PROP_HP, false));
|
||||
stats.put("_hp_percent", new Stat("HP_PERCENT", FightProperty.FIGHT_PROP_HP_PERCENT, true));
|
||||
stats.put("_base_attack", new Stat("BASE_ATTACK", FightProperty.FIGHT_PROP_BASE_ATTACK, false));
|
||||
stats.put("_attack", new Stat("ATTACK", FightProperty.FIGHT_PROP_ATTACK, false));
|
||||
stats.put("_attack_percent", new Stat("ATTACK_PERCENT", FightProperty.FIGHT_PROP_ATTACK_PERCENT, true));
|
||||
stats.put("_base_defense", new Stat("BASE_DEFENSE", FightProperty.FIGHT_PROP_BASE_DEFENSE, false));
|
||||
stats.put("_defense", new Stat("DEFENSE", FightProperty.FIGHT_PROP_DEFENSE, false));
|
||||
stats.put("_defense_percent", new Stat("DEFENSE_PERCENT", FightProperty.FIGHT_PROP_DEFENSE_PERCENT, true));
|
||||
stats.put("_base_speed", new Stat("BASE_SPEED", FightProperty.FIGHT_PROP_BASE_SPEED, true));
|
||||
stats.put("_speed_percent", new Stat("SPEED_PERCENT", FightProperty.FIGHT_PROP_SPEED_PERCENT, true));
|
||||
stats.put("_hp_mp_percent", new Stat("HP_MP_PERCENT", FightProperty.FIGHT_PROP_HP_MP_PERCENT, true));
|
||||
stats.put("_attack_mp_percent", new Stat("ATTACK_MP_PERCENT", FightProperty.FIGHT_PROP_ATTACK_MP_PERCENT, true));
|
||||
stats.put("_critical", new Stat("CRITICAL", FightProperty.FIGHT_PROP_CRITICAL, true));
|
||||
stats.put("_anti_critical", new Stat("ANTI_CRITICAL", FightProperty.FIGHT_PROP_ANTI_CRITICAL, true));
|
||||
stats.put("_critical_hurt", new Stat("CRITICAL_HURT", FightProperty.FIGHT_PROP_CRITICAL_HURT, true));
|
||||
stats.put("_charge_efficiency", new Stat("CHARGE_EFFICIENCY", FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, true));
|
||||
stats.put("_add_hurt", new Stat("ADD_HURT", FightProperty.FIGHT_PROP_ADD_HURT, true));
|
||||
stats.put("_sub_hurt", new Stat("SUB_HURT", FightProperty.FIGHT_PROP_SUB_HURT, true));
|
||||
stats.put("_heal_add", new Stat("HEAL_ADD", FightProperty.FIGHT_PROP_HEAL_ADD, true));
|
||||
stats.put("_healed_add", new Stat("HEALED_ADD", FightProperty.FIGHT_PROP_HEALED_ADD, false));
|
||||
stats.put("_element_mastery", new Stat("ELEMENT_MASTERY", FightProperty.FIGHT_PROP_ELEMENT_MASTERY, true));
|
||||
stats.put("_physical_sub_hurt", new Stat("PHYSICAL_SUB_HURT", FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, true));
|
||||
stats.put("_physical_add_hurt", new Stat("PHYSICAL_ADD_HURT", FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, true));
|
||||
stats.put("_defence_ignore_ratio", new Stat("DEFENCE_IGNORE_RATIO", FightProperty.FIGHT_PROP_DEFENCE_IGNORE_RATIO, true));
|
||||
stats.put("_defence_ignore_delta", new Stat("DEFENCE_IGNORE_DELTA", FightProperty.FIGHT_PROP_DEFENCE_IGNORE_DELTA, true));
|
||||
stats.put("_fire_add_hurt", new Stat("FIRE_ADD_HURT", FightProperty.FIGHT_PROP_FIRE_ADD_HURT, true));
|
||||
stats.put("_elec_add_hurt", new Stat("ELEC_ADD_HURT", FightProperty.FIGHT_PROP_ELEC_ADD_HURT, true));
|
||||
stats.put("_water_add_hurt", new Stat("WATER_ADD_HURT", FightProperty.FIGHT_PROP_WATER_ADD_HURT, true));
|
||||
stats.put("_grass_add_hurt", new Stat("GRASS_ADD_HURT", FightProperty.FIGHT_PROP_GRASS_ADD_HURT, true));
|
||||
stats.put("_wind_add_hurt", new Stat("WIND_ADD_HURT", FightProperty.FIGHT_PROP_WIND_ADD_HURT, true));
|
||||
stats.put("_rock_add_hurt", new Stat("ROCK_ADD_HURT", FightProperty.FIGHT_PROP_ROCK_ADD_HURT, true));
|
||||
stats.put("_ice_add_hurt", new Stat("ICE_ADD_HURT", FightProperty.FIGHT_PROP_ICE_ADD_HURT, true));
|
||||
stats.put("_hit_head_add_hurt", new Stat("HIT_HEAD_ADD_HURT", FightProperty.FIGHT_PROP_HIT_HEAD_ADD_HURT, true));
|
||||
stats.put("_fire_sub_hurt", new Stat("FIRE_SUB_HURT", FightProperty.FIGHT_PROP_FIRE_SUB_HURT, true));
|
||||
stats.put("_elec_sub_hurt", new Stat("ELEC_SUB_HURT", FightProperty.FIGHT_PROP_ELEC_SUB_HURT, true));
|
||||
stats.put("_water_sub_hurt", new Stat("WATER_SUB_HURT", FightProperty.FIGHT_PROP_WATER_SUB_HURT, true));
|
||||
stats.put("_grass_sub_hurt", new Stat("GRASS_SUB_HURT", FightProperty.FIGHT_PROP_GRASS_SUB_HURT, true));
|
||||
stats.put("_wind_sub_hurt", new Stat("WIND_SUB_HURT", FightProperty.FIGHT_PROP_WIND_SUB_HURT, true));
|
||||
stats.put("_rock_sub_hurt", new Stat("ROCK_SUB_HURT", FightProperty.FIGHT_PROP_ROCK_SUB_HURT, true));
|
||||
stats.put("_ice_sub_hurt", new Stat("ICE_SUB_HURT", FightProperty.FIGHT_PROP_ICE_SUB_HURT, true));
|
||||
stats.put("_effect_hit", new Stat("EFFECT_HIT", FightProperty.FIGHT_PROP_EFFECT_HIT, true));
|
||||
stats.put("_effect_resist", new Stat("EFFECT_RESIST", FightProperty.FIGHT_PROP_EFFECT_RESIST, true));
|
||||
stats.put("_freeze_resist", new Stat("FREEZE_RESIST", FightProperty.FIGHT_PROP_FREEZE_RESIST, true));
|
||||
stats.put("_torpor_resist", new Stat("TORPOR_RESIST", FightProperty.FIGHT_PROP_TORPOR_RESIST, true));
|
||||
stats.put("_dizzy_resist", new Stat("DIZZY_RESIST", FightProperty.FIGHT_PROP_DIZZY_RESIST, true));
|
||||
stats.put("_freeze_shorten", new Stat("FREEZE_SHORTEN", FightProperty.FIGHT_PROP_FREEZE_SHORTEN, true));
|
||||
stats.put("_torpor_shorten", new Stat("TORPOR_SHORTEN", FightProperty.FIGHT_PROP_TORPOR_SHORTEN, true));
|
||||
stats.put("_dizzy_shorten", new Stat("DIZZY_SHORTEN", FightProperty.FIGHT_PROP_DIZZY_SHORTEN, true));
|
||||
stats.put("_max_fire_energy", new Stat("MAX_FIRE_ENERGY", FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, true));
|
||||
stats.put("_max_elec_energy", new Stat("MAX_ELEC_ENERGY", FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, true));
|
||||
stats.put("_max_water_energy", new Stat("MAX_WATER_ENERGY", FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, true));
|
||||
stats.put("_max_grass_energy", new Stat("MAX_GRASS_ENERGY", FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY, true));
|
||||
stats.put("_max_wind_energy", new Stat("MAX_WIND_ENERGY", FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, true));
|
||||
stats.put("_max_ice_energy", new Stat("MAX_ICE_ENERGY", FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, true));
|
||||
stats.put("_max_rock_energy", new Stat("MAX_ROCK_ENERGY", FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, true));
|
||||
stats.put("_skill_cd_minus_ratio", new Stat("SKILL_CD_MINUS_RATIO", FightProperty.FIGHT_PROP_SKILL_CD_MINUS_RATIO, true));
|
||||
stats.put("_shield_cost_minus_ratio", new Stat("SHIELD_COST_MINUS_RATIO", FightProperty.FIGHT_PROP_SHIELD_COST_MINUS_RATIO, true));
|
||||
stats.put("_cur_fire_energy", new Stat("CUR_FIRE_ENERGY", FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, false));
|
||||
stats.put("_cur_elec_energy", new Stat("CUR_ELEC_ENERGY", FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY, false));
|
||||
stats.put("_cur_water_energy", new Stat("CUR_WATER_ENERGY", FightProperty.FIGHT_PROP_CUR_WATER_ENERGY, false));
|
||||
stats.put("_cur_grass_energy", new Stat("CUR_GRASS_ENERGY", FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY, false));
|
||||
stats.put("_cur_wind_energy", new Stat("CUR_WIND_ENERGY", FightProperty.FIGHT_PROP_CUR_WIND_ENERGY, false));
|
||||
stats.put("_cur_ice_energy", new Stat("CUR_ICE_ENERGY", FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, false));
|
||||
stats.put("_cur_rock_energy", new Stat("CUR_ROCK_ENERGY", FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY, false));
|
||||
stats.put("_cur_hp", new Stat("CUR_HP", FightProperty.FIGHT_PROP_CUR_HP, false));
|
||||
stats.put("_max_hp", new Stat("MAX_HP", FightProperty.FIGHT_PROP_MAX_HP, false));
|
||||
stats.put("_cur_attack", new Stat("CUR_ATTACK", FightProperty.FIGHT_PROP_CUR_ATTACK, false));
|
||||
stats.put("_cur_defense", new Stat("CUR_DEFENSE", FightProperty.FIGHT_PROP_CUR_DEFENSE, false));
|
||||
stats.put("_cur_speed", new Stat("CUR_SPEED", FightProperty.FIGHT_PROP_CUR_SPEED, true));
|
||||
stats.put("_nonextra_attack", new Stat("NONEXTRA_ATTACK", FightProperty.FIGHT_PROP_NONEXTRA_ATTACK, true));
|
||||
stats.put("_nonextra_defense", new Stat("NONEXTRA_DEFENSE", FightProperty.FIGHT_PROP_NONEXTRA_DEFENSE, true));
|
||||
stats.put("_nonextra_critical", new Stat("NONEXTRA_CRITICAL", FightProperty.FIGHT_PROP_NONEXTRA_CRITICAL, true));
|
||||
stats.put("_nonextra_anti_critical", new Stat("NONEXTRA_ANTI_CRITICAL", FightProperty.FIGHT_PROP_NONEXTRA_ANTI_CRITICAL, true));
|
||||
stats.put("_nonextra_critical_hurt", new Stat("NONEXTRA_CRITICAL_HURT", FightProperty.FIGHT_PROP_NONEXTRA_CRITICAL_HURT, true));
|
||||
stats.put("_nonextra_charge_efficiency", new Stat("NONEXTRA_CHARGE_EFFICIENCY", FightProperty.FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY, true));
|
||||
stats.put("_nonextra_element_mastery", new Stat("NONEXTRA_ELEMENT_MASTERY", FightProperty.FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY, true));
|
||||
stats.put("_nonextra_physical_sub_hurt", new Stat("NONEXTRA_PHYSICAL_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT, true));
|
||||
stats.put("_nonextra_fire_add_hurt", new Stat("NONEXTRA_FIRE_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT, true));
|
||||
stats.put("_nonextra_elec_add_hurt", new Stat("NONEXTRA_ELEC_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT, true));
|
||||
stats.put("_nonextra_water_add_hurt", new Stat("NONEXTRA_WATER_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WATER_ADD_HURT, true));
|
||||
stats.put("_nonextra_grass_add_hurt", new Stat("NONEXTRA_GRASS_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT, true));
|
||||
stats.put("_nonextra_wind_add_hurt", new Stat("NONEXTRA_WIND_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WIND_ADD_HURT, true));
|
||||
stats.put("_nonextra_rock_add_hurt", new Stat("NONEXTRA_ROCK_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT, true));
|
||||
stats.put("_nonextra_ice_add_hurt", new Stat("NONEXTRA_ICE_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ICE_ADD_HURT, true));
|
||||
stats.put("_nonextra_fire_sub_hurt", new Stat("NONEXTRA_FIRE_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT, true));
|
||||
stats.put("_nonextra_elec_sub_hurt", new Stat("NONEXTRA_ELEC_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT, true));
|
||||
stats.put("_nonextra_water_sub_hurt", new Stat("NONEXTRA_WATER_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WATER_SUB_HURT, true));
|
||||
stats.put("_nonextra_grass_sub_hurt", new Stat("NONEXTRA_GRASS_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT, true));
|
||||
stats.put("_nonextra_wind_sub_hurt", new Stat("NONEXTRA_WIND_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WIND_SUB_HURT, true));
|
||||
stats.put("_nonextra_rock_sub_hurt", new Stat("NONEXTRA_ROCK_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT, true));
|
||||
stats.put("_nonextra_ice_sub_hurt", new Stat("NONEXTRA_ICE_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ICE_SUB_HURT, true));
|
||||
stats.put("_nonextra_skill_cd_minus_ratio", new Stat("NONEXTRA_SKILL_CD_MINUS_RATIO", FightProperty.FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO, true));
|
||||
stats.put("_nonextra_shield_cost_minus_ratio", new Stat("NONEXTRA_SHIELD_COST_MINUS_RATIO", FightProperty.FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO, true));
|
||||
stats.put("_nonextra_physical_add_hurt", new Stat("NONEXTRA_PHYSICAL_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT, true));
|
||||
for (FightProperty prop : FightProperty.values()) {
|
||||
String name = prop.toString().substring(10); // FIGHT_PROP_BASE_HP -> _BASE_HP
|
||||
String key = name.toLowerCase(); // _BASE_HP -> _base_hp
|
||||
name = name.substring(1); // _BASE_HP -> BASE_HP
|
||||
this.stats.put(key, new Stat(name, prop));
|
||||
}
|
||||
|
||||
// Compatibility aliases
|
||||
this.stats.put("mhp", this.stats.get("maxhp"));
|
||||
this.stats.put("hp", new Stat(FightProperty.FIGHT_PROP_CUR_HP)); // Overrides FIGHT_PROP_HP
|
||||
this.stats.put("atk", new Stat(FightProperty.FIGHT_PROP_CUR_ATTACK)); // Overrides FIGHT_PROP_ATTACK
|
||||
this.stats.put("atkb", new Stat(FightProperty.FIGHT_PROP_BASE_ATTACK)); // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff.
|
||||
this.stats.put("eanemo", this.stats.get("anemo%"));
|
||||
this.stats.put("ecryo", this.stats.get("cryo%"));
|
||||
this.stats.put("edendro", this.stats.get("dendro%"));
|
||||
this.stats.put("edend", this.stats.get("dendro%"));
|
||||
this.stats.put("eelectro", this.stats.get("electro%"));
|
||||
this.stats.put("eelec", this.stats.get("electro%"));
|
||||
this.stats.put("ethunder", this.stats.get("electro%"));
|
||||
this.stats.put("egeo", this.stats.get("geo%"));
|
||||
this.stats.put("ehydro", this.stats.get("hydro%"));
|
||||
this.stats.put("epyro", this.stats.get("pyro%"));
|
||||
this.stats.put("ephys", this.stats.get("phys%"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -206,8 +96,8 @@ public final class SetStatsCommand implements CommandHandler {
|
||||
Stat stat = stats.get(statStr);
|
||||
entity.setFightProperty(stat.prop, value);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, stat.prop));
|
||||
if (stat.percent) {
|
||||
valueStr = String.format("%.1f%%", value*100f);
|
||||
if (FightProperty.isPercentage(stat.prop)) {
|
||||
valueStr = String.format("%.1f%%", value * 100f);
|
||||
} else {
|
||||
valueStr = String.format("%.0f", value);
|
||||
}
|
||||
|
@ -20,8 +20,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public class GameDepot {
|
||||
private static Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicMainPropDepot = new Int2ObjectOpenHashMap<>();
|
||||
private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot = new Int2ObjectOpenHashMap<>();
|
||||
private static Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicRandomMainPropDepot = new Int2ObjectOpenHashMap<>();
|
||||
private static Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot = new Int2ObjectOpenHashMap<>();
|
||||
private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
|
||||
private static Int2ObjectMap<SpatialIndex<SpawnGroupEntry>> spawnLists = new Int2ObjectOpenHashMap<>();
|
||||
@ -31,8 +32,10 @@ public class GameDepot {
|
||||
if (data.getWeight() <= 0 || data.getPropDepotId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
WeightedList<ReliquaryMainPropData> list = relicMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new WeightedList<>());
|
||||
list.add(data.getWeight(), data);
|
||||
List<ReliquaryMainPropData> list = relicMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new ArrayList<>());
|
||||
list.add(data);
|
||||
WeightedList<ReliquaryMainPropData> weightedList = relicRandomMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new WeightedList<>());
|
||||
weightedList.add(data.getWeight(), data);
|
||||
}
|
||||
for (ReliquaryAffixData data : GameData.getReliquaryAffixDataMap().values()) {
|
||||
if (data.getWeight() <= 0 || data.getDepotId() <= 0) {
|
||||
@ -48,14 +51,18 @@ public class GameDepot {
|
||||
}
|
||||
|
||||
public static ReliquaryMainPropData getRandomRelicMainProp(int depot) {
|
||||
WeightedList<ReliquaryMainPropData> depotList = relicMainPropDepot.get(depot);
|
||||
WeightedList<ReliquaryMainPropData> depotList = relicRandomMainPropDepot.get(depot);
|
||||
if (depotList == null) {
|
||||
return null;
|
||||
}
|
||||
return depotList.next();
|
||||
}
|
||||
|
||||
public static List<ReliquaryAffixData> getRandomRelicAffixList(int depot) {
|
||||
public static List<ReliquaryMainPropData> getRelicMainPropList(int depot) {
|
||||
return relicMainPropDepot.get(depot);
|
||||
}
|
||||
|
||||
public static List<ReliquaryAffixData> getRelicAffixList(int depot) {
|
||||
return relicAffixDepot.get(depot);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import emu.grasscutter.game.inventory.*;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = {"MaterialExcelConfigData.json",
|
||||
"WeaponExcelConfigData.json",
|
||||
@ -19,23 +20,23 @@ import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
public class ItemData extends GameResource {
|
||||
|
||||
private int id;
|
||||
private int stackLimit = 1;
|
||||
private int maxUseCount;
|
||||
private int rankLevel;
|
||||
private String effectName;
|
||||
private int[] satiationParams;
|
||||
private int rank;
|
||||
private int weight;
|
||||
private int gadgetId;
|
||||
@Getter private int stackLimit = 1;
|
||||
@Getter private int maxUseCount;
|
||||
@Getter private int rankLevel;
|
||||
@Getter private String effectName;
|
||||
@Getter private int[] satiationParams;
|
||||
@Getter private int rank;
|
||||
@Getter private int weight;
|
||||
@Getter private int gadgetId;
|
||||
|
||||
private int[] destroyReturnMaterial;
|
||||
private int[] destroyReturnMaterialCount;
|
||||
@Getter private int[] destroyReturnMaterial;
|
||||
@Getter private int[] destroyReturnMaterialCount;
|
||||
|
||||
private List<ItemUseData> itemUse;
|
||||
@Getter private List<ItemUseData> itemUse;
|
||||
|
||||
// Food
|
||||
private String foodQuality;
|
||||
private String useTarget;
|
||||
@Getter private String foodQuality;
|
||||
@Getter private String useTarget;
|
||||
private String[] iseParam;
|
||||
|
||||
// String enums
|
||||
@ -45,42 +46,42 @@ public class ItemData extends GameResource {
|
||||
private String effectType;
|
||||
private String destroyRule;
|
||||
|
||||
// Relic
|
||||
private int mainPropDepotId;
|
||||
private int appendPropDepotId;
|
||||
private int appendPropNum;
|
||||
private int setId;
|
||||
private int[] addPropLevels;
|
||||
private int baseConvExp;
|
||||
private int maxLevel;
|
||||
|
||||
// Weapon
|
||||
private int weaponPromoteId;
|
||||
private int weaponBaseExp;
|
||||
private int storyId;
|
||||
private int avatarPromoteId;
|
||||
private int awakenMaterial;
|
||||
private int[] awakenCosts;
|
||||
private int[] skillAffix;
|
||||
private WeaponProperty[] weaponProp;
|
||||
|
||||
// Hash
|
||||
private String icon;
|
||||
private long nameTextMapHash;
|
||||
|
||||
// Post load
|
||||
// Post load enum forms of above
|
||||
private transient MaterialType materialEnumType;
|
||||
private transient ItemType itemEnumType;
|
||||
private transient EquipType equipEnumType;
|
||||
|
||||
private IntSet addPropLevelSet;
|
||||
// Relic
|
||||
@Getter private int mainPropDepotId;
|
||||
@Getter private int appendPropDepotId;
|
||||
@Getter private int appendPropNum;
|
||||
@Getter private int setId;
|
||||
private int[] addPropLevels;
|
||||
@Getter private int baseConvExp;
|
||||
@Getter private int maxLevel;
|
||||
|
||||
// Weapon
|
||||
@Getter private int weaponPromoteId;
|
||||
@Getter private int weaponBaseExp;
|
||||
@Getter private int storyId;
|
||||
@Getter private int avatarPromoteId;
|
||||
@Getter private int awakenMaterial;
|
||||
@Getter private int[] awakenCosts;
|
||||
@Getter private int[] skillAffix;
|
||||
private WeaponProperty[] weaponProp;
|
||||
|
||||
// Hash
|
||||
@Getter private String icon;
|
||||
@Getter private long nameTextMapHash;
|
||||
|
||||
@Getter private IntSet addPropLevelSet;
|
||||
|
||||
// Furniture
|
||||
private int comfort;
|
||||
private List<Integer> furnType;
|
||||
private List<Integer> furnitureGadgetID;
|
||||
@Getter private int comfort;
|
||||
@Getter private List<Integer> furnType;
|
||||
@Getter private List<Integer> furnitureGadgetID;
|
||||
@SerializedName("JFDLJGDFIGL")
|
||||
private int roomSceneId;
|
||||
@Getter private int roomSceneId;
|
||||
|
||||
@Override
|
||||
public int getId(){
|
||||
@ -91,138 +92,18 @@ public class ItemData extends GameResource {
|
||||
return this.materialType;
|
||||
}
|
||||
|
||||
public int getStackLimit(){
|
||||
return this.stackLimit;
|
||||
}
|
||||
|
||||
public int getMaxUseCount(){
|
||||
return this.maxUseCount;
|
||||
}
|
||||
|
||||
public String getUseTarget(){
|
||||
return this.useTarget;
|
||||
}
|
||||
|
||||
public String[] getUseParam(){
|
||||
return this.iseParam;
|
||||
}
|
||||
|
||||
public int getRankLevel(){
|
||||
return this.rankLevel;
|
||||
}
|
||||
|
||||
public String getFoodQuality(){
|
||||
return this.foodQuality;
|
||||
}
|
||||
|
||||
public String getEffectName(){
|
||||
return this.effectName;
|
||||
}
|
||||
|
||||
public int[] getSatiationParams(){
|
||||
return this.satiationParams;
|
||||
}
|
||||
|
||||
public int[] getDestroyReturnMaterial(){
|
||||
return this.destroyReturnMaterial;
|
||||
}
|
||||
|
||||
public int[] getDestroyReturnMaterialCount(){
|
||||
return this.destroyReturnMaterialCount;
|
||||
}
|
||||
|
||||
public List<ItemUseData> getItemUse() {
|
||||
return itemUse;
|
||||
}
|
||||
|
||||
public long getNameTextMapHash(){
|
||||
return this.nameTextMapHash;
|
||||
}
|
||||
|
||||
public String getIcon(){
|
||||
return this.icon;
|
||||
}
|
||||
|
||||
public String getItemTypeString(){
|
||||
return this.itemType;
|
||||
}
|
||||
|
||||
public int getRank(){
|
||||
return this.rank;
|
||||
}
|
||||
|
||||
public int getGadgetId() {
|
||||
return gadgetId;
|
||||
}
|
||||
|
||||
public int getBaseConvExp() {
|
||||
return baseConvExp;
|
||||
}
|
||||
|
||||
public int getMainPropDepotId() {
|
||||
return mainPropDepotId;
|
||||
}
|
||||
|
||||
public int getAppendPropDepotId() {
|
||||
return appendPropDepotId;
|
||||
}
|
||||
|
||||
public int getAppendPropNum() {
|
||||
return appendPropNum;
|
||||
}
|
||||
|
||||
public int getSetId() {
|
||||
return setId;
|
||||
}
|
||||
|
||||
public int getWeaponPromoteId() {
|
||||
return weaponPromoteId;
|
||||
}
|
||||
|
||||
public int getWeaponBaseExp() {
|
||||
return weaponBaseExp;
|
||||
}
|
||||
|
||||
public int getAwakenMaterial() {
|
||||
return awakenMaterial;
|
||||
}
|
||||
|
||||
public int[] getAwakenCosts() {
|
||||
return awakenCosts;
|
||||
}
|
||||
|
||||
public IntSet getAddPropLevelSet() {
|
||||
return addPropLevelSet;
|
||||
}
|
||||
|
||||
public int[] getSkillAffix() {
|
||||
return skillAffix;
|
||||
}
|
||||
|
||||
public WeaponProperty[] getWeaponProperties() {
|
||||
return weaponProp;
|
||||
}
|
||||
|
||||
public int getMaxLevel() {
|
||||
return maxLevel;
|
||||
}
|
||||
|
||||
public int getComfort() {
|
||||
return comfort;
|
||||
}
|
||||
|
||||
public List<Integer> getFurnType() {
|
||||
return furnType;
|
||||
}
|
||||
|
||||
public List<Integer> getFurnitureGadgetID() {
|
||||
return furnitureGadgetID;
|
||||
}
|
||||
|
||||
public int getRoomSceneId() {
|
||||
return roomSceneId;
|
||||
}
|
||||
|
||||
public ItemType getItemType() {
|
||||
return this.itemEnumType;
|
||||
}
|
||||
@ -274,26 +155,10 @@ public class ItemData extends GameResource {
|
||||
}
|
||||
|
||||
public static class WeaponProperty {
|
||||
private FightProperty fightProp;
|
||||
private String propType;
|
||||
private float initValue;
|
||||
private String type;
|
||||
|
||||
public String getPropType(){
|
||||
return this.propType;
|
||||
}
|
||||
|
||||
public float getInitValue(){
|
||||
return this.initValue;
|
||||
}
|
||||
|
||||
public String getType(){
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public FightProperty getFightProp() {
|
||||
return fightProp;
|
||||
}
|
||||
@Getter private FightProperty fightProp;
|
||||
@Getter private String propType;
|
||||
@Getter private float initValue;
|
||||
@Getter private String type;
|
||||
|
||||
public void onLoad() {
|
||||
this.fightProp = FightProperty.getPropByName(propType);
|
||||
|
@ -242,6 +242,23 @@ public class Avatar {
|
||||
this.promoteLevel = promoteLevel;
|
||||
}
|
||||
|
||||
static public int getMinPromoteLevel(int level) {
|
||||
if (level > 80) {
|
||||
return 6;
|
||||
} else if (level > 70) {
|
||||
return 5;
|
||||
} else if (level > 60) {
|
||||
return 4;
|
||||
} else if (level > 50) {
|
||||
return 3;
|
||||
} else if (level > 40) {
|
||||
return 2;
|
||||
} else if (level > 20) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Int2ObjectMap<GameItem> getEquips() {
|
||||
return equips;
|
||||
}
|
||||
|
@ -33,34 +33,36 @@ import emu.grasscutter.net.proto.SceneReliquaryInfoOuterClass.SceneReliquaryInfo
|
||||
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
|
||||
import emu.grasscutter.net.proto.WeaponOuterClass.Weapon;
|
||||
import emu.grasscutter.utils.WeightedList;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Entity(value = "items", useDiscriminator = false)
|
||||
public class GameItem {
|
||||
@Id private ObjectId id;
|
||||
@Indexed private int ownerId;
|
||||
private int itemId;
|
||||
private int count;
|
||||
@Getter @Setter private int itemId;
|
||||
@Getter @Setter private int count;
|
||||
|
||||
@Transient private long guid; // Player unique id
|
||||
@Transient private ItemData itemData;
|
||||
@Transient @Getter private long guid; // Player unique id
|
||||
@Transient @Getter @Setter private ItemData itemData;
|
||||
|
||||
// Equips
|
||||
private int level;
|
||||
private int exp;
|
||||
private int totalExp;
|
||||
private int promoteLevel;
|
||||
private boolean locked;
|
||||
@Getter @Setter private int level;
|
||||
@Getter @Setter private int exp;
|
||||
@Getter @Setter private int totalExp;
|
||||
@Getter @Setter private int promoteLevel;
|
||||
@Getter @Setter private boolean locked;
|
||||
|
||||
// Weapon
|
||||
private List<Integer> affixes;
|
||||
private int refinement = 0;
|
||||
@Getter private List<Integer> affixes;
|
||||
@Getter @Setter private int refinement = 0;
|
||||
|
||||
// Relic
|
||||
private int mainPropId;
|
||||
private List<Integer> appendPropIdList;
|
||||
@Getter @Setter private int mainPropId;
|
||||
@Getter private List<Integer> appendPropIdList;
|
||||
|
||||
private int equipCharacter;
|
||||
@Transient private int weaponEntityId;
|
||||
@Getter @Setter private int equipCharacter;
|
||||
@Transient @Getter @Setter private int weaponEntityId;
|
||||
|
||||
public GameItem() {
|
||||
// Morphia only
|
||||
@ -82,42 +84,37 @@ public class GameItem {
|
||||
this.itemId = data.getId();
|
||||
this.itemData = data;
|
||||
|
||||
if (data.getItemType() == ItemType.ITEM_VIRTUAL) {
|
||||
switch (data.getItemType()) {
|
||||
case ITEM_VIRTUAL:
|
||||
this.count = count;
|
||||
} else {
|
||||
this.count = Math.min(count, data.getStackLimit());
|
||||
break;
|
||||
case ITEM_WEAPON:
|
||||
this.count = 1;
|
||||
this.level = Math.max(this.count, 1); // ??????????????????
|
||||
this.affixes = new ArrayList<>(2);
|
||||
if (data.getSkillAffix() != null) {
|
||||
for (int skillAffix : data.getSkillAffix()) {
|
||||
if (skillAffix > 0) {
|
||||
this.affixes.add(skillAffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ITEM_RELIQUARY:
|
||||
this.count = 1;
|
||||
this.level = 1;
|
||||
this.appendPropIdList = new ArrayList<>();
|
||||
// Create main property
|
||||
ReliquaryMainPropData mainPropData = GameDepot.getRandomRelicMainProp(data.getMainPropDepotId());
|
||||
if (mainPropData != null) {
|
||||
this.mainPropId = mainPropData.getId();
|
||||
}
|
||||
// Create extra stats
|
||||
this.addAppendProps(data.getAppendPropNum());
|
||||
break;
|
||||
default:
|
||||
this.count = Math.min(count, data.getStackLimit());
|
||||
}
|
||||
|
||||
// Equip data
|
||||
if (getItemType() == ItemType.ITEM_WEAPON) {
|
||||
this.level = Math.max(this.count, 1);
|
||||
this.affixes = new ArrayList<>(2);
|
||||
if (getItemData().getSkillAffix() != null) {
|
||||
for (int skillAffix : getItemData().getSkillAffix()) {
|
||||
if (skillAffix > 0) {
|
||||
this.affixes.add(skillAffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (getItemType() == ItemType.ITEM_RELIQUARY) {
|
||||
this.level = 1;
|
||||
this.appendPropIdList = new ArrayList<>();
|
||||
// Create main property
|
||||
ReliquaryMainPropData mainPropData = GameDepot.getRandomRelicMainProp(getItemData().getMainPropDepotId());
|
||||
if (mainPropData != null) {
|
||||
this.mainPropId = mainPropData.getId();
|
||||
}
|
||||
// Create extra stats
|
||||
if (getItemData().getAppendPropNum() > 0) {
|
||||
for (int i = 0; i < getItemData().getAppendPropNum(); i++) {
|
||||
this.addAppendProp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectId getObjectId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
@ -128,162 +125,88 @@ public class GameItem {
|
||||
this.ownerId = player.getUid();
|
||||
this.guid = player.getNextGameGuid();
|
||||
}
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public void setItemId(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public long getGuid() {
|
||||
return guid;
|
||||
public ObjectId getObjectId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ItemType getItemType() {
|
||||
return this.itemData.getItemType();
|
||||
}
|
||||
|
||||
public ItemData getItemData() {
|
||||
return itemData;
|
||||
}
|
||||
|
||||
public void setItemData(ItemData materialData) {
|
||||
this.itemData = materialData;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public void setExp(int exp) {
|
||||
this.exp = exp;
|
||||
}
|
||||
|
||||
public int getTotalExp() {
|
||||
return totalExp;
|
||||
}
|
||||
|
||||
public void setTotalExp(int totalExp) {
|
||||
this.totalExp = totalExp;
|
||||
}
|
||||
|
||||
public int getPromoteLevel() {
|
||||
return promoteLevel;
|
||||
}
|
||||
|
||||
public void setPromoteLevel(int promoteLevel) {
|
||||
this.promoteLevel = promoteLevel;
|
||||
public static int getMinPromoteLevel(int level) {
|
||||
if (level > 80) {
|
||||
return 6;
|
||||
} else if (level > 70) {
|
||||
return 5;
|
||||
} else if (level > 60) {
|
||||
return 4;
|
||||
} else if (level > 50) {
|
||||
return 3;
|
||||
} else if (level > 40) {
|
||||
return 2;
|
||||
} else if (level > 20) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getEquipSlot() {
|
||||
return this.getItemData().getEquipType().getValue();
|
||||
}
|
||||
|
||||
public int getEquipCharacter() {
|
||||
return equipCharacter;
|
||||
}
|
||||
|
||||
public void setEquipCharacter(int equipCharacter) {
|
||||
this.equipCharacter = equipCharacter;
|
||||
}
|
||||
|
||||
public boolean isEquipped() {
|
||||
return this.getEquipCharacter() > 0;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public void setLocked(boolean locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
public boolean isDestroyable() {
|
||||
return !this.isLocked() && !this.isEquipped();
|
||||
}
|
||||
|
||||
public int getWeaponEntityId() {
|
||||
return weaponEntityId;
|
||||
}
|
||||
|
||||
public void setWeaponEntityId(int weaponEntityId) {
|
||||
this.weaponEntityId = weaponEntityId;
|
||||
}
|
||||
|
||||
public List<Integer> getAffixes() {
|
||||
return affixes;
|
||||
}
|
||||
|
||||
public int getRefinement() {
|
||||
return refinement;
|
||||
}
|
||||
|
||||
public void setRefinement(int refinement) {
|
||||
this.refinement = refinement;
|
||||
}
|
||||
|
||||
public int getMainPropId() {
|
||||
return mainPropId;
|
||||
}
|
||||
|
||||
public void setMainPropId(int mainPropId) {
|
||||
this.mainPropId = mainPropId;
|
||||
}
|
||||
|
||||
public List<Integer> getAppendPropIdList() {
|
||||
return appendPropIdList;
|
||||
}
|
||||
|
||||
public void addAppendProp() {
|
||||
if (this.getAppendPropIdList() == null) {
|
||||
if (this.appendPropIdList == null) {
|
||||
this.appendPropIdList = new ArrayList<>();
|
||||
}
|
||||
|
||||
if (this.getAppendPropIdList().size() < 4) {
|
||||
addNewAppendProp();
|
||||
if (this.appendPropIdList.size() < 4) {
|
||||
this.addNewAppendProp();
|
||||
} else {
|
||||
upgradeRandomAppendProp();
|
||||
this.upgradeRandomAppendProp();
|
||||
}
|
||||
}
|
||||
|
||||
public void addAppendProps(int quantity) {
|
||||
int num = Math.max(quantity, 0);
|
||||
for (int i = 0; i < num; i++) {
|
||||
this.addAppendProp();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<FightProperty> getAppendFightProperties() {
|
||||
Set<FightProperty> props = new HashSet<>();
|
||||
// Previously this would check no more than the first four affixes, however custom artifacts may not respect this order.
|
||||
for (int appendPropId : this.appendPropIdList) {
|
||||
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get(appendPropId);
|
||||
if (affixData != null) {
|
||||
props.add(affixData.getFightProp());
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private void addNewAppendProp() {
|
||||
List<ReliquaryAffixData> affixList = GameDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId());
|
||||
List<ReliquaryAffixData> affixList = GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
|
||||
|
||||
if (affixList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build blacklist - Dont add same stat as main/sub stat
|
||||
Set<FightProperty> blacklist = new HashSet<>();
|
||||
ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(this.getMainPropId());
|
||||
Set<FightProperty> blacklist = this.getAppendFightProperties();
|
||||
ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(this.mainPropId);
|
||||
if (mainPropData != null) {
|
||||
blacklist.add(mainPropData.getFightProp());
|
||||
}
|
||||
int len = Math.min(4, this.getAppendPropIdList().size());
|
||||
for (int i = 0; i < len; i++) {
|
||||
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get((int) this.getAppendPropIdList().get(i));
|
||||
if (affixData != null) {
|
||||
blacklist.add(affixData.getFightProp());
|
||||
}
|
||||
}
|
||||
|
||||
// Build random list
|
||||
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
||||
@ -299,25 +222,18 @@ public class GameItem {
|
||||
|
||||
// Add random stat
|
||||
ReliquaryAffixData affixData = randomList.next();
|
||||
this.getAppendPropIdList().add(affixData.getId());
|
||||
this.appendPropIdList.add(affixData.getId());
|
||||
}
|
||||
|
||||
private void upgradeRandomAppendProp() {
|
||||
List<ReliquaryAffixData> affixList = GameDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId());
|
||||
List<ReliquaryAffixData> affixList = GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
|
||||
|
||||
if (affixList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build whitelist
|
||||
Set<FightProperty> whitelist = new HashSet<>();
|
||||
int len = Math.min(4, this.getAppendPropIdList().size());
|
||||
for (int i = 0; i < len; i++) {
|
||||
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get((int) this.getAppendPropIdList().get(i));
|
||||
if (affixData != null) {
|
||||
whitelist.add(affixData.getFightProp());
|
||||
}
|
||||
}
|
||||
Set<FightProperty> whitelist = this.getAppendFightProperties();
|
||||
|
||||
// Build random list
|
||||
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
||||
@ -329,7 +245,7 @@ public class GameItem {
|
||||
|
||||
// Add random stat
|
||||
ReliquaryAffixData affixData = randomList.next();
|
||||
this.getAppendPropIdList().add(affixData.getId());
|
||||
this.appendPropIdList.add(affixData.getId());
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
|
@ -147,7 +147,7 @@ public class InventoryManager {
|
||||
int totalExp = relic.getTotalExp();
|
||||
int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level);
|
||||
int upgrades = 0;
|
||||
List<Integer> oldAppendPropIdList = relic.getAppendPropIdList();
|
||||
List<Integer> oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
|
||||
|
||||
while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) {
|
||||
// Do calculations
|
||||
@ -169,13 +169,7 @@ public class InventoryManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (upgrades > 0) {
|
||||
oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
|
||||
while (upgrades > 0) {
|
||||
relic.addAppendProp();
|
||||
upgrades -= 1;
|
||||
}
|
||||
}
|
||||
relic.addAppendProps(upgrades);
|
||||
|
||||
// Save
|
||||
relic.setLevel(level);
|
||||
|
@ -1,7 +1,13 @@
|
||||
package emu.grasscutter.game.props;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@ -133,4 +139,65 @@ public enum FightProperty {
|
||||
public static FightProperty getPropByName(String name) {
|
||||
return stringMap.getOrDefault(name, FIGHT_PROP_NONE);
|
||||
}
|
||||
|
||||
public static FightProperty getPropByShortName(String name) {
|
||||
return shortNameMap.getOrDefault(name, FIGHT_PROP_NONE);
|
||||
}
|
||||
|
||||
public static Set<String> getShortNames() {
|
||||
return shortNameMap.keySet();
|
||||
}
|
||||
|
||||
// This was originally for relic properties so some names might not be applicable for e.g. setstats
|
||||
private static final Map<String, FightProperty> shortNameMap = Map.ofEntries(
|
||||
// Normal relic stats
|
||||
entry("hp", FIGHT_PROP_HP),
|
||||
entry("atk", FIGHT_PROP_ATTACK),
|
||||
entry("def", FIGHT_PROP_DEFENSE),
|
||||
entry("hp%", FIGHT_PROP_HP_PERCENT),
|
||||
entry("atk%", FIGHT_PROP_ATTACK_PERCENT),
|
||||
entry("def%", FIGHT_PROP_DEFENSE_PERCENT),
|
||||
entry("em", FIGHT_PROP_ELEMENT_MASTERY),
|
||||
entry("er", FIGHT_PROP_CHARGE_EFFICIENCY),
|
||||
entry("hb", FIGHT_PROP_HEAL_ADD),
|
||||
entry("heal", FIGHT_PROP_HEAL_ADD),
|
||||
entry("cd", FIGHT_PROP_CRITICAL_HURT),
|
||||
entry("cdmg", FIGHT_PROP_CRITICAL_HURT),
|
||||
entry("cr", FIGHT_PROP_CRITICAL),
|
||||
entry("crate", FIGHT_PROP_CRITICAL),
|
||||
entry("phys%", FIGHT_PROP_PHYSICAL_ADD_HURT),
|
||||
entry("dendro%", FIGHT_PROP_GRASS_ADD_HURT),
|
||||
entry("geo%", FIGHT_PROP_ROCK_ADD_HURT),
|
||||
entry("anemo%", FIGHT_PROP_WIND_ADD_HURT),
|
||||
entry("hydro%", FIGHT_PROP_WATER_ADD_HURT),
|
||||
entry("cryo%", FIGHT_PROP_ICE_ADD_HURT),
|
||||
entry("electro%", FIGHT_PROP_ELEC_ADD_HURT),
|
||||
entry("pyro%", FIGHT_PROP_FIRE_ADD_HURT),
|
||||
// Other stats
|
||||
entry("maxhp", FIGHT_PROP_MAX_HP),
|
||||
entry("dmg", FIGHT_PROP_ADD_HURT), // This seems to get reset after attacks
|
||||
entry("cdr", FIGHT_PROP_SKILL_CD_MINUS_RATIO),
|
||||
entry("heali", FIGHT_PROP_HEALED_ADD),
|
||||
entry("shield", FIGHT_PROP_SHIELD_COST_MINUS_RATIO),
|
||||
entry("defi", FIGHT_PROP_DEFENCE_IGNORE_RATIO),
|
||||
entry("resall", FIGHT_PROP_SUB_HURT), // This seems to get reset after attacks
|
||||
entry("resanemo", FIGHT_PROP_WIND_SUB_HURT),
|
||||
entry("rescryo", FIGHT_PROP_ICE_SUB_HURT),
|
||||
entry("resdendro", FIGHT_PROP_GRASS_SUB_HURT),
|
||||
entry("reselectro", FIGHT_PROP_ELEC_SUB_HURT),
|
||||
entry("resgeo", FIGHT_PROP_ROCK_SUB_HURT),
|
||||
entry("reshydro", FIGHT_PROP_WATER_SUB_HURT),
|
||||
entry("respyro", FIGHT_PROP_FIRE_SUB_HURT),
|
||||
entry("resphys", FIGHT_PROP_PHYSICAL_SUB_HURT)
|
||||
);
|
||||
|
||||
private static final List<FightProperty> flatProps = Arrays.asList(
|
||||
FIGHT_PROP_BASE_HP, FIGHT_PROP_HP, FIGHT_PROP_BASE_ATTACK, FIGHT_PROP_ATTACK, FIGHT_PROP_BASE_DEFENSE,
|
||||
FIGHT_PROP_DEFENSE, FIGHT_PROP_HEALED_ADD, FIGHT_PROP_CUR_FIRE_ENERGY, FIGHT_PROP_CUR_ELEC_ENERGY,
|
||||
FIGHT_PROP_CUR_WATER_ENERGY, FIGHT_PROP_CUR_GRASS_ENERGY, FIGHT_PROP_CUR_WIND_ENERGY, FIGHT_PROP_CUR_ICE_ENERGY,
|
||||
FIGHT_PROP_CUR_ROCK_ENERGY, FIGHT_PROP_CUR_HP, FIGHT_PROP_MAX_HP, FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_CUR_DEFENSE);
|
||||
|
||||
public static boolean isPercentage(FightProperty prop) {
|
||||
return !flatProps.contains(prop);
|
||||
}
|
||||
}
|
||||
|
@ -30,21 +30,7 @@ public class PacketGetMailItemRsp extends BasePacket {
|
||||
if (!message.isAttachmentGot) {//No duplicated item
|
||||
for (Mail.MailItem mailItem : message.itemList) {
|
||||
EquipParamOuterClass.EquipParam.Builder item = EquipParamOuterClass.EquipParam.newBuilder();
|
||||
int promoteLevel = 0;
|
||||
|
||||
if (mailItem.itemLevel > 80) { // 80/90
|
||||
promoteLevel = 6;
|
||||
} else if (mailItem.itemLevel > 70) { // 70/80
|
||||
promoteLevel = 5;
|
||||
} else if (mailItem.itemLevel > 60) { // 60/70
|
||||
promoteLevel = 4;
|
||||
} else if (mailItem.itemLevel > 50) { // 50/60
|
||||
promoteLevel = 3;
|
||||
} else if (mailItem.itemLevel > 40) { // 40/50
|
||||
promoteLevel = 2;
|
||||
} else if (mailItem.itemLevel > 20) { // 20/40
|
||||
promoteLevel = 1;
|
||||
}
|
||||
int promoteLevel = GameItem.getMinPromoteLevel(mailItem.itemLevel);
|
||||
|
||||
item.setItemId(mailItem.itemId);
|
||||
item.setItemNum(mailItem.itemCount);
|
||||
|
60
src/main/java/emu/grasscutter/utils/SparseSet.java
Normal file
60
src/main/java/emu/grasscutter/utils/SparseSet.java
Normal file
@ -0,0 +1,60 @@
|
||||
package emu.grasscutter.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public final class SparseSet {
|
||||
/*
|
||||
* A convenience class for constructing integer sets out of large ranges
|
||||
* Designed to be fed literal strings from this project only -
|
||||
* can and will throw exceptions to tell you to fix your code if you feed it garbage. :)
|
||||
*/
|
||||
private static class Range {
|
||||
private final int min, max;
|
||||
|
||||
public Range(int min, int max) {
|
||||
if (min > max) {
|
||||
throw new IllegalArgumentException("Range passed minimum higher than maximum - " + Integer.toString(min) + " > " + Integer.toString(max));
|
||||
}
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public boolean check(int value) {
|
||||
return value >= this.min && value <= this.max;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Range> rangeEntries;
|
||||
private final Set<Integer> denseEntries;
|
||||
|
||||
public SparseSet(String csv) {
|
||||
this.rangeEntries = new ArrayList<>();
|
||||
this.denseEntries = new TreeSet<>();
|
||||
|
||||
for (String token : csv.replace("\n", "").replace(" ", "").split(",")) {
|
||||
String[] tokens = token.split("-");
|
||||
switch (tokens.length) {
|
||||
case 1:
|
||||
this.denseEntries.add(Integer.parseInt(tokens[0]));
|
||||
break;
|
||||
case 2:
|
||||
this.rangeEntries.add(new Range(Integer.parseInt(tokens[0]), Integer.parseInt(tokens[1])));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid token passed to SparseSet initializer - " + token + " (split length " + Integer.toString(tokens.length) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(int i) {
|
||||
for (Range range : this.rangeEntries) {
|
||||
if (range.check(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.denseEntries.contains(i);
|
||||
}
|
||||
}
|
@ -132,35 +132,16 @@
|
||||
"in_dungeon_error": "You are already in that dungeon.",
|
||||
"description": "Enter a dungeon"
|
||||
},
|
||||
"giveAll": {
|
||||
"usage": "Usage: giveall [player] [amount]",
|
||||
"started": "Receiving all items...",
|
||||
"success": "Successfully gave all items to %s.",
|
||||
"invalid_amount_or_playerId": "Invalid amount or player ID.",
|
||||
"description": "Gives all items"
|
||||
},
|
||||
"giveArtifact": {
|
||||
"usage": "Usage: giveart|gart [player] <artifactID> <mainPropID> [<appendPropID>[,<times>]]... [level]",
|
||||
"id_error": "Invalid artifact ID.",
|
||||
"success": "Given %s to %s.",
|
||||
"description": "Gives the player a specified artifact"
|
||||
},
|
||||
"giveChar": {
|
||||
"usage": "Usage: givechar <player> <avatarID> [level]",
|
||||
"given": "Given %s with level %s to %s.",
|
||||
"invalid_avatar_id": "Invalid avatar ID.",
|
||||
"invalid_avatar_level": "Invalid avatar level.",
|
||||
"invalid_avatar_or_player_id": "Invalid avatar or player ID.",
|
||||
"description": "Gives the player a specified character"
|
||||
},
|
||||
"give": {
|
||||
"usage": "Usage: give <player> <itemID|itemName> [amount] [level] [refinement]",
|
||||
"refinement_only_applicable_weapons": "Refinement is only applicable to weapons.",
|
||||
"refinement_must_between_1_and_5": "Refinement must be between 1 and 5.",
|
||||
"usage": "Usage: give <itemID|avatarID|\"all\"|\"weapons\"|\"mats\"|\"avatars\"> [x<amount>] [lv<level>] [r<refinement>]",
|
||||
"usage_relic": "Usage: give <artifactID> [mainPropID] [<appendPropID>[,<times>]]... [lv<level 0-20>]",
|
||||
"illegal_relic": "This artifactID belongs to a blacklisted range, it may not be the one you wanted.",
|
||||
"given": "Given %s of %s to %s.",
|
||||
"given_with_level_and_refinement": "Given %s with level %s, refinement %s %s times to %s.",
|
||||
"given_level": "Given %s with level %s %s times to %s.",
|
||||
"description": "Gives an item to you or the specified player"
|
||||
"given_avatar": "Given %s with level %s to %s.",
|
||||
"giveall_success": "Successfully gave all items.",
|
||||
"description": "Gives an item to you or the specified player. Can also give all weapons, avatars and/or materials, and can construct custom artifacts."
|
||||
},
|
||||
"godmode": {
|
||||
"success": "Godmode is now %s for %s.",
|
||||
@ -202,10 +183,6 @@
|
||||
"success": "There are %s player(s) online:",
|
||||
"description": "List online players"
|
||||
},
|
||||
"nostamina": {
|
||||
"success": "NoStamina is now %s for %s.",
|
||||
"description": "Keep your endurance to the maximum."
|
||||
},
|
||||
"permission": {
|
||||
"usage": "Usage: permission <add|remove> <username> <permission>",
|
||||
"add": "Permission added.",
|
||||
|
@ -146,21 +146,14 @@
|
||||
"success": "%s a été donné à %s.",
|
||||
"description": "Donne au joueur l'artéfact spécifié."
|
||||
},
|
||||
"giveChar": {
|
||||
"usage": "Usage: givechar <joueur> <avatarID> [niveau]",
|
||||
"given": "%s avec le niveau %s a été donné à %s.",
|
||||
"invalid_avatar_id": "ID de l'avatar invalide.",
|
||||
"invalid_avatar_level": "Niveau de l'avatar invalide.",
|
||||
"invalid_avatar_or_player_id": "ID de l'avatar ou du joueur invalide.",
|
||||
"description": "Donne au joueur le personnage spécifié"
|
||||
},
|
||||
"give": {
|
||||
"usage": "Usage: give <joueur> <itemID|itemName> [quantité] [niveau] [raffinement]",
|
||||
"usage": "Usage: give <joueur> <itemID|avatarID> [quantité] [niveau] [raffinement]",
|
||||
"refinement_only_applicable_weapons": "Le raffinement est uniquement applicable aux armes.",
|
||||
"refinement_must_between_1_and_5": "Le raffinement doit être compris entre 1 et 5.",
|
||||
"given": "Given %s of %s to %s.",
|
||||
"given_with_level_and_refinement": "%s avec le niveau %s, raffinement %s %s fois à %s.",
|
||||
"given_level": "%s avec le niveau %s %s fois à %s.",
|
||||
"given_avatar": "%s avec le niveau %s a été donné à %s.",
|
||||
"description": "Donne un objet au joueur spécifié"
|
||||
},
|
||||
"godmode": {
|
||||
|
@ -134,20 +134,14 @@
|
||||
"id_error": "Błędne ID artefaktu.",
|
||||
"success": "Dano %s dla %s."
|
||||
},
|
||||
"giveChar": {
|
||||
"usage": "Użycie: givechar <gracz> <avatarId> [ilość]",
|
||||
"given": "Dano %s z poziomem %s dla %s.",
|
||||
"invalid_avatar_id": "Błędne ID postaci.",
|
||||
"invalid_avatar_level": "Błędny poziom postaci.",
|
||||
"invalid_avatar_or_player_id": "Błędne ID postaci lub gracza."
|
||||
},
|
||||
"give": {
|
||||
"usage": "Użycie: give <gracz> <id przedmiotu | nazwa przedmiotu> [ilość] [poziom] [refinement]",
|
||||
"usage": "Użycie: give <gracz> <id przedmiotu | avatarID> [ilość] [poziom] [refinement]",
|
||||
"refinement_only_applicable_weapons": "Ulepszenie można zastosować tylko dla broni.",
|
||||
"refinement_must_between_1_and_5": "Ulepszenie musi być pomiędzy 1, a 5.",
|
||||
"given": "Dano %s %s dla %s.",
|
||||
"given_with_level_and_refinement": "Dano %s z poziomem %s, ulepszeniem %s %s razy dla %s",
|
||||
"given_level": "Dano %s z poziomem %s %s razy dla %s"
|
||||
"given_level": "Dano %s z poziomem %s %s razy dla %s",
|
||||
"given_avatar": "Dano %s z poziomem %s dla %s."
|
||||
},
|
||||
"godmode": {
|
||||
"success": "Godmode jest teraz %s dla %s."
|
||||
|
@ -145,21 +145,14 @@
|
||||
"success": "已将 %s 给予 %s。",
|
||||
"description": "给予指定圣遗物"
|
||||
},
|
||||
"giveChar": {
|
||||
"usage": "用法:givechar <玩家> <角色ID> [等级]",
|
||||
"given": "已将角色 %s [等级 %s] 给与 %s。",
|
||||
"invalid_avatar_id": "无效的角色ID。",
|
||||
"invalid_avatar_level": "无效的角色等级。",
|
||||
"invalid_avatar_or_player_id": "无效的角色ID/玩家ID。",
|
||||
"description": "给予指定角色"
|
||||
},
|
||||
"give": {
|
||||
"usage": "用法:give <玩家> <物品ID|物品名> [数量] [等级] [精炼等级]",
|
||||
"usage": "用法:give <玩家> <物品ID|角色ID> [数量] [等级] [精炼等级]",
|
||||
"refinement_only_applicable_weapons": "只有武器可以设置精炼等级。",
|
||||
"refinement_must_between_1_and_5": "精炼等级必须在 1-5 之间。",
|
||||
"given": "已将 %s 个 %s 给予 %s。",
|
||||
"given_with_level_and_refinement": "已将 %s [等级 %s, 精炼 %s] %s 个给予 %s。",
|
||||
"given_level": "已将 %s [等级 %s] %s 个给予 %s。",
|
||||
"given_avatar": "已将角色 %s [等级 %s] 给与 %s。",
|
||||
"description": "给予指定物品"
|
||||
},
|
||||
"godmode": {
|
||||
|
@ -144,21 +144,14 @@
|
||||
"success": "已把 %s 給予 %s。",
|
||||
"description": "給予指定聖遺物。"
|
||||
},
|
||||
"giveChar": {
|
||||
"usage": "用法:givechar <player> <avatarId> [level]",
|
||||
"given": "已將 %s 等級 %s 給予 %s。",
|
||||
"invalid_avatar_id": "無效的角色ID。",
|
||||
"invalid_avatar_level": "無效的角色等級。.",
|
||||
"invalid_avatar_or_player_id": "無效的角色ID/玩家ID。",
|
||||
"description": "給予指定角色。"
|
||||
},
|
||||
"give": {
|
||||
"usage": "用法:give <player> <itemId|itemName> [amount] [level] [refinement]",
|
||||
"usage": "用法:give <player> <itemId|avatarId> [amount] [level] [refinement]",
|
||||
"refinement_only_applicable_weapons": "精煉度只能施加在武器上面。",
|
||||
"refinement_must_between_1_and_5": "精煉度必需在 1 到 5 之間。",
|
||||
"given": "已經將 %s 個 %s 給予 %s。",
|
||||
"given_with_level_and_refinement": "已將 %s [等級%s, 精煉%s] %s個給予 %s",
|
||||
"given_level": "已將 %s 等級 %s %s 個給予 %s",
|
||||
"given_avatar": "已將 %s 等級 %s 給予 %s。",
|
||||
"description": "給予指定物品。"
|
||||
},
|
||||
"godmode": {
|
||||
|
Loading…
Reference in New Issue
Block a user