Commit fb1bacb0 authored by AnimeGitB's avatar AnimeGitB
Browse files

Refactor avatar skilldepot and constellation/talent changing

Ensures Traveler retains talent levels and constellations on inactive elements when switching elements.
Relevant for any other skillDepot-changing activities like Windtrace too, though keeping those in the db might not be as useful.

Refactor avatar talent upgrade and access
Refactor skillExtraCharges
parent 78b7fb70
...@@ -244,32 +244,11 @@ public final class GiveCommand implements CommandHandler { ...@@ -244,32 +244,11 @@ public final class GiveCommand implements CommandHandler {
} }
private static Avatar makeAvatar(AvatarData avatarData, int level, int promoteLevel, int constellation) { private static Avatar makeAvatar(AvatarData avatarData, int level, int promoteLevel, int constellation) {
// Calculate ascension level.
Avatar avatar = new Avatar(avatarData); Avatar avatar = new Avatar(avatarData);
avatar.setLevel(level); avatar.setLevel(level);
avatar.setPromoteLevel(promoteLevel); avatar.setPromoteLevel(promoteLevel);
avatar.forceConstellationLevel(constellation);
// 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.recalcStats(); avatar.recalcStats();
return avatar; return avatar;
} }
......
...@@ -37,9 +37,6 @@ public final class ResetConstCommand implements CommandHandler { ...@@ -37,9 +37,6 @@ public final class ResetConstCommand implements CommandHandler {
} }
private void resetConstellation(Avatar avatar) { private void resetConstellation(Avatar avatar) {
avatar.getTalentIdList().clear(); avatar.forceConstellationLevel(-1);
avatar.setCoreProudSkillLevel(0);
avatar.recalcStats();
avatar.save();
} }
} }
...@@ -15,7 +15,6 @@ import emu.grasscutter.utils.Position; ...@@ -15,7 +15,6 @@ import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
@Command( @Command(
label = "setConst", label = "setConst",
...@@ -33,7 +32,7 @@ public final class SetConstCommand implements CommandHandler { ...@@ -33,7 +32,7 @@ public final class SetConstCommand implements CommandHandler {
try { try {
int constLevel = Integer.parseInt(args.get(0)); int constLevel = Integer.parseInt(args.get(0));
if (constLevel < 0 || constLevel > 6) { if (constLevel < -1 || constLevel > 6) {
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.range_error"); CommandHandler.sendTranslatedMessage(sender, "commands.setConst.range_error");
return; return;
} }
...@@ -52,19 +51,7 @@ public final class SetConstCommand implements CommandHandler { ...@@ -52,19 +51,7 @@ public final class SetConstCommand implements CommandHandler {
private void setConstellation(Player player, Avatar avatar, int constLevel) { private void setConstellation(Player player, Avatar avatar, int constLevel) {
int currentConstLevel = avatar.getCoreProudSkillLevel(); int currentConstLevel = avatar.getCoreProudSkillLevel();
IntArrayList talentIds = new IntArrayList(avatar.getSkillDepot().getTalents()); avatar.forceConstellationLevel(constLevel);
Set<Integer> 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());
}
// force player to reload scene when necessary // force player to reload scene when necessary
if (constLevel < currentConstLevel) { if (constLevel < currentConstLevel) {
......
...@@ -4,10 +4,7 @@ import emu.grasscutter.command.Command; ...@@ -4,10 +4,7 @@ import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.excels.AvatarSkillDepotData; import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketAvatarSkillChangeNotify;
import emu.grasscutter.server.packet.send.PacketAvatarSkillUpgradeRsp;
import java.util.List; import java.util.List;
...@@ -17,34 +14,6 @@ import java.util.List; ...@@ -17,34 +14,6 @@ import java.util.List;
permission = "player.settalent", permission = "player.settalent",
permissionTargeted = "player.settalent.others") permissionTargeted = "player.settalent.others")
public final class TalentCommand implements CommandHandler { 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 @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1){ if (args.size() < 1){
...@@ -52,9 +21,19 @@ public final class TalentCommand implements CommandHandler { ...@@ -52,9 +21,19 @@ public final class TalentCommand implements CommandHandler {
return; return;
} }
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity(); Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar();
Avatar avatar = entity.getAvatar(); AvatarSkillDepotData skillDepot = avatar.getSkillDepot();
String cmdSwitch = args.get(0); 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) { switch (cmdSwitch) {
default -> { default -> {
sendUsageMessage(sender); sendUsageMessage(sender);
...@@ -62,42 +41,56 @@ public final class TalentCommand implements CommandHandler { ...@@ -62,42 +41,56 @@ public final class TalentCommand implements CommandHandler {
} }
case "set" -> { case "set" -> {
if (args.size() < 3) { if (args.size() < 3) {
sendUsageMessage(sender);
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
try { try {
int skillId = Integer.parseInt(args.get(1)); skillId = Integer.parseInt(args.get(1));
int newLevel = Integer.parseInt(args.get(2));
setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id"); CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id");
return; 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" -> { case "n", "e", "q" -> {
if (args.size() < 2) { if (args.size() < 2) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; 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 { try {
int newLevel = Integer.parseInt(args.get(1)); newLevel = Integer.parseInt(args.get(1));
setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level"); CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level");
return; 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" -> { 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.normal_attack_id", Integer.toString(skillIdNorAtk));
CommandHandler.sendTranslatedMessage(sender, "commands.talent.e_skill_id", Integer.toString(skillIdE)); CommandHandler.sendTranslatedMessage(sender, "commands.talent.e_skill_id", Integer.toString(skillIdE));
CommandHandler.sendTranslatedMessage(sender, "commands.talent.q_skill_id", Integer.toString(skillIdQ)); CommandHandler.sendTranslatedMessage(sender, "commands.talent.q_skill_id", Integer.toString(skillIdQ));
......
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import java.util.List; import java.util.List;
import java.util.stream.IntStream;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.GameDepot;
...@@ -71,4 +72,9 @@ public class AvatarSkillDepotData extends GameResource { ...@@ -71,4 +72,9 @@ public class AvatarSkillDepotData extends GameResource {
@Getter private int proudSkillGroupId; @Getter private int proudSkillGroupId;
@Getter private int needAvatarPromoteLevel; @Getter private int needAvatarPromoteLevel;
} }
public IntStream getSkillsAndEnergySkill() {
return IntStream.concat(this.skills.stream().mapToInt(i -> i), IntStream.of(this.energySkill))
.filter(skillId -> skillId > 0);
}
} }
...@@ -3,99 +3,59 @@ package emu.grasscutter.data.excels; ...@@ -3,99 +3,59 @@ package emu.grasscutter.data.excels;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import lombok.Getter;
@ResourceType(name = "ProudSkillExcelConfigData.json") @ResourceType(name = "ProudSkillExcelConfigData.json")
public class ProudSkillData extends GameResource { public class ProudSkillData extends GameResource {
private int proudSkillId;
private int proudSkillId; @Getter private int proudSkillGroupId;
private int proudSkillGroupId; @Getter private int level;
private int level; @Getter private int coinCost;
private int coinCost; @Getter private int breakLevel;
private int breakLevel; @Getter private int proudSkillType;
private int proudSkillType; @Getter private String openConfig;
private String openConfig; @Getter private List<ItemParamData> costItems;
private List<ItemParamData> costItems; @Getter private List<String> filterConds;
private List<String> filterConds; @Getter private List<String> lifeEffectParams;
private List<String> lifeEffectParams; @Getter private FightPropData[] addProps;
private FightPropData[] addProps; @Getter private float[] paramList;
private float[] paramList; @Getter private long[] paramDescList;
private long[] paramDescList; @Getter private long nameTextMapHash;
private long nameTextMapHash; @Transient private Iterable<ItemParamData> totalCostItems;
@Override @Override
public int getId() { public int getId() {
return proudSkillId; return proudSkillId;
} }
public int getProudSkillGroupId() { public Iterable<ItemParamData> getTotalCostItems() {
return proudSkillGroupId; if (this.totalCostItems == null) {
} ArrayList<ItemParamData> total = (this.costItems != null) ? new ArrayList<>(this.costItems) : new ArrayList<>(1);
if (this.coinCost > 0)
public int getLevel() { total.add(new ItemParamData(202, this.coinCost));
return level; this.totalCostItems = total;
} }
return this.totalCostItems;
public int getCoinCost() { }
return coinCost;
} @Override
public void onLoad() {
public int getBreakLevel() { if (this.getOpenConfig() != null & this.getOpenConfig().length() > 0) {
return breakLevel; this.openConfig = "Avatar_" + this.getOpenConfig();
} }
// Fight props
public int getProudSkillType() { ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
return proudSkillType; for (FightPropData prop : getAddProps()) {
} if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad();
public String getOpenConfig() { parsed.add(prop);
return openConfig; }
} }
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
public List<ItemParamData> getCostItems() { }
return costItems;
}
public List<String> getFilterConds() {
return filterConds;
}
public List<String> 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<FightPropData> parsed = new ArrayList<FightPropData>(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()]);
}
} }
...@@ -7,7 +7,7 @@ import java.util.HashMap; ...@@ -7,7 +7,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.Set; import java.util.Set;
...@@ -19,6 +19,7 @@ import dev.morphia.annotations.Indexed; ...@@ -19,6 +19,7 @@ import dev.morphia.annotations.Indexed;
import dev.morphia.annotations.PostLoad; import dev.morphia.annotations.PostLoad;
import dev.morphia.annotations.PrePersist; import dev.morphia.annotations.PrePersist;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.GameConstants;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.OpenConfigEntry; import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier;
...@@ -56,14 +57,14 @@ import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; ...@@ -56,14 +57,14 @@ import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData;
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo;
import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip; import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip;
import emu.grasscutter.server.packet.send.PacketAbilityChangeNotify; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropNotify;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 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.Getter;
import lombok.Setter; import lombok.Setter;
...@@ -93,12 +94,11 @@ public class Avatar { ...@@ -93,12 +94,11 @@ public class Avatar {
private List<Integer> fetters; private List<Integer> fetters;
@Getter private Map<Integer, Integer> skillLevelMap; // Talent levels private Map<Integer, Integer> skillLevelMap; // Talent levels
private Map<Integer, Integer> skillExtraChargeMap; // Charges private Map<Integer, Integer> skillExtraChargeMap; // Charges
@Getter private Map<Integer, Integer> proudSkillBonusMap; // Talent bonus levels (from const) @Getter private Map<Integer, Integer> proudSkillBonusMap; // Talent bonus levels (from const)
@Getter private int skillDepotId; @Getter private int skillDepotId;
@Getter @Setter private int coreProudSkillLevel; // Constellation level private Set<Integer> talentIdList; // Constellation id list
@Getter private Set<Integer> talentIdList; // Constellation id list
@Getter private Set<Integer> proudSkillList; // Character passives @Getter private Set<Integer> proudSkillList; // Character passives
@Getter @Setter private int flyCloak; @Getter @Setter private int flyCloak;
...@@ -147,7 +147,14 @@ public class Avatar { ...@@ -147,7 +147,14 @@ public class Avatar {
.forEach(id -> this.setFightProperty(id, 0f)); .forEach(id -> this.setFightProperty(id, 0f));
// Skill depot // 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 // Set stats
this.recalcStats(); this.recalcStats();
...@@ -219,32 +226,21 @@ public class Avatar { ...@@ -219,32 +226,21 @@ public class Avatar {
// Set id and depot // Set id and depot
this.skillDepotId = skillDepot.getId(); this.skillDepotId = skillDepot.getId();
this.skillDepot = skillDepot; this.skillDepot = skillDepot;
// Clear, then add skills // Add any missing skills
getSkillLevelMap().clear(); this.skillDepot.getSkillsAndEnergySkill()
if (skillDepot.getEnergySkill() > 0) { .forEach(skillId -> this.skillLevelMap.putIfAbsent(skillId, 1));
getSkillLevelMap().put(skillDepot.getEnergySkill(), 1);
}
for (int skillId : skillDepot.getSkills()) {
if (skillId > 0) {
getSkillLevelMap().put(skillId, 1);
}
}
// Add proud skills // Add proud skills
this.getProudSkillList().clear(); this.proudSkillList.clear();
for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { skillDepot.getInherentProudSkillOpens().stream()
if (openData.getProudSkillGroupId() == 0) { .filter(openData -> openData.getProudSkillGroupId() > 0)
continue; .filter(openData -> openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel())
} .mapToInt(openData -> (openData.getProudSkillGroupId() * 100) + 1)
if (openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel()) { .filter(proudSkillId -> GameData.getProudSkillDataMap().containsKey(proudSkillId))
int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; .forEach(proudSkillId -> this.proudSkillList.add(proudSkillId));
if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { this.recalcStats();
this.getProudSkillList().add(proudSkillId);
}
}
}
} }
public Map<Integer, Integer> getSkillExtraChargeMap() { private Map<Integer, Integer> getSkillExtraChargeMap() {
if (skillExtraChargeMap == null) { if (skillExtraChargeMap == null) {
skillExtraChargeMap = new HashMap<>(); skillExtraChargeMap = new HashMap<>();
} }
...@@ -266,16 +262,12 @@ public class Avatar { ...@@ -266,16 +262,12 @@ public class Avatar {
} }
public void setCurrentEnergy(float currentEnergy) { public void setCurrentEnergy(float currentEnergy) {
if (this.getSkillDepot() != null && this.getSkillDepot().getEnergySkillData() != null) { var depot = this.skillDepot;
ElementType element = this.getSkillDepot().getElementType(); if (depot != null && depot.getEnergySkillData() != null) {
this.setFightProperty(element.getMaxEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); ElementType element = depot.getElementType();
var maxEnergy = depot.getEnergySkillData().getCostElemVal();
if (GAME_OPTIONS.energyUsage) { this.setFightProperty(element.getMaxEnergyProp(), maxEnergy);
this.setFightProperty(element.getCurEnergyProp(), currentEnergy); this.setFightProperty(element.getCurEnergyProp(), GAME_OPTIONS.energyUsage ? currentEnergy : maxEnergy);
}
else {
this.setFightProperty(element.getCurEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal());
}
} }
} }
...@@ -307,6 +299,26 @@ public class Avatar { ...@@ -307,6 +299,26 @@ public class Avatar {
return getFightProperties().getOrDefault(prop.getId(), 0f); return getFightProperties().getOrDefault(prop.getId(), 0f);
} }
public Map<Integer, Integer> 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) { public boolean equipItem(GameItem item, boolean shouldRecalc) {
// Sanity check equip type // Sanity check equip type
EquipType itemEquipType = item.getItemData().getEquipType(); EquipType itemEquipType = item.getItemData().getEquipType();
...@@ -549,17 +561,13 @@ public class Avatar { ...@@ -549,17 +561,13 @@ public class Avatar {
} }
// Constellations // Constellations
if (this.getTalentIdList().size() > 0) { this.getTalentIdList().intStream()
for (int talentId : this.getTalentIdList()) { .mapToObj(GameData.getAvatarTalentDataMap()::get)
AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); .filter(Objects::nonNull)
if (avatarTalentData == null) { .map(AvatarTalentData::getOpenConfig)
return; .filter(Objects::nonNull)
} .forEach(openConfig -> this.addToExtraAbilityEmbryos(openConfig, false));
// Add any skill strings from this constellation
// Add any skill strings from this constellation
this.addToExtraAbilityEmbryos(avatarTalentData.getOpenConfig(), false);
}
}
// Set % stats // Set % stats
this.setFightProperty( this.setFightProperty(
...@@ -614,71 +622,179 @@ public class Avatar { ...@@ -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() { public void recalcConstellations() {
// Clear first // Clear first
this.getProudSkillBonusMap().clear(); this.getProudSkillBonusMap().clear();
this.getSkillExtraChargeMap().clear(); this.getSkillExtraChargeMap().clear();
// Sanity checks // Sanity checks
if (getData() == null || this.skillDepot == null) { if (this.data == null || this.skillDepot == null) {
return; return;
} }
if (this.getTalentIdList().size() > 0) { this.getTalentIdList().intStream()
for (int talentId : this.getTalentIdList()) { .mapToObj(GameData.getAvatarTalentDataMap()::get)
AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); .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) { private boolean calcConstellationExtraCharges(OpenConfigEntry entry) {
continue; var skillPointModifiers = entry.getSkillPointModifiers();
} if (skillPointModifiers == null) return false;
// Get open config to find which skill should be boosted for (var mod : skillPointModifiers) {
OpenConfigEntry entry = GameData.getOpenConfigEntries().get(avatarTalentData.getOpenConfig()); AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(mod.getSkillId());
if (entry == null) {
continue;
}
// Check if we can add charges to a skill if (skillData == null) continue;
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();
int charges = skillData.getMaxChargeNum() + mod.getDelta(); this.getSkillExtraChargeMap().put(mod.getSkillId(), charges);
}
return true;
}
this.getSkillExtraChargeMap().put(mod.getSkillId(), charges); private boolean calcConstellationExtraLevels(OpenConfigEntry entry) {
} int skillId = switch(entry.getExtraTalentIndex()) {
continue; 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;
}
// Check if a skill can be boosted by +3 levels // Get proud skill group id
int skillId = 0; AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId);
if (entry.getExtraTalentIndex() == 2 && this.skillDepot.getSkills().size() >= 2) { if (skillData == null) {
// E skill return false;
skillId = this.skillDepot.getSkills().get(1); }
} else if (entry.getExtraTalentIndex() == 9) {
// Ult skill
skillId = this.skillDepot.getEnergySkill();
}
// Sanity check // Add to bonus list
if (skillId == 0) { this.getProudSkillBonusMap().put(skillData.getProudSkillGroupId(), 3);
continue; return true;
} }
// Get proud skill group id public boolean upgradeSkill(int skillId) {
AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId);
if (skillData == null) return false;
if (skillData == null) { // Get data for next skill level
continue; int newLevel = this.skillLevelMap.getOrDefault(skillId, 0) + 1;
} if (newLevel > 10) return false;
// Add to bonus list // Proud skill data
this.getProudSkillBonusMap().put(skillData.getProudSkillGroupId(), 3); 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() { public EntityAvatar getAsEntity() {
...@@ -709,14 +825,11 @@ public class Avatar { ...@@ -709,14 +825,11 @@ public class Avatar {
} }
if (this.getFetterList() != null) { if (this.fetters != null) {
for (int i = 0; i < this.getFetterList().size(); i++) { this.fetters.forEach(fetterId -> avatarFetter.addFetterList(
avatarFetter.addFetterList( FetterData.newBuilder()
FetterData.newBuilder() .setFetterId(fetterId)
.setFetterId(this.getFetterList().get(i)) .setFetterState(FetterState.FINISH.getValue())));
.setFetterState(FetterState.FINISH.getValue())
);
}
} }
int cardId = this.getNameCardId(); int cardId = this.getNameCardId();
...@@ -742,13 +855,10 @@ public class Avatar { ...@@ -742,13 +855,10 @@ public class Avatar {
.setWearingFlycloakId(this.getFlyCloak()) .setWearingFlycloakId(this.getFlyCloak())
.setCostumeId(this.getCostume()); .setCostumeId(this.getCostume());
for (Entry<Integer, Integer> entry : this.getSkillExtraChargeMap().entrySet()) { this.getSkillExtraChargeMap().forEach((skillId, count) ->
avatarInfo.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build()); avatarInfo.putSkillMap(skillId, AvatarSkillInfo.newBuilder().setMaxChargeCount(count).build()));
}
for (GameItem item : this.getEquips().values()) { this.getEquips().forEach((k, item) -> avatarInfo.addEquipGuidList(item.getGuid()));
avatarInfo.addEquipGuidList(item.getGuid());
}
avatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel())); 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())); avatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp()));
......
...@@ -338,11 +338,7 @@ public class TeamManager extends BasePlayerDataManager { ...@@ -338,11 +338,7 @@ public class TeamManager extends BasePlayerDataManager {
this.getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(this.getPlayer())); this.getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(this.getPlayer()));
// Skill charges packet - Yes, this is official server behavior as of 2.6.0 // Skill charges packet - Yes, this is official server behavior as of 2.6.0
for (EntityAvatar entity : this.getActiveTeam()) { this.getActiveTeam().stream().map(EntityAvatar::getAvatar).forEach(Avatar::sendSkillExtraChargeMap);
if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) {
this.getPlayer().sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar()));
}
}
// Run callback // Run callback
if (responsePacket != null) { if (responsePacket != null) {
......
...@@ -7,14 +7,11 @@ import java.util.Map; ...@@ -7,14 +7,11 @@ import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import emu.grasscutter.data.GameData; 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.ItemParamData;
import emu.grasscutter.data.common.ItemUseData; import emu.grasscutter.data.common.ItemUseData;
import emu.grasscutter.data.excels.AvatarPromoteData; import emu.grasscutter.data.excels.AvatarPromoteData;
import emu.grasscutter.data.excels.AvatarSkillData; import emu.grasscutter.data.excels.AvatarSkillData;
import emu.grasscutter.data.excels.AvatarSkillDepotData; import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.excels.AvatarTalentData;
import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.ProudSkillData; import emu.grasscutter.data.excels.ProudSkillData;
import emu.grasscutter.data.excels.WeaponPromoteData; import emu.grasscutter.data.excels.WeaponPromoteData;
...@@ -649,120 +646,22 @@ public class InventorySystem extends BaseGameSystem { ...@@ -649,120 +646,22 @@ public class InventorySystem extends BaseGameSystem {
player.sendPacket(new PacketAvatarFetterDataNotify(avatar)); player.sendPacket(new PacketAvatarFetterDataNotify(avatar));
} }
@Deprecated(forRemoval = true)
public void upgradeAvatarSkill(Player player, long guid, int skillId) { public void upgradeAvatarSkill(Player player, long guid, int skillId) {
// Sanity checks // Sanity checks
Avatar avatar = player.getAvatars().getAvatarByGuid(guid); Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
if (avatar == null) { if (avatar == null) return;
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 avatar.upgradeSkill(skillId);
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<ItemParamData> costs = new ArrayList<ItemParamData>(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));
} }
@Deprecated(forRemoval = true)
public void unlockAvatarConstellation(Player player, long guid) { public void unlockAvatarConstellation(Player player, long guid) {
// Sanity checks // Sanity check
Avatar avatar = player.getAvatars().getAvatarByGuid(guid); Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
if (avatar == null) { if (avatar == null) return;
return;
}
// Get talent avatar.unlockConstellation();
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();
} }
public void destroyMaterial(Player player, List<MaterialInfo> list) { public void destroyMaterial(Player player, List<MaterialInfo> list) {
......
...@@ -5,6 +5,7 @@ import emu.grasscutter.data.GameData; ...@@ -5,6 +5,7 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.binout.SceneNpcBornEntry; import emu.grasscutter.data.binout.SceneNpcBornEntry;
import emu.grasscutter.data.excels.*; import emu.grasscutter.data.excels.*;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.dungeons.DungeonSettleListener; import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
...@@ -270,22 +271,19 @@ public class Scene { ...@@ -270,22 +271,19 @@ public class Scene {
} }
public void spawnPlayer(Player player) { public void spawnPlayer(Player player) {
if (this.isInScene(player.getTeamManager().getCurrentAvatarEntity())) { var teamManager = player.getTeamManager();
if (this.isInScene(teamManager.getCurrentAvatarEntity())) {
return; return;
} }
if (player.getTeamManager().getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { if (teamManager.getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
player.getTeamManager().getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f); 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 // Notify the client of any extra skill charges
for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) { teamManager.getActiveTeam().stream().map(EntityAvatar::getAvatar).forEach(Avatar::sendSkillExtraChargeMap);
if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) {
player.sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar()));
}
}
} }
private void addEntityDirectly(GameEntity entity) { private void addEntityDirectly(GameEntity entity) {
......
...@@ -19,50 +19,49 @@ import emu.grasscutter.server.packet.send.PacketAvatarSkillDepotChangeNotify; ...@@ -19,50 +19,49 @@ import emu.grasscutter.server.packet.send.PacketAvatarSkillDepotChangeNotify;
@Opcodes(PacketOpcodes.AvatarChangeElementTypeReq) @Opcodes(PacketOpcodes.AvatarChangeElementTypeReq)
public class HandlerAvatarChangeElementTypeReq extends PacketHandler { public class HandlerAvatarChangeElementTypeReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarChangeElementTypeReq req = AvatarChangeElementTypeReq.parseFrom(payload); AvatarChangeElementTypeReq req = AvatarChangeElementTypeReq.parseFrom(payload);
WorldAreaData area = GameData.getWorldAreaDataMap().get(req.getAreaId()); WorldAreaData area = GameData.getWorldAreaDataMap().get(req.getAreaId());
if (area == null || area.getElementType() == null || area.getElementType().getDepotValue() <= 0) { if (area == null || area.getElementType() == null || area.getElementType().getDepotValue() <= 0) {
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
return; return;
} }
// Get current avatar, should be one of the main characters // Get current avatar, should be one of the main characters
EntityAvatar mainCharacterEntity = session.getPlayer().getTeamManager().getCurrentAvatarEntity(); EntityAvatar mainCharacterEntity = session.getPlayer().getTeamManager().getCurrentAvatarEntity();
Avatar mainCharacter = mainCharacterEntity.getAvatar();
int intialSkillDepotId = 0;
if (mainCharacterEntity.getAvatar().getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) { int skillDepotId = area.getElementType().getDepotValue();
intialSkillDepotId = 500; switch (mainCharacter.getAvatarId()) {
} else if (mainCharacterEntity.getAvatar().getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) { case GameConstants.MAIN_CHARACTER_MALE -> skillDepotId += 500;
intialSkillDepotId = 700; case GameConstants.MAIN_CHARACTER_FEMALE -> skillDepotId += 700;
} else { default -> {
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
return; return;
} }
intialSkillDepotId += area.getElementType().getDepotValue(); }
// Sanity checks for skill depots // Sanity checks for skill depots
Avatar mainCharacter = mainCharacterEntity.getAvatar(); AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(skillDepotId);
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(intialSkillDepotId); if (skillDepot == null || skillDepot.getId() == mainCharacter.getSkillDepotId()) {
if (skillDepot == null || skillDepot.getId() == mainCharacter.getSkillDepotId()) { session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE));
session.send(new PacketAvatarChangeElementTypeRsp(Retcode.RET_SVR_ERROR_VALUE)); return;
return; }
}
// Set skill depot
// Success mainCharacter.setSkillDepotData(skillDepot);
session.send(new PacketAvatarChangeElementTypeRsp());
// Success
// Set skill depot session.send(new PacketAvatarChangeElementTypeRsp());
mainCharacter.setSkillDepotData(skillDepot);
// Ability change packet
// Ability change packet session.send(new PacketAvatarSkillDepotChangeNotify(mainCharacter));
session.send(new PacketAvatarSkillDepotChangeNotify(mainCharacter)); session.send(new PacketAbilityChangeNotify(mainCharacterEntity));
session.send(new PacketAbilityChangeNotify(mainCharacterEntity)); session.send(new PacketAvatarFightPropNotify(mainCharacter));
session.send(new PacketAvatarFightPropNotify(mainCharacter)); }
}
} }
...@@ -13,8 +13,10 @@ public class HandlerAvatarSkillUpgradeReq extends PacketHandler { ...@@ -13,8 +13,10 @@ public class HandlerAvatarSkillUpgradeReq extends PacketHandler {
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AvatarSkillUpgradeReq req = AvatarSkillUpgradeReq.parseFrom(payload); AvatarSkillUpgradeReq req = AvatarSkillUpgradeReq.parseFrom(payload);
// Sanity checks
var avatar = session.getPlayer().getAvatars().getAvatarByGuid(req.getAvatarGuid());
if (avatar == null) return;
// Level up avatar talent // Level up avatar talent
session.getServer().getInventorySystem().upgradeAvatarSkill(session.getPlayer(), req.getAvatarGuid(), req.getAvatarSkillId()); avatar.upgradeSkill(req.getAvatarSkillId());
} }
} }
...@@ -14,7 +14,9 @@ public class HandlerUnlockAvatarTalentReq extends PacketHandler { ...@@ -14,7 +14,9 @@ public class HandlerUnlockAvatarTalentReq extends PacketHandler {
UnlockAvatarTalentReq req = UnlockAvatarTalentReq.parseFrom(payload); UnlockAvatarTalentReq req = UnlockAvatarTalentReq.parseFrom(payload);
// Unlock avatar const // 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());
} }
} }
package emu.grasscutter.server.packet.send; 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.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarSkillInfoNotifyOuterClass.AvatarSkillInfoNotify; import emu.grasscutter.net.proto.AvatarSkillInfoNotifyOuterClass.AvatarSkillInfoNotify;
import emu.grasscutter.net.proto.AvatarSkillInfoOuterClass.AvatarSkillInfo; import emu.grasscutter.net.proto.AvatarSkillInfoOuterClass.AvatarSkillInfo;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
public class PacketAvatarSkillInfoNotify extends BasePacket { public class PacketAvatarSkillInfoNotify extends BasePacket {
public PacketAvatarSkillInfoNotify(long avatarGuid, Int2IntMap skillExtraChargeMap) {
public PacketAvatarSkillInfoNotify(Avatar avatar) { super(PacketOpcodes.AvatarSkillInfoNotify);
super(PacketOpcodes.AvatarSkillInfoNotify);
var proto = AvatarSkillInfoNotify.newBuilder().setGuid(avatarGuid);
skillExtraChargeMap.forEach((skillId, count) ->
proto.putSkillMap(skillId, AvatarSkillInfo.newBuilder().setMaxChargeCount(count).build()));
AvatarSkillInfoNotify.Builder proto = AvatarSkillInfoNotify.newBuilder() this.setData(proto);
.setGuid(avatar.getGuid()); }
for (Entry<Integer, Integer> entry : avatar.getSkillExtraChargeMap().entrySet()) {
proto.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build());
}
this.setData(proto);
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment