diff --git a/src/main/java/emu/grasscutter/data/common/DynamicFloat.java b/src/main/java/emu/grasscutter/data/common/DynamicFloat.java index 1e5e2ca3d..ea29e7074 100644 --- a/src/main/java/emu/grasscutter/data/common/DynamicFloat.java +++ b/src/main/java/emu/grasscutter/data/common/DynamicFloat.java @@ -3,13 +3,10 @@ package emu.grasscutter.data.common; import emu.grasscutter.data.excels.ProudSkillData; import emu.grasscutter.game.ability.Ability; import it.unimi.dsi.fastutil.floats.FloatArrayList; -import it.unimi.dsi.fastutil.objects.Object2FloatArrayMap; -import it.unimi.dsi.fastutil.objects.Object2FloatMap; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Optional; -import lombok.Getter; -import lombok.val; +import it.unimi.dsi.fastutil.objects.*; +import lombok.*; + +import java.util.*; @Getter public class DynamicFloat { @@ -114,6 +111,7 @@ public class DynamicFloat { s = s.substring(1); } else if (s.startsWith("-%")) { s = s.substring(2); + negative = true; } this.op = Op.KEY; diff --git a/src/main/java/emu/grasscutter/data/excels/codex/CodexWeaponData.java b/src/main/java/emu/grasscutter/data/excels/codex/CodexWeaponData.java index 24c2826d5..be441b9bf 100644 --- a/src/main/java/emu/grasscutter/data/excels/codex/CodexWeaponData.java +++ b/src/main/java/emu/grasscutter/data/excels/codex/CodexWeaponData.java @@ -8,6 +8,7 @@ import emu.grasscutter.data.ResourceType; public class CodexWeaponData extends GameResource { private int Id; private int weaponId; + private int gadgetId; private int sortOrder; public int getSortOrder() { @@ -18,6 +19,10 @@ public class CodexWeaponData extends GameResource { return weaponId; } + public int getGadgetId() { + return weaponId; + } + public int getId() { return Id; } diff --git a/src/main/java/emu/grasscutter/game/ability/actions/ActionSetGlobalValueToOverrideMap.java b/src/main/java/emu/grasscutter/game/ability/actions/ActionSetGlobalValueToOverrideMap.java index 6934044fa..1997a755d 100644 --- a/src/main/java/emu/grasscutter/game/ability/actions/ActionSetGlobalValueToOverrideMap.java +++ b/src/main/java/emu/grasscutter/game/ability/actions/ActionSetGlobalValueToOverrideMap.java @@ -32,6 +32,8 @@ public class ActionSetGlobalValueToOverrideMap extends AbilityActionHandler { entity.getGlobalAbilityValues().put(globalValueKey, globalValue); + //TODO: ChangeServerGlobalValueNotify + return true; } } diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index 7992e98dd..211cd37f2 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -25,6 +25,7 @@ import emu.grasscutter.data.excels.weapon.WeaponCurveData; import emu.grasscutter.data.excels.weapon.WeaponPromoteData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.entity.EntityWeapon; import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.ItemType; @@ -459,10 +460,14 @@ public class Avatar { } // Set equip - getEquips().put(itemEquipType.getValue(), item); + this.getEquips().put(itemEquipType.getValue(), item); if (itemEquipType == EquipType.EQUIP_WEAPON && getPlayer().getWorld() != null) { - item.setWeaponEntityId(this.getPlayer().getWorld().getNextEntityId(EntityIdType.WEAPON)); + if (!(item.getWeaponEntity() != null && item.getWeaponEntity().getScene() == getPlayer().getScene())) { + item.setWeaponEntity(new EntityWeapon(this.getPlayer().getScene(), item.getItemData().getGadgetId())); + this.getPlayer().getScene().getWeaponEntities().put(item.getWeaponEntity().getId(), item.getWeaponEntity()); + } + //item.setWeaponEntityId(this.getPlayer().getWorld().getNextEntityId(EntityIdType.WEAPON)); } item.setEquipCharacter(this.getAvatarId()); @@ -1257,7 +1262,11 @@ public class Avatar { item.setEquipCharacter(this.getAvatarId()); item.setOwner(player); if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) { - item.setWeaponEntityId(player.getWorld().getNextEntityId(EntityIdType.WEAPON)); + if (!(item.getWeaponEntity() != null && item.getWeaponEntity().getScene() == player.getScene())) { + item.setWeaponEntity(new EntityWeapon(player.getScene(), item.getItemData().getGadgetId())); + player.getScene().getWeaponEntities().put(item.getWeaponEntity().getId(), item.getWeaponEntity()); + } + player.sendPacket(new PacketAvatarEquipChangeNotify(this, item)); } }); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index 16ff1352c..8f285e7ea 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -63,7 +63,10 @@ public class EntityAvatar extends GameEntity { var weapon = this.getAvatar().getWeapon(); if (weapon != null) { - weapon.setWeaponEntityId(world.getNextEntityId(EntityIdType.WEAPON)); + if (!(weapon.getWeaponEntity() != null && weapon.getWeaponEntity().getScene() == scene)) { + weapon.setWeaponEntity(new EntityWeapon(this.getPlayer().getScene(), weapon.getItemData().getGadgetId())); + scene.getWeaponEntities().put(weapon.getWeaponEntity().getId(), weapon.getWeaponEntity()); + } } } else { Grasscutter.getLogger() @@ -107,7 +110,10 @@ public class EntityAvatar extends GameEntity { */ public int getWeaponEntityId() { var avatar = this.getAvatar(); - return avatar.getWeapon() == null ? 0 : avatar.getWeapon().getWeaponEntityId(); + + if (avatar.getWeapon() != null && avatar.getWeapon().getWeaponEntity() != null) { + return avatar.getWeapon().getWeaponEntity().getId(); + } else return 0; } @Override diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index b5d23085a..75f76be6d 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -55,7 +55,7 @@ public class EntityMonster extends GameEntity { @Getter private final ConfigEntityMonster configEntityMonster; @Getter private final Position bornPos; @Getter private final int level; - @Getter private int weaponEntityId; + @Getter private EntityWeapon weaponEntity; @Getter @Setter private int poseId; @Getter @Setter private int aiId = -1; @@ -81,7 +81,9 @@ public class EntityMonster extends GameEntity { // Monster weapon if (getMonsterWeaponId() > 0) { - this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON); + this.weaponEntity = new EntityWeapon(scene, getMonsterWeaponId()); + scene.getWeaponEntities().put(this.weaponEntity.getId(), this.weaponEntity); + //this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON); } this.recalcStats(); @@ -349,7 +351,7 @@ public class EntityMonster extends GameEntity { if (this.getMonsterWeaponId() > 0) { SceneWeaponInfo weaponInfo = SceneWeaponInfo.newBuilder() - .setEntityId(this.weaponEntityId) + .setEntityId(this.getWeaponEntity() != null ? this.getWeaponEntity().getId() : 0) .setGadgetId(this.getMonsterWeaponId()) .setAbilityInfo(AbilitySyncStateInfo.newBuilder()) .build(); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityWeapon.java b/src/main/java/emu/grasscutter/game/entity/EntityWeapon.java new file mode 100644 index 000000000..d9c2f4d90 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/EntityWeapon.java @@ -0,0 +1,85 @@ +package emu.grasscutter.game.entity; + +import javax.annotation.Nullable; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.AbilityData; +import emu.grasscutter.data.binout.config.ConfigEntityGadget; +import emu.grasscutter.data.binout.config.fields.ConfigAbilityData; +import emu.grasscutter.data.excels.GadgetData; +import emu.grasscutter.game.entity.gadget.GadgetContent; +import emu.grasscutter.game.props.EntityIdType; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; +import emu.grasscutter.scripts.EntityControllerScriptManager; +import emu.grasscutter.utils.Position; +import it.unimi.dsi.fastutil.ints.Int2FloatMap; +import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString(callSuper = true) +public class EntityWeapon extends EntityBaseGadget { + @Getter private final GadgetData gadgetData; + @Getter(onMethod = @__(@Override)) @Setter + private int gadgetId; + @Nullable @Getter + private ConfigEntityGadget configGadget; + + @Getter(onMethod = @__(@Override), lazy = true) + private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap(); + + @Getter private final Position bornPos; + @Getter private final Position bornRot; + + public EntityWeapon(Scene scene, int gadgetId) { + super(scene); + + this.gadgetData = GameData.getGadgetDataMap().get(gadgetId); + if (gadgetData!=null && gadgetData.getJsonName()!=null) { + this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName()); + } + + this.gadgetId = gadgetId; + this.gadgetId = gadgetId; + this.bornPos = this.getPosition().clone(); + this.bornRot = this.getRotation().clone(); + fillFightProps(configGadget); + if(GameData.getGadgetMappingMap().containsKey(gadgetId)) { + String controllerName = GameData.getGadgetMappingMap().get(gadgetId).getServerController(); + setEntityController(EntityControllerScriptManager.getGadgetController(controllerName)); + if(getEntityController() == null) { + Grasscutter.getLogger().warn("Gadget controller {} not found", controllerName); + } + } + + this.id = scene.getWorld().getNextEntityId(EntityIdType.WEAPON); + Grasscutter.getLogger().warn("New weapon entity id {} at scene {}", this.id, this.getScene().getId()); + + initAbilities(); + } + + private void addConfigAbility(ConfigAbilityData abilityData){ + AbilityData data = GameData.getAbilityData(abilityData.getAbilityName()); + if(data != null) + getScene().getWorld().getHost().getAbilityManager().addAbilityToEntity( + this, data); + } + + @Override + public void initAbilities() { + //TODO: handle predynamic, static and dynamic here + if(this.configGadget != null && this.configGadget.getAbilities() != null) { + for (var ability : this.configGadget.getAbilities()) { + addConfigAbility(ability); + } + } + } + + @Override + public SceneEntityInfo toProto() { + return null; + } +} diff --git a/src/main/java/emu/grasscutter/game/inventory/GameItem.java b/src/main/java/emu/grasscutter/game/inventory/GameItem.java index f93d29c29..8bf300114 100644 --- a/src/main/java/emu/grasscutter/game/inventory/GameItem.java +++ b/src/main/java/emu/grasscutter/game/inventory/GameItem.java @@ -8,6 +8,7 @@ import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData; import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData; import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.entity.EntityWeapon; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; @@ -56,7 +57,7 @@ public class GameItem { @Getter private List appendPropIdList; @Getter @Setter private int equipCharacter; - @Transient @Getter @Setter private int weaponEntityId; + @Transient @Getter @Setter private EntityWeapon weaponEntity; @Transient @Getter private boolean newItem = false; public GameItem() { @@ -275,14 +276,13 @@ public class GameItem { } public SceneWeaponInfo createSceneWeaponInfo() { - SceneWeaponInfo.Builder weaponInfo = - SceneWeaponInfo.newBuilder() - .setEntityId(this.getWeaponEntityId()) - .setItemId(this.getItemId()) - .setGuid(this.getGuid()) - .setLevel(this.getLevel()) - .setGadgetId(this.getItemData().getGadgetId()) - .setAbilityInfo(AbilitySyncStateInfo.newBuilder().setIsInited(getAffixes().size() > 0)); + var weaponInfo = SceneWeaponInfo.newBuilder() + .setEntityId(this.getWeaponEntity() != null ? this.getWeaponEntity().getId() : 0) + .setItemId(this.getItemId()) + .setGuid(this.getGuid()) + .setLevel(this.getLevel()) + .setGadgetId(this.getItemData().getGadgetId()) + .setAbilityInfo(AbilitySyncStateInfo.newBuilder().setIsInited(getAffixes().size() > 0)); if (this.getAffixes() != null && this.getAffixes().size() > 0) { for (int affix : this.getAffixes()) { diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 61f788a68..8d4ddc7c5 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -55,6 +55,7 @@ public final class Scene { @Getter private final SceneData sceneData; @Getter private final List players; @Getter private final Map entities; + @Getter private final Map weaponEntities; @Getter private final Set spawnedEntities; @Getter private final Set deadSpawnedEntities; @Getter private final Set loadedBlocks; @@ -85,6 +86,7 @@ public final class Scene { this.sceneData = sceneData; this.players = new CopyOnWriteArrayList<>(); this.entities = new ConcurrentHashMap<>(); + this.weaponEntities = new ConcurrentHashMap<>(); this.prevScene = 3; this.sceneRoutes = GameData.getSceneRoutes(getId()); @@ -126,8 +128,9 @@ public final class Scene { // Check for an avatar. var entity = this.entities.get(id); - if (entity == null && (id >> 24) == EntityType.Avatar.getValue()) { - for (var player : this.getPlayers()) { + if (entity == null) entity = this.weaponEntities.get(id); + if (entity == null && (id >> 24) == EntityIdType.AVATAR.getId()) { + for (var player : getPlayers()) { for (var avatar : player.getTeamManager().getActiveTeam()) { if (avatar.getId() == id) return avatar; } @@ -797,7 +800,7 @@ public final class Scene { for (GameEntity entity : this.getEntities().values()) { var spawnEntry = entity.getSpawnEntry(); - if (spawnEntry != null && !visible.contains(spawnEntry)) { + if (spawnEntry != null && !(entity instanceof EntityWeapon) && !visible.contains(spawnEntry)) { toRemove.add(entity); spawnedEntities.remove(spawnEntry); }