From 08f361954a1232656e37f92d4b579c3b4856c9f5 Mon Sep 17 00:00:00 2001 From: Alexander Hartmann Date: Fri, 16 Sep 2022 19:04:20 +0200 Subject: [PATCH] Extend spawn command (#1777) * add missing EntityTypes * small command refactorings and improvements * move common command patterns and methods to CommandHelpers * let the spawn command detect the entityType instead of spawning every entity as EntityVehicle * add extra options for spawning gadgets for better debuging and testing * More spawn command additions and cleanups+EntityVehicle changes * Moved remaining patterns from GiveCommand and ClearCommand to CommandHelpers * Added patterns for hp, maxhp, atk, def and (monster)ai for the spawn command * Moved intParam parsing via regex to the CommandHelpers * Read most of EntityVehicle stats from the ConfigGadget instead of hardcoding them Co-authored-by: hartie95 --- .../grasscutter/command/CommandHelpers.java | 52 +++++ .../command/commands/ClearCommand.java | 53 ++---- .../command/commands/GiveCommand.java | 66 ++----- .../command/commands/SpawnCommand.java | 177 +++++++++++++----- .../game/entity/EntityBaseGadget.java | 28 +++ .../grasscutter/game/entity/EntityGadget.java | 18 +- .../game/entity/EntityMonster.java | 7 + .../game/entity/EntityVehicle.java | 43 +++-- .../grasscutter/game/props/EntityType.java | 23 ++- 9 files changed, 298 insertions(+), 169 deletions(-) create mode 100644 src/main/java/emu/grasscutter/command/CommandHelpers.java diff --git a/src/main/java/emu/grasscutter/command/CommandHelpers.java b/src/main/java/emu/grasscutter/command/CommandHelpers.java new file mode 100644 index 000000000..ac52feef2 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/CommandHelpers.java @@ -0,0 +1,52 @@ +package emu.grasscutter.command; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CommandHelpers { + public static final Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java doesn't have raw string literals :( + public static final Pattern amountRegex = Pattern.compile("((?<=x)\\d+|\\d+(?=x)(?!x\\d))"); + public static final Pattern refineRegex = Pattern.compile("r(\\d+)"); + public static final Pattern rankRegex = Pattern.compile("(\\d+)\\*"); + public static final Pattern constellationRegex = Pattern.compile("c(\\d+)"); + public static final Pattern stateRegex = Pattern.compile("state(\\d+)"); + public static final Pattern blockRegex = Pattern.compile("blk(\\d+)"); + public static final Pattern groupRegex = Pattern.compile("grp(\\d+)"); + public static final Pattern configRegex = Pattern.compile("cfg(\\d+)"); + public static final Pattern hpRegex = Pattern.compile("hp(\\d+)"); + public static final Pattern maxHPRegex = Pattern.compile("maxhp(\\d+)"); + public static final Pattern atkRegex = Pattern.compile("atk(\\d+)"); + public static final Pattern defRegex = Pattern.compile("def(\\d+)"); + public static final Pattern aiRegex = Pattern.compile("ai(\\d+)"); + + public 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) + } + return -1; + } + + public static List parseIntParameters(List args, @Nonnull T params, Map> map) { + for (int i = args.size() - 1; i >= 0; i--) { + String arg = args.get(i).toLowerCase(); + boolean deleteArg = false; + int argNum; + for (var entry : map.entrySet()) { + if ((argNum = matchIntOrNeg(entry.getKey(), arg)) != -1) { + entry.getValue().accept(params, argNum); + deleteArg = true; + break; + } + } + if (deleteArg) { + args.remove(i); + } + } + return args; + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java index 9792ae54c..0e28dade7 100644 --- a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java @@ -6,35 +6,34 @@ import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; +import lombok.Setter; import java.util.List; -import java.util.regex.Matcher; +import java.util.Map; +import java.util.function.BiConsumer; import java.util.regex.Pattern; import java.util.stream.Stream; +import static emu.grasscutter.command.CommandHelpers.*; + @Command( label = "clear", usage = {"(all|wp|art|mat) [lv] [r] [*]"}, permission = "player.clearinv", permissionTargeted = "player.clearinv.others") public final class ClearCommand implements CommandHandler { - 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 rankRegex = Pattern.compile("(\\d+)\\*"); - 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) - } - return -1; - } + private static final Map> intCommandHandlers = Map.ofEntries( + Map.entry(lvlRegex, ClearItemParameters::setLvl), + Map.entry(refineRegex, ClearItemParameters::setRefinement), + Map.entry(rankRegex, ClearItemParameters::setRank) + ); private static class ClearItemParameters { - public int lvl = 1; - public int refinement = 1; - public int rank = 4; - }; + @Setter public int lvl = 1; + @Setter public int refinement = 1; + @Setter public int rank = 4; + } private Stream getOther(ItemType type, Inventory playerInventory, ClearItemParameters param) { return playerInventory.getItems().values().stream() @@ -59,28 +58,8 @@ public final class ClearCommand implements CommandHandler { Inventory playerInventory = targetPlayer.getInventory(); ClearItemParameters param = new ClearItemParameters(); - // 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. "lv90r5*" - if ((argNum = matchIntOrNeg(lvlRegex, arg)) != -1) { - param.lvl = argNum; - deleteArg = true; - } - if ((argNum = matchIntOrNeg(refineRegex, arg)) != -1) { - param.refinement = argNum; - deleteArg = true; - } - if ((argNum = matchIntOrNeg(rankRegex, arg)) != -1) { - param.rank = argNum; - deleteArg = true; - } - if (deleteArg) { - args.remove(i); - } - } + // Extract any tagged int arguments (e.g. "lv90", "x100", "r5") + parseIntParameters(args, param, intCommandHandlers); if (args.size() < 1) { sendUsageMessage(sender); diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index f1d4c5eea..c619e9061 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -1,6 +1,5 @@ package emu.grasscutter.command.commands; -import emu.grasscutter.GameConstants; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; @@ -16,13 +15,16 @@ import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.utils.SparseSet; +import lombok.Setter; import java.util.ArrayList; import java.util.List; - -import java.util.regex.Matcher; +import java.util.Map; +import java.util.function.BiConsumer; import java.util.regex.Pattern; +import static emu.grasscutter.command.CommandHelpers.*; + @Command( label = "give", aliases = {"g", "item", "giveitem"}, @@ -33,20 +35,7 @@ import java.util.regex.Pattern; permissionTargeted = "player.give.others", threading = true) public final class GiveCommand implements CommandHandler { - 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 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) - } - return -1; - } - - private static enum GiveAllType { + private enum GiveAllType { NONE, ALL, WEAPONS, @@ -54,48 +43,31 @@ public final class GiveCommand implements CommandHandler { AVATARS } + private static final Map> intCommandHandlers = Map.ofEntries( + Map.entry(lvlRegex, GiveItemParameters::setLvl), + Map.entry(refineRegex, GiveItemParameters::setRefinement), + Map.entry(amountRegex, GiveItemParameters::setAmount), + Map.entry(constellationRegex, GiveItemParameters::setConstellation) + ); + private static class GiveItemParameters { public int id; - public int lvl = 0; - public int amount = 1; - public int refinement = 1; - public int constellation = -1; + @Setter public int lvl = 0; + @Setter public int amount = 1; + @Setter public int refinement = 1; + @Setter public int constellation = -1; public int mainPropId = -1; public List appendPropIdList; public ItemData data; public AvatarData avatarData; public GiveAllType giveAllType = GiveAllType.NONE; - }; + } private GiveItemParameters parseArgs(Player sender, List 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) { - param.lvl = argNum; - deleteArg = true; - } - if ((argNum = matchIntOrNeg(refineRegex, arg)) != -1) { - param.refinement = argNum; - deleteArg = true; - } - if ((argNum = matchIntOrNeg(constellationRegex, arg)) != -1) { - param.constellation = argNum; - deleteArg = true; - } - if ((argNum = matchIntOrNeg(amountRegex, arg)) != -1) { - param.amount = argNum; - deleteArg = true; - } - if (deleteArg) { - args.remove(i); - } - } + parseIntParameters(args, param, intCommandHandlers); // At this point, first remaining argument MUST be itemId/avatarId if (args.size() < 1) { diff --git a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java index 4828b86ca..574d15f68 100644 --- a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java @@ -8,53 +8,70 @@ import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.MonsterData; import emu.grasscutter.game.entity.*; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.utils.Position; import emu.grasscutter.game.world.Scene; +import emu.grasscutter.utils.Position; +import lombok.Setter; import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.regex.Pattern; -import static emu.grasscutter.config.Configuration.*; +import static emu.grasscutter.command.CommandHelpers.*; +import static emu.grasscutter.config.Configuration.GAME_OPTIONS; import static emu.grasscutter.utils.Language.translate; @Command( label = "spawn", - usage = {"spawn [amount] [level(monster only)] [ (monster only)]"}, aliases = {"drop"}, + usage = { + " [x] [blk] [grp] [cfg] ", + " [x] [state] [maxhp] [hp(0 for infinite)] [atk] [def] [blk] [grp] [cfg] ", + " [x] [lv] [ai] [maxhp] [hp(0 for infinite)] [atk] [def] [blk] [grp] [cfg] "}, permission = "server.spawn", permissionTargeted = "server.spawn.others") public final class SpawnCommand implements CommandHandler { + private static final Map> intCommandHandlers = Map.ofEntries( + Map.entry(lvlRegex, SpawnParameters::setLvl), + Map.entry(amountRegex, SpawnParameters::setAmount), + Map.entry(stateRegex, SpawnParameters::setState), + Map.entry(blockRegex, SpawnParameters::setBlockId), + Map.entry(groupRegex, SpawnParameters::setGroupId), + Map.entry(configRegex, SpawnParameters::setConfigId), + Map.entry(maxHPRegex, SpawnParameters::setMaxHP), + Map.entry(hpRegex, SpawnParameters::setHp), + Map.entry(defRegex, SpawnParameters::setDef), + Map.entry(atkRegex, SpawnParameters::setAtk), + Map.entry(aiRegex, SpawnParameters::setAi) + ); @Override public void execute(Player sender, Player targetPlayer, List args) { - int id = 0; // This is just to shut up the linter, it's not a real default - int amount = 1; - int level = 1; - float x = 0, y = 0, z = 0; + SpawnParameters param = new SpawnParameters(); + + parseIntParameters(args, param, intCommandHandlers); + + // At this point, first remaining argument MUST be the id and the rest the pos + if (args.size() < 1) { + sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar + throw new IllegalArgumentException(); + } switch (args.size()) { - case 6: + case 4: try { - x = Float.parseFloat(args.get(3)); - y = Float.parseFloat(args.get(4)); - z = Float.parseFloat(args.get(5)); + float x, y, z; + x = Float.parseFloat(args.get(1)); + y = Float.parseFloat(args.get(2)); + z = Float.parseFloat(args.get(3)); + param.pos = new Position(x, y, z); } catch (NumberFormatException ignored) { CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); } // Fallthrough - case 3: - try { - level = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); - } // Fallthrough - case 2: - try { - amount = Integer.parseInt(args.get(1)); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount")); - } // Fallthrough case 1: try { - id = Integer.parseInt(args.get(0)); + param.id = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId")); } @@ -64,45 +81,102 @@ public final class SpawnCommand implements CommandHandler { return; } - MonsterData monsterData = GameData.getMonsterDataMap().get(id); - GadgetData gadgetData = GameData.getGadgetDataMap().get(id); - ItemData itemData = GameData.getItemDataMap().get(id); + MonsterData monsterData = GameData.getMonsterDataMap().get(param.id); + GadgetData gadgetData = GameData.getGadgetDataMap().get(param.id); + ItemData itemData = GameData.getItemDataMap().get(param.id); if (monsterData == null && gadgetData == null && itemData == null) { CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId")); return; } - Scene scene = targetPlayer.getScene(); + param.scene = targetPlayer.getScene(); - if (scene.getEntities().size() + amount > GAME_OPTIONS.sceneEntityLimit) { - amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0); - CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount)); - if (amount <= 0) { + if (param.scene.getEntities().size() + param.amount > GAME_OPTIONS.sceneEntityLimit) { + param.amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - param.scene.getEntities().size(), param.amount), 0); + CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", param.amount)); + if (param.amount <= 0) { return; } } - double maxRadius = Math.sqrt(amount * 0.2 / Math.PI); - Position center = (x != 0 && y != 0 && z != 0) - ? (new Position(x, y, z)) - : (targetPlayer.getPosition()); - for (int i = 0; i < amount; i++) { - Position pos = GetRandomPositionInCircle(center, maxRadius).addY(3); + double maxRadius = Math.sqrt(param.amount * 0.2 / Math.PI); + if (param.pos == null) { + param.pos = targetPlayer.getPosition(); + } + + for (int i = 0; i < param.amount; i++) { + Position pos = GetRandomPositionInCircle(param.pos, maxRadius).addY(3); GameEntity entity = null; if (itemData != null) { - entity = new EntityItem(scene, null, itemData, pos, 1, true); + entity = createItem(itemData, param, pos); } if (gadgetData != null) { pos.addY(-3); - entity = new EntityVehicle(scene, targetPlayer, id, 0, pos, targetPlayer.getRotation()); + entity = createGadget(gadgetData, param, pos, targetPlayer); } if (monsterData != null) { - entity = new EntityMonster(scene, monsterData, pos, level); + entity = createMonster(monsterData, param, pos); } + applyCommonParameters(entity, param); - scene.addEntity(entity); + param.scene.addEntity(entity); + } + CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.success", param.amount, param.id)); + } + + ; + + private EntityItem createItem(ItemData itemData, SpawnParameters param, Position pos) { + return new EntityItem(param.scene, null, itemData, pos, 1, true); + } + + private EntityMonster createMonster(MonsterData monsterData, SpawnParameters param, Position pos) { + var entity = new EntityMonster(param.scene, monsterData, pos, param.lvl); + if (param.ai != -1) { + entity.setAiId(param.ai); + } + return entity; + } + + private EntityBaseGadget createGadget(GadgetData gadgetData, SpawnParameters param, Position pos, Player targetPlayer) { + EntityBaseGadget entity; + if (gadgetData.getType() == EntityType.Vehicle) { + entity = new EntityVehicle(param.scene, targetPlayer, param.id, 0, pos, targetPlayer.getRotation()); + } else { + entity = new EntityGadget(param.scene, param.id, pos, targetPlayer.getRotation()); + if (param.state != -1) { + ((EntityGadget) entity).setState(param.state); + } + } + + return entity; + } + + private void applyCommonParameters(GameEntity entity, SpawnParameters param) { + if (param.blockId != -1) { + entity.setBlockId(param.blockId); + } + if (param.groupId != -1) { + entity.setGroupId(param.groupId); + } + if (param.configId != -1) { + entity.setConfigId(param.configId); + } + if (param.maxHP != -1) { + entity.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, param.maxHP); + entity.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, param.maxHP); + } + if (param.hp != -1) { + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, param.hp == 0 ? Float.MAX_VALUE : param.hp); + } + if (param.atk != -1) { + entity.setFightProperty(FightProperty.FIGHT_PROP_ATTACK, param.atk); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk); + } + if (param.def != -1) { + entity.setFightProperty(FightProperty.FIGHT_PROP_DEFENSE, param.def); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def); } - CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.success", amount, id)); } private Position GetRandomPositionInCircle(Position origin, double radius) { @@ -112,4 +186,21 @@ public final class SpawnCommand implements CommandHandler { target.addX((float) (r * Math.cos(angle))).addZ((float) (r * Math.sin(angle))); return target; } + + private static class SpawnParameters { + @Setter public int id; + @Setter public int lvl = 1; + @Setter public int amount = 1; + @Setter public int blockId = -1; + @Setter public int groupId = -1; + @Setter public int configId = -1; + @Setter public int state = -1; + @Setter public int hp = -1; + @Setter public int maxHP = -1; + @Setter public int atk = -1; + @Setter public int def = -1; + @Setter public int ai = -1; + @Setter public Position pos = null; + public Scene scene = null; + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java index 8d6e7d878..6004c983d 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java @@ -1,5 +1,7 @@ package emu.grasscutter.game.entity; +import emu.grasscutter.data.binout.ConfigGadget; +import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.world.Scene; public abstract class EntityBaseGadget extends GameEntity { @@ -14,4 +16,30 @@ public abstract class EntityBaseGadget extends GameEntity { public void onDeath(int killerId) { super.onDeath(killerId); // Invoke super class's onDeath() method. } + + protected void fillFightProps(ConfigGadget configGadget) { + if (configGadget == null || configGadget.getCombat() == null) { + return; + } + var combatData = configGadget.getCombat(); + var combatProperties = combatData.getProperty(); + + var targetHp = combatProperties.getHP(); + setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, targetHp); + setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, targetHp); + if (combatProperties.isInvincible()) { + targetHp = Float.POSITIVE_INFINITY; + } + setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, targetHp); + + var atk = combatProperties.getAttack(); + setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, atk); + setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk); + + var def = combatProperties.getDefence(); + setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, def); + setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def); + + setLockHP(combatProperties.isLockHP()); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java index 8e438b9da..a314a495c 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java @@ -60,7 +60,7 @@ public class EntityGadget extends EntityBaseGadget { this.gadgetId = gadgetId; this.pos = pos.clone(); this.rot = rot != null ? rot.clone() : new Position(); - fillFightProps(); + fillFightProps(configGadget); } public EntityGadget(Scene scene, int gadgetId, Position pos) { @@ -72,22 +72,6 @@ public class EntityGadget extends EntityBaseGadget { this.content = content; } - private void fillFightProps() { - if (configGadget == null || configGadget.getCombat() == null) { - return; - } - var combatData = configGadget.getCombat(); - var combatProperties = combatData.getProperty(); - - var targetHp = combatProperties.getHP(); - setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, targetHp); - if (combatProperties.isInvincible()) { - targetHp = Float.POSITIVE_INFINITY; - } - setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, targetHp); - setLockHP(combatProperties.isLockHP()); - } - public GadgetData getGadgetData() { return data; } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index d60c96df7..b5b50b261 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -37,6 +37,8 @@ import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; +import lombok.Getter; +import lombok.Setter; public class EntityMonster extends GameEntity { private final MonsterData monsterData; @@ -48,6 +50,8 @@ public class EntityMonster extends GameEntity { private final int level; private int weaponEntityId; private int poseId; + @Getter @Setter + private int aiId=-1; public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) { super(scene); @@ -289,6 +293,9 @@ public class EntityMonster extends GameEntity { monsterInfo.addWeaponList(weaponInfo); } + if(this.aiId!=-1){ + monsterInfo.setAiConfigId(aiId); + } entityInfo.setMonster(monsterInfo); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java index b4fd370e5..1cf252614 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java @@ -1,16 +1,18 @@ package emu.grasscutter.game.entity; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.ConfigGadget; +import emu.grasscutter.data.excels.GadgetData; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.world.Scene; - import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; -import emu.grasscutter.net.proto.FightPropPairOuterClass.*; +import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair; import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; @@ -18,18 +20,18 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; -import emu.grasscutter.net.proto.VehicleInfoOuterClass.*; -import emu.grasscutter.net.proto.VehicleMemberOuterClass.*; +import emu.grasscutter.net.proto.VehicleInfoOuterClass.VehicleInfo; +import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; - import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import lombok.Getter; import lombok.Setter; -import java.util.List; +import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.List; public class EntityVehicle extends EntityBaseGadget { @@ -44,6 +46,7 @@ public class EntityVehicle extends EntityBaseGadget { @Getter @Setter private float curStamina; @Getter private List vehicleMembers; + @Nullable @Getter private ConfigGadget configGadget; public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) { super(scene); @@ -54,21 +57,21 @@ public class EntityVehicle extends EntityBaseGadget { this.rot = new Position(rot); this.gadgetId = gadgetId; this.pointId = pointId; - this.curStamina = 240; - this.vehicleMembers = new ArrayList(); - - switch (gadgetId) { - case 45001001,45001002 -> { // TODO: Not hardcode this. Waverider (skiff) - this.addFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 10000); - this.addFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, 100); - this.addFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, 100); - this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 10000); - this.addFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, 0); - this.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0); - this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0); - this.addFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 10000); - } + this.curStamina = 240; // might be in configGadget.GCALKECLLLP.JBAKBEFIMBN.ANBMPHPOALP + this.vehicleMembers = new ArrayList<>(); + GadgetData data = GameData.getGadgetDataMap().get(gadgetId); + if (data != null && data.getJsonName() != null) { + this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName()); } + + fillFightProps(configGadget); + } + + @Override + protected void fillFightProps(ConfigGadget configGadget) { + super.fillFightProps(configGadget); + this.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0); + this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0); } @Override diff --git a/src/main/java/emu/grasscutter/game/props/EntityType.java b/src/main/java/emu/grasscutter/game/props/EntityType.java index efe6694c4..50a7de33a 100644 --- a/src/main/java/emu/grasscutter/game/props/EntityType.java +++ b/src/main/java/emu/grasscutter/game/props/EntityType.java @@ -62,19 +62,32 @@ public enum EntityType { MiracleRing (51), Foundation (52), WidgetGadget (53), + Vehicle (54), + SubEquip (55), + FishRod (56), + CustomTile (57), + FishPool (58), + CustomGadget (59), + BlackMud (60), + RoguelikeOperatorGadget (61), + NightCrowGadget (62), + Projector (63), + Screen (64), + EchoShell (65), + UIInteractGadget (66), PlaceHolder (99); - + private final int value; private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); private static final Map stringMap = new HashMap<>(); - + static { Stream.of(values()).forEach(e -> { map.put(e.getValue(), e); stringMap.put(e.name(), e); }); } - + private EntityType(int value) { this.value = value; } @@ -82,11 +95,11 @@ public enum EntityType { public int getValue() { return value; } - + public static EntityType getTypeByValue(int value) { return map.getOrDefault(value, None); } - + public static EntityType getTypeByName(String name) { return stringMap.getOrDefault(name, None); }