From 4d8caf5a8cfeb57368d5bfdbd1e03e6dbc962766 Mon Sep 17 00:00:00 2001
From: AnimeGitB <AnimeGitB@bigblueball.in>
Date: Sat, 5 Nov 2022 19:00:17 +1030
Subject: [PATCH] Refactor Entity data

---
 .../grasscutter/data/excels/MonsterData.java  | 178 ++++--------------
 .../emu/grasscutter/game/avatar/Avatar.java   |  31 +--
 .../grasscutter/game/avatar/AvatarStat.java   | 124 ------------
 .../grasscutter/game/entity/EntityAvatar.java |  70 +++----
 .../game/entity/EntityBaseGadget.java         |  12 ++
 .../game/entity/EntityClientGadget.java       |  79 ++------
 .../grasscutter/game/entity/EntityGadget.java | 113 +++--------
 .../grasscutter/game/entity/EntityItem.java   |  57 +-----
 .../game/entity/EntityMonster.java            | 150 ++++-----------
 .../grasscutter/game/entity/EntityNPC.java    |  27 +--
 .../grasscutter/game/entity/EntityRegion.java |  17 +-
 .../game/entity/EntityVehicle.java            |  26 +--
 .../grasscutter/game/entity/GameEntity.java   |  12 +-
 .../game/entity/platform/EntityPlatform.java  |  32 +---
 .../grasscutter/game/props/FightProperty.java |  35 +++-
 15 files changed, 226 insertions(+), 737 deletions(-)
 delete mode 100644 src/main/java/emu/grasscutter/game/avatar/AvatarStat.java

diff --git a/src/main/java/emu/grasscutter/data/excels/MonsterData.java b/src/main/java/emu/grasscutter/data/excels/MonsterData.java
index 758937fd..de31c338 100644
--- a/src/main/java/emu/grasscutter/data/excels/MonsterData.java
+++ b/src/main/java/emu/grasscutter/data/excels/MonsterData.java
@@ -1,19 +1,28 @@
 package emu.grasscutter.data.excels;
 
 import java.util.List;
+import java.util.Set;
+
+import com.google.gson.annotations.SerializedName;
 
 import emu.grasscutter.data.GameData;
 import emu.grasscutter.data.GameResource;
 import emu.grasscutter.data.ResourceType;
 import emu.grasscutter.data.ResourceType.LoadPriority;
 import emu.grasscutter.data.common.PropGrowCurve;
+import emu.grasscutter.game.props.FightProperty;
 import emu.grasscutter.game.props.MonsterType;
+import lombok.Getter;
 
 @ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
