diff --git a/proto/AvatarSkillMaxChargeCountNotify.proto b/proto/AvatarSkillMaxChargeCountNotify.proto new file mode 100644 index 000000000..abbd17d2d --- /dev/null +++ b/proto/AvatarSkillMaxChargeCountNotify.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +message AvatarSkillMaxChargeCountNotify { + uint64 avatar_guid = 1; + uint32 skill_id = 2; + uint32 max_charge_count = 3; +} diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 180080e51..5478052d8 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -307,18 +307,7 @@ public class ResourceLoader { } for (Entry e : config.entrySet()) { - List abilityList = new ArrayList<>(); - int extraTalentIndex = 0; - - for (OpenConfigData entry : e.getValue()) { - if (entry.$type.contains("AddAbility")) { - abilityList.add(entry.abilityName); - } else if (entry.talentIndex > 0) { - extraTalentIndex = entry.talentIndex; - } - } - - OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), abilityList, extraTalentIndex); + OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue()); map.put(entry.getName(), entry); } } @@ -354,9 +343,11 @@ public class ResourceLoader { public OpenConfigData[] data; } - private static class OpenConfigData { + public static class OpenConfigData { public String $type; public String abilityName; public int talentIndex; + public int skillID; + public int pointDelta; } } diff --git a/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java b/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java index d01467637..8ff646fa9 100644 --- a/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java +++ b/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java @@ -1,18 +1,39 @@ package emu.grasscutter.data.custom; +import java.util.ArrayList; import java.util.List; +import emu.grasscutter.data.ResourceLoader.OpenConfigData; + public class OpenConfigEntry { private String name; private String[] addAbilities; private int extraTalentIndex; - - public OpenConfigEntry(String name, List abilityList, int extraTalentIndex) { + private SkillPointModifier[] skillPointModifiers; + + public OpenConfigEntry(String name, OpenConfigData[] data) { this.name = name; - this.extraTalentIndex = extraTalentIndex; + + List abilityList = new ArrayList<>(); + List modList = new ArrayList<>(); + + for (OpenConfigData entry : data) { + if (entry.$type.contains("AddAbility")) { + abilityList.add(entry.abilityName); + } else if (entry.talentIndex > 0) { + this.extraTalentIndex = entry.talentIndex; + } else if (entry.$type.contains("ModifySkillPoint")) { + modList.add(new SkillPointModifier(entry.skillID, entry.pointDelta)); + } + } + if (abilityList.size() > 0) { this.addAbilities = abilityList.toArray(new String[0]); } + + if (modList.size() > 0) { + this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]); + } } public String getName() { @@ -26,4 +47,26 @@ public class OpenConfigEntry { public int getExtraTalentIndex() { return extraTalentIndex; } + + public SkillPointModifier[] getSkillPointModifiers() { + return skillPointModifiers; + } + + public static class SkillPointModifier { + private int skillId; + private int delta; + + public SkillPointModifier(int skillId, int delta) { + this.skillId = skillId; + this.delta = delta; + } + + public int getSkillId() { + return skillId; + } + + public int getDelta() { + return delta; + } + } } diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index cf5446ab7..de66e8671 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import org.bson.types.ObjectId; @@ -18,6 +19,7 @@ import dev.morphia.annotations.Transient; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.custom.OpenConfigEntry; +import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.AvatarPromoteData; import emu.grasscutter.data.def.AvatarSkillData; @@ -46,6 +48,7 @@ import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo; import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo; +import emu.grasscutter.net.proto.AvatarSkillInfoOuterClass.AvatarSkillInfo; import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo; @@ -83,6 +86,7 @@ public class Avatar { private List fetters; private Map skillLevelMap; // Talent levels + private Map skillExtraChargeMap; // Charges private Map proudSkillBonusMap; // Talent bonus levels (from const) private int skillDepotId; private int coreProudSkillLevel; // Constellation level @@ -123,6 +127,7 @@ public class Avatar { this.flyCloak = 140001; this.skillLevelMap = new HashMap<>(); + this.skillExtraChargeMap = new HashMap<>(); this.talentIdList = new HashSet<>(); this.proudSkillList = new HashSet<>(); @@ -283,6 +288,13 @@ public class Avatar { public Map getSkillLevelMap() { return skillLevelMap; } + + public Map getSkillExtraChargeMap() { + if (skillExtraChargeMap == null) { + skillExtraChargeMap = new HashMap<>(); + } + return skillExtraChargeMap; + } public Map getProudSkillBonusMap() { return proudSkillBonusMap; @@ -676,9 +688,10 @@ public class Avatar { } } - public void recalcProudSkillBonusMap() { + public void recalcConstellations() { // Clear first this.getProudSkillBonusMap().clear(); + this.getSkillExtraChargeMap().clear(); // Sanity checks if (getData() == null || getData().getSkillDepot() == null) { @@ -699,6 +712,21 @@ public class Avatar { continue; } + // Check if we can add charges to a skill + if (entry.getSkillPointModifiers() != null) { + for (SkillPointModifier mod : entry.getSkillPointModifiers()) { + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(mod.getSkillId()); + + if (skillData == null) continue; + + int charges = skillData.getMaxChargeNum() + mod.getDelta(); + + this.getSkillExtraChargeMap().put(mod.getSkillId(), charges); + } + continue; + } + + // Check if a skill can be boosted by +3 levels int skillId = 0; if (entry.getExtraTalentIndex() == 2 && this.getData().getSkillDepot().getSkills().size() >= 2) { @@ -788,6 +816,10 @@ public class Avatar { .setWearingFlycloakId(this.getFlyCloak()) .setCostumeId(this.getCostume()); + for (Entry entry : this.getSkillExtraChargeMap().entrySet()) { + avatarInfo.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build()); + } + for (GameItem item : this.getEquips().values()) { avatarInfo.addEquipGuidList(item.getGuid()); } diff --git a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java index 99ccda173..2486e36ab 100644 --- a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java +++ b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java @@ -148,7 +148,7 @@ public class AvatarStorage implements Iterable { avatar.setOwner(getPlayer()); // Force recalc of const boosted skills - avatar.recalcProudSkillBonusMap(); + avatar.recalcConstellations(); // Add to avatar storage this.avatars.put(avatar.getAvatarId(), avatar); diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 1e028c38c..2a9629b73 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -8,6 +8,7 @@ import java.util.stream.Collectors; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.custom.OpenConfigEntry; +import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.def.AvatarPromoteData; import emu.grasscutter.data.def.AvatarSkillData; import emu.grasscutter.data.def.AvatarSkillDepotData; @@ -835,9 +836,22 @@ public class InventoryManager { // Proud skill bonus map (Extra skills) OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig()); - if (entry != null && entry.getExtraTalentIndex() > 0) { - avatar.recalcProudSkillBonusMap(); - player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex())); + if (entry != null) { + if (entry.getExtraTalentIndex() > 0) { + // Check if new constellation adds +3 to a skill level + avatar.recalcConstellations(); + // Packet + player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex())); + } else if (entry.getSkillPointModifiers() != null) { + // Check if new constellation adds skill charges + avatar.recalcConstellations(); + // Packet + for (SkillPointModifier mod : entry.getSkillPointModifiers()) { + player.sendPacket( + new PacketAvatarSkillMaxChargeCountNotify(avatar, mod.getSkillId(), avatar.getSkillExtraChargeMap().getOrDefault(mod.getSkillId(), 0)) + ); + } + } } // Recalc + save avatar diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillMaxChargeCountNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillMaxChargeCountNotify.java new file mode 100644 index 000000000..696cb2027 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillMaxChargeCountNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AvatarSkillMaxChargeCountNotifyOuterClass.AvatarSkillMaxChargeCountNotify; + +public class PacketAvatarSkillMaxChargeCountNotify extends BasePacket { + + public PacketAvatarSkillMaxChargeCountNotify(Avatar avatar, int skillId, int maxCharges) { + super(PacketOpcodes.AvatarSkillMaxChargeCountNotify); + + AvatarSkillMaxChargeCountNotify proto = AvatarSkillMaxChargeCountNotify.newBuilder() + .setAvatarGuid(avatar.getGuid()) + .setSkillId(skillId) + .setMaxChargeCount(maxCharges) + .build(); + + this.setData(proto); + } +}