diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index a32a6cc88..b6f29e841 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -244,32 +244,11 @@ public final class GiveCommand implements CommandHandler { } private static Avatar makeAvatar(AvatarData avatarData, int level, int promoteLevel, int constellation) { - // Calculate ascension level. Avatar avatar = new Avatar(avatarData); avatar.setLevel(level); avatar.setPromoteLevel(promoteLevel); - - // Add constellations. - int talentBase = switch (avatar.getAvatarId()) { - case 10000005 -> 70; - case 10000006 -> 40; - default -> (avatar.getAvatarId() - 10000000) * 10; - }; - - for (int i = 1; i <= constellation; i++) { - avatar.getTalentIdList().add(talentBase + i); - } - - // Main character needs skill depot manually added. - if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) { - avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(504)); - } - else if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) { - avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(704)); - } - + avatar.forceConstellationLevel(constellation); avatar.recalcStats(); - return avatar; } diff --git a/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java index 5c4aa984a..135ec4540 100644 --- a/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java @@ -37,9 +37,6 @@ public final class ResetConstCommand implements CommandHandler { } private void resetConstellation(Avatar avatar) { - avatar.getTalentIdList().clear(); - avatar.setCoreProudSkillLevel(0); - avatar.recalcStats(); - avatar.save(); + avatar.forceConstellationLevel(-1); } } diff --git a/src/main/java/emu/grasscutter/command/commands/SetConstCommand.java b/src/main/java/emu/grasscutter/command/commands/SetConstCommand.java index dfdb1169d..f24f46fc2 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetConstCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetConstCommand.java @@ -15,7 +15,6 @@ import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.IntArrayList; import java.util.List; -import java.util.Set; @Command( label = "setConst", @@ -33,7 +32,7 @@ public final class SetConstCommand implements CommandHandler { try { int constLevel = Integer.parseInt(args.get(0)); - if (constLevel < 0 || constLevel > 6) { + if (constLevel < -1 || constLevel > 6) { CommandHandler.sendTranslatedMessage(sender, "commands.setConst.range_error"); return; } @@ -52,19 +51,7 @@ public final class SetConstCommand implements CommandHandler { private void setConstellation(Player player, Avatar avatar, int constLevel) { int currentConstLevel = avatar.getCoreProudSkillLevel(); - IntArrayList talentIds = new IntArrayList(avatar.getSkillDepot().getTalents()); - Set talentIdList = avatar.getTalentIdList(); - - talentIdList.clear(); - avatar.setCoreProudSkillLevel(0); - - for(int talent = 0; talent < constLevel; talent++) { - AvatarTalentData talentData = GameData.getAvatarTalentDataMap().get(talentIds.getInt(talent)); - int mainCostItemId = talentData.getMainCostItemId(); - - player.getInventory().addItem(mainCostItemId); - Grasscutter.getGameServer().getInventorySystem().unlockAvatarConstellation(player, avatar.getGuid()); - } + avatar.forceConstellationLevel(constLevel); // force player to reload scene when necessary if (constLevel < currentConstLevel) { diff --git a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java index 226203d8b..c1ac1d383 100644 --- a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java @@ -4,10 +4,7 @@ import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.excels.AvatarSkillDepotData; import emu.grasscutter.game.avatar.Avatar; -import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.player.Player; -import emu.grasscutter.server.packet.send.PacketAvatarSkillChangeNotify; -import emu.grasscutter.server.packet.send.PacketAvatarSkillUpgradeRsp; import java.util.List; @@ -17,34 +14,6 @@ import java.util.List; permission = "player.settalent", permissionTargeted = "player.settalent.others") public final class TalentCommand implements CommandHandler { - private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) { - var skillLevelMap = avatar.getSkillLevelMap(); - int oldLevel = skillLevelMap.get(talentId); - if (talentLevel < 0 || talentLevel > 15) { - CommandHandler.sendTranslatedMessage(sender, "commands.talent.lower_16"); - return; - } - - // Upgrade skill - skillLevelMap.put(talentId, talentLevel); - avatar.save(); - - // Packet - player.sendPacket(new PacketAvatarSkillChangeNotify(avatar, talentId, oldLevel, talentLevel)); - player.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, talentId, oldLevel, talentLevel)); - - String successMessage = "commands.talent.set_id"; - AvatarSkillDepotData depot = avatar.getSkillDepot(); - if (talentId == depot.getSkills().get(0)) { - successMessage = "commands.talent.set_atk"; - } else if (talentId == depot.getSkills().get(1)) { - successMessage = "commands.talent.set_e"; - } else if (talentId == depot.getEnergySkill()) { - successMessage = "commands.talent.set_q"; - } - CommandHandler.sendTranslatedMessage(sender, successMessage, talentLevel); - } - @Override public void execute(Player sender, Player targetPlayer, List args) { if (args.size() < 1){ @@ -52,9 +21,19 @@ public final class TalentCommand implements CommandHandler { return; } - EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity(); - Avatar avatar = entity.getAvatar(); - String cmdSwitch = args.get(0); + Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar(); + AvatarSkillDepotData skillDepot = avatar.getSkillDepot(); + if (skillDepot == null) { // Avatars without skill depots aren't a suitable target even with manual skillId specified + CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id"); + return; + } + int skillIdNorAtk = skillDepot.getSkills().get(0); + int skillIdE = skillDepot.getSkills().get(1); + int skillIdQ = skillDepot.getEnergySkill(); + int skillId = 0; + int newLevel = -1; + + String cmdSwitch = args.get(0).toLowerCase(); switch (cmdSwitch) { default -> { sendUsageMessage(sender); @@ -62,42 +41,56 @@ public final class TalentCommand implements CommandHandler { } case "set" -> { if (args.size() < 3) { - sendUsageMessage(sender); sendUsageMessage(sender); return; } try { - int skillId = Integer.parseInt(args.get(1)); - int newLevel = Integer.parseInt(args.get(2)); - setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel); + skillId = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id"); return; } + try { + newLevel = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_level"); + return; + } + + if (avatar.setSkillLevel(skillId, newLevel)) { + CommandHandler.sendTranslatedMessage(sender, "commands.talent.set_id", newLevel); + } else { + CommandHandler.sendTranslatedMessage(sender, "commands.talent.lower_16"); + } } case "n", "e", "q" -> { if (args.size() < 2) { sendUsageMessage(sender); return; } - AvatarSkillDepotData SkillDepot = avatar.getSkillDepot(); - int skillId = switch (cmdSwitch) { - default -> SkillDepot.getSkills().get(0); - case "e" -> SkillDepot.getSkills().get(1); - case "q" -> SkillDepot.getEnergySkill(); - }; try { - int newLevel = Integer.parseInt(args.get(1)); - setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel); + newLevel = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level"); return; } + + skillId = switch (cmdSwitch) { + default -> skillIdNorAtk; + case "e" -> skillIdE; + case "q" -> skillIdQ; + }; + if (avatar.setSkillLevel(skillId, newLevel)) { + switch (cmdSwitch) { + default -> CommandHandler.sendTranslatedMessage(sender, "commands.talent.set_atk", newLevel); + case "e" -> CommandHandler.sendTranslatedMessage(sender, "commands.talent.set_e", newLevel); + case "q" -> CommandHandler.sendTranslatedMessage(sender, "commands.talent.set_q", newLevel); + } + } else { + CommandHandler.sendTranslatedMessage(sender, "commands.talent.lower_16"); + } } case "getid" -> { - int skillIdNorAtk = avatar.getSkillDepot().getSkills().get(0); - int skillIdE = avatar.getSkillDepot().getSkills().get(1); - int skillIdQ = avatar.getSkillDepot().getEnergySkill(); CommandHandler.sendTranslatedMessage(sender, "commands.talent.normal_attack_id", Integer.toString(skillIdNorAtk)); CommandHandler.sendTranslatedMessage(sender, "commands.talent.e_skill_id", Integer.toString(skillIdE)); CommandHandler.sendTranslatedMessage(sender, "commands.talent.q_skill_id", Integer.toString(skillIdQ)); diff --git a/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java b/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java index 9d775f5f2..2fbbd72f1 100644 --- a/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java @@ -1,6 +1,7 @@ package emu.grasscutter.data.excels; import java.util.List; +import java.util.stream.IntStream; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameDepot; @@ -71,4 +72,9 @@ public class AvatarSkillDepotData extends GameResource { @Getter private int proudSkillGroupId; @Getter private int needAvatarPromoteLevel; } + + public IntStream getSkillsAndEnergySkill() { + return IntStream.concat(this.skills.stream().mapToInt(i -> i), IntStream.of(this.energySkill)) + .filter(skillId -> skillId > 0); + } } diff --git a/src/main/java/emu/grasscutter/data/excels/ProudSkillData.java b/src/main/java/emu/grasscutter/data/excels/ProudSkillData.java index 34701ffe5..e16bf039b 100644 --- a/src/main/java/emu/grasscutter/data/excels/ProudSkillData.java +++ b/src/main/java/emu/grasscutter/data/excels/ProudSkillData.java @@ -3,99 +3,59 @@ package emu.grasscutter.data.excels; import java.util.ArrayList; import java.util.List; +import dev.morphia.annotations.Transient; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.common.ItemParamData; +import lombok.Getter; @ResourceType(name = "ProudSkillExcelConfigData.json") public class ProudSkillData extends GameResource { - - private int proudSkillId; - private int proudSkillGroupId; - private int level; - private int coinCost; - private int breakLevel; - private int proudSkillType; - private String openConfig; - private List costItems; - private List filterConds; - private List lifeEffectParams; - private FightPropData[] addProps; - private float[] paramList; - private long[] paramDescList; - private long nameTextMapHash; - - @Override - public int getId() { - return proudSkillId; - } + private int proudSkillId; + @Getter private int proudSkillGroupId; + @Getter private int level; + @Getter private int coinCost; + @Getter private int breakLevel; + @Getter private int proudSkillType; + @Getter private String openConfig; + @Getter private List costItems; + @Getter private List filterConds; + @Getter private List lifeEffectParams; + @Getter private FightPropData[] addProps; + @Getter private float[] paramList; + @Getter private long[] paramDescList; + @Getter private long nameTextMapHash; + @Transient private Iterable totalCostItems; - public int getProudSkillGroupId() { - return proudSkillGroupId; - } + @Override + public int getId() { + return proudSkillId; + } - public int getLevel() { - return level; - } + public Iterable getTotalCostItems() { + if (this.totalCostItems == null) { + ArrayList total = (this.costItems != null) ? new ArrayList<>(this.costItems) : new ArrayList<>(1); + if (this.coinCost > 0) + total.add(new ItemParamData(202, this.coinCost)); + this.totalCostItems = total; + } + return this.totalCostItems; + } - public int getCoinCost() { - return coinCost; - } - - public int getBreakLevel() { - return breakLevel; - } - - public int getProudSkillType() { - return proudSkillType; - } - - public String getOpenConfig() { - return openConfig; - } - - public List getCostItems() { - return costItems; - } - - public List getFilterConds() { - return filterConds; - } - - public List getLifeEffectParams() { - return lifeEffectParams; - } - - public FightPropData[] getAddProps() { - return addProps; - } - - public float[] getParamList() { - return paramList; - } - - public long[] getParamDescList() { - return paramDescList; - } - - public long getNameTextMapHash() { - return nameTextMapHash; - } - - @Override - public void onLoad() { - if (this.getOpenConfig() != null & this.getOpenConfig().length() > 0) { - this.openConfig = "Avatar_" + this.getOpenConfig(); - } - // Fight props - ArrayList parsed = new ArrayList(getAddProps().length); - for (FightPropData prop : getAddProps()) { - if (prop.getPropType() != null && prop.getValue() != 0f) { - prop.onLoad(); - parsed.add(prop); - } - } - this.addProps = parsed.toArray(new FightPropData[parsed.size()]); - } + @Override + public void onLoad() { + if (this.getOpenConfig() != null & this.getOpenConfig().length() > 0) { + this.openConfig = "Avatar_" + this.getOpenConfig(); + } + // Fight props + ArrayList parsed = new ArrayList(getAddProps().length); + for (FightPropData prop : getAddProps()) { + if (prop.getPropType() != null && prop.getValue() != 0f) { + prop.onLoad(); + parsed.add(prop); + } + } + this.addProps = parsed.toArray(new FightPropData[parsed.size()]); + } } diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index f1d360250..d178c046f 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -7,7 +7,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.Objects; import java.util.stream.Stream; import java.util.Set; @@ -19,6 +19,7 @@ import dev.morphia.annotations.Indexed; import dev.morphia.annotations.PostLoad; import dev.morphia.annotations.PrePersist; import dev.morphia.annotations.Transient; +import emu.grasscutter.GameConstants; import emu.grasscutter.data.GameData; import emu.grasscutter.data.binout.OpenConfigEntry; import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier; @@ -56,14 +57,14 @@ import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo; import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip; -import emu.grasscutter.server.packet.send.PacketAbilityChangeNotify; -import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify; -import emu.grasscutter.server.packet.send.PacketAvatarFightPropNotify; +import emu.grasscutter.server.packet.send.*; import emu.grasscutter.utils.ProtoHelper; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; import lombok.Getter; import lombok.Setter; @@ -93,12 +94,11 @@ public class Avatar { private List fetters; - @Getter private Map skillLevelMap; // Talent levels + private Map skillLevelMap; // Talent levels private Map skillExtraChargeMap; // Charges @Getter private Map proudSkillBonusMap; // Talent bonus levels (from const) @Getter private int skillDepotId; - @Getter @Setter private int coreProudSkillLevel; // Constellation level - @Getter private Set talentIdList; // Constellation id list + private Set talentIdList; // Constellation id list @Getter private Set proudSkillList; // Character passives @Getter @Setter private int flyCloak; @@ -147,7 +147,14 @@ public class Avatar { .forEach(id -> this.setFightProperty(id, 0f)); // Skill depot - this.setSkillDepotData(data.getSkillDepot()); + this.setSkillDepotData(switch (this.avatarId) { + case GameConstants.MAIN_CHARACTER_MALE -> + GameData.getAvatarSkillDepotDataMap().get(504); // Hack to start with anemo skills + case GameConstants.MAIN_CHARACTER_FEMALE -> + GameData.getAvatarSkillDepotDataMap().get(704); + default -> + data.getSkillDepot(); + }); // Set stats this.recalcStats(); @@ -219,32 +226,21 @@ public class Avatar { // Set id and depot this.skillDepotId = skillDepot.getId(); this.skillDepot = skillDepot; - // Clear, then add skills - getSkillLevelMap().clear(); - if (skillDepot.getEnergySkill() > 0) { - getSkillLevelMap().put(skillDepot.getEnergySkill(), 1); - } - for (int skillId : skillDepot.getSkills()) { - if (skillId > 0) { - getSkillLevelMap().put(skillId, 1); - } - } + // Add any missing skills + this.skillDepot.getSkillsAndEnergySkill() + .forEach(skillId -> this.skillLevelMap.putIfAbsent(skillId, 1)); // Add proud skills - this.getProudSkillList().clear(); - for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { - if (openData.getProudSkillGroupId() == 0) { - continue; - } - if (openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel()) { - int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; - if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { - this.getProudSkillList().add(proudSkillId); - } - } - } + this.proudSkillList.clear(); + skillDepot.getInherentProudSkillOpens().stream() + .filter(openData -> openData.getProudSkillGroupId() > 0) + .filter(openData -> openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel()) + .mapToInt(openData -> (openData.getProudSkillGroupId() * 100) + 1) + .filter(proudSkillId -> GameData.getProudSkillDataMap().containsKey(proudSkillId)) + .forEach(proudSkillId -> this.proudSkillList.add(proudSkillId)); + this.recalcStats(); } - public Map getSkillExtraChargeMap() { + private Map getSkillExtraChargeMap() { if (skillExtraChargeMap == null) { skillExtraChargeMap = new HashMap<>(); } @@ -266,16 +262,12 @@ public class Avatar { } public void setCurrentEnergy(float currentEnergy) { - if (this.getSkillDepot() != null && this.getSkillDepot().getEnergySkillData() != null) { - ElementType element = this.getSkillDepot().getElementType(); - this.setFightProperty(element.getMaxEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); - - if (GAME_OPTIONS.energyUsage) { - this.setFightProperty(element.getCurEnergyProp(), currentEnergy); - } - else { - this.setFightProperty(element.getCurEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); - } + var depot = this.skillDepot; + if (depot != null && depot.getEnergySkillData() != null) { + ElementType element = depot.getElementType(); + var maxEnergy = depot.getEnergySkillData().getCostElemVal(); + this.setFightProperty(element.getMaxEnergyProp(), maxEnergy); + this.setFightProperty(element.getCurEnergyProp(), GAME_OPTIONS.energyUsage ? currentEnergy : maxEnergy); } } @@ -307,6 +299,26 @@ public class Avatar { return getFightProperties().getOrDefault(prop.getId(), 0f); } + public Map getSkillLevelMap() { // Returns a copy of the skill levels for the current skillDepot. + var map = new Int2IntOpenHashMap(); + this.skillDepot.getSkillsAndEnergySkill() + .forEach(skillId -> map.computeIfAbsent(skillId, this.skillLevelMap::get)); + return map; + } + + public IntSet getTalentIdList() { // Returns a copy of the unlocked constellations for the current skillDepot. + var talents = new IntOpenHashSet(this.getSkillDepot().getTalents()); + talents.removeIf(id -> !this.talentIdList.contains(id)); + return talents; + } + + public int getCoreProudSkillLevel() { + var lockedTalents = new IntOpenHashSet(this.getSkillDepot().getTalents()); + lockedTalents.removeAll(this.getTalentIdList()); + // One below the lowest locked talent, or 6 if there are no locked talents. + return lockedTalents.intStream().map(i -> i % 10).min().orElse(7) - 1; + } + public boolean equipItem(GameItem item, boolean shouldRecalc) { // Sanity check equip type EquipType itemEquipType = item.getItemData().getEquipType(); @@ -549,17 +561,13 @@ public class Avatar { } // Constellations - if (this.getTalentIdList().size() > 0) { - for (int talentId : this.getTalentIdList()) { - AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); - if (avatarTalentData == null) { - return; - } - - // Add any skill strings from this constellation - this.addToExtraAbilityEmbryos(avatarTalentData.getOpenConfig(), false); - } - } + this.getTalentIdList().intStream() + .mapToObj(GameData.getAvatarTalentDataMap()::get) + .filter(Objects::nonNull) + .map(AvatarTalentData::getOpenConfig) + .filter(Objects::nonNull) + .forEach(openConfig -> this.addToExtraAbilityEmbryos(openConfig, false)); + // Add any skill strings from this constellation // Set % stats this.setFightProperty( @@ -614,71 +622,179 @@ public class Avatar { } } + public void calcConstellation(OpenConfigEntry entry, boolean notifyClient) { + if (entry == null) return; + + // Check if new constellation adds +3 to a skill level + if (this.calcConstellationExtraLevels(entry) && notifyClient) { + // Packet + this.getPlayer().sendPacket(new PacketProudSkillExtraLevelNotify(this, entry.getExtraTalentIndex())); + } + // Check if new constellation adds skill charges + if (this.calcConstellationExtraCharges(entry) && notifyClient) { + // Packet + Stream.of(entry.getSkillPointModifiers()) + .mapToInt(SkillPointModifier::getSkillId) + .forEach(skillId -> { + this.getPlayer().sendPacket( + new PacketAvatarSkillMaxChargeCountNotify(this, skillId, this.getSkillExtraChargeMap().getOrDefault(skillId, 0)) + ); + }); + } + } + public void recalcConstellations() { // Clear first this.getProudSkillBonusMap().clear(); this.getSkillExtraChargeMap().clear(); // Sanity checks - if (getData() == null || this.skillDepot == null) { + if (this.data == null || this.skillDepot == null) { return; } - if (this.getTalentIdList().size() > 0) { - for (int talentId : this.getTalentIdList()) { - AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); + this.getTalentIdList().intStream() + .mapToObj(GameData.getAvatarTalentDataMap()::get) + .filter(Objects::nonNull) + .map(AvatarTalentData::getOpenConfig) + .filter(Objects::nonNull) + .filter(openConfig -> openConfig.length() > 0) + .map(GameData.getOpenConfigEntries()::get) + .filter(Objects::nonNull) + .forEach(e -> this.calcConstellation(e, false)); + } - if (avatarTalentData == null || avatarTalentData.getOpenConfig() == null || avatarTalentData.getOpenConfig().length() == 0) { - continue; - } + private boolean calcConstellationExtraCharges(OpenConfigEntry entry) { + var skillPointModifiers = entry.getSkillPointModifiers(); + if (skillPointModifiers == null) return false; - // Get open config to find which skill should be boosted - OpenConfigEntry entry = GameData.getOpenConfigEntries().get(avatarTalentData.getOpenConfig()); - if (entry == null) { - continue; - } + for (var mod : skillPointModifiers) { + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(mod.getSkillId()); - // 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; - if (skillData == null) continue; + int charges = skillData.getMaxChargeNum() + mod.getDelta(); - 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.skillDepot.getSkills().size() >= 2) { - // E skill - skillId = this.skillDepot.getSkills().get(1); - } else if (entry.getExtraTalentIndex() == 9) { - // Ult skill - skillId = this.skillDepot.getEnergySkill(); - } - - // Sanity check - if (skillId == 0) { - continue; - } - - // Get proud skill group id - AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); - - if (skillData == null) { - continue; - } - - // Add to bonus list - this.getProudSkillBonusMap().put(skillData.getProudSkillGroupId(), 3); - } + this.getSkillExtraChargeMap().put(mod.getSkillId(), charges); } + return true; + } + + private boolean calcConstellationExtraLevels(OpenConfigEntry entry) { + int skillId = switch(entry.getExtraTalentIndex()) { + case 9 -> this.skillDepot.getEnergySkill(); // Ult skill + case 2 -> (this.skillDepot.getSkills().size() >= 2) ? this.skillDepot.getSkills().get(1) : 0; // E skill + default -> 0; + }; + // Sanity check + if (skillId == 0) { + return false; + } + + // Get proud skill group id + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); + + if (skillData == null) { + return false; + } + + // Add to bonus list + this.getProudSkillBonusMap().put(skillData.getProudSkillGroupId(), 3); + return true; + } + + public boolean upgradeSkill(int skillId) { + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); + if (skillData == null) return false; + + // Get data for next skill level + int newLevel = this.skillLevelMap.getOrDefault(skillId, 0) + 1; + if (newLevel > 10) return false; + + // Proud skill data + int proudSkillId = (skillData.getProudSkillGroupId() * 100) + newLevel; + ProudSkillData proudSkill = GameData.getProudSkillDataMap().get(proudSkillId); + if (proudSkill == null) return false; + + // Make sure break level is correct + if (this.getPromoteLevel() < proudSkill.getBreakLevel()) return false; + + // Pay materials and mora if possible + if (!this.getPlayer().getInventory().payItems(proudSkill.getTotalCostItems())) return false; + + // Upgrade skill + this.setSkillLevel(skillId, newLevel); + return true; + } + + public boolean setSkillLevel(int skillId, int level) { + if (level < 0 || level > 15) return false; + int oldLevel = this.skillLevelMap.getOrDefault(skillId, 0); // just taking the return value of put would have null concerns + this.skillLevelMap.put(skillId, level); + this.save(); + + // Packet + this.getPlayer().sendPacket(new PacketAvatarSkillChangeNotify(this, skillId, oldLevel, level)); + this.getPlayer().sendPacket(new PacketAvatarSkillUpgradeRsp(this, skillId, oldLevel, level)); + return true; + } + + public boolean unlockConstellation() { + return this.unlockConstellation(false); + } + public boolean unlockConstellation(boolean skipPayment) { + int currentTalentLevel = this.getCoreProudSkillLevel(); + int talentId = this.skillDepot.getTalents().get(currentTalentLevel); + return this.unlockConstellation(talentId, skipPayment); + } + public boolean unlockConstellation(int talentId) { + return unlockConstellation(talentId, false); + } + public boolean unlockConstellation(int talentId, boolean skipPayment) { + // Get talent + AvatarTalentData talentData = GameData.getAvatarTalentDataMap().get(talentId); + if (talentData == null) return false; + + // Pay constellation item if possible + if (!skipPayment && !this.getPlayer().getInventory().payItem(talentData.getMainCostItemId(), 1)) { + return false; + } + + // Apply + recalc + this.talentIdList.add(talentData.getId()); + + // Packet + this.getPlayer().sendPacket(new PacketAvatarUnlockTalentNotify(this, talentId)); + this.getPlayer().sendPacket(new PacketUnlockAvatarTalentRsp(this, talentId)); + + // Proud skill bonus map (Extra skills) + this.calcConstellation(GameData.getOpenConfigEntries().get(talentData.getOpenConfig()), true); + + // Recalc + save avatar + this.recalcStats(true); + this.save(); + return true; + } + + public void forceConstellationLevel(int level) { + if (level > 6) return; // Sanity check + + if (level < 0) { // Special case for resetConst to remove inactive depots too + this.talentIdList.clear(); + this.recalcStats(); + return; + } + this.talentIdList.removeAll(this.getTalentIdList()); // Only remove constellations from active depot + for (int i = 0; i < level; i++) + this.unlockConstellation(true); + this.recalcStats(); + } + + public boolean sendSkillExtraChargeMap() { + var map = this.getSkillExtraChargeMap(); + if (map.isEmpty()) return false; + this.getPlayer().sendPacket(new PacketAvatarSkillInfoNotify(this.guid, new Int2IntOpenHashMap(map))); + return true; } public EntityAvatar getAsEntity() { @@ -709,14 +825,11 @@ public class Avatar { } - if (this.getFetterList() != null) { - for (int i = 0; i < this.getFetterList().size(); i++) { - avatarFetter.addFetterList( - FetterData.newBuilder() - .setFetterId(this.getFetterList().get(i)) - .setFetterState(FetterState.FINISH.getValue()) - ); - } + if (this.fetters != null) { + this.fetters.forEach(fetterId -> avatarFetter.addFetterList( + FetterData.newBuilder() + .setFetterId(fetterId) + .setFetterState(FetterState.FINISH.getValue()))); } int cardId = this.getNameCardId(); @@ -742,13 +855,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()); - } + this.getSkillExtraChargeMap().forEach((skillId, count) -> + avatarInfo.putSkillMap(skillId, AvatarSkillInfo.newBuilder().setMaxChargeCount(count).build())); - for (GameItem item : this.getEquips().values()) { - avatarInfo.addEquipGuidList(item.getGuid()); - } + this.getEquips().forEach((k, item) -> avatarInfo.addEquipGuidList(item.getGuid())); avatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel())); avatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp())); diff --git a/src/main/java/emu/grasscutter/game/player/TeamManager.java b/src/main/java/emu/grasscutter/game/player/TeamManager.java index c6f118ad8..6340cf09b 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/player/TeamManager.java @@ -338,11 +338,7 @@ public class TeamManager extends BasePlayerDataManager { this.getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(this.getPlayer())); // Skill charges packet - Yes, this is official server behavior as of 2.6.0 - for (EntityAvatar entity : this.getActiveTeam()) { - if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) { - this.getPlayer().sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); - } - } + this.getActiveTeam().stream().map(EntityAvatar::getAvatar).forEach(Avatar::sendSkillExtraChargeMap); // Run callback if (responsePacket != null) { diff --git a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java index b5e377a42..7cf393625 100644 --- a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java +++ b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java @@ -7,14 +7,11 @@ import java.util.Map; import java.util.stream.Collectors; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.binout.OpenConfigEntry; -import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemUseData; import emu.grasscutter.data.excels.AvatarPromoteData; import emu.grasscutter.data.excels.AvatarSkillData; import emu.grasscutter.data.excels.AvatarSkillDepotData; -import emu.grasscutter.data.excels.AvatarTalentData; import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ProudSkillData; import emu.grasscutter.data.excels.WeaponPromoteData; @@ -649,120 +646,22 @@ public class InventorySystem extends BaseGameSystem { player.sendPacket(new PacketAvatarFetterDataNotify(avatar)); } + @Deprecated(forRemoval = true) public void upgradeAvatarSkill(Player player, long guid, int skillId) { // Sanity checks Avatar avatar = player.getAvatars().getAvatarByGuid(guid); - if (avatar == null) { - return; - } + if (avatar == null) return; - // Make sure avatar has skill - if (!avatar.getSkillLevelMap().containsKey(skillId)) { - return; - } - - AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); - if (skillData == null) { - return; - } - - // Get data for next skill level - int currentLevel = avatar.getSkillLevelMap().get(skillId); - int nextLevel = currentLevel + 1; - int proudSkillId = (skillData.getProudSkillGroupId() * 100) + nextLevel; - - // Capped at level 10 talent - if (nextLevel > 10) { - return; - } - - // Proud skill data - ProudSkillData proudSkill = GameData.getProudSkillDataMap().get(proudSkillId); - if (proudSkill == null) { - return; - } - - // Make sure break level is correct - if (avatar.getPromoteLevel() < proudSkill.getBreakLevel()) { - return; - } - - // Pay materials and mora if possible - List costs = new ArrayList(proudSkill.getCostItems()); // Can this be null? - if (proudSkill.getCoinCost() > 0) { - costs.add(new ItemParamData(202, proudSkill.getCoinCost())); - } - if (!player.getInventory().payItems(costs)) { - return; - } - - // Upgrade skill - avatar.getSkillLevelMap().put(skillId, nextLevel); - avatar.save(); - - // Packet - player.sendPacket(new PacketAvatarSkillChangeNotify(avatar, skillId, currentLevel, nextLevel)); - player.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, skillId, currentLevel, nextLevel)); + avatar.upgradeSkill(skillId); } + @Deprecated(forRemoval = true) public void unlockAvatarConstellation(Player player, long guid) { - // Sanity checks + // Sanity check Avatar avatar = player.getAvatars().getAvatarByGuid(guid); - if (avatar == null) { - return; - } + if (avatar == null) return; - // Get talent - int currentTalentLevel = avatar.getCoreProudSkillLevel(); - int nextTalentId = ((avatar.getAvatarId() % 10000000) * 10) + currentTalentLevel + 1; - - if (avatar.getAvatarId() == 10000006) { - // Lisa is special in that her talentId starts with 4 instead of 6. - nextTalentId = 40 + currentTalentLevel + 1; - } - - AvatarTalentData talentData = GameData.getAvatarTalentDataMap().get(nextTalentId); - - if (talentData == null) { - return; - } - - // Pay constellation item if possible - if (!player.getInventory().payItem(talentData.getMainCostItemId(), 1)) { - return; - } - - // Apply + recalc - avatar.getTalentIdList().add(talentData.getId()); - avatar.setCoreProudSkillLevel(currentTalentLevel + 1); - - // Packet - player.sendPacket(new PacketAvatarUnlockTalentNotify(avatar, nextTalentId)); - player.sendPacket(new PacketUnlockAvatarTalentRsp(avatar, nextTalentId)); - - // Proud skill bonus map (Extra skills) - OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig()); - 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 - avatar.recalcStats(true); - avatar.save(); + avatar.unlockConstellation(); } public void destroyMaterial(Player player, List list) { diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 1406c25b8..8e6e05a9c 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -5,6 +5,7 @@ import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.binout.SceneNpcBornEntry; import emu.grasscutter.data.excels.*; +import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.dungeons.DungeonSettleListener; import emu.grasscutter.game.entity.*; import emu.grasscutter.game.player.Player; @@ -270,22 +271,19 @@ public class Scene { } public void spawnPlayer(Player player) { - if (this.isInScene(player.getTeamManager().getCurrentAvatarEntity())) { + var teamManager = player.getTeamManager(); + if (this.isInScene(teamManager.getCurrentAvatarEntity())) { return; } - if (player.getTeamManager().getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { - player.getTeamManager().getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f); + if (teamManager.getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { + teamManager.getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f); } - this.addEntity(player.getTeamManager().getCurrentAvatarEntity()); + this.addEntity(teamManager.getCurrentAvatarEntity()); // Notify the client of any extra skill charges - for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) { - if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) { - player.sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); - } - } + teamManager.getActiveTeam().stream().map(EntityAvatar::getAvatar).forEach(Avatar::sendSkillExtraChargeMap); } private void addEntityDirectly(GameEntity entity) { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarChangeElementTypeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarChangeElementTypeReq.java index 4c6e4085a..57d652005 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarChangeElementTypeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarChangeElementTypeReq.java @@ -19,50 +19,49 @@ import emu.grasscutter.server.packet.send.PacketAvatarSkillDepotChangeNotify; @Opcodes(PacketOpcodes.AvatarChangeElementTypeReq) public class HandlerAvatarChangeElementTypeReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - AvatarChangeElementTypeReq req = AvatarChangeElementTypeReq.parseFrom(payload); - - WorldAreaData area = GameData.getWorldAreaDataMap().get(req.getAreaId()); - - if (area == null || area.getElementType() == null || area.getElementType().getDepotValue() <= 0) { - session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); - return; - } - - // Get current avatar, should be one of the main characters - EntityAvatar mainCharacterEntity = session.getPlayer().getTeamManager().getCurrentAvatarEntity(); - - int intialSkillDepotId = 0; - if (mainCharacterEntity.getAvatar().getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) { - intialSkillDepotId = 500; - } else if (mainCharacterEntity.getAvatar().getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) { - intialSkillDepotId = 700; - } else { - session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); - return; - } - intialSkillDepotId += area.getElementType().getDepotValue(); - - // Sanity checks for skill depots - Avatar mainCharacter = mainCharacterEntity.getAvatar(); - AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(intialSkillDepotId); - if (skillDepot == null || skillDepot.getId() == mainCharacter.getSkillDepotId()) { - session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); - return; - } - - // Success - session.send(new PacketAvatarChangeElementTypeRsp()); - - // Set skill depot - mainCharacter.setSkillDepotData(skillDepot); - - // Ability change packet - session.send(new PacketAvatarSkillDepotChangeNotify(mainCharacter)); - session.send(new PacketAbilityChangeNotify(mainCharacterEntity)); - session.send(new PacketAvatarFightPropNotify(mainCharacter)); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + AvatarChangeElementTypeReq req = AvatarChangeElementTypeReq.parseFrom(payload); + + WorldAreaData area = GameData.getWorldAreaDataMap().get(req.getAreaId()); + + if (area == null || area.getElementType() == null || area.getElementType().getDepotValue() <= 0) { + session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); + return; + } + + // Get current avatar, should be one of the main characters + EntityAvatar mainCharacterEntity = session.getPlayer().getTeamManager().getCurrentAvatarEntity(); + Avatar mainCharacter = mainCharacterEntity.getAvatar(); + + int skillDepotId = area.getElementType().getDepotValue(); + switch (mainCharacter.getAvatarId()) { + case GameConstants.MAIN_CHARACTER_MALE -> skillDepotId += 500; + case GameConstants.MAIN_CHARACTER_FEMALE -> skillDepotId += 700; + default -> { + session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); + return; + } + } + + // Sanity checks for skill depots + AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(skillDepotId); + if (skillDepot == null || skillDepot.getId() == mainCharacter.getSkillDepotId()) { + session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); + return; + } + + // Set skill depot + mainCharacter.setSkillDepotData(skillDepot); + + // Success + session.send(new PacketAvatarChangeElementTypeRsp()); + + // Ability change packet + session.send(new PacketAvatarSkillDepotChangeNotify(mainCharacter)); + session.send(new PacketAbilityChangeNotify(mainCharacterEntity)); + session.send(new PacketAvatarFightPropNotify(mainCharacter)); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java index a371a8e83..c90b1a507 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java @@ -13,8 +13,10 @@ public class HandlerAvatarSkillUpgradeReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { AvatarSkillUpgradeReq req = AvatarSkillUpgradeReq.parseFrom(payload); + // Sanity checks + var avatar = session.getPlayer().getAvatars().getAvatarByGuid(req.getAvatarGuid()); + if (avatar == null) return; // Level up avatar talent - session.getServer().getInventorySystem().upgradeAvatarSkill(session.getPlayer(), req.getAvatarGuid(), req.getAvatarSkillId()); + avatar.upgradeSkill(req.getAvatarSkillId()); } - } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java index 5016017f6..2723ed60f 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java @@ -14,7 +14,9 @@ public class HandlerUnlockAvatarTalentReq extends PacketHandler { UnlockAvatarTalentReq req = UnlockAvatarTalentReq.parseFrom(payload); // Unlock avatar const - session.getServer().getInventorySystem().unlockAvatarConstellation(session.getPlayer(), req.getAvatarGuid()); + var avatar = session.getPlayer().getAvatars().getAvatarByGuid(req.getAvatarGuid()); + if (avatar == null) return; + avatar.unlockConstellation(req.getTalentId()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillInfoNotify.java index b099e5646..f7fdba686 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillInfoNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillInfoNotify.java @@ -1,25 +1,20 @@ package emu.grasscutter.server.packet.send; -import java.util.Map.Entry; - -import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.AvatarSkillInfoNotifyOuterClass.AvatarSkillInfoNotify; import emu.grasscutter.net.proto.AvatarSkillInfoOuterClass.AvatarSkillInfo; +import it.unimi.dsi.fastutil.ints.Int2IntMap; public class PacketAvatarSkillInfoNotify extends BasePacket { - - public PacketAvatarSkillInfoNotify(Avatar avatar) { - super(PacketOpcodes.AvatarSkillInfoNotify); + public PacketAvatarSkillInfoNotify(long avatarGuid, Int2IntMap skillExtraChargeMap) { + super(PacketOpcodes.AvatarSkillInfoNotify); - AvatarSkillInfoNotify.Builder proto = AvatarSkillInfoNotify.newBuilder() - .setGuid(avatar.getGuid()); - - for (Entry entry : avatar.getSkillExtraChargeMap().entrySet()) { - proto.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build()); - } - - this.setData(proto); - } + var proto = AvatarSkillInfoNotify.newBuilder().setGuid(avatarGuid); + + skillExtraChargeMap.forEach((skillId, count) -> + proto.putSkillMap(skillId, AvatarSkillInfo.newBuilder().setMaxChargeCount(count).build())); + + this.setData(proto); + } }