+@Getter
 public class MonsterData extends GameResource {
-	private int id;
-	
-	private String monsterName;
+    static public Set<FightProperty> definedFightProperties = Set.of(FightProperty.FIGHT_PROP_BASE_HP, FightProperty.FIGHT_PROP_BASE_ATTACK, FightProperty.FIGHT_PROP_BASE_DEFENSE, FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, FightProperty.FIGHT_PROP_FIRE_SUB_HURT, FightProperty.FIGHT_PROP_ELEC_SUB_HURT, FightProperty.FIGHT_PROP_WATER_SUB_HURT, FightProperty.FIGHT_PROP_GRASS_SUB_HURT, FightProperty.FIGHT_PROP_WIND_SUB_HURT, FightProperty.FIGHT_PROP_ROCK_SUB_HURT, FightProperty.FIGHT_PROP_ICE_SUB_HURT);
+
+    @Getter(onMethod = @__(@Override))
+    private int id;
+
+    private String monsterName;
     private MonsterType type;
     private String serverScript;
     private List<Integer> affix;
@@ -28,9 +37,14 @@ public class MonsterData extends GameResource {
     private int describeId;
     private int combatBGMLevel;
     private int entityBudgetLevel;
-    private float hpBase;
-    private float attackBase;
-    private float defenseBase;
+
+    @SerializedName("hpBase")
+    private float baseHp;
+    @SerializedName("attackBase")
+    private float baseAttack;
+    @SerializedName("defenseBase")
+    private float baseDefense;
+
     private float fireSubHurt;
     private float elecSubHurt;
     private float grassSubHurt;
@@ -46,127 +60,23 @@ public class MonsterData extends GameResource {
     // Transient
     private int weaponId;
     private MonsterDescribeData describeData;
-    
-	@Override
-	public int getId() {
-		return this.id;
-	}
-	
-	public String getMonsterName() {
-		return monsterName;
-	}
 
-	public MonsterType getType() {
-		return type;
-	}
-
-	public String getServerScript() {
-		return serverScript;
-	}
-
-	public List<Integer> getAffix() {
-		return affix;
-	}
-
-	public String getAi() {
-		return ai;
-	}
-
-	public int[] getEquips() {
-		return equips;
-	}
-
-	public List<HpDrops> getHpDrops() {
-		return hpDrops;
-	}
-
-	public int getKillDropId() {
-		return killDropId;
-	}
-
-	public String getExcludeWeathers() {
-		return excludeWeathers;
-	}
-
-	public int getFeatureTagGroupID() {
-		return featureTagGroupID;
-	}
-
-	public int getMpPropID() {
-		return mpPropID;
-	}
-
-	public String getSkin() {
-		return skin;
-	}
-
-	public int getDescribeId() {
-		return describeId;
-	}
-
-	public int getCombatBGMLevel() {
-		return combatBGMLevel;
-	}
-
-	public int getEntityBudgetLevel() {
-		return entityBudgetLevel;
-	}
-
-	public float getBaseHp() {
-		return hpBase;
-	}
-
-	public float getBaseAttack() {
-		return attackBase;
-	}
-
-	public float getBaseDefense() {
-		return defenseBase;
-	}
-
-	public float getElecSubHurt() {
-		return elecSubHurt;
-	}
-
-	public float getGrassSubHurt() {
-		return grassSubHurt;
-	}
-
-	public float getWaterSubHurt() {
-		return waterSubHurt;
-	}
-
-	public float getWindSubHurt() {
-		return windSubHurt;
-	}
-
-	public float getIceSubHurt() {
-		return iceSubHurt;
-	}
-
-	public float getPhysicalSubHurt() {
-		return physicalSubHurt;
-	}
-
-	public List<PropGrowCurve> getPropGrowCurves() {
-		return propGrowCurves;
-	}
-
-	public long getNameTextMapHash() {
-		return nameTextMapHash;
-	}
-
-	public int getCampID() {
-		return campID;
-	}
-
-	public MonsterDescribeData getDescribeData() {
-		return describeData;
-	}
-
-	public int getWeaponId() {
-		return weaponId;
-	}
+    public float getFightProperty(FightProperty prop) {
+        return switch (prop) {
+            case FIGHT_PROP_BASE_HP -> this.baseHp;
+            case FIGHT_PROP_BASE_ATTACK -> this.baseAttack;
+            case FIGHT_PROP_BASE_DEFENSE -> this.baseDefense;
+            case FIGHT_PROP_PHYSICAL_SUB_HURT -> this.physicalSubHurt;
+            case FIGHT_PROP_FIRE_SUB_HURT -> this.fireSubHurt;
+            case FIGHT_PROP_ELEC_SUB_HURT -> this.elecSubHurt;
+            case FIGHT_PROP_WATER_SUB_HURT -> this.waterSubHurt;
+            case FIGHT_PROP_GRASS_SUB_HURT -> this.grassSubHurt;
+            case FIGHT_PROP_WIND_SUB_HURT -> this.windSubHurt;
+            case FIGHT_PROP_ROCK_SUB_HURT -> this.rockSubHurt;
+            case FIGHT_PROP_ICE_SUB_HURT -> this.iceSubHurt;
+            default -> 0f;
+        };
+    }
 
 	@Override
 	public void onLoad() {
@@ -185,16 +95,10 @@ public class MonsterData extends GameResource {
 			}
 		}
 	}
-	
-	public class HpDrops {
-	    private int DropId;
-	    private int HpPercent;
 
-	    public int getDropId(){
-	        return this.DropId;
-	    }
-	    public int getHpPercent(){
-	        return this.HpPercent;
-	    }
-	}
+    @Getter
+    public class HpDrops {
+        private int DropId;
+        private int HpPercent;
+    }
 }
diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java
index 4286a9a5..adb3dc75 100644
--- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java
+++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java
@@ -58,6 +58,7 @@ import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo;
 import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip;
 import emu.grasscutter.server.packet.send.*;
 import emu.grasscutter.utils.ProtoHelper;
+import it.unimi.dsi.fastutil.ints.Int2FloatMap;
 import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -89,7 +90,7 @@ public class Avatar {
     private float currentEnergy;
 
     @Transient @Getter private final Int2ObjectMap<GameItem> equips;
-    @Transient private final Int2FloatOpenHashMap fightProp;
+    @Transient @Getter private final Int2FloatOpenHashMap fightProperties;
     @Transient @Getter private final Int2FloatOpenHashMap fightPropOverrides;
     @Transient @Getter private Set<String> extraAbilityEmbryos;
 
@@ -115,7 +116,7 @@ public class Avatar {
     @Deprecated // Do not use. Morhpia only!
     public Avatar() {
         this.equips = new Int2ObjectOpenHashMap<>();
-        this.fightProp = new Int2FloatOpenHashMap();
+        this.fightProperties = new Int2FloatOpenHashMap();
         this.fightPropOverrides = new Int2FloatOpenHashMap();
         this.extraAbilityEmbryos = new HashSet<>();
         this.fetters = new ArrayList<>(); // TODO Move to avatar
@@ -276,10 +277,6 @@ public class Avatar {
         }
     }
 
-    public Int2FloatOpenHashMap getFightProperties() {
-        return fightProp;
-    }
-
     public void setFightProperty(FightProperty prop, float value) {
         this.getFightProperties().put(prop.getId(), value);
     }
@@ -399,9 +396,9 @@ public class Avatar {
 
     public void recalcStats(boolean forceSendAbilityChange) {
         // Setup
-        AvatarData data = this.getAvatarData();
-        AvatarPromoteData promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel());
-        Int2IntOpenHashMap setMap = new Int2IntOpenHashMap();
+        var data = this.getAvatarData();
+        var promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel());
+        var setMap = new Int2IntOpenHashMap();
 
         // Extra ability embryos
         Set<String> prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos();
@@ -579,21 +576,11 @@ public class Avatar {
             // Add any skill strings from this constellation
 
         // Set % stats
-        this.setFightProperty(
-            FightProperty.FIGHT_PROP_MAX_HP,
-            (getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
-        );
-        this.setFightProperty(
-            FightProperty.FIGHT_PROP_CUR_ATTACK,
-            (getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
-        );
-        this.setFightProperty(
-            FightProperty.FIGHT_PROP_CUR_DEFENSE,
-            (getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
-        );
+        FightProperty.forEachCompoundProperty(c -> this.setFightProperty(c.getResult(),
+            this.getFightProperty(c.getFlat()) + (this.getFightProperty(c.getBase()) * (1f + this.getFightProperty(c.getPercent())))));
 
         // Reapply all overrides
-        this.fightProp.putAll(this.fightPropOverrides);
+        this.fightProperties.putAll(this.fightPropOverrides);
 
         // Set current hp
         this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
diff --git a/src/main/java/emu/grasscutter/game/avatar/AvatarStat.java b/src/main/java/emu/grasscutter/game/avatar/AvatarStat.java
deleted file mode 100644
index fc20d00d..00000000
--- a/src/main/java/emu/grasscutter/game/avatar/AvatarStat.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package emu.grasscutter.game.avatar;
-
-import java.util.stream.Stream;
-
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
-
-public enum AvatarStat {
-	FIGHT_PROP_NONE(0),
-	FIGHT_PROP_BASE_HP(1),
-	FIGHT_PROP_HP(2),
-	FIGHT_PROP_HP_PERCENT(3),
-	FIGHT_PROP_BASE_ATTACK(4),
-	FIGHT_PROP_ATTACK(5),
-	FIGHT_PROP_ATTACK_PERCENT(6),
-	FIGHT_PROP_BASE_DEFENSE(7),
-	FIGHT_PROP_DEFENSE(8),
-	FIGHT_PROP_DEFENSE_PERCENT(9),
-	FIGHT_PROP_BASE_SPEED(10),
-	FIGHT_PROP_SPEED_PERCENT(11),
-	FIGHT_PROP_HP_MP_PERCENT(12),
-	FIGHT_PROP_ATTACK_MP_PERCENT(13),
-	FIGHT_PROP_CRITICAL(20),
-	FIGHT_PROP_ANTI_CRITICAL(21),
-	FIGHT_PROP_CRITICAL_HURT(22),
-	FIGHT_PROP_CHARGE_EFFICIENCY(23),
-	FIGHT_PROP_ADD_HURT(24),
-	FIGHT_PROP_SUB_HURT(25),
-	FIGHT_PROP_HEAL_ADD(26),
-	FIGHT_PROP_HEALED_ADD(27),
-	FIGHT_PROP_ELEMENT_MASTERY(28),
-	FIGHT_PROP_PHYSICAL_SUB_HURT(29),
-	FIGHT_PROP_PHYSICAL_ADD_HURT(30),
-	FIGHT_PROP_DEFENCE_IGNORE_RATIO(31),
-	FIGHT_PROP_DEFENCE_IGNORE_DELTA(32),
-	FIGHT_PROP_FIRE_ADD_HURT(40),
-	FIGHT_PROP_ELEC_ADD_HURT(41),
-	FIGHT_PROP_WATER_ADD_HURT(42),
-	FIGHT_PROP_GRASS_ADD_HURT(43),
-	FIGHT_PROP_WIND_ADD_HURT(44),
-	FIGHT_PROP_ROCK_ADD_HURT(45),
-	FIGHT_PROP_ICE_ADD_HURT(46),
-	FIGHT_PROP_HIT_HEAD_ADD_HURT(47),
-	FIGHT_PROP_FIRE_SUB_HURT(50),
-	FIGHT_PROP_ELEC_SUB_HURT(51),
-	FIGHT_PROP_WATER_SUB_HURT(52),
-	FIGHT_PROP_GRASS_SUB_HURT(53),
-	FIGHT_PROP_WIND_SUB_HURT(54),
-	FIGHT_PROP_ROCK_SUB_HURT(55),
-	FIGHT_PROP_ICE_SUB_HURT(56),
-	FIGHT_PROP_EFFECT_HIT(60),
-	FIGHT_PROP_EFFECT_RESIST(61),
-	FIGHT_PROP_FREEZE_RESIST(62),
-	FIGHT_PROP_TORPOR_RESIST(63),
-	FIGHT_PROP_DIZZY_RESIST(64),
-	FIGHT_PROP_FREEZE_SHORTEN(65),
-	FIGHT_PROP_TORPOR_SHORTEN(66),
-	FIGHT_PROP_DIZZY_SHORTEN(67),
-	FIGHT_PROP_MAX_FIRE_ENERGY(70),
-	FIGHT_PROP_MAX_ELEC_ENERGY(71),
-	FIGHT_PROP_MAX_WATER_ENERGY(72),
-	FIGHT_PROP_MAX_GRASS_ENERGY(73),
-	FIGHT_PROP_MAX_WIND_ENERGY(74),
-	FIGHT_PROP_MAX_ICE_ENERGY(75),
-	FIGHT_PROP_MAX_ROCK_ENERGY(76),
-	FIGHT_PROP_SKILL_CD_MINUS_RATIO(80),
-	FIGHT_PROP_SHIELD_COST_MINUS_RATIO(81),
-	FIGHT_PROP_CUR_FIRE_ENERGY(1000),
-	FIGHT_PROP_CUR_ELEC_ENERGY(1001),
-	FIGHT_PROP_CUR_WATER_ENERGY(1002),
-	FIGHT_PROP_CUR_GRASS_ENERGY(1003),
-	FIGHT_PROP_CUR_WIND_ENERGY(1004),
-	FIGHT_PROP_CUR_ICE_ENERGY(1005),
-	FIGHT_PROP_CUR_ROCK_ENERGY(1006),
-	FIGHT_PROP_CUR_HP(1010),
-	FIGHT_PROP_MAX_HP(2000),
-	FIGHT_PROP_CUR_ATTACK(2001),
-	FIGHT_PROP_CUR_DEFENSE(2002),
-	FIGHT_PROP_CUR_SPEED(2003),
-	FIGHT_PROP_NONEXTRA_ATTACK(3000),
-	FIGHT_PROP_NONEXTRA_DEFENSE(3001),
-	FIGHT_PROP_NONEXTRA_CRITICAL(3002),
-	FIGHT_PROP_NONEXTRA_ANTI_CRITICAL(3003),
-	FIGHT_PROP_NONEXTRA_CRITICAL_HURT(3004),
-	FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY(3005),
-	FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY(3006),
-	FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT(3007),
-	FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT(3008),
-	FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT(3009),
-	FIGHT_PROP_NONEXTRA_WATER_ADD_HURT(3010),
-	FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT(3011),
-	FIGHT_PROP_NONEXTRA_WIND_ADD_HURT(3012),
-	FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT(3013),
-	FIGHT_PROP_NONEXTRA_ICE_ADD_HURT(3014),
-	FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT(3015),
-	FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT(3016),
-	FIGHT_PROP_NONEXTRA_WATER_SUB_HURT(3017),
-	FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT(3018),
-	FIGHT_PROP_NONEXTRA_WIND_SUB_HURT(3019),
-	FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT(3020),
-	FIGHT_PROP_NONEXTRA_ICE_SUB_HURT(3021),
-	FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO(3022),
-	FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO(3023),
-	FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT(3024);
-	
-	private final int id;
-	private static final Int2ObjectMap<AvatarStat> map = new Int2ObjectOpenHashMap<>();
-	
-	static {
-		Stream.of(values()).forEach(e -> map.put(e.getId(), e));
-	}
-	
-	private AvatarStat(int id) {
-		this.id = id;
-	}
-	
-	public int getId() {
-		return id;
-	}
-	
-	public static AvatarStat getStatById(int value) {
-		return map.getOrDefault(value, FIGHT_PROP_NONE);
-	}
-}
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java
index 8bce738f..ef8ca5dc 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java
@@ -21,7 +21,6 @@ import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
 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.PlayerDieTypeOuterClass.PlayerDieType;
 import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
 import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
@@ -38,20 +37,25 @@ import emu.grasscutter.utils.Position;
 import emu.grasscutter.utils.ProtoHelper;
 import emu.grasscutter.utils.Utils;
 import it.unimi.dsi.fastutil.ints.Int2FloatMap;
-import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
+import lombok.Getter;
 import lombok.val;
 
 public class EntityAvatar extends GameEntity {
-    private final Avatar avatar;
+    @Getter private final Avatar avatar;
 
-    private PlayerDieType killedType;
-    private int killedBy;
+    @Getter private PlayerDieType killedType;
+    @Getter private int killedBy;
+
+    public EntityAvatar(Avatar avatar) {
+        this(null, avatar);
+    }
 
     public EntityAvatar(Scene scene, Avatar avatar) {
         super(scene);
         this.avatar = avatar;
         this.avatar.setCurrentEnergy();
-        this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
+        if (scene != null)
+            this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
 
         GameItem weapon = this.getAvatar().getWeapon();
         if (weapon != null) {
@@ -59,47 +63,20 @@ public class EntityAvatar extends GameEntity {
         }
     }
 
-    public EntityAvatar(Avatar avatar) {
-        super(null);
-        this.avatar = avatar;
-        this.avatar.setCurrentEnergy();
-    }
-
-    public Player getPlayer() {
-        return avatar.getPlayer();
-    }
+    public Player getPlayer() {return this.avatar.getPlayer();}
 
     @Override
-    public Position getPosition() {
-        return getPlayer().getPosition();
-    }
+    public Position getPosition() {return getPlayer().getPosition();}
 
     @Override
-    public Position getRotation() {
-        return getPlayer().getRotation();
-    }
-
-    public Avatar getAvatar() {
-        return avatar;
-    }
-
-    public int getKilledBy() {
-        return killedBy;
-    }
-
-    public PlayerDieType getKilledType() {
-        return killedType;
-    }
+    public Position getRotation() {return getPlayer().getRotation();}
 
     @Override
     public boolean isAlive() {
         return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
     }
 
-    @Override
-    public Int2FloatOpenHashMap getFightProperties() {
-        return getAvatar().getFightProperties();
-    }
+    @Override public Int2FloatMap getFightProperties() {return getAvatar().getFightProperties();}
 
     public int getWeaponEntityId() {
         if (getAvatar().getWeapon() != null) {
@@ -145,11 +122,8 @@ public class EntityAvatar extends GameEntity {
 
     public void clearEnergy(ChangeEnergyReason reason) {
         // Fight props.
-        FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
-        FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
-
-        // Get max energy.
-        float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
+        val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
+        float curEnergy = this.getFightProperty(curEnergyProp);
 
         // Set energy to zero.
         this.avatar.setCurrentEnergy(curEnergyProp, 0);
@@ -158,7 +132,7 @@ public class EntityAvatar extends GameEntity {
         this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
 
         if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
-            this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason));
+            this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
         }
     }
 
@@ -167,18 +141,16 @@ public class EntityAvatar extends GameEntity {
     }
     public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
         // Get current and maximum energy for this avatar.
-        FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
-        FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
+        val elementType = this.getAvatar().getSkillDepot().getElementType();
+        val curEnergyProp = elementType.getCurEnergyProp();
+        val maxEnergyProp = elementType.getMaxEnergyProp();
 
         float curEnergy = this.getFightProperty(curEnergyProp);
         float maxEnergy = this.getFightProperty(maxEnergyProp);
 
-        // Get energy recharge.
-        float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
-
         // Scale amount by energy recharge, if the amount is not flat.
         if (!isFlat) {
-            amount *= energyRecharge;
+            amount *= this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
         }
 
         // Determine the new energy value.
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java
index 6004c983..b2370449 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java
@@ -3,11 +3,23 @@ package emu.grasscutter.game.entity;
 import emu.grasscutter.data.binout.ConfigGadget;
 import emu.grasscutter.game.props.FightProperty;
 import emu.grasscutter.game.world.Scene;
+import emu.grasscutter.utils.Position;
+import lombok.Getter;
 
 public abstract class EntityBaseGadget extends GameEntity {
+    @Getter(onMethod = @__(@Override))
+    protected final Position position;
+    @Getter(onMethod = @__(@Override))
+    protected final Position rotation;
 
     public EntityBaseGadget(Scene scene) {
+        this(scene, null, null);
+    }
+
+    public EntityBaseGadget(Scene scene, Position position, Position rotation) {
         super(scene);
+        this.position = position != null ? position.clone() : new Position();
+        this.rotation = rotation != null ? rotation.clone() : new Position();
     }
 
     public abstract int getGadgetId();
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java
index 34fcf918..ef376bd4 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java
@@ -19,30 +19,28 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
 import emu.grasscutter.net.proto.VectorOuterClass.Vector;
 import emu.grasscutter.utils.Position;
 import emu.grasscutter.utils.ProtoHelper;
-import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2FloatMap;
+import lombok.Getter;
 
 public class EntityClientGadget extends EntityBaseGadget {
-    private final Player owner;
+    @Getter private final Player owner;
 
-    private final Position pos;
-    private final Position rot;
+    @Getter(onMethod = @__(@Override))
+    private int gadgetId;
 
-    private int configId;
-    private int campId;
-    private int campType;
-    private int ownerEntityId;
-    private int targetEntityId;
-    private boolean asyncLoad;
+    @Getter private int campId;
+    @Getter private int campType;
+    @Getter private int ownerEntityId;
+    @Getter private int targetEntityId;
+    @Getter private boolean asyncLoad;
 
-    private int originalOwnerEntityId;
+    @Getter private int originalOwnerEntityId;
 
     public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
-        super(scene);
+        super(scene, new Position(notify.getInitPos()), new Position(notify.getInitEulerAngles()));
         this.owner = player;
         this.id = notify.getEntityId();
-        this.pos = new Position(notify.getInitPos());
-        this.rot = new Position(notify.getInitEulerAngles());
-        this.configId = notify.getConfigId();
+        this.gadgetId = notify.getConfigId();
         this.campId = notify.getCampId();
         this.campType = notify.getCampType();
         this.ownerEntityId = notify.getPropOwnerEntityId();
@@ -58,61 +56,12 @@ public class EntityClientGadget extends EntityBaseGadget {
         }
     }
 
-    @Override
-    public int getGadgetId() {
-        return configId;
-    }
-
-    public Player getOwner() {
-        return owner;
-    }
-
-    public int getCampId() {
-        return campId;
-    }
-
-    public int getCampType() {
-        return campType;
-    }
-
-    public int getOwnerEntityId() {
-        return ownerEntityId;
-    }
-
-    public int getTargetEntityId() {
-        return targetEntityId;
-    }
-
-    public boolean isAsyncLoad() {
-        return this.asyncLoad;
-    }
-
-    public int getOriginalOwnerEntityId() {
-        return this.originalOwnerEntityId;
-    }
-
     @Override
     public void onDeath(int killerId) {
         super.onDeath(killerId); // Invoke super class's onDeath() method.
     }
 
-    @Override
-    public Int2FloatOpenHashMap getFightProperties() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public Position getPosition() {
-        // TODO Auto-generated method stub
-        return this.pos;
-    }
-
-    @Override
-    public Position getRotation() {
-        // TODO Auto-generated method stub
-        return this.rot;
-    }
+    @Override public Int2FloatMap getFightProperties() {return null;}
 
     @Override
     public SceneEntityInfo toProto() {
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java
index a7996b6d..0689214d 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java
@@ -6,8 +6,6 @@ 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.FightProperty;
 import emu.grasscutter.game.props.PlayerProperty;
 import emu.grasscutter.game.world.Scene;
 import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
@@ -29,77 +27,47 @@ import emu.grasscutter.scripts.data.ScriptArgs;
 import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
 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 lombok.ToString;
 
+import java.util.Optional;
+
 import javax.annotation.Nullable;
 
 @ToString(callSuper = true)
 public class EntityGadget extends EntityBaseGadget {
-    private final GadgetData data;
-    private final Position pos;
-    private final Position rot;
+    @Getter private final GadgetData gadgetData;
+    @Getter(onMethod = @__(@Override)) @Setter
     private int gadgetId;
 
-    private int state;
-    private int pointType;
-    private GadgetContent content;
-    private Int2FloatOpenHashMap fightProp;
-    private SceneGadget metaGadget;
+    @Getter @Setter private int state;
+    @Getter @Setter private int pointType;
+    @Getter private GadgetContent content;
+    @Getter(onMethod = @__(@Override), lazy = true)
+    private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
+    @Getter @Setter 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(configGadget);
-    }
-
     public EntityGadget(Scene scene, int gadgetId, Position pos) {
-        this(scene, gadgetId, pos, new Position());
-    }
-
-    public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
-        this(scene, gadgetId, pos, rot);
-        this.content = content;
+        this(scene, gadgetId, pos, null, null);
     }
 
-    public GadgetData getGadgetData() {
-        return data;
-    }
-
-    @Override
-    public Position getPosition() {
-        return this.pos;
-    }
-
-    @Override
-    public Position getRotation() {
-        return this.rot;
-    }
-
-    public int getGadgetId() {
-        return gadgetId;
+    public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
+        this(scene, gadgetId, pos, rot, null);
     }
 
-    public void setGadgetId(int gadgetId) {
+    public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
+        super(scene, pos, rot);
+        this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
+        this.configGadget = Optional.ofNullable(this.gadgetData).map(GadgetData::getJsonName).map(GameData.getGadgetConfigData()::get).orElse(null);
+        this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
         this.gadgetId = gadgetId;
-    }
-
-    public int getState() {
-        return state;
-    }
-
-    public void setState(int state) {
-        this.state = state;
+        this.content = content;
+        fillFightProps(configGadget);
     }
 
     public void updateState(int state) {
@@ -108,39 +76,18 @@ public class EntityGadget extends EntityBaseGadget {
         getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
     }
 
-    public int getPointType() {
-        return pointType;
-    }
-
-    public void setPointType(int pointType) {
-        this.pointType = pointType;
-    }
-
-    public GadgetContent getContent() {
-        return content;
-    }
-
-    @Deprecated // Dont use!
+    @Deprecated(forRemoval = true) // Dont use!
     public void setContent(GadgetContent content) {
         this.content = this.content == null ? content : this.content;
     }
 
-    public SceneGadget getMetaGadget() {
-        return metaGadget;
-    }
-
-    public void setMetaGadget(SceneGadget metaGadget) {
-        this.metaGadget = metaGadget;
-    }
-
     // TODO refactor
     public void buildContent() {
-        if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
+        if (this.getContent() != null || this.getGadgetData() == null || this.getGadgetData().getType() == null) {
             return;
         }
 
-        EntityType type = getGadgetData().getType();
-        GadgetContent content = switch (type) {
+        this.content = switch (this.getGadgetData().getType()) {
             case GatherPoint -> new GadgetGatherPoint(this);
             case GatherObject -> new GadgetGatherObject(this);
             case Worktop -> new GadgetWorktop(this);
@@ -149,14 +96,6 @@ public class EntityGadget extends EntityBaseGadget {
             case Gadget -> new GadgetObject(this);
             default -> null;
         };
-
-        this.content = content;
-    }
-
-    @Override
-    public Int2FloatOpenHashMap getFightProperties() {
-        if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
-        return this.fightProp;
     }
 
     @Override
@@ -216,7 +155,7 @@ public class EntityGadget extends EntityBaseGadget {
         entityInfo.addPropList(pair);
 
         // We do not use the getter to null check because the getter will create a fight prop map if it is null
-        if (this.fightProp != null) {
+        if (this.fightProperties != null) {
             addAllFightPropsToEntityInfo(entityInfo);
         }
 
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java
index c4befb40..cd3d81d6 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java
@@ -25,57 +25,33 @@ import emu.grasscutter.net.proto.VectorOuterClass.Vector;
 import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
 import emu.grasscutter.utils.Position;
 import emu.grasscutter.utils.ProtoHelper;
-import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2FloatMap;
+import lombok.Getter;
 
 public class EntityItem extends EntityBaseGadget {
-    private final Position pos;
-    private final Position rot;
-
-    private final GameItem item;
-    private final long guid;
-
-    private final boolean share;
+    @Getter private final GameItem item;
+    @Getter private final long guid;
+    @Getter private final boolean share;
 
     public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
-        super(scene);
-        this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
-        this.pos = new Position(pos);
-        this.rot = new Position();
-        this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
-        this.item = new GameItem(itemData, count);
-        this.share = true;
+        this(scene, player, itemData, pos, count, true);
     }
 
     // In official game, some drop items are shared to all players, and some other items are independent to all players
     // For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
     // but if you broke regional mine, when someone picked up the drop then it disappeared
     public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
-        super(scene);
+        super(scene, pos, null);
         this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
-        this.pos = new Position(pos);
-        this.rot = new Position();
         this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
         this.item = new GameItem(itemData, count);
         this.share = share;
     }
 
-    @Override
-    public int getId() {
-        return this.id;
-    }
-
-    private GameItem getItem() {
-        return this.item;
-    }
-
     public ItemData getItemData() {
         return this.getItem().getItemData();
     }
 
-    public long getGuid() {
-        return guid;
-    }
-
     public int getCount() {
         return this.getItem().getCount();
     }
@@ -85,24 +61,7 @@ public class EntityItem extends EntityBaseGadget {
         return this.getItemData().getGadgetId();
     }
 
-    @Override
-    public Position getPosition() {
-        return this.pos;
-    }
-
-    @Override
-    public Position getRotation() {
-        return this.rot;
-    }
-
-    @Override
-    public Int2FloatOpenHashMap getFightProperties() {
-        return null;
-    }
-
-    public boolean isShare() {
-        return share;
-    }
+    @Override public Int2FloatMap getFightProperties() {return null;}
 
     @Override
     public void onInteract(Player player, GadgetInteractReq interactReq) {
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java
index 31e4b863..19727b11 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java
@@ -1,14 +1,12 @@
 package emu.grasscutter.game.entity;
 
-import emu.grasscutter.Grasscutter;
+import java.util.Optional;
+
 import emu.grasscutter.data.GameData;
-import emu.grasscutter.data.common.ItemParamData;
 import emu.grasscutter.data.common.PropGrowCurve;
 import emu.grasscutter.data.excels.EnvAnimalGatherConfigData;
-import emu.grasscutter.data.excels.ItemData;
 import emu.grasscutter.data.excels.MonsterCurveData;
 import emu.grasscutter.data.excels.MonsterData;
-import emu.grasscutter.game.inventory.GameItem;
 import emu.grasscutter.game.player.Player;
 import emu.grasscutter.game.props.ActionReason;
 import emu.grasscutter.game.props.EntityIdType;
@@ -16,13 +14,11 @@ import emu.grasscutter.game.props.FightProperty;
 import emu.grasscutter.game.props.PlayerProperty;
 import emu.grasscutter.game.props.WatcherTriggerType;
 import emu.grasscutter.game.world.Scene;
-import emu.grasscutter.net.proto.VisionTypeOuterClass;
 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.MonsterBornTypeOuterClass.MonsterBornType;
 import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
@@ -35,31 +31,32 @@ import emu.grasscutter.scripts.constants.EventType;
 import emu.grasscutter.scripts.data.ScriptArgs;
 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;
-    private final Int2FloatOpenHashMap fightProp;
-
-    private final Position pos;
-    private final Position rot;
-    private final Position bornPos;
-    private final int level;
+    @Getter private final MonsterData monsterData;
+    @Getter(onMethod = @__(@Override))
+    private final Int2FloatOpenHashMap fightProperties;
+
+    @Getter(onMethod = @__(@Override))
+    private final Position position;
+    @Getter(onMethod = @__(@Override))
+    private final Position rotation;
+    @Getter private final Position bornPos;
+    @Getter private final int level;
     private int weaponEntityId;
-    private int poseId;
-    @Getter @Setter
-    private int aiId=-1;
+    @Getter @Setter private int poseId;
+    @Getter @Setter private int aiId = -1;
 
     public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
         super(scene);
         this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
         this.monsterData = monsterData;
-        this.fightProp = new Int2FloatOpenHashMap();
-        this.pos = new Position(pos);
-        this.rot = new Position();
+        this.fightProperties = new Int2FloatOpenHashMap();
+        this.position = new Position(pos);
+        this.rotation = new Position();
         this.bornPos = getPosition().clone();
         this.level = level;
 
@@ -71,59 +68,19 @@ public class EntityMonster extends GameEntity {
         this.recalcStats();
     }
 
-    @Override
-    public int getId() {
-        return this.id;
-    }
-
-    public MonsterData getMonsterData() {
-        return monsterData;
-    }
-
     public int getMonsterWeaponId() {
-        return getMonsterData().getWeaponId();
+        return this.getMonsterData().getWeaponId();
     }
 
     private int getMonsterId() {
         return this.getMonsterData().getId();
     }
 
-    public int getLevel() {
-        return level;
-    }
-
-    @Override
-    public Position getPosition() {
-        return this.pos;
-    }
-
-    @Override
-    public Position getRotation() {
-        return this.rot;
-    }
-
-    public Position getBornPos() {
-        return bornPos;
-    }
-
-    @Override
-    public Int2FloatOpenHashMap getFightProperties() {
-        return fightProp;
-    }
-
     @Override
     public boolean isAlive() {
         return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
     }
 
-    public int getPoseId() {
-        return poseId;
-    }
-
-    public void setPoseId(int poseId) {
-        this.poseId = poseId;
-    }
-
     @Override
     public void onInteract(Player player, GadgetInteractReq interactReq) {
         EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
@@ -163,27 +120,24 @@ public class EntityMonster extends GameEntity {
     @Override
     public void onDeath(int killerId) {
         super.onDeath(killerId); // Invoke super class's onDeath() method.
+        var scene = this.getScene();
+        var challenge = Optional.ofNullable(scene.getChallenge());
+        var scriptManager = scene.getScriptManager();
+
+        Optional.ofNullable(this.getSpawnEntry()).ifPresent(scene.getDeadSpawnedEntities()::add);
 
-        if (this.getSpawnEntry() != null) {
-            this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
-        }
         // first set the challenge data
-        if (getScene().getChallenge() != null) {
-            getScene().getChallenge().onMonsterDeath(this);
-        }
-        if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
-            if (getScene().getScriptManager().getScriptMonsterSpawnService() != null) {
-                getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
-            }
+        challenge.ifPresent(c -> c.onMonsterDeath(this));
+
+        if (scriptManager.isInit() && this.getGroupId() > 0) {
+            Optional.ofNullable(scriptManager.getScriptMonsterSpawnService()).ifPresent(s -> s.onMonsterDead(this));
+
             // prevent spawn monster after success
-            if (getScene().getChallenge() != null && getScene().getChallenge().inProgress()) {
-                getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
-            }else if (getScene().getChallenge() == null) {
-                getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
-            }
+            if (challenge.map(c -> c.inProgress()).orElse(true))
+                scriptManager.callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
         }
         // Battle Pass trigger
-        getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
+        scene.getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
     }
 
     public void recalcStats() {
@@ -197,18 +151,7 @@ public class EntityMonster extends GameEntity {
         this.getFightProperties().clear();
 
         // Base stats
-        this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp());
-        this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack());
-        this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense());
-
-        this.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, data.getPhysicalSubHurt());
-        this.setFightProperty(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, .1f);
-        this.setFightProperty(FightProperty.FIGHT_PROP_ELEC_SUB_HURT, data.getElecSubHurt());
-        this.setFightProperty(FightProperty.FIGHT_PROP_WATER_SUB_HURT, data.getWaterSubHurt());
-        this.setFightProperty(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, data.getGrassSubHurt());
-        this.setFightProperty(FightProperty.FIGHT_PROP_WIND_SUB_HURT, data.getWindSubHurt());
-        this.setFightProperty(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, .1f);
-        this.setFightProperty(FightProperty.FIGHT_PROP_ICE_SUB_HURT, data.getIceSubHurt());
+        MonsterData.definedFightProperties.forEach(prop -> this.setFightProperty(prop, data.getFightProperty(prop)));
 
         // Level curve
         MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
@@ -220,18 +163,8 @@ public class EntityMonster extends GameEntity {
         }
 
         // Set % stats
-        this.setFightProperty(
-            FightProperty.FIGHT_PROP_MAX_HP,
-            (getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
-        );
-        this.setFightProperty(
-            FightProperty.FIGHT_PROP_CUR_ATTACK,
-            (getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
-        );
-        this.setFightProperty(
-            FightProperty.FIGHT_PROP_CUR_DEFENSE,
-            (getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
-        );
+        FightProperty.forEachCompoundProperty(c -> this.setFightProperty(c.getResult(),
+            this.getFightProperty(c.getFlat()) + (this.getFightProperty(c.getBase()) * (1f + this.getFightProperty(c.getPercent())))));
 
         // Set current hp
         this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
@@ -239,14 +172,14 @@ public class EntityMonster extends GameEntity {
 
     @Override
     public SceneEntityInfo toProto() {
-        EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
+        var authority = EntityAuthorityInfo.newBuilder()
                 .setAbilityInfo(AbilitySyncStateInfo.newBuilder())
                 .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
                 .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
                 .setBornPos(this.getBornPos().toProto())
                 .build();
 
-        SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
+        var entityInfo = SceneEntityInfo.newBuilder()
                 .setEntityId(getId())
                 .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
                 .setMotionInfo(this.getMotionInfo())
@@ -257,13 +190,12 @@ public class EntityMonster extends GameEntity {
 
         this.addAllFightPropsToEntityInfo(entityInfo);
 
-        PropPair pair = PropPair.newBuilder()
-                .setType(PlayerProperty.PROP_LEVEL.getId())
-                .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
-                .build();
-        entityInfo.addPropList(pair);
+        entityInfo.addPropList(PropPair.newBuilder()
+            .setType(PlayerProperty.PROP_LEVEL.getId())
+            .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
+            .build());
 
-        SceneMonsterInfo.Builder monsterInfo = SceneMonsterInfo.newBuilder()
+        var monsterInfo = SceneMonsterInfo.newBuilder()
                 .setMonsterId(getMonsterId())
                 .setGroupId(this.getGroupId())
                 .setConfigId(this.getConfigId())
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityNPC.java b/src/main/java/emu/grasscutter/game/entity/EntityNPC.java
index 6d1fead5..4a26f251 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityNPC.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityNPC.java
@@ -5,14 +5,16 @@ import emu.grasscutter.game.world.Scene;
 import emu.grasscutter.net.proto.*;
 import emu.grasscutter.scripts.data.SceneNPC;
 import emu.grasscutter.utils.Position;
-import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2FloatMap;
+import lombok.Getter;
 
 public class EntityNPC extends GameEntity{
-
+    @Getter(onMethod = @__(@Override))
     private final Position position;
+    @Getter(onMethod = @__(@Override))
     private final Position rotation;
     private final SceneNPC metaNpc;
-    private final int suiteId;
+    @Getter private final int suiteId;
 
     public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) {
         super(scene);
@@ -27,24 +29,7 @@ public class EntityNPC extends GameEntity{
 
     }
 
-    @Override
-    public Int2FloatOpenHashMap getFightProperties() {
-        return null;
-    }
-
-    @Override
-    public Position getPosition() {
-        return position;
-    }
-
-    @Override
-    public Position getRotation() {
-        return rotation;
-    }
-
-    public int getSuiteId() {
-        return suiteId;
-    }
+    @Override public Int2FloatMap getFightProperties() {return null;}
 
     @Override
     public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityRegion.java b/src/main/java/emu/grasscutter/game/entity/EntityRegion.java
index 32fac134..fef33ac8 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityRegion.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityRegion.java
@@ -5,7 +5,7 @@ import emu.grasscutter.game.world.Scene;
 import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
 import emu.grasscutter.scripts.data.SceneRegion;
 import emu.grasscutter.utils.Position;
-import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2FloatMap;
 import lombok.Getter;
 
 import java.util.Set;
@@ -57,20 +57,11 @@ public class EntityRegion extends GameEntity{
     }
     public boolean entityLeave() {return this.entityLeave;}
     public void resetEntityLeave() {this.entityLeave = false;}
-    @Override
-    public Int2FloatOpenHashMap getFightProperties() {
-        return null;
-    }
+    @Override public Int2FloatMap getFightProperties() {return null;}
 
-    @Override
-    public Position getPosition() {
-        return position;
-    }
+    @Override public Position getPosition() {return position;}
 
-    @Override
-    public Position getRotation() {
-        return null;
-    }
+    @Override public Position getRotation() {return null;}
 
     @Override
     public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java
index 462a0d40..7bb4f0da 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java
@@ -12,7 +12,6 @@ import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncState
 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.FightPropPair;
 import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
 import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
 import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
@@ -36,10 +35,8 @@ import java.util.List;
 public class EntityVehicle extends EntityBaseGadget {
 
     @Getter private final Player owner;
-    private final Int2FloatMap fightProp;
-
-    private final Position pos;
-    private final Position rot;
+    @Getter(onMethod = @__(@Override))
+    private final Int2FloatMap fightProperties;
 
     @Getter private final int pointId;
     @Getter private final int gadgetId;
@@ -49,12 +46,10 @@ public class EntityVehicle extends EntityBaseGadget {
     @Nullable @Getter private ConfigGadget configGadget;
 
     public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
-        super(scene);
+        super(scene, pos, rot);
         this.owner = player;
         this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
-        this.fightProp = new Int2FloatOpenHashMap();
-        this.pos = new Position(pos);
-        this.rot = new Position(rot);
+        this.fightProperties = new Int2FloatOpenHashMap();
         this.gadgetId = gadgetId;
         this.pointId = pointId;
         this.curStamina = 240; // might be in configGadget.GCALKECLLLP.JBAKBEFIMBN.ANBMPHPOALP
@@ -74,19 +69,6 @@ public class EntityVehicle extends EntityBaseGadget {
         this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0);
     }
 
-    @Override
-    public Int2FloatMap getFightProperties() {
-        return fightProp;
-    }
-
-    @Override
-    public Position getPosition() { return this.pos; }
-
-    @Override
-    public Position getRotation() {
-        return this.rot;
-    }
-
     @Override
     public SceneEntityInfo toProto() {
 
diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java
index 220edcb4..aed62ef3 100644
--- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java
+++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java
@@ -33,7 +33,7 @@ public abstract class GameEntity {
     @Getter @Setter private int configId;
     @Getter @Setter private int groupId;
 
-    private MotionState moveState;
+    @Getter @Setter private MotionState motionState;
     @Getter @Setter private int lastMoveSceneTimeMs;
     @Getter @Setter private int lastMoveReliableSeq;
 
@@ -45,7 +45,7 @@ public abstract class GameEntity {
 
     public GameEntity(Scene scene) {
         this.scene = scene;
-        this.moveState = MotionState.MOTION_STATE_NONE;
+        this.motionState = MotionState.MOTION_STATE_NONE;
     }
 
     public int getEntityType() {
@@ -84,14 +84,6 @@ public abstract class GameEntity {
 
     public abstract Position getRotation();
 
-    public MotionState getMotionState() {
-        return moveState;
-    }
-
-    public void setMotionState(MotionState moveState) {
-        this.moveState = moveState;
-    }
-
     public void setFightProperty(FightProperty prop, float value) {
         this.getFightProperties().put(prop.getId(), value);
     }
diff --git a/src/main/java/emu/grasscutter/game/entity/platform/EntityPlatform.java b/src/main/java/emu/grasscutter/game/entity/platform/EntityPlatform.java
index 3ec85c35..cc946d68 100644
--- a/src/main/java/emu/grasscutter/game/entity/platform/EntityPlatform.java
+++ b/src/main/java/emu/grasscutter/game/entity/platform/EntityPlatform.java
@@ -20,12 +20,12 @@ import javax.annotation.Nullable;
 public class EntityPlatform extends EntityBaseGadget {
     @Getter
     private final Player owner;
+    @Getter(onMethod = @__(@Override))
     private final int gadgetId;
     @Getter
     private final EntityClientGadget gadget;
-    private final Int2FloatMap fightProp;
-    private final Position pos;
-    private final Position rot;
+    @Getter(onMethod = @__(@Override))
+    private final Int2FloatMap fightProperties;
     @Nullable
     @Getter
     private ConfigGadget configGadget;
@@ -39,13 +39,11 @@ public class EntityPlatform extends EntityBaseGadget {
     private boolean isActive;
 
     public EntityPlatform(EntityClientGadget gadget, Scene scene, Player player, int gadgetId, Position pos, Position rot, MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType) {
-        super(scene);
+        super(scene, pos, rot);
         this.gadget = gadget;
         this.owner = player;
         this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
-        this.fightProp = new Int2FloatOpenHashMap();
-        this.pos = new Position(pos);
-        this.rot = new Position(rot);
+        this.fightProperties = new Int2FloatOpenHashMap();
         this.movingPlatformType = movingPlatformType;
         this.gadgetId = gadgetId;
         GadgetData data = GameData.getGadgetDataMap().get(gadgetId);
@@ -56,26 +54,6 @@ public class EntityPlatform extends EntityBaseGadget {
         fillFightProps(configGadget);
     }
 
-    @Override
-    public int getGadgetId() {
-        return gadgetId;
-    }
-
-    @Override
-    public Int2FloatMap getFightProperties() {
-        return fightProp;
-    }
-
-    @Override
-    public Position getPosition() {
-        return pos;
-    }
-
-    @Override
-    public Position getRotation() {
-        return rot;
-    }
-
     @Override
     public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
         var platform = PlatformInfoOuterClass.PlatformInfo.newBuilder()
diff --git a/src/main/java/emu/grasscutter/game/props/FightProperty.java b/src/main/java/emu/grasscutter/game/props/FightProperty.java
index a51ecdba..599b1564 100644
--- a/src/main/java/emu/grasscutter/game/props/FightProperty.java
+++ b/src/main/java/emu/grasscutter/game/props/FightProperty.java
@@ -4,6 +4,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 import static java.util.Map.entry;
 
@@ -12,6 +13,7 @@ import java.util.stream.Stream;
 
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import lombok.Getter;
 
 public enum FightProperty {
 	FIGHT_PROP_NONE(0),
@@ -195,8 +197,37 @@ public enum FightProperty {
         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); 
-    
+        FIGHT_PROP_CUR_ROCK_ENERGY, FIGHT_PROP_CUR_HP, FIGHT_PROP_MAX_HP, FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_CUR_DEFENSE);
+
+    @Getter
+    public static class CompoundProperty {
+        private FightProperty result;
+        private FightProperty base;
+        private FightProperty percent;
+        private FightProperty flat;
+
+        public CompoundProperty(FightProperty result, FightProperty base, FightProperty percent, FightProperty flat) {
+            this.result = result;
+            this.base = base;
+            this.percent = percent;
+            this.flat = flat;
+        }
+    }
+
+    private static Map<FightProperty, CompoundProperty> compoundProperties = Map.ofEntries(
+        entry(FIGHT_PROP_MAX_HP, new CompoundProperty(FIGHT_PROP_MAX_HP, FIGHT_PROP_BASE_HP, FIGHT_PROP_HP_PERCENT, FIGHT_PROP_HP)),
+        entry(FIGHT_PROP_CUR_ATTACK, new CompoundProperty(FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_BASE_ATTACK, FIGHT_PROP_ATTACK_PERCENT, FIGHT_PROP_ATTACK)),
+        entry(FIGHT_PROP_CUR_DEFENSE, new CompoundProperty(FIGHT_PROP_CUR_DEFENSE, FIGHT_PROP_BASE_DEFENSE, FIGHT_PROP_DEFENSE_PERCENT, FIGHT_PROP_DEFENSE))
+    );
+
+    public static CompoundProperty getCompoundProperty(FightProperty result) {
+        return compoundProperties.get(result);
+    }
+
+    public static void forEachCompoundProperty(Consumer<CompoundProperty> consumer) {
+        compoundProperties.values().forEach(consumer);
+    }
+
     public static boolean isPercentage(FightProperty prop) {
         return !flatProps.contains(prop);
     }
-- 
GitLab