From 08fdcf6ed4cf2eb6e1cf8a51b2c6408e950f03fe Mon Sep 17 00:00:00 2001
From: Alexander Hartmann <hartmannaf@googlemail.com>
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 <mail@hartie95.de>
---
 .../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 67b688e2..a8bc9cc8 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<QuestEncryptionKey> questsKeys = new Int2ObjectOpenHashMap<>();
     private static final Int2ObjectMap<HomeworldDefaultSaveData> homeworldDefaultSaveData = new Int2ObjectOpenHashMap<>();
     private static final Int2ObjectMap<SceneNpcBornData> npcBornData = new Int2ObjectOpenHashMap<>();
+    @Getter private static final Map<String, ConfigGadget> gadgetConfigData = new HashMap<>();
 
     // ExcelConfigs
     private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java
index f6824436..0a580d46 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<String, ConfigGadget> 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<String, ConfigGadget> 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 00000000..9bb9adc0
--- /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 00000000..5f1da0c7
--- /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 00000000..f00d74d7
--- /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 7f640f9d..8e438b9d 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 3881a2c9..27c85218 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<String> metaOverrideMap;
     private Int2ObjectMap<String> 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
-- 
GitLab