mirror of
https://github.com/Melledy/Nebula.git
synced 2026-09-19 17:19:58 +02:00
1380 lines
45 KiB
Java
1380 lines
45 KiB
Java
package emu.nebula.game.player;
|
|
|
|
import dev.morphia.annotations.AlsoLoad;
|
|
import dev.morphia.annotations.Entity;
|
|
import dev.morphia.annotations.Id;
|
|
import dev.morphia.annotations.Indexed;
|
|
import emu.nebula.GameConstants;
|
|
import emu.nebula.Nebula;
|
|
import emu.nebula.data.GameData;
|
|
import emu.nebula.data.resources.MallMonthlyCardDef;
|
|
import emu.nebula.database.GameDatabaseObject;
|
|
import emu.nebula.game.account.Account;
|
|
import emu.nebula.game.achievement.AchievementCondition;
|
|
import emu.nebula.game.achievement.AchievementManager;
|
|
import emu.nebula.game.activity.ActivityManager;
|
|
import emu.nebula.game.agent.AgentManager;
|
|
import emu.nebula.game.battlepass.BattlePassManager;
|
|
import emu.nebula.game.character.CharacterStorage;
|
|
import emu.nebula.game.dating.DatingManager;
|
|
import emu.nebula.game.formation.FormationManager;
|
|
import emu.nebula.game.friends.FriendList;
|
|
import emu.nebula.game.gacha.GachaManager;
|
|
import emu.nebula.game.infinitytower.InfinityTowerManager;
|
|
import emu.nebula.game.instance.InstanceManager;
|
|
import emu.nebula.game.inventory.Inventory;
|
|
import emu.nebula.game.mail.Mailbox;
|
|
import emu.nebula.game.quest.QuestCondition;
|
|
import emu.nebula.game.quest.QuestManager;
|
|
import emu.nebula.game.scoreboss.ScoreBossManager;
|
|
import emu.nebula.game.story.StoryManager;
|
|
import emu.nebula.game.tower.StarTowerManager;
|
|
import emu.nebula.game.tracehunt.TraceHuntManager;
|
|
import emu.nebula.game.vampire.VampireSurvivorManager;
|
|
import emu.nebula.net.GameSession;
|
|
import emu.nebula.net.NetMsgId;
|
|
import emu.nebula.net.NetMsgPacket;
|
|
import emu.nebula.proto.Notify.MonthlyCardRewards;
|
|
import emu.nebula.proto.Notify.SigninRewardUpdate;
|
|
import emu.nebula.proto.PlayerData.DictionaryEntry;
|
|
import emu.nebula.proto.PlayerData.DictionaryTab;
|
|
import emu.nebula.proto.PlayerData.PlayerInfo;
|
|
import emu.nebula.proto.Public;
|
|
import emu.nebula.proto.Public.*;
|
|
import emu.nebula.util.ResetCycle;
|
|
import emu.nebula.util.Utils;
|
|
import emu.nebula.util.ints.String2IntMap;
|
|
import lombok.Getter;
|
|
import us.hebi.quickbuf.ProtoMessage;
|
|
import us.hebi.quickbuf.RepeatedInt;
|
|
|
|
import java.lang.String;
|
|
import java.util.Stack;
|
|
|
|
@Getter
|
|
@Entity(value = "players", useDiscriminator = false)
|
|
public class Player implements GameDatabaseObject {
|
|
@Id private int uid;
|
|
@Indexed private String accountUid;
|
|
|
|
private transient Account account;
|
|
private transient GameSession session;
|
|
|
|
@Indexed
|
|
@AlsoLoad("playerRemoteToken")
|
|
private String remoteToken;
|
|
|
|
// Details
|
|
private String name;
|
|
private String signature;
|
|
private boolean gender;
|
|
private int headIcon;
|
|
private int skinId;
|
|
private int titlePrefix;
|
|
private int titleSuffix;
|
|
private long music;
|
|
private int[] honor;
|
|
private int[] showChars;
|
|
private int[] boards;
|
|
|
|
private int level;
|
|
private int exp;
|
|
private int energy;
|
|
private long energyLastUpdate;
|
|
|
|
private int signInIndex;
|
|
|
|
private long lastEpochDay;
|
|
private long lastLogin;
|
|
private long createTime;
|
|
private String2IntMap monthlyCardExpireDays;
|
|
private String2IntMap monthlyCardLastRewardDays;
|
|
|
|
// Managers
|
|
private final transient CharacterStorage characters;
|
|
private final transient FriendList friendList;
|
|
private final transient BattlePassManager battlePassManager;
|
|
private final transient DatingManager datingManager;
|
|
private final transient StarTowerManager starTowerManager;
|
|
private final transient InstanceManager instanceManager;
|
|
private final transient InfinityTowerManager infinityTowerManager;
|
|
private final transient VampireSurvivorManager vampireSurvivorManager;
|
|
private final transient ScoreBossManager scoreBossManager;
|
|
|
|
// Referenced data
|
|
private transient Inventory inventory;
|
|
private transient FormationManager formations;
|
|
private transient Mailbox mailbox;
|
|
private transient GachaManager gachaManager;
|
|
private transient PlayerProgress progress;
|
|
private transient StoryManager storyManager;
|
|
private transient QuestManager questManager;
|
|
private transient AchievementManager achievementManager;
|
|
private transient AgentManager agentManager;
|
|
private transient TraceHuntManager traceHuntManager;
|
|
private transient ActivityManager activityManager;
|
|
|
|
// Extra
|
|
private transient Stack<NetMsgPacket> nextPackages;
|
|
private transient boolean loaded;
|
|
|
|
@Deprecated // Morphia only
|
|
public Player() {
|
|
// Init player managers
|
|
this.characters = new CharacterStorage(this);
|
|
this.friendList = new FriendList(this);
|
|
this.battlePassManager = new BattlePassManager(this);
|
|
this.datingManager = new DatingManager(this);
|
|
this.starTowerManager = new StarTowerManager(this);
|
|
this.instanceManager = new InstanceManager(this);
|
|
this.infinityTowerManager = new InfinityTowerManager(this);
|
|
this.vampireSurvivorManager = new VampireSurvivorManager(this);
|
|
this.scoreBossManager = new ScoreBossManager(this);
|
|
|
|
// Init next packages stack
|
|
this.nextPackages = new Stack<>();
|
|
}
|
|
|
|
public Player(Account account, String name, boolean gender) {
|
|
this();
|
|
|
|
// Set uid first
|
|
if (account.getReservedPlayerUid() > 0) {
|
|
this.uid = account.getReservedPlayerUid();
|
|
} else {
|
|
this.uid = Nebula.getGameDatabase().getNextObjectId(Player.class);
|
|
}
|
|
|
|
// Set basic info
|
|
this.accountUid = account.getUid();
|
|
this.createTime = Nebula.getCurrentServerTime();
|
|
|
|
this.name = name;
|
|
this.signature = "";
|
|
this.gender = gender;
|
|
this.headIcon = gender ? 101 : 102;
|
|
this.skinId = 10301;
|
|
this.titlePrefix = 1;
|
|
this.titleSuffix = 2;
|
|
this.honor = new int[3];
|
|
this.showChars = new int[3];
|
|
this.boards = new int[] {410301};
|
|
|
|
this.level = 1;
|
|
this.energy = 240;
|
|
this.energyLastUpdate = this.createTime;
|
|
this.monthlyCardExpireDays = new String2IntMap();
|
|
this.monthlyCardLastRewardDays = new String2IntMap();
|
|
|
|
// Setup inventory
|
|
this.inventory = new Inventory(this);
|
|
|
|
// Add starter characters
|
|
this.getCharacters().addCharacter(103);
|
|
this.getCharacters().addCharacter(112);
|
|
this.getCharacters().addCharacter(113);
|
|
|
|
// Add starter discs
|
|
this.getCharacters().addDisc(211001);
|
|
this.getCharacters().addDisc(211005);
|
|
this.getCharacters().addDisc(211007);
|
|
this.getCharacters().addDisc(211008);
|
|
}
|
|
|
|
public Account getAccount() {
|
|
if (this.account == null) {
|
|
this.account = Nebula.getAccountDatabase().getObjectByField(Account.class, "_id", this.getAccountUid());
|
|
}
|
|
|
|
return this.account;
|
|
}
|
|
|
|
public void setSession(GameSession session) {
|
|
// Don't set session if it's the same session
|
|
if (this.session == session) {
|
|
return;
|
|
}
|
|
|
|
// Cache previous session
|
|
var prevSession = this.session;
|
|
|
|
// Set session
|
|
this.session = session;
|
|
|
|
// Clear player reference from the previous session
|
|
if (prevSession != null) {
|
|
prevSession.clearPlayer();
|
|
}
|
|
|
|
// We cleared session, now remove player from cache
|
|
if (this.session == null) {
|
|
Nebula.getGameContext().getPlayerModule().removeFromCache(this);
|
|
}
|
|
}
|
|
|
|
public boolean hasSession() {
|
|
return this.session != null;
|
|
}
|
|
|
|
public void setLevel(int level) {
|
|
int oldLevel = this.level;
|
|
|
|
// Set player world class (level)
|
|
this.level = level;
|
|
|
|
// Save to database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "level", this.level);
|
|
|
|
// Trigger achievement
|
|
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
|
|
|
if (oldLevel != this.level) {
|
|
this.queueBattlePassUnlockNotify(oldLevel);
|
|
}
|
|
}
|
|
|
|
public void setExp(int exp) {
|
|
this.exp = exp;
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "exp", this.exp);
|
|
}
|
|
|
|
public void setRemoteToken(String token) {
|
|
// Skip if tokens are the same
|
|
if (this.getRemoteToken() == null) {
|
|
if (token == null) {
|
|
return;
|
|
}
|
|
} else if (this.getRemoteToken().equals(token)) {
|
|
return;
|
|
}
|
|
|
|
// Set remote token
|
|
this.remoteToken = token;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "remoteToken", this.remoteToken);
|
|
}
|
|
|
|
public boolean getGender() {
|
|
return this.gender;
|
|
}
|
|
|
|
public boolean editName(String newName) {
|
|
// Sanity check
|
|
if (newName == null || newName.isEmpty() || newName.equals(this.getName())) {
|
|
return false;
|
|
}
|
|
|
|
// Limit name length
|
|
if (newName.length() > 20) {
|
|
newName = newName.substring(0, 19);
|
|
}
|
|
|
|
// Set name
|
|
this.name = newName;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "name", this.getName());
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
public void editGender() {
|
|
// Set name
|
|
this.gender = !this.gender;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "gender", this.getGender());
|
|
}
|
|
|
|
public boolean editTitle(int prefix, int suffix) {
|
|
// Check to make sure we own these titles
|
|
if (!getInventory().getTitles().contains(prefix) || !getInventory().getTitles().contains(suffix)) {
|
|
return false;
|
|
}
|
|
|
|
// Skip if we are not changing titles
|
|
if (this.titlePrefix == prefix && this.titleSuffix == suffix) {
|
|
return true;
|
|
}
|
|
|
|
// TODO check if title is prefix or suffix
|
|
|
|
// Set
|
|
this.titlePrefix = prefix;
|
|
this.titleSuffix = suffix;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "titlePrefix", this.getTitlePrefix(), "titleSuffix", this.getTitleSuffix());
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean editHeadIcon(int id) {
|
|
// Skip if we are not changing head icon
|
|
if (this.headIcon == id) {
|
|
return true;
|
|
}
|
|
|
|
// Make sure we own the head icon
|
|
if (!getInventory().hasHeadIcon(id)) {
|
|
return false;
|
|
}
|
|
|
|
// Set
|
|
this.headIcon = id;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "headIcon", this.getHeadIcon());
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
public boolean editSignature(String signature) {
|
|
// Sanity check
|
|
if (signature == null) {
|
|
return false;
|
|
}
|
|
|
|
// Limit signature to 30 max chars
|
|
if (signature.length() > 30) {
|
|
signature = signature.substring(0, 29);
|
|
}
|
|
|
|
// Set signature
|
|
this.signature = signature;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "signature", this.getSignature());
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
public boolean setMusic(long id) {
|
|
// Make sure we own the disc
|
|
if (id != 0 && !this.getCharacters().hasDisc((int) id)) {
|
|
return false;
|
|
}
|
|
|
|
// Set main menu music
|
|
this.music = id;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "music", this.getMusic());
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
public boolean setSkin(int skinId) {
|
|
// Skip if we are setting the same skin
|
|
if (this.skinId == skinId) {
|
|
return true;
|
|
}
|
|
|
|
// Make sure we own this skin
|
|
if (!getInventory().hasSkin(skinId)) {
|
|
return false;
|
|
}
|
|
|
|
// Set skin
|
|
this.skinId = skinId;
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "skinId", this.getSkinId());
|
|
|
|
// Success
|
|
return false;
|
|
}
|
|
|
|
public boolean setShowChars(RepeatedInt charIds) {
|
|
// Sanity check
|
|
if (charIds.length() > this.getShowChars().length) {
|
|
return false;
|
|
}
|
|
|
|
// Verify that we have the correct characters
|
|
for (int id : charIds) {
|
|
if (id != 0 && !getCharacters().hasCharacter(id)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// TODO check duplicates
|
|
|
|
// Clear
|
|
this.showChars[0] = 0;
|
|
this.showChars[1] = 0;
|
|
this.showChars[2] = 0;
|
|
|
|
// Set
|
|
for (int i = 0; i < charIds.length(); i++) {
|
|
this.showChars[i] = charIds.get(i);
|
|
}
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "showChars", this.getShowChars());
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
public boolean setHonor(RepeatedInt honorIds) {
|
|
// Sanity check
|
|
if (honorIds.length() > this.getHonor().length) {
|
|
return false;
|
|
}
|
|
|
|
// Verify that we have the honor titles
|
|
for (int id : honorIds) {
|
|
// Empty honor title
|
|
if (id == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Make sure we own the honor title
|
|
if (!getInventory().hasHonor(id)) {
|
|
return false;
|
|
}
|
|
|
|
// Make sure honor exists and won't crash the client
|
|
var honor = GameData.getHonorDataTable().get(id);
|
|
|
|
if (honor == null || !honor.isValid()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// TODO check duplicates
|
|
|
|
// Clear
|
|
this.honor[0] = 0;
|
|
this.honor[1] = 0;
|
|
this.honor[2] = 0;
|
|
|
|
// Set
|
|
for (int i = 0; i < honorIds.length(); i++) {
|
|
this.honor[i] = honorIds.get(i);
|
|
}
|
|
|
|
// Update in database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "honor", this.getHonor());
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
public boolean setBoard(RepeatedInt ids) {
|
|
// Length check
|
|
if (ids.length() <= 0 || ids.length() > GameConstants.MAX_SHOWCASE_IDS) {
|
|
return false;
|
|
}
|
|
|
|
// Get max length
|
|
this.boards = new int[ids.length()];
|
|
|
|
// Copy ids to our boards array
|
|
for (int i = 0; i < ids.length(); i++) {
|
|
int id = ids.get(i);
|
|
this.boards[i] = id;
|
|
}
|
|
|
|
// Save to database
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "boards", this.getBoards());
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
public void setNewbieInfo(int groupId, int stepId) {
|
|
// TODO
|
|
}
|
|
|
|
public int getMaxExp() {
|
|
var data = GameData.getWorldClassDataTable().get(this.level + 1);
|
|
return data != null ? data.getExp() : 0;
|
|
}
|
|
|
|
public PlayerChangeInfo addExp(int amount, PlayerChangeInfo changes) {
|
|
// Check if changes is null
|
|
if (changes == null) {
|
|
changes = new PlayerChangeInfo();
|
|
}
|
|
|
|
// Sanity
|
|
if (amount <= 0) {
|
|
return changes;
|
|
}
|
|
|
|
// Setup
|
|
int oldLevel = this.getLevel();
|
|
int oldExp = this.getExp();
|
|
int expRequired = this.getMaxExp();
|
|
|
|
// Add exp
|
|
this.exp += amount;
|
|
|
|
// Check for level ups
|
|
while (this.exp >= expRequired && expRequired > 0) {
|
|
// Add level
|
|
this.level += 1;
|
|
this.exp -= expRequired;
|
|
|
|
// Recalculate exp required
|
|
expRequired = this.getMaxExp();
|
|
|
|
// Set level reward
|
|
this.getQuestManager().getLevelRewards().setBit(this.level);
|
|
}
|
|
|
|
// Save to database
|
|
Nebula.getGameDatabase().update(
|
|
this,
|
|
this.getUid(),
|
|
"level",
|
|
this.getLevel(),
|
|
"exp",
|
|
this.getExp()
|
|
);
|
|
|
|
// Save level rewards if we changed it
|
|
if (oldLevel != this.getLevel()) {
|
|
// Update level rewards
|
|
this.getQuestManager().saveLevelRewards();
|
|
|
|
this.addNextPackage(
|
|
NetMsgId.world_class_reward_state_notify,
|
|
WorldClassRewardState.newInstance()
|
|
.setFlag(getQuestManager().getLevelRewards().toBigEndianByteArray())
|
|
);
|
|
|
|
// Trigger achievement
|
|
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
|
|
|
this.queueBattlePassUnlockNotify(oldLevel);
|
|
}
|
|
|
|
// Calculate changes
|
|
var proto = WorldClass.newInstance()
|
|
.setAddClass(this.getLevel() - oldLevel)
|
|
.setExpChange(this.getExp() - oldExp);
|
|
|
|
changes.add(proto);
|
|
|
|
return changes;
|
|
}
|
|
|
|
// Energy
|
|
|
|
public int getEnergy() {
|
|
// Cache time
|
|
long time = Nebula.getCurrentServerTime();
|
|
|
|
// Calculate time diff
|
|
double diff = time - this.energyLastUpdate;
|
|
long bonusEnergy = (int) Math.floor(diff / GameConstants.ENERGY_REGEN_TIME);
|
|
|
|
if (this.energy < GameConstants.MAX_ENERGY) {
|
|
this.energy = Math.min(this.energy + (int) bonusEnergy, GameConstants.MAX_ENERGY);
|
|
this.energyLastUpdate = (bonusEnergy * GameConstants.ENERGY_REGEN_TIME) + this.energyLastUpdate;
|
|
} else {
|
|
this.energyLastUpdate = time;
|
|
}
|
|
|
|
return this.energy;
|
|
}
|
|
|
|
public PlayerChangeInfo addEnergy(int amount, PlayerChangeInfo change) {
|
|
// Sanity check
|
|
if (amount <= 0) {
|
|
return change == null ? new PlayerChangeInfo() : change;
|
|
}
|
|
|
|
// Complete
|
|
return modifyEnergy(amount, change);
|
|
}
|
|
|
|
public PlayerChangeInfo consumeEnergy(int amount, PlayerChangeInfo change) {
|
|
// Sanity check
|
|
if (amount <= 0) {
|
|
return change == null ? new PlayerChangeInfo() : change;
|
|
}
|
|
|
|
// Consume energy
|
|
change = modifyEnergy(-amount, change);
|
|
|
|
// Trigger quest
|
|
this.trigger(QuestCondition.EnergyDeplete, amount);
|
|
|
|
// Trigger trace hunt
|
|
this.getTraceHuntManager().onSpendEnergy(amount, change);
|
|
|
|
// Complete
|
|
return change;
|
|
}
|
|
|
|
private PlayerChangeInfo modifyEnergy(int amount, PlayerChangeInfo change) {
|
|
// Check if changes is null
|
|
if (change == null) {
|
|
change = new PlayerChangeInfo();
|
|
}
|
|
|
|
// Update energy
|
|
this.getEnergy();
|
|
|
|
// Remove energy
|
|
this.energy = Math.max(this.energy + amount, 0);
|
|
|
|
// Save to database
|
|
Nebula.getGameDatabase().update(
|
|
this,
|
|
this.getUid(),
|
|
"energy",
|
|
this.getEnergy(),
|
|
"energyLastUpdate",
|
|
this.getEnergyLastUpdate()
|
|
);
|
|
|
|
// Add to change
|
|
change.add(this.getEnergyProto());
|
|
|
|
// Complete
|
|
return change;
|
|
}
|
|
|
|
// Dailies
|
|
|
|
public void checkResetDailies() {
|
|
long currentResetDay = Utils.getResetEpochDay();
|
|
|
|
// Sanity check to make sure daily reset isn't being triggered wrong
|
|
if (currentResetDay <= this.getLastEpochDay()) {
|
|
// Fix sign-in index
|
|
// TODO remove later
|
|
if (this.getSignInIndex() <= 0) {
|
|
this.getSignInRewards(false);
|
|
}
|
|
|
|
this.refreshMonthlyCardRewards(false);
|
|
|
|
// End
|
|
return;
|
|
}
|
|
|
|
// Check if week has changed (Resets on Monday)
|
|
int curWeek = Utils.getResetPeriodIndex(ResetCycle.WEEKLY, this.getLastResetDayTimeSeconds());
|
|
boolean hasWeekChanged = Utils.getResetPeriodIndex(ResetCycle.WEEKLY, Nebula.getCurrentServerTime()) > curWeek;
|
|
|
|
// Check if month was changed
|
|
int curMonth = Utils.getResetPeriodIndex(ResetCycle.MONTHLY, this.getLastResetDayTimeSeconds());
|
|
boolean hasMonthChanged = Utils.getResetPeriodIndex(ResetCycle.MONTHLY, Nebula.getCurrentServerTime()) > curMonth;
|
|
|
|
// Reset dailies
|
|
this.resetDailies(hasWeekChanged, hasMonthChanged);
|
|
|
|
// Trigger quest/achievement login
|
|
this.trigger(QuestCondition.LoginTotal, 1);
|
|
|
|
// Give sign-in rewards
|
|
this.getSignInRewards(hasMonthChanged);
|
|
this.refreshMonthlyCardRewards(true);
|
|
|
|
// Update last epoch day
|
|
this.lastEpochDay = currentResetDay;
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "lastEpochDay", this.lastEpochDay);
|
|
}
|
|
|
|
private void getSignInRewards(boolean resetMonthly) {
|
|
// Check monthly reset
|
|
if (resetMonthly) {
|
|
this.signInIndex = 0;
|
|
}
|
|
|
|
// Get next sign-in index
|
|
int nextSignIn = this.signInIndex + 1;
|
|
int group = Utils.getDaysOfMonth(Utils.getResetEpochDay());
|
|
|
|
var data = GameData.getSignInDataTable().get((group << 16) + nextSignIn);
|
|
if (data == null) {
|
|
return;
|
|
}
|
|
|
|
// Add rewards
|
|
var change = this.getInventory().addItem(data.getItemId(), data.getItemQty());
|
|
|
|
// Add package
|
|
this.addNextPackage(
|
|
NetMsgId.signin_reward_change_notify,
|
|
SigninRewardUpdate.newInstance()
|
|
.setIndex(nextSignIn)
|
|
.setSwitch(resetMonthly)
|
|
.setChange(change.toProto())
|
|
);
|
|
|
|
// Update sign-in index
|
|
this.signInIndex = nextSignIn;
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "signInIndex", this.signInIndex);
|
|
}
|
|
|
|
public void resetDailies(boolean resetWeekly, boolean resetMonthly) {
|
|
// Reset daily quests
|
|
this.getQuestManager().resetDailyQuests(resetWeekly);
|
|
this.getBattlePassManager().getBattlePass().resetDailyQuests(resetWeekly);
|
|
|
|
// Add daily joint drill tickets
|
|
int tickets = this.getInventory().getResourceCount(GameConstants.JOINT_DRILL_TICKET_ID);
|
|
if (tickets < 3) {
|
|
this.getInventory().addItem(GameConstants.JOINT_DRILL_TICKET_ID, 3 - tickets);
|
|
}
|
|
|
|
// Reset daily trace hunt items
|
|
this.getTraceHuntManager().resetDailyQuota();
|
|
|
|
// Check to reset weeklies
|
|
if (resetWeekly) {
|
|
// Add weekly boss entry item
|
|
int entries = this.getInventory().getResourceCount(GameConstants.WEEKLY_ENTRY_ITEM_ID);
|
|
if (entries < 3) {
|
|
this.getInventory().addItem(GameConstants.WEEKLY_ENTRY_ITEM_ID, 3 - entries);
|
|
}
|
|
|
|
// Reset weekly tower tickets
|
|
this.getProgress().clearWeeklyTowerTicketLog();
|
|
this.getInventory().resetWeeklyMallPackagePurchases();
|
|
}
|
|
|
|
// Check if we need to reset monthly
|
|
if (resetMonthly) {
|
|
// Reset monthly shop purchases
|
|
this.getInventory().resetShopPurchases();
|
|
this.getInventory().resetMonthlyMallPackagePurchases();
|
|
}
|
|
}
|
|
|
|
// Trigger quests + achievements
|
|
|
|
public void trigger(int condition, int progress, int param1, int param2) {
|
|
this.getQuestManager().trigger(condition, progress, param1, param2);
|
|
this.getBattlePassManager().getBattlePass().trigger(condition, progress, param1, param2);
|
|
this.getAchievementManager().trigger(condition, progress, param1, param2);
|
|
}
|
|
|
|
public void trigger(QuestCondition condition, int progress) {
|
|
this.trigger(condition.getValue(), progress, 0, 0);
|
|
}
|
|
|
|
public void trigger(QuestCondition condition, int progress, int param1) {
|
|
this.trigger(condition.getValue(), progress, param1, 0);
|
|
}
|
|
|
|
public void trigger(AchievementCondition condition, int progress) {
|
|
this.trigger(condition.getValue(), progress, 0, 0);
|
|
}
|
|
|
|
public void trigger(AchievementCondition condition, int progress, int param1, int param2) {
|
|
this.trigger(condition.getValue(), progress, param1, param2);
|
|
}
|
|
|
|
// Login
|
|
|
|
private <T extends PlayerManager> T loadManagerFromDatabase(Class<T> cls) {
|
|
var manager = Nebula.getGameDatabase().getObjectByField(cls, "_id", this.getUid());
|
|
|
|
if (manager != null) {
|
|
manager.setPlayer(this);
|
|
manager.onLoad();
|
|
} else {
|
|
try {
|
|
manager = cls.getDeclaredConstructor(Player.class).newInstance(this);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
return manager;
|
|
}
|
|
|
|
/**
|
|
* Called when the player is loaded from the database
|
|
*/
|
|
public void onLoad() {
|
|
// Load from database
|
|
this.getCharacters().loadFromDatabase();
|
|
this.getFriendList().loadFromDatabase();
|
|
this.getStarTowerManager().loadFromDatabase();
|
|
this.getBattlePassManager().loadFromDatabase();
|
|
|
|
// Load inventory before referenced classes
|
|
if (this.inventory == null) {
|
|
this.inventory = this.loadManagerFromDatabase(Inventory.class);
|
|
}
|
|
this.getInventory().migrateFromDatabase();
|
|
|
|
// Load referenced classes from the database
|
|
this.formations = this.loadManagerFromDatabase(FormationManager.class);
|
|
this.mailbox = this.loadManagerFromDatabase(Mailbox.class);
|
|
this.progress = this.loadManagerFromDatabase(PlayerProgress.class);
|
|
this.gachaManager = this.loadManagerFromDatabase(GachaManager.class);
|
|
this.storyManager = this.loadManagerFromDatabase(StoryManager.class);
|
|
this.questManager = this.loadManagerFromDatabase(QuestManager.class);
|
|
this.achievementManager = this.loadManagerFromDatabase(AchievementManager.class);
|
|
this.agentManager = this.loadManagerFromDatabase(AgentManager.class);
|
|
this.traceHuntManager = this.loadManagerFromDatabase(TraceHuntManager.class);
|
|
this.activityManager = this.loadManagerFromDatabase(ActivityManager.class);
|
|
|
|
// Database fixes
|
|
if (this.showChars == null) {
|
|
this.showChars = new int[3];
|
|
this.save();
|
|
}
|
|
|
|
if (this.monthlyCardExpireDays == null) {
|
|
this.monthlyCardExpireDays = new String2IntMap();
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "monthlyCardExpireDays", this.monthlyCardExpireDays);
|
|
}
|
|
|
|
if (this.monthlyCardLastRewardDays == null) {
|
|
this.monthlyCardLastRewardDays = new String2IntMap();
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "monthlyCardLastRewardDays", this.monthlyCardLastRewardDays);
|
|
}
|
|
|
|
// Init activities
|
|
this.getActivityManager().init();
|
|
|
|
// Load complete
|
|
this.loaded = true;
|
|
}
|
|
|
|
public void onCreate() {
|
|
// Send welcome mail
|
|
this.getMailbox().sendWelcomeMail();
|
|
}
|
|
|
|
public void onLogin() {
|
|
// See if we need to reset dailies
|
|
this.checkResetDailies();
|
|
|
|
// Fix any broken honor ids
|
|
this.checkBrokenHonor();
|
|
|
|
// Update last login time
|
|
this.lastLogin = System.currentTimeMillis();
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "lastLogin", this.getLastLogin());
|
|
}
|
|
|
|
/**
|
|
* Returns whether the player currently has at least one purchasable
|
|
* free mall package (`CurrencyType = 3`) for the mall entrance red dot.
|
|
*/
|
|
public boolean hasAvailableFreeMallPackage() {
|
|
for (var data : GameData.getMallPackageDataTable()) {
|
|
if (!data.isFreePackage()) {
|
|
continue;
|
|
}
|
|
|
|
if (data.canPurchase(this)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Queues the mall entrance red dot state for free mall packages (`CurrencyType = 3`).
|
|
* Specific package entries are still refreshed from the package list itself.
|
|
*/
|
|
public void queueMallPackageStateNotify() {
|
|
this.addNextPackage(
|
|
NetMsgId.mall_package_state_notify,
|
|
MallPackageState.newInstance().setNew(this.hasAvailableFreeMallPackage())
|
|
);
|
|
}
|
|
|
|
public void queueBattlePassStateNotify() {
|
|
this.addNextPackage(
|
|
NetMsgId.battle_pass_state_notify,
|
|
this.buildBattlePassStateProto()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Pushes a full battle-pass snapshot for clients that only refresh their
|
|
* local battle-pass cache/red dots from the page-info payload.
|
|
*/
|
|
public void queueBattlePassInfoNotify() {
|
|
this.addNextPackage(
|
|
NetMsgId.battle_pass_info_succeed_ack,
|
|
this.getBattlePassManager().getBattlePass().toProto()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Battle-pass unlock is a special case: clients need the full page snapshot
|
|
* to initialize local battle-pass cache and refresh the main-entry red dot.
|
|
*/
|
|
private void queueBattlePassUnlockNotify(int oldLevel) {
|
|
if (oldLevel < GameConstants.BATTLE_PASS_UNLOCK_LEVEL && this.isBattlePassUnlocked()) {
|
|
this.queueBattlePassInfoNotify();
|
|
}
|
|
}
|
|
|
|
private Public.BattlePassState buildBattlePassStateProto() {
|
|
int state = this.isBattlePassUnlocked()
|
|
? this.getBattlePassManager().getBattlePass().getClientState()
|
|
: 0;
|
|
|
|
return Public.BattlePassState.newInstance().setState(state);
|
|
}
|
|
|
|
/**
|
|
* Returns the number of future game days still covered after the current game day.
|
|
* On the last claimable day this value is 0 even though today's reward can still be claimed.
|
|
*/
|
|
public int getMonthlyCardRemainingDays(String cardId) {
|
|
if (this.monthlyCardExpireDays == null) {
|
|
return 0;
|
|
}
|
|
|
|
int endDay = this.monthlyCardExpireDays.get(cardId);
|
|
long currentDay = Utils.getResetEpochDay();
|
|
|
|
if (endDay < currentDay) {
|
|
return 0;
|
|
}
|
|
|
|
return (int) (endDay - currentDay);
|
|
}
|
|
|
|
public boolean receivedMonthlyCardRewardToday(String cardId) {
|
|
return cardId != null
|
|
&& this.monthlyCardLastRewardDays != null
|
|
&& this.monthlyCardLastRewardDays.get(cardId) >= Utils.getResetEpochDay();
|
|
}
|
|
|
|
public long getMonthlyCardEndTime(String cardId) {
|
|
if (cardId == null || this.monthlyCardExpireDays == null) {
|
|
return 0;
|
|
}
|
|
|
|
int endDay = this.monthlyCardExpireDays.get(cardId);
|
|
if (endDay <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
return Utils.getResetTimeSecondsByEpochDay((long) endDay + 1);
|
|
}
|
|
|
|
private void setMonthlyCardRewardEndDay(String cardId, int endDay) {
|
|
if (this.monthlyCardExpireDays == null) {
|
|
this.monthlyCardExpireDays = new String2IntMap();
|
|
}
|
|
|
|
this.monthlyCardExpireDays.put(cardId, endDay);
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "monthlyCardExpireDays", this.monthlyCardExpireDays);
|
|
}
|
|
|
|
private void setMonthlyCardLastRewardDay(String cardId, int rewardDay) {
|
|
if (this.monthlyCardLastRewardDays == null) {
|
|
this.monthlyCardLastRewardDays = new String2IntMap();
|
|
}
|
|
|
|
this.monthlyCardLastRewardDays.put(cardId, rewardDay);
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "monthlyCardLastRewardDays", this.monthlyCardLastRewardDays);
|
|
}
|
|
|
|
public void activateMonthlyCard(String cardId, int durationDays) {
|
|
if (cardId == null || durationDays <= 0) {
|
|
return;
|
|
}
|
|
|
|
int currentDay = Math.toIntExact(Utils.getResetEpochDay());
|
|
int remainingDays = this.getMonthlyCardRemainingDays(cardId);
|
|
boolean claimedToday = this.receivedMonthlyCardRewardToday(cardId);
|
|
|
|
// remainingDays:
|
|
// - > 0: directly add durationDays
|
|
// - == 0 and claimed today: add full durationDays
|
|
// - == 0 and not claimed today: purchase auto-claims today's reward, so keep durationDays - 1 future days
|
|
int newRemainingDays = remainingDays > 0 || claimedToday
|
|
? remainingDays + durationDays
|
|
: Math.max(durationDays - 1, 0);
|
|
|
|
this.setMonthlyCardRewardEndDay(cardId, currentDay + newRemainingDays);
|
|
}
|
|
|
|
public PlayerChangeInfo createMonthlyCardRewardChange(String cardId) {
|
|
var cardData = this.getMonthlyCardData(cardId);
|
|
if (cardData == null || !this.canClaimMonthlyCardReward(cardId)) {
|
|
return null;
|
|
}
|
|
|
|
var rewards = cardData.getDailyRewards();
|
|
if (rewards.isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
var change = this.getInventory().addItems(rewards);
|
|
this.setMonthlyCardLastRewardDay(cardId, Math.toIntExact(Utils.getResetEpochDay()));
|
|
return change;
|
|
}
|
|
|
|
public void grantMonthlyCardReward(String cardId, boolean notifyOnly) {
|
|
var cardData = this.getMonthlyCardData(cardId);
|
|
if (cardData == null) {
|
|
return;
|
|
}
|
|
|
|
var change = this.createMonthlyCardRewardChange(cardId);
|
|
if (change == null) {
|
|
return;
|
|
}
|
|
|
|
var rewards = cardData.getDailyRewards();
|
|
|
|
var notify = MonthlyCardRewards.newInstance()
|
|
.setId(cardData.getMonthlyCardId())
|
|
.setRemaining(this.getMonthlyCardRemainingDays(cardId))
|
|
.setSwitch(!notifyOnly)
|
|
.setEndTime(this.getMonthlyCardEndTime(cardId));
|
|
|
|
var changeProto = change.toProto();
|
|
notify.setChange(changeProto);
|
|
rewards.toItemTemplateStream().forEach(notify::addRewards);
|
|
|
|
this.addNextPackage(NetMsgId.monthly_card_rewards_notify, notify);
|
|
|
|
if (!changeProto.isEmpty()) {
|
|
this.addNextPackage(NetMsgId.items_change_notify, changeProto);
|
|
}
|
|
}
|
|
|
|
private MallMonthlyCardDef getMonthlyCardData(String cardId) {
|
|
return cardId == null ? null : GameData.getMallMonthlyCardDataTable().get(cardId.hashCode());
|
|
}
|
|
|
|
private boolean canClaimMonthlyCardReward(String cardId) {
|
|
if (this.monthlyCardExpireDays == null) {
|
|
return false;
|
|
}
|
|
|
|
int today = Math.toIntExact(Utils.getResetEpochDay());
|
|
int endDay = this.monthlyCardExpireDays.get(cardId);
|
|
return endDay > 0 && today <= endDay && !this.receivedMonthlyCardRewardToday(cardId);
|
|
}
|
|
|
|
private void refreshMonthlyCardRewards(boolean notifyOnly) {
|
|
if (this.monthlyCardExpireDays == null || this.monthlyCardExpireDays.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
for (var entry : this.monthlyCardExpireDays.object2IntEntrySet()) {
|
|
this.grantMonthlyCardReward(entry.getKey(), notifyOnly);
|
|
}
|
|
}
|
|
|
|
private long getLastResetDayTimeSeconds() {
|
|
return Utils.getResetTimeSecondsByEpochDay(this.getLastEpochDay());
|
|
}
|
|
|
|
// Next packages
|
|
|
|
public boolean hasNextPackages() {
|
|
return this.getNextPackages().size() > 0;
|
|
}
|
|
|
|
public void addNextPackage(int msgId, ProtoMessage<?> proto) {
|
|
this.getNextPackages().add(new NetMsgPacket(msgId, proto));
|
|
}
|
|
|
|
// Misc
|
|
|
|
/**
|
|
* Called AFTER a response is sent to the client
|
|
*/
|
|
public void afterResponse() {
|
|
// Check if we need save achievements
|
|
if (this.getAchievementManager().isQueueSave()) {
|
|
this.getAchievementManager().save();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks the player's honor ids to make sure they don't crash the client
|
|
*/
|
|
private void checkBrokenHonor() {
|
|
boolean changed = false;
|
|
|
|
for (int i = 0; i < this.honor.length; i++) {
|
|
int honorId = this.honor[i];
|
|
|
|
if (honorId == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Get honor data
|
|
var honor = GameData.getHonorDataTable().get(honorId);
|
|
|
|
// Check if honor is valid
|
|
if (honor == null || !honor.isValid()) {
|
|
this.honor[i] = 0;
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
// Update in database
|
|
if (changed) {
|
|
Nebula.getGameDatabase().update(this, this.getUid(), "honor", this.getHonor());
|
|
}
|
|
}
|
|
|
|
// Proto
|
|
|
|
public PlayerInfo toProto() {
|
|
PlayerInfo proto = PlayerInfo.newInstance()
|
|
.setServerTs(Nebula.getCurrentServerTime())
|
|
.setSigninIndex(this.getSignInIndex())
|
|
.setTowerTicket(this.getProgress().getTowerTickets())
|
|
.setDailyShopRewardStatus(this.getQuestManager().hasDailyShopReward())
|
|
.setDailyMallRewardStatus(this.getQuestManager().hasDailyMallReward())
|
|
.setMusicInfo(this.getMusic())
|
|
.setAchievements(new byte[64]);
|
|
|
|
var acc = proto.getMutableAcc()
|
|
.setNickName(this.getName())
|
|
.setSignature(this.getSignature())
|
|
.setGender(this.getGender())
|
|
.setId(this.getUid())
|
|
.setHeadIcon(this.getHeadIcon())
|
|
.setSkinId(this.getSkinId())
|
|
.setTitlePrefix(this.getTitlePrefix())
|
|
.setTitleSuffix(this.getTitleSuffix())
|
|
.setCreateTime(this.getCreateTime());
|
|
|
|
// Set showcase character
|
|
for (int charId : this.getShowChars()) {
|
|
var info = CharShow.newInstance();
|
|
var character = this.getCharacters().getCharacterById(charId);
|
|
|
|
if (character != null) {
|
|
info.setCharId(character.getCharId())
|
|
.setLevel(character.getLevel())
|
|
.setSkin(character.getSkin());
|
|
}
|
|
|
|
acc.addChars(info);
|
|
}
|
|
|
|
// Set honor
|
|
for (int honorId : this.getHonor()) {
|
|
var info = HonorInfo.newInstance();
|
|
|
|
if (honorId != 0) {
|
|
info.setId(honorId);
|
|
}
|
|
|
|
proto.addHonors(info);
|
|
}
|
|
|
|
this.getInventory().getAllHonorTitles().forEach(proto::addHonorList);
|
|
|
|
// Set world class
|
|
proto.getMutableWorldClass()
|
|
.setCur(this.getLevel())
|
|
.setLastExp(this.getExp());
|
|
|
|
proto.getMutableEnergy().setEnergy(this.getEnergyProto());
|
|
|
|
// Add characters/discs/res/items
|
|
for (var character : getCharacters().getCharacterCollection()) {
|
|
proto.addChars(character.toProto());
|
|
}
|
|
|
|
for (var disc : getCharacters().getDiscCollection()) {
|
|
proto.addDiscs(disc.toProto());
|
|
}
|
|
|
|
for (var item : getInventory().getItems().int2IntEntrySet()) {
|
|
var info = Item.newInstance()
|
|
.setTid(item.getIntKey())
|
|
.setQty(item.getIntValue());
|
|
|
|
proto.addItems(info);
|
|
}
|
|
|
|
for (var res : getInventory().getResources().int2IntEntrySet()) {
|
|
var info = Res.newInstance()
|
|
.setTid(res.getIntKey())
|
|
.setQty(res.getIntValue());
|
|
|
|
proto.addRes(info);
|
|
}
|
|
|
|
// Formations
|
|
var formations = proto.getMutableFormation();
|
|
for (var f : this.getFormations().getFormations().values()) {
|
|
formations.addInfo(f.toProto());
|
|
}
|
|
|
|
// Set player states
|
|
var state = proto.getMutableState()
|
|
.setStorySet(this.getStoryManager().hasNew())
|
|
.setFriend(this.getFriendList().hasPendingRequests());
|
|
|
|
state.getMutableMail()
|
|
.setNew(this.getMailbox().hasNewMail());
|
|
|
|
// Keep BattlePass state container present for client-side login cache flow.
|
|
// Some clients assume State.BattlePass exists during player_data bootstrap.
|
|
state.setBattlePass(this.buildBattlePassStateProto());
|
|
|
|
state.getMutableAchievement()
|
|
.setNew(this.getAchievementManager().hasNewAchievements());
|
|
|
|
state.getMutableFriendEnergy()
|
|
.setState(this.getFriendList().hasEnergy());
|
|
state.getMutableMallPackage();
|
|
state.getMutableTravelerDuelQuest()
|
|
.setType(QuestType.TravelerDuel);
|
|
state.getMutableStarTower();
|
|
state.getMutableStarTowerBook();
|
|
state.getMutableScoreBoss();
|
|
state.getMutableCharAffinityRewards();
|
|
state.getMutableTraceHunt().setBossRewardCanReceive(this.getTraceHuntManager().isHuntComplete());
|
|
|
|
// Force complete tutorials
|
|
for (var guide : GameData.getGuideGroupDataTable()) {
|
|
var info = NewbieInfo.newInstance()
|
|
.setGroupId(guide.getId())
|
|
.setStepId(-1);
|
|
|
|
acc.addNewbies(info);
|
|
}
|
|
|
|
acc.addNewbies(NewbieInfo.newInstance().setGroupId(GameConstants.INTRO_GUIDE_ID).setStepId(-1));
|
|
|
|
// Story
|
|
this.getStoryManager().encodePlayerInfo(proto);
|
|
|
|
// Add titles
|
|
for (int titleId : this.getInventory().getTitles()) {
|
|
var titleProto = Title.newInstance()
|
|
.setTitleId(titleId);
|
|
|
|
proto.addTitles(titleProto);
|
|
}
|
|
|
|
// Add board ids
|
|
for (int boardId : this.getBoards()) {
|
|
proto.addBoard(boardId);
|
|
}
|
|
|
|
// Quests
|
|
this.getQuestManager().encodePlayerInfo(proto);
|
|
|
|
// Add dictionary tabs
|
|
for (var dictionaryData : GameData.getDictionaryTabDataTable()) {
|
|
var dictionaryProto = DictionaryTab.newInstance()
|
|
.setTabId(dictionaryData.getId());
|
|
|
|
for (var entry : dictionaryData.getEntries()) {
|
|
var entryProto = DictionaryEntry.newInstance()
|
|
.setIndex(entry.getIndex())
|
|
.setStatus(2); // 2 = complete
|
|
|
|
dictionaryProto.addEntries(entryProto);
|
|
}
|
|
|
|
proto.addDictionaries(dictionaryProto);
|
|
}
|
|
|
|
// Add progress
|
|
this.getProgress().encodePlayerInfo(proto);
|
|
|
|
// Handbook
|
|
proto.addHandbook(this.getCharacters().getCharacterHandbook());
|
|
proto.addHandbook(this.getCharacters().getDiscHandbook());
|
|
proto.addHandbook(this.getStoryManager().getCgHandbook());
|
|
|
|
// Phone
|
|
var phone = proto.getMutablePhone();
|
|
phone.setNewMessage(this.getCharacters().getNewPhoneMessageCount());
|
|
|
|
// Agent
|
|
var agentProto = proto.getMutableAgent();
|
|
|
|
for (var agent : getAgentManager().getAgents().values()) {
|
|
agentProto.addInfos(agent.toProto());
|
|
}
|
|
|
|
// Activities
|
|
for (var activity : getActivityManager().getActivities().values()) {
|
|
proto.addActivities(activity.toProto());
|
|
}
|
|
|
|
// Trace hunt
|
|
proto.getMutableHuntPermit()
|
|
.setTid(GameConstants.TRACE_HUNT_PERMIT_ITEM_ID)
|
|
.setDailyCount(this.getTraceHuntManager().getDailyPermits())
|
|
.setGrantedCount(this.getTraceHuntManager().getHuntPermits());
|
|
|
|
proto.getMutableTraceRequest()
|
|
.setTid(GameConstants.TRACE_HUNT_REQUEST_ITEM_ID)
|
|
.setDailyCount(this.getTraceHuntManager().getDailyRequests())
|
|
.setGrantedCount(this.getTraceHuntManager().getTraceRequests());
|
|
|
|
// Complete
|
|
return proto;
|
|
}
|
|
|
|
public Friend getFriendProto() {
|
|
var proto = Friend.newInstance()
|
|
.setId(this.getUid())
|
|
.setWorldClass(this.getLevel())
|
|
.setHeadIcon(this.getHeadIcon())
|
|
.setNickName(this.getName())
|
|
.setSignature(this.getSignature())
|
|
.setTitlePrefix(this.getTitlePrefix())
|
|
.setTitleSuffix(this.getTitleSuffix())
|
|
.setLastLoginTime(this.getLastLogin() * 1_000_000L);
|
|
|
|
for (int charId : this.getShowChars()) {
|
|
var info = CharShow.newInstance()
|
|
.setCharId(charId)
|
|
.setLevel(1) // TODO
|
|
.setSkin((charId * 100) + 1); // TODO
|
|
|
|
proto.addCharShows(info);
|
|
}
|
|
|
|
for (int honorId : this.getHonor()) {
|
|
var info = HonorInfo.newInstance()
|
|
.setId(honorId);
|
|
|
|
proto.addHonors(info);
|
|
}
|
|
|
|
return proto;
|
|
}
|
|
|
|
public Energy getEnergyProto() {
|
|
long nextDuration = Math.max(GameConstants.ENERGY_REGEN_TIME - (Nebula.getCurrentServerTime() - getEnergyLastUpdate()), 1);
|
|
|
|
var proto = Energy.newInstance()
|
|
.setUpdateTime(this.getEnergyLastUpdate())
|
|
.setNextDuration(nextDuration)
|
|
.setPrimary(this.getEnergy())
|
|
.setIsPrimary(true);
|
|
|
|
return proto;
|
|
}
|
|
|
|
public boolean isBattlePassUnlocked() {
|
|
return this.getLevel() >= GameConstants.BATTLE_PASS_UNLOCK_LEVEL;
|
|
}
|
|
|
|
}
|