From 08fdcf6ed4cf2eb6e1cf8a51b2c6408e950f03fe Mon Sep 17 00:00:00 2001 From: Alexander Hartmann Date: Thu, 15 Sep 2022 04:26:20 +0200 Subject: [PATCH] fixed gadget hp properties and invincibility handling (#1773) * fixed gadget hp properties and invincibility handling * Allow killing of hp locked entities, if the damage is higher then the hp Co-authored-by: hartie95 --- .../java/emu/grasscutter/data/GameData.java | 1 + .../emu/grasscutter/data/ResourceLoader.java | 26 +++++++++++++++ .../grasscutter/data/binout/ConfigGadget.java | 15 +++++++++ .../data/binout/ConfigGadgetCombat.java | 12 +++++++ .../binout/ConfigGadgetCombatProperty.java | 19 +++++++++++ .../grasscutter/game/entity/EntityGadget.java | 33 +++++++++++++++---- .../grasscutter/game/entity/GameEntity.java | 15 ++++++--- 7 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/binout/ConfigGadget.java create mode 100644 src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombat.java create mode 100644 src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombatProperty.java diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 67b688e26..a8bc9cc8b 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -29,6 +29,7 @@ public class GameData { private static final Int2ObjectMap questsKeys = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap homeworldDefaultSaveData = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap npcBornData = new Int2ObjectOpenHashMap<>(); + @Getter private static final Map gadgetConfigData = new HashMap<>(); // ExcelConfigs private static final Int2ObjectMap playerLevelDataMap = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index f68244361..0a580d468 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -68,6 +68,7 @@ public class ResourceLoader { // Process into depots GameDepot.load(); // Load spawn data and quests + loadGadgetConfigData(); loadSpawnData(); loadQuests(); loadScriptSceneData(); @@ -493,6 +494,31 @@ public class ResourceLoader { Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas."); } + @SneakyThrows + private static void loadGadgetConfigData() { + Files.list(Path.of(RESOURCE("BinOutput/Gadget/"))).forEach(filePath -> { + var file = filePath.toFile(); + if (file.isDirectory() || !file.getName().endsWith("json")) { + return; + } + + Map config; + + try { + config = JsonUtils.loadToMap(filePath.toString(), String.class, ConfigGadget.class); + } catch (Exception e) { + Grasscutter.getLogger().error("failed to load ConfigGadget entries for "+filePath, e); + return; + } + + for (Entry e : config.entrySet()) { + GameData.getGadgetConfigData().put(e.getKey(), e.getValue()); + } + }); + + Grasscutter.getLogger().debug("Loaded {} ConfigGadget entries.", GameData.getGadgetConfigData().size()); + } + @SneakyThrows private static void loadBlossomResources() { GameDepot.setBlossomConfig(DataLoader.loadClass("BlossomConfig.json", BlossomConfig.class)); diff --git a/src/main/java/emu/grasscutter/data/binout/ConfigGadget.java b/src/main/java/emu/grasscutter/data/binout/ConfigGadget.java new file mode 100644 index 000000000..9bb9adc02 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/ConfigGadget.java @@ -0,0 +1,15 @@ +package emu.grasscutter.data.binout; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +import javax.annotation.Nullable; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ConfigGadget { + // There are more values that can be added that might be useful in the json + @Nullable + ConfigGadgetCombat combat; +} diff --git a/src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombat.java b/src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombat.java new file mode 100644 index 000000000..5f1da0c76 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombat.java @@ -0,0 +1,12 @@ +package emu.grasscutter.data.binout; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ConfigGadgetCombat { + // There are more values that can be added that might be useful in the json + ConfigGadgetCombatProperty property; +} diff --git a/src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombatProperty.java b/src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombatProperty.java new file mode 100644 index 000000000..f00d74d78 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/ConfigGadgetCombatProperty.java @@ -0,0 +1,19 @@ +package emu.grasscutter.data.binout; + +import com.google.gson.annotations.SerializedName; +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ConfigGadgetCombatProperty { + float HP; + boolean isLockHP; + boolean isInvincible; + boolean isGhostToAllied; + float attack; + float defence; + float weight; + boolean useCreatorProperty; +} diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java index 7f640f9d8..8e438b9da 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java @@ -1,21 +1,20 @@ 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.entity.gadget.*; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityType; -import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.world.Scene; -import emu.grasscutter.game.world.SpawnDataEntry; 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.EntityClientDataOuterClass.EntityClientData; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; -import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; @@ -24,18 +23,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.VisionTypeOuterClass.VisionType; import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; -import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; 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.ToString; +import javax.annotation.Nullable; + @ToString(callSuper = true) public class EntityGadget extends EntityBaseGadget { private final GadgetData data; @@ -48,14 +47,20 @@ public class EntityGadget extends EntityBaseGadget { private GadgetContent content; private Int2FloatOpenHashMap fightProp; private SceneGadget metaGadget; + @Nullable @Getter + private ConfigGadget configGadget; public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) { super(scene); this.data = GameData.getGadgetDataMap().get(gadgetId); + if(data!=null && data.getJsonName()!=null) { + this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName()); + } this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.gadgetId = gadgetId; this.pos = pos.clone(); this.rot = rot != null ? rot.clone() : new Position(); + fillFightProps(); } public EntityGadget(Scene scene, int gadgetId, Position pos) { @@ -67,6 +72,22 @@ 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/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index 3881a2c9e..27c85218b 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -37,6 +37,8 @@ public abstract class GameEntity { @Getter @Setter private int lastMoveSceneTimeMs; @Getter @Setter private int lastMoveReliableSeq; + @Getter @Setter private boolean lockHP; + // Abilities private Object2FloatMap metaOverrideMap; private Int2ObjectMap metaModifiers; @@ -106,6 +108,10 @@ public abstract class GameEntity { return this.getFightProperties().getOrDefault(prop.getId(), 0f); } + public boolean hasFightProperty(FightProperty prop) { + return this.getFightProperties().containsKey(prop.getId()); + } + public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) { for (Int2FloatMap.Entry entry : this.getFightProperties().int2FloatEntrySet()) { if (entry.getIntKey() == 0) { @@ -153,7 +159,7 @@ public abstract class GameEntity { public void damage(float amount, int killerId) { // Check if the entity has properties. - if (this.getFightProperties() == null) { + if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) { return; } @@ -164,9 +170,10 @@ public abstract class GameEntity { return; // If the event is canceled, do not damage the entity. } - if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) != Float.POSITIVE_INFINITY) { - // Add negative HP to the current HP property. - this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage())); + float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + if (curHp != Float.POSITIVE_INFINITY && !lockHP || lockHP && curHp <= event.getDamage()) { + // Add negative HP to the current HP property. + this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage())); } // Check if dead