mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-22 11:54:39 +01:00
Fix player fields not being set
line seps r weird
This commit is contained in:
@@ -5,7 +5,7 @@ import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.PlayerLevelData;
|
||||
import emu.grasscutter.data.excels.WeatherData;
|
||||
import emu.grasscutter.data.excels.world.WeatherData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.CoopRequest;
|
||||
@@ -15,6 +15,7 @@ import emu.grasscutter.game.activity.ActivityManager;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||
import emu.grasscutter.game.battlepass.BattlePassManager;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.expedition.ExpeditionInfo;
|
||||
import emu.grasscutter.game.friends.FriendsList;
|
||||
@@ -44,27 +45,26 @@ import emu.grasscutter.game.props.ClimateType;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.game.quest.QuestManager;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.shop.ShopLimit;
|
||||
import emu.grasscutter.game.tower.TowerData;
|
||||
import emu.grasscutter.game.tower.TowerManager;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.proto.*;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
||||
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
||||
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass;
|
||||
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
|
||||
import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||
import emu.grasscutter.net.proto.SocialShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
||||
import emu.grasscutter.scripts.data.SceneRegion;
|
||||
import emu.grasscutter.server.event.player.PlayerJoinEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerQuitEvent;
|
||||
@@ -88,6 +88,7 @@ import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
|
||||
@@ -116,12 +117,13 @@ public class Player {
|
||||
@Getter @Setter private int sceneId;
|
||||
@Getter @Setter private int regionId;
|
||||
@Getter private int mainCharacterId;
|
||||
@Setter private boolean godmode; // Getter is inGodmode
|
||||
private boolean stamina; // Getter is getUnlimitedStamina, Setter is setUnlimitedStamina
|
||||
@Getter @Setter private boolean inGodMode;
|
||||
@Getter @Setter private boolean unlimitedStamina;
|
||||
|
||||
@Getter private Set<Integer> nameCardList;
|
||||
@Getter private Set<Integer> flyCloakList;
|
||||
@Getter private Set<Integer> costumeList;
|
||||
@Getter private Set<Integer> personalLineList;
|
||||
@Getter @Setter private Set<Integer> rewardedLevels;
|
||||
@Getter @Setter private Set<Integer> homeRewardedLevels;
|
||||
@Getter @Setter private Set<Integer> realmList;
|
||||
@@ -205,7 +207,11 @@ public class Player {
|
||||
@Getter @Setter private int nextResinRefresh;
|
||||
@Getter @Setter private int resinBuyCount;
|
||||
@Getter @Setter private int lastDailyReset;
|
||||
@Getter private transient MpSettingType mpSetting = MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY; // TODO
|
||||
@Getter private transient MpSettingType mpSetting = MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY;
|
||||
@Getter private long playerGameTime = 0;
|
||||
|
||||
@Getter private PlayerProgress playerProgress;
|
||||
@Getter private Set<Integer> activeQuestTimers;
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
|
||||
@@ -242,7 +248,7 @@ public class Player {
|
||||
this.unlockedCombines = new HashSet<>();
|
||||
this.unlockedFurniture = new HashSet<>();
|
||||
this.unlockedFurnitureSuite = new HashSet<>();
|
||||
this.activeCookCompounds=new HashMap<>();
|
||||
this.activeCookCompounds = new HashMap<>();
|
||||
this.activeForges = new ArrayList<>();
|
||||
this.unlockedRecipies = new HashMap<>();
|
||||
this.questGlobalVariables = new HashMap<>();
|
||||
@@ -250,6 +256,8 @@ public class Player {
|
||||
this.unlockedSceneAreas = new HashMap<>();
|
||||
this.unlockedScenePoints = new HashMap<>();
|
||||
this.chatEmojiIdList = new ArrayList<>();
|
||||
this.playerProgress = new PlayerProgress();
|
||||
this.activeQuestTimers = new HashSet<>();
|
||||
|
||||
this.attackResults = new LinkedBlockingQueue<>();
|
||||
this.coopRequests = new Int2ObjectOpenHashMap<>();
|
||||
@@ -276,7 +284,7 @@ public class Player {
|
||||
this.progressManager = new PlayerProgressManager(this);
|
||||
this.furnitureManager = new FurnitureManager(this);
|
||||
this.cookingManager = new CookingManager(this);
|
||||
this.cookingCompoundManager=new CookingCompoundManager(this);
|
||||
this.cookingCompoundManager = new CookingCompoundManager(this);
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
}
|
||||
|
||||
@@ -312,10 +320,22 @@ public class Player {
|
||||
this.progressManager = new PlayerProgressManager(this);
|
||||
this.furnitureManager = new FurnitureManager(this);
|
||||
this.cookingManager = new CookingManager(this);
|
||||
this.cookingCompoundManager=new CookingCompoundManager(this);
|
||||
this.cookingCompoundManager = new CookingCompoundManager(this);
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the player's game time if it has changed.
|
||||
*
|
||||
* @param gameTime The new game time.
|
||||
*/
|
||||
public void updatePlayerGameTime(long gameTime) {
|
||||
if (this.playerGameTime == gameTime) return;
|
||||
this.playerGameTime = gameTime;
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public int getUid() {
|
||||
return id;
|
||||
}
|
||||
@@ -456,6 +476,8 @@ public class Player {
|
||||
|
||||
// Handle open state unlocks from level-up.
|
||||
this.getProgressManager().tryUnlockOpenStates();
|
||||
this.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_PLAYER_LEVEL_UP, level);
|
||||
this.getQuestManager().queueEvent(QuestCond.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER, level);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -523,6 +545,7 @@ public class Player {
|
||||
public boolean setHomeCoin(int coin) {
|
||||
return this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin);
|
||||
}
|
||||
|
||||
private int getExpRequired(int level) {
|
||||
PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level);
|
||||
return levelData != null ? levelData.getExp() : 0;
|
||||
@@ -564,14 +587,14 @@ public class Player {
|
||||
|
||||
int newWorldLevel =
|
||||
(currentLevel >= 55) ? 8 :
|
||||
(currentLevel >= 50) ? 7 :
|
||||
(currentLevel >= 45) ? 6 :
|
||||
(currentLevel >= 40) ? 5 :
|
||||
(currentLevel >= 35) ? 4 :
|
||||
(currentLevel >= 30) ? 3 :
|
||||
(currentLevel >= 25) ? 2 :
|
||||
(currentLevel >= 20) ? 1 :
|
||||
0;
|
||||
(currentLevel >= 50) ? 7 :
|
||||
(currentLevel >= 45) ? 6 :
|
||||
(currentLevel >= 40) ? 5 :
|
||||
(currentLevel >= 35) ? 4 :
|
||||
(currentLevel >= 30) ? 3 :
|
||||
(currentLevel >= 25) ? 2 :
|
||||
(currentLevel >= 20) ? 1 :
|
||||
0;
|
||||
|
||||
if (newWorldLevel != currentWorldLevel) {
|
||||
this.setWorldLevel(newWorldLevel);
|
||||
@@ -596,11 +619,12 @@ public class Player {
|
||||
|
||||
public void onEnterRegion(SceneRegion region) {
|
||||
getQuestManager().forEachActiveQuest(quest -> {
|
||||
if (quest.getTriggers().containsKey("ENTER_REGION_"+ region.config_id)) {
|
||||
if (quest.getTriggerData() != null && quest.getTriggers().containsKey("ENTER_REGION_"+ region.config_id)) {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_"+ region.config_id, true))) {
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_" + region.config_id, true))) {
|
||||
//getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_TRIGGER_FIRE, quest.getTriggerData().get("ENTER_REGION_"+ region.config_id).getId(),0);
|
||||
getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
|
||||
quest.getTriggerData().get("ENTER_REGION_" + region.config_id).getId(), 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -609,11 +633,12 @@ public class Player {
|
||||
|
||||
public void onLeaveRegion(SceneRegion region) {
|
||||
getQuestManager().forEachActiveQuest(quest -> {
|
||||
if (quest.getTriggers().containsKey("LEAVE_REGION_"+ region.config_id)) {
|
||||
if (quest.getTriggers().containsKey("LEAVE_REGION_" + region.config_id)) {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("LEAVE_REGION_"+ region.config_id, true))) {
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("LEAVE_REGION_" + region.config_id, true))) {
|
||||
getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_TRIGGER_FIRE, quest.getTriggerData().get("LEAVE_REGION_"+ region.config_id).getId(),0);
|
||||
getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
|
||||
quest.getTriggerData().get("LEAVE_REGION_" + region.config_id).getId(), 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -705,9 +730,7 @@ public class Player {
|
||||
} else {
|
||||
moonCardDuration += 30;
|
||||
}
|
||||
if (!moonCardGetTimes.contains(moonCardStartTime)) {
|
||||
moonCardGetTimes.add(moonCardStartTime);
|
||||
}
|
||||
moonCardGetTimes.add(moonCardStartTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -776,18 +799,6 @@ public class Player {
|
||||
this.save();
|
||||
}
|
||||
|
||||
public boolean getUnlimitedStamina() {
|
||||
return stamina;
|
||||
}
|
||||
|
||||
public void setUnlimitedStamina(boolean stamina) {
|
||||
this.stamina = stamina;
|
||||
}
|
||||
|
||||
public boolean inGodmode() {
|
||||
return godmode;
|
||||
}
|
||||
|
||||
public boolean hasSentLoginPackets() {
|
||||
return hasSentLoginPackets;
|
||||
}
|
||||
@@ -819,6 +830,85 @@ public class Player {
|
||||
addAvatar(avatar, true);
|
||||
}
|
||||
|
||||
public void addAvatar(int avatarId) {
|
||||
// I dont see why we cant do this lolz
|
||||
addAvatar(new Avatar(avatarId), true);
|
||||
}
|
||||
|
||||
public List<Integer> getTrialAvatarParam (int trialAvatarId) {
|
||||
if (GameData.getTrialAvatarCustomData().isEmpty()) { // use default data if custom data not available
|
||||
if (GameData.getTrialAvatarDataMap().get(trialAvatarId) == null) return List.of();
|
||||
|
||||
return GameData.getTrialAvatarDataMap().get(trialAvatarId)
|
||||
.getTrialAvatarParamList();
|
||||
}
|
||||
// use custom data
|
||||
if (GameData.getTrialAvatarCustomData().get(trialAvatarId) == null) return List.of();
|
||||
|
||||
var trialCustomParams = GameData.getTrialAvatarCustomData().get(trialAvatarId).getTrialAvatarParamList();
|
||||
return trialCustomParams.isEmpty() ? List.of() : Stream.of(trialCustomParams.get(0).split(";")).map(Integer::parseInt).toList();
|
||||
}
|
||||
|
||||
public boolean addTrialAvatar(int trialAvatarId, GrantReason reason, int questMainId){
|
||||
List<Integer> trialAvatarBasicParam = getTrialAvatarParam(trialAvatarId);
|
||||
if (trialAvatarBasicParam.isEmpty()) return false;
|
||||
|
||||
Avatar avatar = new Avatar(trialAvatarBasicParam.get(0));
|
||||
if (avatar.getAvatarData() == null || !hasSentLoginPackets()) return false;
|
||||
|
||||
avatar.setOwner(this);
|
||||
// Add trial weapons and relics
|
||||
avatar.setTrialAvatarInfo(trialAvatarBasicParam.get(1), trialAvatarId, reason, questMainId);
|
||||
avatar.equipTrialItems();
|
||||
// Recalc stats
|
||||
avatar.recalcStats();
|
||||
|
||||
// Packet, mimic official server behaviour, add to player's bag but not saving to db
|
||||
sendPacket(new PacketAvatarAddNotify(avatar, false));
|
||||
// add to avatar to temporary trial team
|
||||
getTeamManager().addAvatarToTrialTeam(avatar);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean addTrialAvatarForQuest(int trialAvatarId, int questMainId) {
|
||||
// TODO: Find method for 'setupTrialAvatarTeamForQuest'.
|
||||
getTeamManager().setupTrialAvatars(true);
|
||||
if (!addTrialAvatar(
|
||||
trialAvatarId,
|
||||
GrantReason.GRANT_REASON_BY_QUEST,
|
||||
questMainId)) return false;
|
||||
getTeamManager().trialAvatarTeamPostUpdate();
|
||||
// Packet, mimic official server behaviour, neccessary to stop player from modifying team
|
||||
sendPacket(new PacketAvatarTeamUpdateNotify(this));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addTrialAvatarsForActivity(List<Integer> trialAvatarIds) {
|
||||
getTeamManager().setupTrialAvatars(false);
|
||||
trialAvatarIds.forEach(trialAvatarId -> addTrialAvatar(
|
||||
trialAvatarId,
|
||||
GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY,
|
||||
0));
|
||||
getTeamManager().trialAvatarTeamPostUpdate(0);
|
||||
}
|
||||
|
||||
public boolean removeTrialAvatarForQuest(int trialAvatarId) {
|
||||
if (!getTeamManager().isUsingTrialTeam()) return false;
|
||||
|
||||
sendPacket(new PacketAvatarDelNotify(List.of(getTeamManager().getTrialAvatarGuid(trialAvatarId))));
|
||||
getTeamManager().removeTrialAvatarTeam(trialAvatarId);
|
||||
sendPacket(new PacketAvatarTeamUpdateNotify());
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeTrialAvatarForActivity() {
|
||||
if (!getTeamManager().isUsingTrialTeam()) return;
|
||||
|
||||
sendPacket(new PacketAvatarDelNotify(getTeamManager().getActiveTeam().stream()
|
||||
.map(x -> x.getAvatar().getGuid()).toList()));
|
||||
getTeamManager().removeTrialAvatarTeam();
|
||||
}
|
||||
|
||||
public void addFlycloak(int flycloakId) {
|
||||
this.getFlyCloakList().add(flycloakId);
|
||||
this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId));
|
||||
@@ -829,6 +919,11 @@ public class Player {
|
||||
this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId));
|
||||
}
|
||||
|
||||
public void addPersonalLine(int personalLineId) {
|
||||
this.getPersonalLineList().add(personalLineId);
|
||||
session.getPlayer().getQuestManager().queueEvent(QuestCond.QUEST_COND_PERSONAL_LINE_UNLOCK, personalLineId);
|
||||
}
|
||||
|
||||
public void addNameCard(int nameCardId) {
|
||||
this.getNameCardList().add(nameCardId);
|
||||
this.sendPacket(new PacketUnlockNameCardNotify(nameCardId));
|
||||
@@ -844,6 +939,11 @@ public class Player {
|
||||
this.sendPacket(new PacketSetNameCardRsp(nameCardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to this player.
|
||||
*
|
||||
* @param message The message to send.
|
||||
*/
|
||||
public void dropMessage(Object message) {
|
||||
if (this.messageHandler != null) {
|
||||
this.messageHandler.append(message.toString());
|
||||
@@ -853,6 +953,46 @@ public class Player {
|
||||
this.getServer().getChatSystem().sendPrivateMessageFromServer(getUid(), message.toString());
|
||||
}
|
||||
|
||||
public void setAvatarsAbilityForScene(Scene scene) {
|
||||
try {
|
||||
var levelEntityConfig = scene.getSceneData().getLevelEntityConfig();
|
||||
var config = GameData.getConfigLevelEntityDataMap().get(levelEntityConfig);
|
||||
if (config == null){
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> avatarIds = scene.getSceneData().getSpecifiedAvatarList();
|
||||
List<EntityAvatar> specifiedAvatarList = getTeamManager().getActiveTeam();
|
||||
|
||||
if (avatarIds != null && avatarIds.size() > 0){
|
||||
// certain scene could limit specifc avatars' entry
|
||||
specifiedAvatarList.clear();
|
||||
for (int id : avatarIds){
|
||||
var avatar = getAvatars().getAvatarById(id);
|
||||
if (avatar == null){
|
||||
continue;
|
||||
}
|
||||
specifiedAvatarList.add(new EntityAvatar(scene, avatar));
|
||||
}
|
||||
}
|
||||
|
||||
for (EntityAvatar entityAvatar : specifiedAvatarList){
|
||||
var avatarData = entityAvatar.getAvatar().getAvatarData();
|
||||
if (avatarData == null){
|
||||
continue;
|
||||
}
|
||||
avatarData.buildEmbryo();
|
||||
if (config.getAvatarAbilities() == null){
|
||||
continue; // continue and not break because has to rebuild ability for the next avatar if any
|
||||
}
|
||||
for (var abilities : config.getAvatarAbilities()){
|
||||
avatarData.getAbilities().add(Utils.abilityHash(abilities.getAbilityName()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e){
|
||||
Grasscutter.getLogger().error("Error applying level entity config for scene {}", scene.getSceneData().getId(), e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends a message to another player.
|
||||
*
|
||||
@@ -865,7 +1005,9 @@ public class Player {
|
||||
|
||||
// ---------------------MAIL------------------------
|
||||
|
||||
public List<Mail> getAllMail() { return this.getMailHandler().getMail(); }
|
||||
public List<Mail> getAllMail() {
|
||||
return this.getMailHandler().getMail();
|
||||
}
|
||||
|
||||
public void sendMail(Mail message) {
|
||||
this.getMailHandler().sendMail(message);
|
||||
@@ -875,7 +1017,9 @@ public class Player {
|
||||
return this.getMailHandler().deleteMail(mailId);
|
||||
}
|
||||
|
||||
public Mail getMail(int index) { return this.getMailHandler().getMailById(index); }
|
||||
public Mail getMail(int index) {
|
||||
return this.getMailHandler().getMailById(index);
|
||||
}
|
||||
|
||||
public int getMailId(Mail message) {
|
||||
return this.getMailHandler().getMailIndex(message);
|
||||
@@ -909,13 +1053,13 @@ public class Player {
|
||||
|
||||
public OnlinePlayerInfo getOnlinePlayerInfo() {
|
||||
OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder()
|
||||
.setUid(this.getUid())
|
||||
.setNickname(this.getNickname())
|
||||
.setPlayerLevel(this.getLevel())
|
||||
.setMpSettingType(this.getMpSetting())
|
||||
.setNameCardId(this.getNameCardId())
|
||||
.setSignature(this.getSignature())
|
||||
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()));
|
||||
.setUid(this.getUid())
|
||||
.setNickname(this.getNickname())
|
||||
.setPlayerLevel(this.getLevel())
|
||||
.setMpSettingType(this.getMpSetting())
|
||||
.setNameCardId(this.getNameCardId())
|
||||
.setSignature(this.getSignature())
|
||||
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()));
|
||||
|
||||
if (this.getWorld() != null) {
|
||||
onlineInfo.setCurPlayerNumInWorld(getWorld().getPlayerCount());
|
||||
@@ -941,12 +1085,12 @@ public class Player {
|
||||
if (this.getShowAvatarList() != null) {
|
||||
for (int avatarId : this.getShowAvatarList()) {
|
||||
socialShowAvatarInfoList.add(
|
||||
socialShowAvatarInfoList.size(),
|
||||
SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder()
|
||||
.setAvatarId(avatarId)
|
||||
.setLevel(getAvatars().getAvatarById(avatarId).getLevel())
|
||||
.setCostumeId(getAvatars().getAvatarById(avatarId).getCostume())
|
||||
.build()
|
||||
socialShowAvatarInfoList.size(),
|
||||
SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder()
|
||||
.setAvatarId(avatarId)
|
||||
.setLevel(getAvatars().getAvatarById(avatarId).getLevel())
|
||||
.setCostumeId(getAvatars().getAvatarById(avatarId).getCostume())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -957,32 +1101,31 @@ public class Player {
|
||||
if (showAvatarList != null) {
|
||||
for (int avatarId : showAvatarList) {
|
||||
socialShowAvatarInfoList.add(
|
||||
socialShowAvatarInfoList.size(),
|
||||
SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder()
|
||||
.setAvatarId(avatarId)
|
||||
.setLevel(avatars.getAvatarById(avatarId).getLevel())
|
||||
.setCostumeId(avatars.getAvatarById(avatarId).getCostume())
|
||||
.build()
|
||||
socialShowAvatarInfoList.size(),
|
||||
SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder()
|
||||
.setAvatarId(avatarId)
|
||||
.setLevel(avatars.getAvatarById(avatarId).getLevel())
|
||||
.setCostumeId(avatars.getAvatarById(avatarId).getCostume())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SocialDetail.Builder social = SocialDetail.newBuilder()
|
||||
.setUid(this.getUid())
|
||||
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()))
|
||||
.setNickname(this.getNickname())
|
||||
.setSignature(this.getSignature())
|
||||
.setLevel(this.getLevel())
|
||||
.setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty())
|
||||
.setWorldLevel(this.getWorldLevel())
|
||||
.setNameCardId(this.getNameCardId())
|
||||
.setIsShowAvatar(this.isShowAvatars())
|
||||
.addAllShowAvatarInfoList(socialShowAvatarInfoList)
|
||||
.addAllShowNameCardIdList(this.getShowNameCardInfoList())
|
||||
.setFinishAchievementNum(this.getFinishedAchievementNum())
|
||||
.setFriendEnterHomeOptionValue(this.getHome() == null ? 0 : this.getHome().getEnterHomeOption());
|
||||
return social;
|
||||
return SocialDetail.newBuilder()
|
||||
.setUid(this.getUid())
|
||||
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()))
|
||||
.setNickname(this.getNickname())
|
||||
.setSignature(this.getSignature())
|
||||
.setLevel(this.getLevel())
|
||||
.setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty())
|
||||
.setWorldLevel(this.getWorldLevel())
|
||||
.setNameCardId(this.getNameCardId())
|
||||
.setIsShowAvatar(this.isShowAvatars())
|
||||
.addAllShowAvatarInfoList(socialShowAvatarInfoList)
|
||||
.addAllShowNameCardIdList(this.getShowNameCardInfoList())
|
||||
.setFinishAchievementNum(this.getFinishedAchievementNum())
|
||||
.setFriendEnterHomeOptionValue(this.getHome() == null ? 0 : this.getHome().getEnterHomeOption());
|
||||
}
|
||||
|
||||
public int getFinishedAchievementNum() {
|
||||
@@ -1025,17 +1168,17 @@ public class Player {
|
||||
|
||||
public PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo getWorldPlayerLocationInfo() {
|
||||
return PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo.newBuilder()
|
||||
.setSceneId(this.getSceneId())
|
||||
.setPlayerLoc(this.getPlayerLocationInfo())
|
||||
.build();
|
||||
.setSceneId(this.getSceneId())
|
||||
.setPlayerLoc(this.getPlayerLocationInfo())
|
||||
.build();
|
||||
}
|
||||
|
||||
public PlayerLocationInfo getPlayerLocationInfo() {
|
||||
return PlayerLocationInfo.newBuilder()
|
||||
.setUid(this.getUid())
|
||||
.setPos(this.getPosition().toProto())
|
||||
.setRot(this.getRotation().toProto())
|
||||
.build();
|
||||
.setUid(this.getUid())
|
||||
.setPos(this.getPosition().toProto())
|
||||
.setRot(this.getRotation().toProto())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void loadBattlePassManager() {
|
||||
@@ -1045,7 +1188,7 @@ public class Player {
|
||||
}
|
||||
|
||||
public PlayerCollectionRecords getCollectionRecordStore() {
|
||||
if (this.collectionRecordStore==null) {
|
||||
if (this.collectionRecordStore == null) {
|
||||
this.collectionRecordStore = new PlayerCollectionRecords();
|
||||
}
|
||||
return collectionRecordStore;
|
||||
@@ -1121,6 +1264,8 @@ public class Player {
|
||||
|
||||
// Home resources
|
||||
this.getHome().updateHourlyResources(this);
|
||||
|
||||
this.getQuestManager().onTick();
|
||||
}
|
||||
|
||||
private synchronized void doDailyReset() {
|
||||
@@ -1189,12 +1334,17 @@ public class Player {
|
||||
this.achievements = Achievements.getByPlayer(this);
|
||||
this.getAvatars().loadFromDatabase();
|
||||
this.getInventory().loadFromDatabase();
|
||||
this.loadBattlePassManager(); // Call before avatar postLoad to avoid null pointer
|
||||
this.getAvatars().postLoad(); // Needs to be called after inventory is handled
|
||||
|
||||
this.getFriendsList().loadFromDatabase();
|
||||
this.getMailHandler().loadFromDatabase();
|
||||
this.getQuestManager().loadFromDatabase();
|
||||
|
||||
this.loadBattlePassManager();
|
||||
this.getAvatars().postLoad(); // Needs to be called after inventory is handled
|
||||
}
|
||||
|
||||
public void onPlayerBorn() {
|
||||
getQuestManager().onPlayerBorn();
|
||||
}
|
||||
|
||||
public void onLogin() {
|
||||
@@ -1274,7 +1424,8 @@ public class Player {
|
||||
session.setState(SessionState.ACTIVE);
|
||||
|
||||
// Call join event.
|
||||
PlayerJoinEvent event = new PlayerJoinEvent(this); event.call();
|
||||
PlayerJoinEvent event = new PlayerJoinEvent(this);
|
||||
event.call();
|
||||
if (event.isCanceled()) { // If event is not cancelled, continue.
|
||||
session.close();
|
||||
return;
|
||||
@@ -1313,11 +1464,12 @@ public class Player {
|
||||
this.getFriendsList().save();
|
||||
|
||||
// Call quit event.
|
||||
PlayerQuitEvent event = new PlayerQuitEvent(this); event.call();
|
||||
}catch (Throwable e) {
|
||||
PlayerQuitEvent event = new PlayerQuitEvent(this);
|
||||
event.call();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid());
|
||||
}finally {
|
||||
} finally {
|
||||
removeFromServer();
|
||||
}
|
||||
}
|
||||
@@ -1332,23 +1484,15 @@ public class Player {
|
||||
public int getLegendaryKey() {
|
||||
return this.getProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY);
|
||||
}
|
||||
|
||||
public synchronized void addLegendaryKey(int count) {
|
||||
this.setProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY, getLegendaryKey() + count);
|
||||
}
|
||||
|
||||
public synchronized void useLegendaryKey(int count) {
|
||||
this.setProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY, getLegendaryKey() - count);
|
||||
}
|
||||
|
||||
public enum SceneLoadState {
|
||||
NONE(0), LOADING(1), INIT(2), LOADED(3);
|
||||
|
||||
@Getter private final int value;
|
||||
|
||||
SceneLoadState(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int getPropertyMin(PlayerProperty prop) {
|
||||
if (prop.isDynamicRange()) {
|
||||
return 0;
|
||||
@@ -1391,4 +1535,15 @@ public class Player {
|
||||
}
|
||||
}
|
||||
|
||||
public enum SceneLoadState {
|
||||
NONE(0), LOADING(1), INIT(2), LOADED(3);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
|
||||
SceneLoadState(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,222 +1,222 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.excels.BuffData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.net.proto.ServerBuffChangeNotifyOuterClass.ServerBuffChangeNotify.ServerBuffChangeType;
|
||||
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
|
||||
import emu.grasscutter.server.packet.send.PacketServerBuffChangeNotify;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.Getter;
|
||||
|
||||
public final class PlayerBuffManager extends BasePlayerManager {
|
||||
private final List<PlayerBuff> pendingBuffs;
|
||||
private final Int2ObjectMap<PlayerBuff> buffs; // Server buffs
|
||||
private int nextBuffUid;
|
||||
|
||||
public PlayerBuffManager(Player player) {
|
||||
super(player);
|
||||
|
||||
this.buffs = new Int2ObjectOpenHashMap<>();
|
||||
this.pendingBuffs = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new uid for a server buff
|
||||
*
|
||||
* @return New integer buff uid
|
||||
*/
|
||||
private int getNextBuffUid() {
|
||||
return ++nextBuffUid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the player has a buff with this group id
|
||||
*
|
||||
* @param groupId Buff group id
|
||||
* @return True if a buff with this group id exists
|
||||
*/
|
||||
public synchronized boolean hasBuff(int groupId) {
|
||||
return this.buffs.containsKey(groupId);
|
||||
}
|
||||
|
||||
/** Clears all player buffs */
|
||||
public synchronized void clearBuffs() {
|
||||
// Remove from player
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(),
|
||||
ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF,
|
||||
this.buffs.values()));
|
||||
|
||||
// Clear
|
||||
this.buffs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a server buff to the player.
|
||||
*
|
||||
* @param buffId Server buff id
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public boolean addBuff(int buffId) {
|
||||
return addBuff(buffId, -1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a server buff to the player.
|
||||
*
|
||||
* @param buffId Server buff id
|
||||
* @param duration Duration of the buff in seconds. Set to 0 for an infinite buff.
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration) {
|
||||
return addBuff(buffId, duration, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a server buff to the player.
|
||||
*
|
||||
* @param buffId Server buff id
|
||||
* @param duration Duration of the buff in seconds. Set to 0 for an infinite buff.
|
||||
* @param target Target avatar
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration, Avatar target) {
|
||||
// Get buff excel data
|
||||
var buffData = GameData.getBuffDataMap().get(buffId);
|
||||
if (buffData == null) return false;
|
||||
|
||||
// Perform onAdded actions
|
||||
var success =
|
||||
Optional.ofNullable(GameData.getAbilityData(buffData.getAbilityName()))
|
||||
.map(data -> data.modifiers.get(buffData.getModifierName()))
|
||||
.map(modifier -> modifier.onAdded)
|
||||
.map(
|
||||
onAdded -> {
|
||||
var shouldHeal = false;
|
||||
for (var ability : onAdded) {
|
||||
if (Objects.requireNonNull(ability.type) == AbilityModifierAction.Type.HealHP) {
|
||||
if (target == null) continue;
|
||||
|
||||
var maxHp = target.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
var amount =
|
||||
ability.amount.get() + ability.amountByTargetMaxHPRatio.get() * maxHp;
|
||||
|
||||
target.getAsEntity().heal(amount);
|
||||
shouldHeal = true;
|
||||
}
|
||||
}
|
||||
|
||||
return shouldHeal;
|
||||
})
|
||||
.orElse(false);
|
||||
|
||||
// Set duration
|
||||
if (duration < 0f) {
|
||||
duration = buffData.getTime();
|
||||
}
|
||||
|
||||
// Don't add buff if duration is equal or less than 0
|
||||
if (duration <= 0) {
|
||||
return success;
|
||||
}
|
||||
|
||||
// Clear previous buff if it exists
|
||||
this.removeBuff(buffData.getGroupId());
|
||||
|
||||
// Create and store buff
|
||||
PlayerBuff buff = new PlayerBuff(getNextBuffUid(), buffData, duration);
|
||||
this.buffs.put(buff.getGroupId(), buff);
|
||||
|
||||
// Packet
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_ADD_SERVER_BUFF, buff));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a buff by its group id
|
||||
*
|
||||
* @param buffGroupId Server buff group id
|
||||
* @return True if a buff was remove
|
||||
*/
|
||||
public synchronized boolean removeBuff(int buffGroupId) {
|
||||
PlayerBuff buff = this.buffs.remove(buffGroupId);
|
||||
|
||||
if (buff != null) {
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, buff));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized void onTick() {
|
||||
// Skip if no buffs
|
||||
if (this.buffs.isEmpty()) return;
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
// Add to pending buffs to remove if buff has expired
|
||||
this.buffs
|
||||
.values()
|
||||
.removeIf(
|
||||
buff -> {
|
||||
if (currentTime <= buff.getEndTime()) return false;
|
||||
this.pendingBuffs.add(buff);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (this.pendingBuffs.size() > 0) {
|
||||
// Send packet
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(),
|
||||
ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF,
|
||||
this.pendingBuffs));
|
||||
this.pendingBuffs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class PlayerBuff {
|
||||
private final int uid;
|
||||
private final BuffData buffData;
|
||||
private final long endTime;
|
||||
|
||||
public PlayerBuff(int uid, BuffData buffData, float duration) {
|
||||
this.uid = uid;
|
||||
this.buffData = buffData;
|
||||
this.endTime = System.currentTimeMillis() + ((long) duration * 1000);
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return getBuffData().getGroupId();
|
||||
}
|
||||
|
||||
public ServerBuff toProto() {
|
||||
return ServerBuff.newBuilder()
|
||||
.setServerBuffUid(this.getUid())
|
||||
.setServerBuffId(this.getBuffData().getId())
|
||||
.setServerBuffType(this.getBuffData().getServerBuffType().getValue())
|
||||
.setInstancedModifierId(1)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.excels.BuffData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.net.proto.ServerBuffChangeNotifyOuterClass.ServerBuffChangeNotify.ServerBuffChangeType;
|
||||
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
|
||||
import emu.grasscutter.server.packet.send.PacketServerBuffChangeNotify;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.Getter;
|
||||
|
||||
public final class PlayerBuffManager extends BasePlayerManager {
|
||||
private final List<PlayerBuff> pendingBuffs;
|
||||
private final Int2ObjectMap<PlayerBuff> buffs; // Server buffs
|
||||
private int nextBuffUid;
|
||||
|
||||
public PlayerBuffManager(Player player) {
|
||||
super(player);
|
||||
|
||||
this.buffs = new Int2ObjectOpenHashMap<>();
|
||||
this.pendingBuffs = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new uid for a server buff
|
||||
*
|
||||
* @return New integer buff uid
|
||||
*/
|
||||
private int getNextBuffUid() {
|
||||
return ++nextBuffUid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the player has a buff with this group id
|
||||
*
|
||||
* @param groupId Buff group id
|
||||
* @return True if a buff with this group id exists
|
||||
*/
|
||||
public synchronized boolean hasBuff(int groupId) {
|
||||
return this.buffs.containsKey(groupId);
|
||||
}
|
||||
|
||||
/** Clears all player buffs */
|
||||
public synchronized void clearBuffs() {
|
||||
// Remove from player
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(),
|
||||
ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF,
|
||||
this.buffs.values()));
|
||||
|
||||
// Clear
|
||||
this.buffs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a server buff to the player.
|
||||
*
|
||||
* @param buffId Server buff id
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public boolean addBuff(int buffId) {
|
||||
return addBuff(buffId, -1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a server buff to the player.
|
||||
*
|
||||
* @param buffId Server buff id
|
||||
* @param duration Duration of the buff in seconds. Set to 0 for an infinite buff.
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration) {
|
||||
return addBuff(buffId, duration, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a server buff to the player.
|
||||
*
|
||||
* @param buffId Server buff id
|
||||
* @param duration Duration of the buff in seconds. Set to 0 for an infinite buff.
|
||||
* @param target Target avatar
|
||||
* @return True if a buff was added
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration, Avatar target) {
|
||||
// Get buff excel data
|
||||
var buffData = GameData.getBuffDataMap().get(buffId);
|
||||
if (buffData == null) return false;
|
||||
|
||||
// Perform onAdded actions
|
||||
var success =
|
||||
Optional.ofNullable(GameData.getAbilityData(buffData.getAbilityName()))
|
||||
.map(data -> data.modifiers.get(buffData.getModifierName()))
|
||||
.map(modifier -> modifier.onAdded)
|
||||
.map(
|
||||
onAdded -> {
|
||||
var shouldHeal = false;
|
||||
for (var ability : onAdded) {
|
||||
if (Objects.requireNonNull(ability.type) == AbilityModifierAction.Type.HealHP) {
|
||||
if (target == null) continue;
|
||||
|
||||
var maxHp = target.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
var amount =
|
||||
ability.amount.get() + ability.amountByTargetMaxHPRatio.get() * maxHp;
|
||||
|
||||
target.getAsEntity().heal(amount);
|
||||
shouldHeal = true;
|
||||
}
|
||||
}
|
||||
|
||||
return shouldHeal;
|
||||
})
|
||||
.orElse(false);
|
||||
|
||||
// Set duration
|
||||
if (duration < 0f) {
|
||||
duration = buffData.getTime();
|
||||
}
|
||||
|
||||
// Don't add buff if duration is equal or less than 0
|
||||
if (duration <= 0) {
|
||||
return success;
|
||||
}
|
||||
|
||||
// Clear previous buff if it exists
|
||||
this.removeBuff(buffData.getGroupId());
|
||||
|
||||
// Create and store buff
|
||||
PlayerBuff buff = new PlayerBuff(getNextBuffUid(), buffData, duration);
|
||||
this.buffs.put(buff.getGroupId(), buff);
|
||||
|
||||
// Packet
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_ADD_SERVER_BUFF, buff));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a buff by its group id
|
||||
*
|
||||
* @param buffGroupId Server buff group id
|
||||
* @return True if a buff was remove
|
||||
*/
|
||||
public synchronized boolean removeBuff(int buffGroupId) {
|
||||
PlayerBuff buff = this.buffs.remove(buffGroupId);
|
||||
|
||||
if (buff != null) {
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, buff));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized void onTick() {
|
||||
// Skip if no buffs
|
||||
if (this.buffs.isEmpty()) return;
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
// Add to pending buffs to remove if buff has expired
|
||||
this.buffs
|
||||
.values()
|
||||
.removeIf(
|
||||
buff -> {
|
||||
if (currentTime <= buff.getEndTime()) return false;
|
||||
this.pendingBuffs.add(buff);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (this.pendingBuffs.size() > 0) {
|
||||
// Send packet
|
||||
getPlayer()
|
||||
.sendPacket(
|
||||
new PacketServerBuffChangeNotify(
|
||||
getPlayer(),
|
||||
ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF,
|
||||
this.pendingBuffs));
|
||||
this.pendingBuffs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class PlayerBuff {
|
||||
private final int uid;
|
||||
private final BuffData buffData;
|
||||
private final long endTime;
|
||||
|
||||
public PlayerBuff(int uid, BuffData buffData, float duration) {
|
||||
this.uid = uid;
|
||||
this.buffData = buffData;
|
||||
this.endTime = System.currentTimeMillis() + ((long) duration * 1000);
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return getBuffData().getGroupId();
|
||||
}
|
||||
|
||||
public ServerBuff toProto() {
|
||||
return ServerBuff.newBuilder()
|
||||
.setServerBuffUid(this.getUid())
|
||||
.setServerBuffId(this.getBuffData().getId())
|
||||
.setServerBuffType(this.getBuffData().getServerBuffType().getValue())
|
||||
.setInstancedModifierId(1)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,136 +1,136 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.codex.CodexAnimalData;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
|
||||
import java.util.*;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class PlayerCodex {
|
||||
@Transient private Player player;
|
||||
|
||||
// itemId is not codexId!
|
||||
@Getter private final Set<Integer> unlockedWeapon;
|
||||
@Getter private final Map<Integer, Integer> unlockedAnimal;
|
||||
@Getter private final Set<Integer> unlockedMaterial;
|
||||
@Getter private final Set<Integer> unlockedBook;
|
||||
@Getter private final Set<Integer> unlockedTip;
|
||||
@Getter private final Set<Integer> unlockedView;
|
||||
@Getter private Set<Integer> unlockedReliquary;
|
||||
@Getter private final Set<Integer> unlockedReliquarySuitCodex;
|
||||
|
||||
public PlayerCodex() {
|
||||
this.unlockedWeapon = new HashSet<>();
|
||||
this.unlockedAnimal = new HashMap<>();
|
||||
this.unlockedMaterial = new HashSet<>();
|
||||
this.unlockedBook = new HashSet<>();
|
||||
this.unlockedTip = new HashSet<>();
|
||||
this.unlockedView = new HashSet<>();
|
||||
this.unlockedReliquary = new HashSet<>();
|
||||
this.unlockedReliquarySuitCodex = new HashSet<>();
|
||||
}
|
||||
|
||||
public PlayerCodex(Player player) {
|
||||
this();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.fixReliquaries();
|
||||
}
|
||||
|
||||
public void checkAddedItem(GameItem item) {
|
||||
val itemData = item.getItemData();
|
||||
val itemId = item.getItemId();
|
||||
switch (itemData.getItemType()) {
|
||||
case ITEM_WEAPON -> {
|
||||
Optional.ofNullable(GameData.getCodexWeaponDataIdMap().get(itemId))
|
||||
.ifPresent(
|
||||
codexData -> {
|
||||
if (this.getUnlockedWeapon().add(itemId)) {
|
||||
this.player.save();
|
||||
this.player.sendPacket(new PacketCodexDataUpdateNotify(2, codexData.getId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
case ITEM_MATERIAL -> {
|
||||
switch (itemData.getMaterialType()) {
|
||||
// Is this check even needed?
|
||||
case MATERIAL_FOOD,
|
||||
MATERIAL_WIDGET,
|
||||
MATERIAL_EXCHANGE,
|
||||
MATERIAL_AVATAR_MATERIAL,
|
||||
MATERIAL_NOTICE_ADD_HP -> {
|
||||
Optional.ofNullable(GameData.getCodexMaterialDataIdMap().get(itemId))
|
||||
.ifPresent(
|
||||
codexData -> {
|
||||
if (this.getUnlockedMaterial().add(itemId)) {
|
||||
this.player.save();
|
||||
this.player.sendPacket(
|
||||
new PacketCodexDataUpdateNotify(4, codexData.getId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
case ITEM_RELIQUARY -> {
|
||||
val reliquaryId = (itemId / 10) * 10; // Normalize to 0-substat form
|
||||
if (this.getUnlockedReliquary().add(reliquaryId)) checkUnlockedSuits(reliquaryId);
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkAnimal(GameEntity target, CodexAnimalData.CountType countType) {
|
||||
if (target instanceof EntityMonster) {
|
||||
val monsterId = ((EntityMonster) target).getMonsterData().getId();
|
||||
val codexAnimal = GameData.getCodexAnimalDataMap().get(monsterId);
|
||||
if (codexAnimal == null) return;
|
||||
|
||||
val animalCountType = codexAnimal.getCountType();
|
||||
if (animalCountType != countType && animalCountType != null) return;
|
||||
|
||||
this.getUnlockedAnimal().merge(monsterId, 1, (i, j) -> i + 1);
|
||||
|
||||
player.save();
|
||||
this.player.sendPacket(new PacketCodexDataUpdateNotify(3, monsterId));
|
||||
}
|
||||
}
|
||||
|
||||
public void checkUnlockedSuits(int reliquaryId) {
|
||||
GameData.getCodexReliquaryArrayList().stream()
|
||||
.filter(x -> !this.getUnlockedReliquarySuitCodex().contains(x.getId()))
|
||||
.filter(x -> x.containsId(reliquaryId))
|
||||
.filter(x -> this.getUnlockedReliquary().containsAll(x.getIds()))
|
||||
.forEach(
|
||||
x -> {
|
||||
int id = x.getId();
|
||||
this.getUnlockedReliquarySuitCodex().add(id);
|
||||
this.player.save();
|
||||
this.player.sendPacket(new PacketCodexDataUpdateNotify(8, id));
|
||||
});
|
||||
}
|
||||
|
||||
@Deprecated // Maybe remove this if we ever stop caring about older dbs
|
||||
private void fixReliquaries() {
|
||||
// Migrate older database entries which were using non-canonical forms of itemIds
|
||||
val newReliquaries = new HashSet<Integer>();
|
||||
this.unlockedReliquary.forEach(i -> newReliquaries.add((i / 10) * 10));
|
||||
this.unlockedReliquary = newReliquaries;
|
||||
|
||||
GameData.getCodexReliquaryArrayList().stream()
|
||||
.filter(x -> !this.getUnlockedReliquarySuitCodex().contains(x.getId()))
|
||||
.filter(x -> this.getUnlockedReliquary().containsAll(x.getIds()))
|
||||
.forEach(x -> this.getUnlockedReliquarySuitCodex().add(x.getId()));
|
||||
this.player.save();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.codex.CodexAnimalData;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
|
||||
import java.util.*;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class PlayerCodex {
|
||||
@Transient private Player player;
|
||||
|
||||
// itemId is not codexId!
|
||||
@Getter private final Set<Integer> unlockedWeapon;
|
||||
@Getter private final Map<Integer, Integer> unlockedAnimal;
|
||||
@Getter private final Set<Integer> unlockedMaterial;
|
||||
@Getter private final Set<Integer> unlockedBook;
|
||||
@Getter private final Set<Integer> unlockedTip;
|
||||
@Getter private final Set<Integer> unlockedView;
|
||||
@Getter private Set<Integer> unlockedReliquary;
|
||||
@Getter private final Set<Integer> unlockedReliquarySuitCodex;
|
||||
|
||||
public PlayerCodex() {
|
||||
this.unlockedWeapon = new HashSet<>();
|
||||
this.unlockedAnimal = new HashMap<>();
|
||||
this.unlockedMaterial = new HashSet<>();
|
||||
this.unlockedBook = new HashSet<>();
|
||||
this.unlockedTip = new HashSet<>();
|
||||
this.unlockedView = new HashSet<>();
|
||||
this.unlockedReliquary = new HashSet<>();
|
||||
this.unlockedReliquarySuitCodex = new HashSet<>();
|
||||
}
|
||||
|
||||
public PlayerCodex(Player player) {
|
||||
this();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.fixReliquaries();
|
||||
}
|
||||
|
||||
public void checkAddedItem(GameItem item) {
|
||||
val itemData = item.getItemData();
|
||||
val itemId = item.getItemId();
|
||||
switch (itemData.getItemType()) {
|
||||
case ITEM_WEAPON -> {
|
||||
Optional.ofNullable(GameData.getCodexWeaponDataIdMap().get(itemId))
|
||||
.ifPresent(
|
||||
codexData -> {
|
||||
if (this.getUnlockedWeapon().add(itemId)) {
|
||||
this.player.save();
|
||||
this.player.sendPacket(new PacketCodexDataUpdateNotify(2, codexData.getId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
case ITEM_MATERIAL -> {
|
||||
switch (itemData.getMaterialType()) {
|
||||
// Is this check even needed?
|
||||
case MATERIAL_FOOD,
|
||||
MATERIAL_WIDGET,
|
||||
MATERIAL_EXCHANGE,
|
||||
MATERIAL_AVATAR_MATERIAL,
|
||||
MATERIAL_NOTICE_ADD_HP -> {
|
||||
Optional.ofNullable(GameData.getCodexMaterialDataIdMap().get(itemId))
|
||||
.ifPresent(
|
||||
codexData -> {
|
||||
if (this.getUnlockedMaterial().add(itemId)) {
|
||||
this.player.save();
|
||||
this.player.sendPacket(
|
||||
new PacketCodexDataUpdateNotify(4, codexData.getId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
case ITEM_RELIQUARY -> {
|
||||
val reliquaryId = (itemId / 10) * 10; // Normalize to 0-substat form
|
||||
if (this.getUnlockedReliquary().add(reliquaryId)) checkUnlockedSuits(reliquaryId);
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkAnimal(GameEntity target, CodexAnimalData.CountType countType) {
|
||||
if (target instanceof EntityMonster) {
|
||||
val monsterId = ((EntityMonster) target).getMonsterData().getId();
|
||||
val codexAnimal = GameData.getCodexAnimalDataMap().get(monsterId);
|
||||
if (codexAnimal == null) return;
|
||||
|
||||
val animalCountType = codexAnimal.getCountType();
|
||||
if (animalCountType != countType && animalCountType != null) return;
|
||||
|
||||
this.getUnlockedAnimal().merge(monsterId, 1, (i, j) -> i + 1);
|
||||
|
||||
player.save();
|
||||
this.player.sendPacket(new PacketCodexDataUpdateNotify(3, monsterId));
|
||||
}
|
||||
}
|
||||
|
||||
public void checkUnlockedSuits(int reliquaryId) {
|
||||
GameData.getCodexReliquaryArrayList().stream()
|
||||
.filter(x -> !this.getUnlockedReliquarySuitCodex().contains(x.getId()))
|
||||
.filter(x -> x.containsId(reliquaryId))
|
||||
.filter(x -> this.getUnlockedReliquary().containsAll(x.getIds()))
|
||||
.forEach(
|
||||
x -> {
|
||||
int id = x.getId();
|
||||
this.getUnlockedReliquarySuitCodex().add(id);
|
||||
this.player.save();
|
||||
this.player.sendPacket(new PacketCodexDataUpdateNotify(8, id));
|
||||
});
|
||||
}
|
||||
|
||||
@Deprecated // Maybe remove this if we ever stop caring about older dbs
|
||||
private void fixReliquaries() {
|
||||
// Migrate older database entries which were using non-canonical forms of itemIds
|
||||
val newReliquaries = new HashSet<Integer>();
|
||||
this.unlockedReliquary.forEach(i -> newReliquaries.add((i / 10) * 10));
|
||||
this.unlockedReliquary = newReliquaries;
|
||||
|
||||
GameData.getCodexReliquaryArrayList().stream()
|
||||
.filter(x -> !this.getUnlockedReliquarySuitCodex().contains(x.getId()))
|
||||
.filter(x -> this.getUnlockedReliquary().containsAll(x.getIds()))
|
||||
.forEach(x -> this.getUnlockedReliquarySuitCodex().add(x.getId()));
|
||||
this.player.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
/** Tracks progress the player made in the world, like obtained items, seen characters and more */
|
||||
@Entity
|
||||
public class PlayerProgress {
|
||||
|
||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
||||
|
||||
// keep track of EXEC_ADD_QUEST_PROGRESS count, will be used in CONTENT_ADD_QUEST_PROGRESS
|
||||
// not sure where to put this, this should be saved to DB but not to individual quest, since
|
||||
// it will be hard to loop and compare
|
||||
private Map<Integer, Integer> questProgressCountMap;
|
||||
|
||||
public PlayerProgress() {
|
||||
this.questProgressCountMap = new Int2IntOpenHashMap();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public boolean hasPlayerObtainedItemHistorically(int itemId) {
|
||||
return itemHistory.containsKey(itemId);
|
||||
}
|
||||
|
||||
public int addToItemHistory(int itemId, int count) {
|
||||
val itemEntry = itemHistory.computeIfAbsent(itemId, (key) -> new ItemEntry(itemId));
|
||||
return itemEntry.addToObtainedCount(count);
|
||||
}
|
||||
|
||||
public int getCurrentProgress(int progressId) {
|
||||
return questProgressCountMap.getOrDefault(progressId, -1);
|
||||
}
|
||||
|
||||
public int addToCurrentProgress(int progressId, int count) {
|
||||
return questProgressCountMap.merge(progressId, count, Integer::sum);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public static class ItemEntry {
|
||||
@Getter private int itemId;
|
||||
@Getter @Setter private int obtainedCount;
|
||||
|
||||
ItemEntry(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
int addToObtainedCount(int amount) {
|
||||
this.obtainedCount += amount;
|
||||
return this.obtainedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
/** Tracks progress the player made in the world, like obtained items, seen characters and more */
|
||||
@Entity
|
||||
public class PlayerProgress {
|
||||
|
||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
||||
|
||||
// keep track of EXEC_ADD_QUEST_PROGRESS count, will be used in CONTENT_ADD_QUEST_PROGRESS
|
||||
// not sure where to put this, this should be saved to DB but not to individual quest, since
|
||||
// it will be hard to loop and compare
|
||||
private Map<Integer, Integer> questProgressCountMap;
|
||||
|
||||
public PlayerProgress() {
|
||||
this.questProgressCountMap = new Int2IntOpenHashMap();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public boolean hasPlayerObtainedItemHistorically(int itemId) {
|
||||
return itemHistory.containsKey(itemId);
|
||||
}
|
||||
|
||||
public int addToItemHistory(int itemId, int count) {
|
||||
val itemEntry = itemHistory.computeIfAbsent(itemId, (key) -> new ItemEntry(itemId));
|
||||
return itemEntry.addToObtainedCount(count);
|
||||
}
|
||||
|
||||
public int getCurrentProgress(int progressId) {
|
||||
return questProgressCountMap.getOrDefault(progressId, -1);
|
||||
}
|
||||
|
||||
public int addToCurrentProgress(int progressId, int count) {
|
||||
return questProgressCountMap.merge(progressId, count, Integer::sum);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public static class ItemEntry {
|
||||
@Getter private int itemId;
|
||||
@Getter @Setter private int obtainedCount;
|
||||
|
||||
ItemEntry(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
int addToObtainedCount(int amount) {
|
||||
this.obtainedCount += amount;
|
||||
return this.obtainedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user