mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-02-04 04:12:54 +08:00
Add lock function to SetStatsCommand
This commit is contained in:
parent
0fa3d0e8d1
commit
b80126fdda
@ -6,14 +6,23 @@ import java.util.Map;
|
|||||||
|
|
||||||
import emu.grasscutter.command.Command;
|
import emu.grasscutter.command.Command;
|
||||||
import emu.grasscutter.command.CommandHandler;
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.entity.EntityAvatar;
|
import emu.grasscutter.game.entity.EntityAvatar;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
|
|
||||||
@Command(label = "setStats", aliases = {"stats", "stat"}, usage = {"<stat> <value>"}, permission = "player.setstats", permissionTargeted = "player.setstats.others")
|
@Command(
|
||||||
|
label = "setStats",
|
||||||
|
aliases = {"stats", "stat"},
|
||||||
|
usage = {
|
||||||
|
"[set] <stat> <value>",
|
||||||
|
"(lock|freeze) <stat> [<value>]", // Can lock to current value
|
||||||
|
"(unlock|unfreeze) <stat>"},
|
||||||
|
permission = "player.setstats",
|
||||||
|
permissionTargeted = "player.setstats.others")
|
||||||
public final class SetStatsCommand implements CommandHandler {
|
public final class SetStatsCommand implements CommandHandler {
|
||||||
static class Stat {
|
private static class Stat {
|
||||||
String name;
|
String name;
|
||||||
FightProperty prop;
|
FightProperty prop;
|
||||||
|
|
||||||
@ -27,9 +36,21 @@ public final class SetStatsCommand implements CommandHandler {
|
|||||||
this.prop = prop;
|
this.prop = prop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Stat> stats;
|
private static enum Action {
|
||||||
|
ACTION_SET("commands.generic.set_to", "commands.generic.set_for_to"),
|
||||||
|
ACTION_LOCK("commands.setStats.locked_to", "commands.setStats.locked_for_to"),
|
||||||
|
ACTION_UNLOCK("commands.setStats.unlocked", "commands.setStats.unlocked_for");
|
||||||
|
public final String messageKeySelf;
|
||||||
|
public final String messageKeyOther;
|
||||||
|
private Action(String messageKeySelf, String messageKeyOther) {
|
||||||
|
this.messageKeySelf = messageKeySelf;
|
||||||
|
this.messageKeyOther = messageKeyOther;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Stat> stats;
|
||||||
|
|
||||||
public SetStatsCommand() {
|
public SetStatsCommand() {
|
||||||
this.stats = new HashMap<>();
|
this.stats = new HashMap<>();
|
||||||
for (String key : FightProperty.getShortNames()) {
|
for (String key : FightProperty.getShortNames()) {
|
||||||
@ -62,50 +83,97 @@ public final class SetStatsCommand implements CommandHandler {
|
|||||||
this.stats.put("ephys", this.stats.get("phys%"));
|
this.stats.put("ephys", this.stats.get("phys%"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static float parsePercent(String input) throws NumberFormatException {
|
||||||
|
if (input.endsWith("%")) {
|
||||||
|
return Float.parseFloat(input.substring(0, input.length()-1))/100f;
|
||||||
|
} else {
|
||||||
|
return Float.parseFloat(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||||
String statStr;
|
String statStr = null;
|
||||||
String valueStr;
|
String valueStr;
|
||||||
|
float value = 0f;
|
||||||
|
|
||||||
if (args.size() == 2) {
|
if (args.size() < 2) {
|
||||||
statStr = args.get(0).toLowerCase();
|
|
||||||
valueStr = args.get(1);
|
|
||||||
} else {
|
|
||||||
sendUsageMessage(sender);
|
sendUsageMessage(sender);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the action and stat
|
||||||
|
String arg0 = args.remove(0).toLowerCase();
|
||||||
|
Action action = switch (arg0) {
|
||||||
|
default -> {statStr = arg0; yield Action.ACTION_SET;} // Implicit set command
|
||||||
|
case "set" -> Action.ACTION_SET; // Explicit set command
|
||||||
|
case "lock", "freeze" -> Action.ACTION_LOCK;
|
||||||
|
case "unlock", "unfreeze" -> Action.ACTION_UNLOCK;
|
||||||
|
};
|
||||||
|
if (statStr == null) {
|
||||||
|
statStr = args.remove(0).toLowerCase();
|
||||||
|
}
|
||||||
|
if (!stats.containsKey(statStr)) {
|
||||||
|
sendUsageMessage(sender); // Invalid stat or action
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Stat stat = stats.get(statStr);
|
||||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||||
|
Avatar avatar = entity.getAvatar();
|
||||||
|
|
||||||
float value;
|
// Get the value if the action requires it
|
||||||
try {
|
try {
|
||||||
if (valueStr.endsWith("%")) {
|
switch (action) {
|
||||||
value = Float.parseFloat(valueStr.substring(0, valueStr.length()-1))/100f;
|
case ACTION_LOCK:
|
||||||
} else {
|
if (args.isEmpty()) { // Lock to current value
|
||||||
value = Float.parseFloat(valueStr);
|
value = avatar.getFightProperty(stat.prop);
|
||||||
|
break;
|
||||||
|
} // Else fall-through and lock to supplied value
|
||||||
|
case ACTION_SET:
|
||||||
|
value = parsePercent(args.remove(0));
|
||||||
|
break;
|
||||||
|
case ACTION_UNLOCK:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.statValue");
|
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.statValue");
|
||||||
return;
|
return;
|
||||||
|
} catch (IndexOutOfBoundsException ignored) {
|
||||||
|
sendUsageMessage(sender);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stats.containsKey(statStr)) {
|
if (!args.isEmpty()) { // Leftover arguments!
|
||||||
Stat stat = stats.get(statStr);
|
|
||||||
entity.setFightProperty(stat.prop, value);
|
|
||||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, stat.prop));
|
|
||||||
if (FightProperty.isPercentage(stat.prop)) {
|
|
||||||
valueStr = String.format("%.1f%%", value * 100f);
|
|
||||||
} else {
|
|
||||||
valueStr = String.format("%.0f", value);
|
|
||||||
}
|
|
||||||
if (targetPlayer == sender) {
|
|
||||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_to", stat.name, valueStr);
|
|
||||||
} else {
|
|
||||||
String uidStr = targetPlayer.getAccount().getId();
|
|
||||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", stat.name, uidStr, valueStr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sendUsageMessage(sender);
|
sendUsageMessage(sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case ACTION_SET:
|
||||||
|
entity.setFightProperty(stat.prop, value);
|
||||||
|
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, stat.prop));
|
||||||
|
break;
|
||||||
|
case ACTION_LOCK:
|
||||||
|
avatar.getFightPropOverrides().put(stat.prop.getId(), value);
|
||||||
|
avatar.recalcStats();
|
||||||
|
break;
|
||||||
|
case ACTION_UNLOCK:
|
||||||
|
avatar.getFightPropOverrides().remove(stat.prop.getId());
|
||||||
|
avatar.recalcStats();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report action
|
||||||
|
if (FightProperty.isPercentage(stat.prop)) {
|
||||||
|
valueStr = String.format("%.1f%%", value * 100f);
|
||||||
|
} else {
|
||||||
|
valueStr = String.format("%.0f", value);
|
||||||
|
}
|
||||||
|
if (targetPlayer == sender) {
|
||||||
|
CommandHandler.sendTranslatedMessage(sender, action.messageKeySelf, stat.name, valueStr);
|
||||||
|
} else {
|
||||||
|
String uidStr = targetPlayer.getAccount().getId();
|
||||||
|
CommandHandler.sendTranslatedMessage(sender, action.messageKeyOther, stat.name, uidStr, valueStr);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
@Entity(value = "avatars", useDiscriminator = false)
|
@Entity(value = "avatars", useDiscriminator = false)
|
||||||
public class Avatar {
|
public class Avatar {
|
||||||
@ -85,6 +86,7 @@ public class Avatar {
|
|||||||
|
|
||||||
@Transient private final Int2ObjectMap<GameItem> equips;
|
@Transient private final Int2ObjectMap<GameItem> equips;
|
||||||
@Transient private final Int2FloatOpenHashMap fightProp;
|
@Transient private final Int2FloatOpenHashMap fightProp;
|
||||||
|
@Transient @Getter private final Int2FloatOpenHashMap fightPropOverrides;
|
||||||
@Transient private Set<String> extraAbilityEmbryos;
|
@Transient private Set<String> extraAbilityEmbryos;
|
||||||
|
|
||||||
private List<Integer> fetters;
|
private List<Integer> fetters;
|
||||||
@ -111,6 +113,7 @@ public class Avatar {
|
|||||||
public Avatar() {
|
public Avatar() {
|
||||||
this.equips = new Int2ObjectOpenHashMap<>();
|
this.equips = new Int2ObjectOpenHashMap<>();
|
||||||
this.fightProp = new Int2FloatOpenHashMap();
|
this.fightProp = new Int2FloatOpenHashMap();
|
||||||
|
this.fightPropOverrides = new Int2FloatOpenHashMap();
|
||||||
this.extraAbilityEmbryos = new HashSet<>();
|
this.extraAbilityEmbryos = new HashSet<>();
|
||||||
this.proudSkillBonusMap = new HashMap<>();
|
this.proudSkillBonusMap = new HashMap<>();
|
||||||
this.fetters = new ArrayList<>(); // TODO Move to avatar
|
this.fetters = new ArrayList<>(); // TODO Move to avatar
|
||||||
@ -728,6 +731,9 @@ public class Avatar {
|
|||||||
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
|
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Reapply all overrides
|
||||||
|
this.fightProp.putAll(this.fightPropOverrides);
|
||||||
|
|
||||||
// Set current hp
|
// Set current hp
|
||||||
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
|
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
|
||||||
|
|
||||||
|
@ -274,7 +274,11 @@
|
|||||||
"description": "Sets accountwide properties. Things like godmode can be enabled this way, as well as changing things like unlocked abyss floor and battle pass progress.\n\tValues for <prop>: godmode | nostamina | unlimitedenergy | abyss | worldlevel | bplevel\n\t(cont.) see PlayerProperty enum for other possible values, of form PROP_MAX_SPRING_VOLUME -> max_spring_volume"
|
"description": "Sets accountwide properties. Things like godmode can be enabled this way, as well as changing things like unlocked abyss floor and battle pass progress.\n\tValues for <prop>: godmode | nostamina | unlimitedenergy | abyss | worldlevel | bplevel\n\t(cont.) see PlayerProperty enum for other possible values, of form PROP_MAX_SPRING_VOLUME -> max_spring_volume"
|
||||||
},
|
},
|
||||||
"setStats": {
|
"setStats": {
|
||||||
"description": "Sets fight property for your current active character\n\tValues for <stat>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Elemental DMG Bonus: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) Elemental RES: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys"
|
"description": "Sets fight property for your current active character\n\tValues for <stat>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Elemental DMG Bonus: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) Elemental RES: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys",
|
||||||
|
"locked_to": "%s locked to %s.",
|
||||||
|
"locked_for_to": "%s for %s locked to %s.",
|
||||||
|
"unlocked": "%s unlocked.",
|
||||||
|
"unlocked_for": "%s for %s unlocked."
|
||||||
},
|
},
|
||||||
"spawn": {
|
"spawn": {
|
||||||
"success": "Spawned %s of %s.",
|
"success": "Spawned %s of %s.",
|
||||||
|
Loading…
Reference in New Issue
Block a user