diff --git a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java index ae67deb19..612951d6a 100644 --- a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java +++ b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java @@ -19,6 +19,8 @@ import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ElementType; import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; +import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier; +import emu.grasscutter.net.proto.AbilityInvocationsNotifyOuterClass.AbilityInvocationsNotify; import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead; import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange; @@ -42,6 +44,14 @@ public class AbilityManager { public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception { // Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray())); + + //AbilityIdentifier identifier = AbilityIdentifier.parseFrom(invoke.getAbilityData()); + //AbilityInvocationsNotify notify = AbilityInvocationsNotify.parseFrom(invoke.getAbilityData()); + + + //Grasscutter.getLogger().info("Ability id: " + Integer.toString(invoke.getEntityId())); + //Grasscutter.getLogger().info("invoke count: " + Double.toString(invoke.getTotalTickTime())); + switch (invoke.getArgumentType()) { case ABILITY_META_OVERRIDE_PARAM: handleOverrideParam(invoke); diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyManager.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyManager.java index c48b6c270..7bedfa140 100644 --- a/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyManager.java +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyManager.java @@ -31,6 +31,7 @@ import java.io.Reader; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; import com.google.gson.reflect.TypeToken; import com.google.protobuf.InvalidProtocolBufferException; @@ -38,6 +39,7 @@ import com.google.protobuf.InvalidProtocolBufferException; public class EnergyManager { private final Player player; private final static Int2ObjectMap> energyDropData = new Int2ObjectOpenHashMap<>(); + private final static Int2ObjectMap> skillParticleGenerationData = new Int2ObjectOpenHashMap<>(); public EnergyManager(Player player) { this.player = player; @@ -61,12 +63,26 @@ public class EnergyManager { catch (Exception ex) { Grasscutter.getLogger().error("Unable to load energy drop data.", ex); } + + // Read the data for particle generation from skills + try (Reader fileReader = new InputStreamReader(DataLoader.load("SkillParticleGeneration.json"))) { + List skillParticleGenerationList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, SkillParticleGenerationEntry.class).getType()); + + for (SkillParticleGenerationEntry entry : skillParticleGenerationList) { + skillParticleGenerationData.put(entry.getAvatarId(), entry.getAmountList()); + } + + Grasscutter.getLogger().info("Skill particle generation data successfully loaded."); + } + catch (Exception ex) { + Grasscutter.getLogger().error("Unable to load skill particle generation data data.", ex); + } } /********** Particle creation for elemental skills. **********/ - private int getCastingAvatarIdForElemBall(int invokeEntityId) { + private int getCastingAvatarEntityIdForElemBall(int invokeEntityId) { // To determine the avatar that has cast the skill that caused the energy particle to be generated, // we have to look at the entity that has invoked the ability. This can either be that avatar directly, // or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar @@ -111,19 +127,41 @@ public class EnergyManager { // We can get that from the avatar's skill depot. int itemId = 2024; + // Generate 2 particles by default + int amount = 2; + // Try to fetch the avatar from the player's party and determine their element. // ToDo: Does this work in co-op? - int avatarId = getCastingAvatarIdForElemBall(invoke.getEntityId()); + int avatarEntityId = getCastingAvatarEntityIdForElemBall(invoke.getEntityId()); Optional avatarEntity = player.getTeamManager().getActiveTeam() .stream() - .filter(character -> character.getId() == avatarId) + .filter(character -> character.getId() == avatarEntityId) .findFirst(); + // Bug: invokes twice sometimes, Ayato, Keqing + // Amber not getting element properly + // ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin) if (avatarEntity.isPresent()) { Avatar avatar = avatarEntity.get().getAvatar(); - if (avatar != null) { + if (avatar != null) { + int avatarId = avatar.getAvatarId(); AvatarSkillDepotData skillDepotData = avatar.getSkillDepot(); + if (!skillParticleGenerationData.containsKey(avatarId)) { + Grasscutter.getLogger().warn("No particle generation data for avatarId {} found.", avatarId); + } + else { + int roll = ThreadLocalRandom.current().nextInt(0, 100); + int percentageStack = 0; + for (SkillParticleGenerationInfo info : skillParticleGenerationData.get(avatarId)) { + int chance = info.getChance(); + percentageStack += chance; + if (roll < percentageStack) { + amount = info.getValue(); + break; + } + } + } if (skillDepotData != null) { ElementType element = skillDepotData.getElementType(); @@ -147,7 +185,8 @@ public class EnergyManager { } // Generate the particle/orb. - generateElemBall(itemId, new Position(action.getPos()), 1); + for (int i = 0; i < amount; i++) + generateElemBall(itemId, new Position(action.getPos()), 1); } /********** diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationEntry.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationEntry.java new file mode 100644 index 000000000..826691e7d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationEntry.java @@ -0,0 +1,16 @@ +package emu.grasscutter.game.managers.EnergyManager; + +import java.util.List; + +public class SkillParticleGenerationEntry { + private int avatarId; + private List amountList; + + public int getAvatarId() { + return this.avatarId; + } + + public List getAmountList() { + return this.amountList; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationInfo.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationInfo.java new file mode 100644 index 000000000..525c3d843 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationInfo.java @@ -0,0 +1,14 @@ +package emu.grasscutter.game.managers.EnergyManager; + +public class SkillParticleGenerationInfo { + private int value; + private int chance; + + public int getValue() { + return this.value; + } + + public int getChance() { + return this.chance; + } +} diff --git a/src/main/resources/defaults/data/SkillParticleGeneration.json b/src/main/resources/defaults/data/SkillParticleGeneration.json new file mode 100644 index 000000000..683927b3b --- /dev/null +++ b/src/main/resources/defaults/data/SkillParticleGeneration.json @@ -0,0 +1,580 @@ +[ + { + "avatarId": 10000002, + "name": "Kamisato Ayaka", + "amountList": [ + { + "value": 4, + "chance": 50 + }, + { + "value": 5, + "chance": 50 + } + ] + }, + { + "avatarId": 10000003, + "name": "Jean", + "amountList": [ + { + "value": 2, + "chance": 33 + }, + { + "value": 3, + "chance": 67 + } + ] + }, + { + "avatarId": 10000005, + "name": "Traveler", + "amountList": [ + { + "value": 3, + "chance": 67 + }, + { + "value": 4, + "chance": 33 + } + ] + }, + { + "avatarId": 10000006, + "name": "Lisa", + "amountList": [ + { + "value": 5, + "chance": 100 + } + ] + }, + { + "avatarId": 10000007, + "name": "Traveler", + "amountList": [ + { + "value": 3, + "chance": 67 + }, + { + "value": 4, + "chance": 33 + } + ] + }, + { + "avatarId": 10000014, + "name": "Barbara", + "amountList": [ + { + "value": 0, + "chance": 100 + } + ] + }, + { + "avatarId": 10000015, + "name": "Kaeya", + "amountList": [ + { + "value": 2, + "chance": 33 + }, + { + "value": 3, + "chance": 67 + } + ] + }, + { + "avatarId": 10000016, + "name": "Diluc", + "amountList": [ + { + "value": 1, + "chance": 33 + }, + { + "value": 2, + "chance": 67 + } + ] + }, + { + "avatarId": 10000020, + "name": "Razor", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000021, + "name": "Amber", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000022, + "name": "Venti", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000023, + "name": "Xiangling", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000024, + "name": "Beidou", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000025, + "name": "Xingqiu", + "amountList": [ + { + "value": 5, + "chance": 100 + } + ] + }, + { + "avatarId": 10000026, + "name": "Xiao", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000027, + "name": "Ningguang", + "amountList": [ + { + "value": 3, + "chance": 33 + }, + { + "value": 4, + "chance": 67 + } + ] + }, + { + "avatarId": 10000029, + "name": "Klee", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000030, + "name": "Zhongli", + "amountList": [ + { + "value": 0, + "chance": 50 + }, + { + "value": 1, + "chance": 50 + } + ] + }, + { + "avatarId": 10000031, + "name": "Fischl", + "amountList": [ + { + "value": 0, + "chance": 33 + }, + { + "value": 1, + "chance": 67 + } + ] + }, + { + "avatarId": 10000032, + "name": "Bennett", + "amountList": [ + { + "value": 2, + "chance": 75 + }, + { + "value": 3, + "chance": 25 + } + ] + }, + { + "avatarId": 10000033, + "name": "Tartaglia", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000034, + "name": "Noelle", + "amountList": [ + { + "value": 0, + "chance": 100 + } + ] + }, + { + "avatarId": 10000035, + "name": "Qiqi", + "amountList": [ + { + "value": 0, + "chance": 100 + } + ] + }, + { + "avatarId": 10000036, + "name": "Chongyun", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000037, + "name": "Ganyu", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000038, + "name": "Albedo", + "amountList": [ + { + "value": 0, + "chance": 33 + }, + { + "value": 1, + "chance": 67 + } + ] + }, + { + "avatarId": 10000039, + "name": "Diona", + "amountList": [ + { + "value": 0, + "chance": 20 + }, + { + "value": 1, + "chance": 80 + } + ] + }, + { + "avatarId": 10000041, + "name": "Mona", + "amountList": [ + { + "value": 3, + "chance": 67 + }, + { + "value": 4, + "chance": 33 + } + ] + }, + { + "avatarId": 10000042, + "name": "Keqing", + "amountList": [ + { + "value": 2, + "chance": 50 + }, + { + "value": 3, + "chance": 50 + } + ] + }, + { + "avatarId": 10000043, + "name": "Sucrose", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000044, + "name": "Xinyan", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000045, + "name": "Rosaria", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000046, + "name": "Hu Tao", + "amountList": [ + { + "value": 2, + "chance": 50 + }, + { + "value": 3, + "chance": 50 + } + ] + }, + { + "avatarId": 10000047, + "name": "Kaedehara Kazuha", + "amountList": [ + { + "value": 3, + "chance": 50 + }, + { + "value": 4, + "chance": 50 + } + ] + }, + { + "avatarId": 10000048, + "name": "Yanfei", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000049, + "name": "Yoimiya", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000050, + "name": "Thoma", + "amountList": [ + { + "value": 3, + "chance": 50 + }, + { + "value": 4, + "chance": 50 + } + ] + }, + { + "avatarId": 10000051, + "name": "Eula", + "amountList": [ + { + "value": 1, + "chance": 50 + }, + { + "value": 2, + "chance": 50 + } + ] + }, + { + "avatarId": 10000052, + "name": "Raiden Shogun", + "amountList": [ + { + "value": 0, + "chance": 50 + }, + { + "value": 1, + "chance": 50 + } + ] + }, + { + "avatarId": 10000053, + "name": "Sayu", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000054, + "name": "Sangonomiya Kokomi", + "amountList": [ + { + "value": 0, + "chance": 33 + }, + { + "value": 1, + "chance": 67 + } + ] + }, + { + "avatarId": 10000055, + "name": "Gorou", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000056, + "name": "Kujou Sara", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000057, + "name": "Arataki Itto", + "amountList": [ + { + "value": 3, + "chance": 50 + }, + { + "value": 4, + "chance": 50 + } + ] + }, + { + "avatarId": 10000058, + "name": "Yae Miko", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000062, + "name": "Aloy", + "amountList": [ + { + "value": 5, + "chance": 100 + } + ] + }, + { + "avatarId": 10000063, + "name": "Shenhe", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000064, + "name": "Yun Jin", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000066, + "name": "Kamisato Ayato", + "amountList": [ + { + "value": 1, + "chance": 50 + }, + { + "value": 2, + "chance": 50 + } + ] + } +] \ No newline at end of file