mirror of
https://github.com/Melledy/Nebula.git
synced 2026-11-04 21:36:51 +01:00
Refactor battle pass and some gacha stuff
This commit is contained in:
1 parent
c9e62500ad
commit
a928bb40f0
10 files changed
+160
-133
No files matched your search
@@ -51,6 +51,8 @@ public class GameConstants {
|
|||||||
public static final int MAX_PRESETS = 50;
|
public static final int MAX_PRESETS = 50;
|
||||||
public static final int MAX_VITA_PROJECTS = 5;
|
public static final int MAX_VITA_PROJECTS = 5;
|
||||||
|
|
||||||
|
public static final int MAX_GACHA_HISTORY_SAVE_RETRIES = 2;
|
||||||
|
|
||||||
public static final int MAX_FRIENDSHIPS = 50;
|
public static final int MAX_FRIENDSHIPS = 50;
|
||||||
public static final int MAX_PENDING_FRIENDSHIPS = 30;
|
public static final int MAX_PENDING_FRIENDSHIPS = 30;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package emu.nebula.game.battlepass;
|
|||||||
|
|
||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.Entity;
|
||||||
import dev.morphia.annotations.Id;
|
import dev.morphia.annotations.Id;
|
||||||
|
|
||||||
import emu.nebula.GameConstants;
|
import emu.nebula.GameConstants;
|
||||||
import emu.nebula.Nebula;
|
import emu.nebula.Nebula;
|
||||||
import emu.nebula.data.GameData;
|
import emu.nebula.data.GameData;
|
||||||
@@ -16,6 +17,7 @@ import emu.nebula.game.quest.QuestType;
|
|||||||
import emu.nebula.net.NetMsgId;
|
import emu.nebula.net.NetMsgId;
|
||||||
import emu.nebula.proto.BattlePassInfoOuterClass.BattlePassInfo;
|
import emu.nebula.proto.BattlePassInfoOuterClass.BattlePassInfo;
|
||||||
import emu.nebula.util.Bitset;
|
import emu.nebula.util.Bitset;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@@ -134,7 +136,7 @@ public class BattlePass implements GameDatabaseObject {
|
|||||||
* Check claimable tasks to show battle pass red dot.
|
* Check claimable tasks to show battle pass red dot.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean hasClaimableQuest() {
|
public synchronized boolean hasClaimableQuest() {
|
||||||
if (!this.getPlayer().isBattlePassUnlocked()) {
|
if (!this.getManager().isUnlocked()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +159,7 @@ public class BattlePass implements GameDatabaseObject {
|
|||||||
* reward lane item for the player's current level/mode.
|
* reward lane item for the player's current level/mode.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean hasClaimableReward() {
|
public synchronized boolean hasClaimableReward() {
|
||||||
if (!this.getPlayer().isBattlePassUnlocked()) {
|
if (!this.getManager().isUnlocked()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +240,7 @@ public class BattlePass implements GameDatabaseObject {
|
|||||||
* Update this quest on the player client
|
* Update this quest on the player client
|
||||||
*/
|
*/
|
||||||
private void syncQuest(GameQuest quest) {
|
private void syncQuest(GameQuest quest) {
|
||||||
if (!getPlayer().hasSession() || !getPlayer().isBattlePassUnlocked()) {
|
if (!getPlayer().hasSession() || !getManager().isUnlocked()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,13 +401,72 @@ public class BattlePass implements GameDatabaseObject {
|
|||||||
// Add rewards
|
// Add rewards
|
||||||
return getPlayer().getInventory().addItems(rewards);
|
return getPlayer().getInventory().addItems(rewards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlayerChangeInfo buyLevels(int levels) {
|
||||||
|
// Sanity check
|
||||||
|
if (levels < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
int costItemId = 2; // Gems
|
||||||
|
int currentLevel = this.getLevel();
|
||||||
|
int totalGemCost = 0;
|
||||||
|
int totalExp = 0;
|
||||||
|
int affordableLevels = 0;
|
||||||
|
|
||||||
|
var inventory = getPlayer().getInventory();
|
||||||
|
|
||||||
|
// Calculate total cost and exp for the levels we can afford
|
||||||
|
for (int i = 1; i <= levels; i++) {
|
||||||
|
// Get level data
|
||||||
|
var data = GameData.getBattlePassLevelDataTable().get(currentLevel + i);
|
||||||
|
|
||||||
|
// Stop if max level reached
|
||||||
|
if (data == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop if not enough gems for this level
|
||||||
|
int cost = data.getQty();
|
||||||
|
if (inventory.getResourceCount(costItemId) < totalGemCost + cost) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalGemCost += cost;
|
||||||
|
totalExp += data.getExp();
|
||||||
|
affordableLevels++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we can buy at least one level
|
||||||
|
if (affordableLevels == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare change info
|
||||||
|
var change = new PlayerChangeInfo();
|
||||||
|
|
||||||
|
// Deduct gems
|
||||||
|
if (totalGemCost > 0) {
|
||||||
|
change.add(inventory.addItem(costItemId, -totalGemCost));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all exp at once
|
||||||
|
this.addExp(totalExp);
|
||||||
|
|
||||||
|
// Save to database
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
// Proto
|
// Proto
|
||||||
|
|
||||||
public BattlePassInfo toProto() {
|
public BattlePassInfo toProto() {
|
||||||
// Return a locked/empty snapshot until the battle pass feature is unlocked,
|
// Return a locked/empty snapshot until the battle pass feature is unlocked,
|
||||||
// so the client cannot derive local quest or reward red dots from battle pass data.
|
// so the client cannot derive local quest or reward red dots from battle pass data.
|
||||||
if (!this.getPlayer().isBattlePassUnlocked()) {
|
if (!this.getManager().isUnlocked()) {
|
||||||
return BattlePassInfo.newInstance()
|
return BattlePassInfo.newInstance()
|
||||||
.setId(0)
|
.setId(0)
|
||||||
.setLevel(0)
|
.setLevel(0)
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package emu.nebula.game.battlepass;
|
package emu.nebula.game.battlepass;
|
||||||
|
|
||||||
|
import emu.nebula.GameConstants;
|
||||||
import emu.nebula.Nebula;
|
import emu.nebula.Nebula;
|
||||||
import emu.nebula.game.player.Player;
|
import emu.nebula.game.player.Player;
|
||||||
import emu.nebula.game.player.PlayerManager;
|
import emu.nebula.game.player.PlayerManager;
|
||||||
|
import emu.nebula.net.NetMsgId;
|
||||||
|
import emu.nebula.proto.Public.BattlePassState;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -13,6 +17,51 @@ public class BattlePassManager extends PlayerManager {
|
|||||||
super(player);
|
super(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUnlocked() {
|
||||||
|
return this.getPlayer().getLevel() >= GameConstants.BATTLE_PASS_UNLOCK_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the battle pass state on the client
|
||||||
|
*/
|
||||||
|
public void updateState() {
|
||||||
|
this.getPlayer().addNextPackage(
|
||||||
|
NetMsgId.battle_pass_state_notify,
|
||||||
|
this.getStateProto()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.getPlayer().addNextPackage(
|
||||||
|
NetMsgId.battle_pass_info_succeed_ack,
|
||||||
|
this.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.
|
||||||
|
*/
|
||||||
|
public void queueUnlockNotify(int oldLevel) {
|
||||||
|
if (oldLevel < GameConstants.BATTLE_PASS_UNLOCK_LEVEL && this.isUnlocked()) {
|
||||||
|
this.queueBattlePassInfoNotify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proto
|
||||||
|
|
||||||
|
public BattlePassState getStateProto() {
|
||||||
|
int state = this.isUnlocked()
|
||||||
|
? this.getBattlePass().getClientState()
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return BattlePassState.newInstance().setState(state);
|
||||||
|
}
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
|
|
||||||
public void loadFromDatabase() {
|
public void loadFromDatabase() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package emu.nebula.game.gacha;
|
|||||||
|
|
||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.Entity;
|
||||||
import dev.morphia.annotations.Id;
|
import dev.morphia.annotations.Id;
|
||||||
|
import emu.nebula.GameConstants;
|
||||||
import emu.nebula.Nebula;
|
import emu.nebula.Nebula;
|
||||||
import emu.nebula.data.resources.GachaDef;
|
import emu.nebula.data.resources.GachaDef;
|
||||||
import emu.nebula.data.resources.GachaNewbieDef;
|
import emu.nebula.data.resources.GachaNewbieDef;
|
||||||
@@ -19,19 +20,20 @@ import java.util.Set;
|
|||||||
@Getter
|
@Getter
|
||||||
@Entity(value = "gacha", useDiscriminator = false)
|
@Entity(value = "gacha", useDiscriminator = false)
|
||||||
public class GachaManager extends PlayerManager implements GameDatabaseObject {
|
public class GachaManager extends PlayerManager implements GameDatabaseObject {
|
||||||
private static final String PATH_BANNERS = "banners.";
|
|
||||||
private static final String PATH_PITY_STATES = "pityStates.";
|
|
||||||
private static final String PATH_NEWBIE_STATES = "newbieStates.";
|
|
||||||
private static final int HISTORY_SAVE_RETRIES = 2;
|
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
private int uid;
|
private int uid;
|
||||||
|
|
||||||
private Map<Integer, GachaBannerInfo> banners = new HashMap<>();
|
private Map<Integer, GachaBannerInfo> banners = new HashMap<>();
|
||||||
private Map<Integer, GachaPityState> pityStates = new HashMap<>();
|
private Map<Integer, GachaPityState> pityStates = new HashMap<>();
|
||||||
private Map<Integer, NewbieGachaState> newbieStates = new HashMap<>();
|
private Map<Integer, NewbieGachaState> newbieStates = new HashMap<>();
|
||||||
|
|
||||||
|
// Not saved to the database
|
||||||
private transient Set<Integer> lockedNewbieObtainIds = new HashSet<>();
|
private transient Set<Integer> lockedNewbieObtainIds = new HashSet<>();
|
||||||
|
|
||||||
|
private static final String PATH_BANNERS = "banners.";
|
||||||
|
private static final String PATH_PITY_STATES = "pityStates.";
|
||||||
|
private static final String PATH_NEWBIE_STATES = "newbieStates.";
|
||||||
|
|
||||||
@Deprecated // Morphia only
|
@Deprecated // Morphia only
|
||||||
public GachaManager() {
|
public GachaManager() {
|
||||||
|
|
||||||
@@ -91,7 +93,7 @@ public class GachaManager extends PlayerManager implements GameDatabaseObject {
|
|||||||
|
|
||||||
var log = new GachaHistoryLog(this.getPlayerUid(), storageId, gachaId, results);
|
var log = new GachaHistoryLog(this.getPlayerUid(), storageId, gachaId, results);
|
||||||
Exception lastError = null;
|
Exception lastError = null;
|
||||||
for (int attempt = 1; attempt <= HISTORY_SAVE_RETRIES + 1; attempt++) {
|
for (int attempt = 1; attempt <= GameConstants.MAX_GACHA_HISTORY_SAVE_RETRIES + 1; attempt++) {
|
||||||
try {
|
try {
|
||||||
Nebula.getGameDatabase().save(log);
|
Nebula.getGameDatabase().save(log);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import dev.morphia.annotations.AlsoLoad;
|
|||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.Entity;
|
||||||
import dev.morphia.annotations.Id;
|
import dev.morphia.annotations.Id;
|
||||||
import dev.morphia.annotations.Indexed;
|
import dev.morphia.annotations.Indexed;
|
||||||
|
|
||||||
import emu.nebula.GameConstants;
|
import emu.nebula.GameConstants;
|
||||||
import emu.nebula.Nebula;
|
import emu.nebula.Nebula;
|
||||||
import emu.nebula.data.GameData;
|
import emu.nebula.data.GameData;
|
||||||
@@ -40,12 +41,13 @@ import emu.nebula.proto.Notify.SigninRewardUpdate;
|
|||||||
import emu.nebula.proto.PlayerData.DictionaryEntry;
|
import emu.nebula.proto.PlayerData.DictionaryEntry;
|
||||||
import emu.nebula.proto.PlayerData.DictionaryTab;
|
import emu.nebula.proto.PlayerData.DictionaryTab;
|
||||||
import emu.nebula.proto.PlayerData.PlayerInfo;
|
import emu.nebula.proto.PlayerData.PlayerInfo;
|
||||||
import emu.nebula.proto.Public;
|
|
||||||
import emu.nebula.proto.Public.*;
|
import emu.nebula.proto.Public.*;
|
||||||
import emu.nebula.util.ResetCycle;
|
import emu.nebula.util.ResetCycle;
|
||||||
import emu.nebula.util.Utils;
|
import emu.nebula.util.Utils;
|
||||||
import emu.nebula.util.ints.String2IntMap;
|
import emu.nebula.util.ints.String2IntMap;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import us.hebi.quickbuf.ProtoMessage;
|
import us.hebi.quickbuf.ProtoMessage;
|
||||||
import us.hebi.quickbuf.RepeatedInt;
|
import us.hebi.quickbuf.RepeatedInt;
|
||||||
|
|
||||||
@@ -231,7 +233,7 @@ public class Player implements GameDatabaseObject {
|
|||||||
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
||||||
|
|
||||||
if (oldLevel != this.level) {
|
if (oldLevel != this.level) {
|
||||||
this.queueBattlePassUnlockNotify(oldLevel);
|
this.getBattlePassManager().queueUnlockNotify(oldLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,7 +558,8 @@ public class Player implements GameDatabaseObject {
|
|||||||
// Trigger achievement
|
// Trigger achievement
|
||||||
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
||||||
|
|
||||||
this.queueBattlePassUnlockNotify(oldLevel);
|
// Send battle pass unlock notify packet
|
||||||
|
this.getBattlePassManager().queueUnlockNotify(oldLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate changes
|
// Calculate changes
|
||||||
@@ -759,6 +762,10 @@ public class Player implements GameDatabaseObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getLastResetDayTimeSeconds() {
|
||||||
|
return Utils.getResetTimeSecondsByEpochDay(this.getLastEpochDay());
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger quests + achievements
|
// Trigger quests + achievements
|
||||||
|
|
||||||
public void trigger(int condition, int progress, int param1, int param2) {
|
public void trigger(int condition, int progress, int param1, int param2) {
|
||||||
@@ -879,6 +886,8 @@ public class Player implements GameDatabaseObject {
|
|||||||
Nebula.getGameDatabase().update(this, this.getUid(), "lastLogin", this.getLastLogin());
|
Nebula.getGameDatabase().update(this, this.getUid(), "lastLogin", this.getLastLogin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mall
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the player currently has at least one purchasable
|
* Returns whether the player currently has at least one purchasable
|
||||||
* free mall package (`CurrencyType = 3`) for the mall entrance red dot.
|
* free mall package (`CurrencyType = 3`) for the mall entrance red dot.
|
||||||
@@ -907,42 +916,8 @@ public class Player implements GameDatabaseObject {
|
|||||||
MallPackageState.newInstance().setNew(this.hasAvailableFreeMallPackage())
|
MallPackageState.newInstance().setNew(this.hasAvailableFreeMallPackage())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queueBattlePassStateNotify() {
|
// Monthly Card
|
||||||
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.
|
* Returns the number of future game days still covered after the current game day.
|
||||||
@@ -1089,10 +1064,6 @@ public class Player implements GameDatabaseObject {
|
|||||||
this.grantMonthlyCardReward(entry.getKey(), notifyOnly);
|
this.grantMonthlyCardReward(entry.getKey(), notifyOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getLastResetDayTimeSeconds() {
|
|
||||||
return Utils.getResetTimeSecondsByEpochDay(this.getLastEpochDay());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next packages
|
// Next packages
|
||||||
|
|
||||||
@@ -1263,7 +1234,7 @@ public class Player implements GameDatabaseObject {
|
|||||||
|
|
||||||
// Keep BattlePass state container present for client-side login cache flow.
|
// Keep BattlePass state container present for client-side login cache flow.
|
||||||
// Some clients assume State.BattlePass exists during player_data bootstrap.
|
// Some clients assume State.BattlePass exists during player_data bootstrap.
|
||||||
state.setBattlePass(this.buildBattlePassStateProto());
|
state.setBattlePass(this.getBattlePassManager().getStateProto());
|
||||||
|
|
||||||
state.getMutableAchievement()
|
state.getMutableAchievement()
|
||||||
.setNew(this.getAchievementManager().hasNewAchievements());
|
.setNew(this.getAchievementManager().hasNewAchievements());
|
||||||
@@ -1415,8 +1386,4 @@ public class Player implements GameDatabaseObject {
|
|||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBattlePassUnlocked() {
|
|
||||||
return this.getLevel() >= GameConstants.BATTLE_PASS_UNLOCK_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
package emu.nebula.server.handlers;
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
import emu.nebula.data.GameData;
|
|
||||||
import emu.nebula.game.player.PlayerChangeInfo;
|
|
||||||
import emu.nebula.net.GameSession;
|
import emu.nebula.net.GameSession;
|
||||||
import emu.nebula.net.HandlerId;
|
import emu.nebula.net.HandlerId;
|
||||||
import emu.nebula.net.NetHandler;
|
import emu.nebula.net.NetHandler;
|
||||||
import emu.nebula.net.NetMsgId;
|
import emu.nebula.net.NetMsgId;
|
||||||
import emu.nebula.proto.BattlePassLevelBuy.BattlePassLevelBuyResp;
|
import emu.nebula.proto.BattlePassLevelBuy.BattlePassLevelBuyResp;
|
||||||
import emu.nebula.proto.Public.ChangeInfo;
|
|
||||||
import emu.nebula.proto.Public.UI32;
|
import emu.nebula.proto.Public.UI32;
|
||||||
|
|
||||||
@HandlerId(NetMsgId.battle_pass_level_buy_req)
|
@HandlerId(NetMsgId.battle_pass_level_buy_req)
|
||||||
@@ -15,85 +12,31 @@ public class HandlerBattlePassLevelBuyReq extends NetHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
if (!session.getPlayer().isBattlePassUnlocked()) {
|
// Sanity check to make sure battlepass is unlocked
|
||||||
|
if (!session.getPlayer().getBattlePassManager().isUnlocked()) {
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_level_buy_failed_ack);
|
return session.encodeMsg(NetMsgId.battle_pass_level_buy_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse request using UI32 (standard uint32 protobuf type)
|
// Parse request
|
||||||
var req = UI32.parseFrom(message);
|
var req = UI32.parseFrom(message);
|
||||||
int levelsToBuy = req.getValue();
|
|
||||||
|
|
||||||
if (levelsToBuy < 1) {
|
// Get battlepass
|
||||||
levelsToBuy = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get battle pass
|
|
||||||
var battlePass = session.getPlayer().getBattlePassManager().getBattlePass();
|
var battlePass = session.getPlayer().getBattlePassManager().getBattlePass();
|
||||||
|
|
||||||
// Calculate total cost and total exp
|
// Buy levels
|
||||||
int currentLevel = battlePass.getLevel();
|
var change = battlePass.buyLevels(req.getValue());
|
||||||
int totalGemCost = 0;
|
|
||||||
int totalExpToAdd = 0;
|
|
||||||
int affordableLevels = 0;
|
|
||||||
int costItemId = 2; // Gems
|
|
||||||
|
|
||||||
for (int i = 1; i <= levelsToBuy; i++) {
|
// Check
|
||||||
int targetLevel = currentLevel + i;
|
if (change == null) {
|
||||||
var levelData = GameData.getBattlePassLevelDataTable().get(targetLevel);
|
|
||||||
|
|
||||||
if (levelData == null) {
|
|
||||||
// Max level reached
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cost = levelData.getQty();
|
|
||||||
int currentGems = session.getPlayer().getInventory().getResourceCount(costItemId);
|
|
||||||
|
|
||||||
if (currentGems < totalGemCost + cost) {
|
|
||||||
// Not enough gems for all levels
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalGemCost += cost;
|
|
||||||
totalExpToAdd += levelData.getExp();
|
|
||||||
affordableLevels++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (affordableLevels == 0) {
|
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_level_buy_failed_ack);
|
return session.encodeMsg(NetMsgId.battle_pass_level_buy_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare change info
|
|
||||||
var change = new PlayerChangeInfo();
|
|
||||||
|
|
||||||
// Deduct gems (total cost)
|
|
||||||
if (totalGemCost > 0) {
|
|
||||||
var gemCost = session.getPlayer().getInventory().addItem(costItemId, -totalGemCost);
|
|
||||||
if (gemCost != null) {
|
|
||||||
change.add(gemCost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all exp at once (preserves current exp)
|
|
||||||
battlePass.addExp(totalExpToAdd);
|
|
||||||
|
|
||||||
// Save to database
|
|
||||||
battlePass.save();
|
|
||||||
|
|
||||||
// Build response
|
// Build response
|
||||||
var rsp = BattlePassLevelBuyResp.newInstance()
|
var rsp = BattlePassLevelBuyResp.newInstance()
|
||||||
.setLevel(battlePass.getLevel());
|
.setLevel(battlePass.getLevel())
|
||||||
|
.setChange(change.toProto());
|
||||||
// Add change info (gem cost)
|
|
||||||
if (!change.isEmpty()) {
|
|
||||||
var changeProto = ChangeInfo.newInstance();
|
|
||||||
for (var any : change.getList()) {
|
|
||||||
changeProto.addProps(any);
|
|
||||||
}
|
|
||||||
rsp.setChange(changeProto);
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getPlayer().queueBattlePassStateNotify();
|
session.getPlayer().getBattlePassManager().updateState();
|
||||||
|
|
||||||
// Encode and send
|
// Encode and send
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_level_buy_succeed_ack, rsp);
|
return session.encodeMsg(NetMsgId.battle_pass_level_buy_succeed_ack, rsp);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package emu.nebula.server.handlers;
|
package emu.nebula.server.handlers;
|
||||||
|
|
||||||
import emu.nebula.Nebula;
|
import emu.nebula.Nebula;
|
||||||
|
import emu.nebula.game.mall.MallOrderCollectResult;
|
||||||
import emu.nebula.net.NetHandler;
|
import emu.nebula.net.NetHandler;
|
||||||
import emu.nebula.net.NetMsgId;
|
import emu.nebula.net.NetMsgId;
|
||||||
import emu.nebula.proto.BattlePassOrderCollect.BattlePassOrderCollectResp;
|
import emu.nebula.proto.BattlePassOrderCollect.BattlePassOrderCollectResp;
|
||||||
@@ -13,7 +14,8 @@ public class HandlerBattlePassOrderCollectReq extends NetHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
if (!session.getPlayer().isBattlePassUnlocked()) {
|
// Sanity check to make sure battlepass is unlocked
|
||||||
|
if (!session.getPlayer().getBattlePassManager().isUnlocked()) {
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_order_collect_failed_ack);
|
return session.encodeMsg(NetMsgId.battle_pass_order_collect_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +28,7 @@ public class HandlerBattlePassOrderCollectReq extends NetHandler {
|
|||||||
if (!battlePass.isPremium()) {
|
if (!battlePass.isPremium()) {
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_order_collect_failed_ack);
|
return session.encodeMsg(NetMsgId.battle_pass_order_collect_failed_ack);
|
||||||
}
|
}
|
||||||
collectResult = emu.nebula.game.mall.MallOrderCollectResult.empty();
|
collectResult = MallOrderCollectResult.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build collect response
|
// Build collect response
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ public class HandlerBattlePassOrderReq extends NetHandler {
|
|||||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
// Parse request
|
// Parse request
|
||||||
var req = BattlePassOrderReq.parseFrom(message);
|
var req = BattlePassOrderReq.parseFrom(message);
|
||||||
|
|
||||||
// Validate mode (1 = Premium, 2 = Luxury)
|
// Validate mode (1 = Premium, 2 = Luxury)
|
||||||
int requestedMode = req.getMode();
|
int requestedMode = req.getMode();
|
||||||
if (!session.getPlayer().isBattlePassUnlocked() || requestedMode < 1 || requestedMode > 2) {
|
if (!session.getPlayer().getBattlePassManager().isUnlocked() || requestedMode < 1 || requestedMode > 2) {
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_order_failed_ack);
|
return session.encodeMsg(NetMsgId.battle_pass_order_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -12,7 +12,7 @@ public class HandlerBattlePassQuestRewardReceiveReq extends NetHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
if (!session.getPlayer().isBattlePassUnlocked()) {
|
if (!session.getPlayer().getBattlePassManager().isUnlocked()) {
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_quest_reward_receive_failed_ack);
|
return session.encodeMsg(NetMsgId.battle_pass_quest_reward_receive_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ public class HandlerBattlePassQuestRewardReceiveReq extends NetHandler {
|
|||||||
.setExp(battlePass.getExp())
|
.setExp(battlePass.getExp())
|
||||||
.setExpThisWeek(battlePass.getExpWeek());
|
.setExpThisWeek(battlePass.getExpWeek());
|
||||||
|
|
||||||
session.getPlayer().queueBattlePassStateNotify();
|
session.getPlayer().getBattlePassManager().updateState();
|
||||||
|
|
||||||
// Encode and send
|
// Encode and send
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_quest_reward_receive_succeed_ack, rsp);
|
return session.encodeMsg(NetMsgId.battle_pass_quest_reward_receive_succeed_ack, rsp);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class HandlerBattlePassRewardReceiveReq extends NetHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
if (!session.getPlayer().isBattlePassUnlocked()) {
|
if (!session.getPlayer().getBattlePassManager().isUnlocked()) {
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_reward_receive_failed_ack);
|
return session.encodeMsg(NetMsgId.battle_pass_reward_receive_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ public class HandlerBattlePassRewardReceiveReq extends NetHandler {
|
|||||||
.setPremiumReward(battlePass.getPremiumReward().toByteArray())
|
.setPremiumReward(battlePass.getPremiumReward().toByteArray())
|
||||||
.setChange(change.toProto());
|
.setChange(change.toProto());
|
||||||
|
|
||||||
session.getPlayer().queueBattlePassStateNotify();
|
session.getPlayer().getBattlePassManager().updateState();
|
||||||
|
|
||||||
// Encode and send
|
// Encode and send
|
||||||
return session.encodeMsg(NetMsgId.battle_pass_reward_receive_succeed_ack, rsp);
|
return session.encodeMsg(NetMsgId.battle_pass_reward_receive_succeed_ack, rsp);
|
||||||
|
|||||||
Reference in new issue
Block a user