Implement battle pass

This commit is contained in:
Melledy
2025-11-13 01:24:42 -08:00
parent 61bc5e7a0c
commit 8d4736c00e
25 changed files with 758 additions and 49 deletions

View File

@@ -22,4 +22,6 @@ public class GameConstants {
public static final int MAX_FORMATIONS = 5;
public static final int MAX_SHOWCASE_IDS = 5;
public static final int BATTLE_PASS_ID = 1;
}

View File

@@ -53,6 +53,12 @@ public class GameData {
@Getter private static DataTable<ResidentShopDef> ResidentShopDataTable = new DataTable<>();
@Getter private static DataTable<ResidentGoodsDef> ResidentGoodsDataTable = new DataTable<>();
// Battle Pass
@Getter private static DataTable<BattlePassDef> BattlePassDataTable = new DataTable<>();
@Getter private static DataTable<BattlePassLevelDef> BattlePassLevelDataTable = new DataTable<>();
@Getter private static DataTable<BattlePassQuestDef> BattlePassQuestDataTable = new DataTable<>();
@Getter private static DataTable<BattlePassRewardDef> BattlePassRewardDataTable = new DataTable<>();
// Commissions
@Getter private static DataTable<AgentDef> AgentDataTable = new DataTable<>();

View File

@@ -0,0 +1,16 @@
package emu.nebula.data.resources;
import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = "BattlePass.json")
public class BattlePassDef extends BaseDef {
private int ID;
@Override
public int getId() {
return ID;
}
}

View File

@@ -0,0 +1,20 @@
package emu.nebula.data.resources;
import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = "BattlePassLevel.json")
public class BattlePassLevelDef extends BaseDef {
private int ID;
private int Exp;
private int Tid;
private int Qty;
@Override
public int getId() {
return ID;
}
}

View File

@@ -0,0 +1,45 @@
package emu.nebula.data.resources;
import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType;
import emu.nebula.game.quest.QuestData;
import emu.nebula.game.quest.QuestHelper;
import emu.nebula.game.quest.QuestType;
import emu.nebula.game.quest.QuestHelper.QuestParams;
import lombok.Getter;
@Getter
@ResourceType(name = "BattlePassQuest.json")
public class BattlePassQuestDef extends BaseDef implements QuestData {
private int Id;
private int Type;
private int Exp;
private transient int questType;
private transient QuestParams params;
@Override
public int getId() {
return Id;
}
public boolean isDaily() {
return this.Type == 1;
}
@Override
public int getCompleteCond() {
return params.getCompleteCond();
}
@Override
public int[] getCompleteCondParams() {
return params.getCompleteCondParams();
}
@Override
public void onLoad() {
this.questType = this.isDaily() ? QuestType.BattlePassDaily : QuestType.BattlePassWeekly;
this.params = QuestHelper.getBattlePassQuestParams().getOrDefault(this.getId(), QuestHelper.DEFAULT);
}
}

View File

@@ -0,0 +1,45 @@
package emu.nebula.data.resources;
import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType;
import emu.nebula.game.inventory.ItemParamMap;
import lombok.Getter;
@Getter
@ResourceType(name = "BattlePassReward.json")
public class BattlePassRewardDef extends BaseDef {
private int ID;
private int Level;
private int Tid1;
private int Qty1;
private int Tid2;
private int Qty2;
private int Tid3;
private int Qty3;
private transient ItemParamMap basicRewards;
private transient ItemParamMap premiumRewards;
@Override
public int getId() {
return (ID << 16) + Level;
}
@Override
public void onLoad() {
this.basicRewards = new ItemParamMap();
this.premiumRewards = new ItemParamMap();
if (this.Tid1 > 0) {
this.basicRewards.add(this.Tid1, this.Qty1);
}
if (this.Tid2 > 0) {
this.premiumRewards.add(this.Tid2, this.Qty2);
}
if (this.Tid3 > 0) {
this.premiumRewards.add(this.Tid3, this.Qty3);
}
}
}

View File

@@ -4,11 +4,13 @@ import java.util.Arrays;
import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType;
import emu.nebula.game.quest.QuestData;
import emu.nebula.game.quest.QuestType;
import lombok.Getter;
@Getter
@ResourceType(name = "DailyQuest.json")
public class DailyQuestDef extends BaseDef {
public class DailyQuestDef extends BaseDef implements QuestData {
private int Id;
private boolean Apear;
private int Active;
@@ -26,6 +28,11 @@ public class DailyQuestDef extends BaseDef {
return Id;
}
@Override
public int getQuestType() {
return QuestType.Daily;
}
public int[] getCompleteCondParams() {
return this.condParams;
}

View File

@@ -73,7 +73,7 @@ public class AgentManager extends PlayerManager implements GameDatabaseObject {
this.getAgents().put(agent.getId(), agent);
// Quest
this.getPlayer().getQuestManager().triggerQuest(QuestCondType.AgentApplyTotal, 1);
this.getPlayer().triggerQuest(QuestCondType.AgentApplyTotal, 1);
// Success
return agent;
@@ -153,6 +153,9 @@ public class AgentManager extends PlayerManager implements GameDatabaseObject {
// Save to database
this.save();
// Quest
this.getPlayer().triggerQuest(QuestCondType.AgentFinishTotal, list.size());
// Success
return change.setSuccess(true);
}

View File

@@ -0,0 +1,339 @@
package emu.nebula.game.battlepass;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.nebula.GameConstants;
import emu.nebula.Nebula;
import emu.nebula.data.GameData;
import emu.nebula.data.resources.BattlePassRewardDef;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.inventory.ItemParamMap;
import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerChangeInfo;
import emu.nebula.game.quest.GameQuest;
import emu.nebula.game.quest.QuestCondType;
import emu.nebula.game.quest.QuestType;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.BattlePassInfoOuterClass.BattlePassInfo;
import emu.nebula.util.Bitset;
import lombok.Getter;
@Getter
@Entity(value = "battlepass", useDiscriminator = false)
public class BattlePass implements GameDatabaseObject {
@Id
private int uid;
private transient BattlePassManager manager;
private int battlePassId;
private int mode;
private int level;
private int exp;
private int expWeek;
private Bitset basicReward;
private Bitset premiumReward;
private Map<Integer, GameQuest> quests;
@Deprecated // Morphia only
public BattlePass() {
}
public BattlePass(BattlePassManager manager) {
this.uid = manager.getPlayerUid();
this.manager = manager;
this.battlePassId = GameConstants.BATTLE_PASS_ID;
this.basicReward = new Bitset();
this.premiumReward = new Bitset();
// Setup battle pass quests
this.quests = new HashMap<>();
for (var data : GameData.getBattlePassQuestDataTable()) {
this.quests.put(data.getId(), new GameQuest(data));
}
// Save to database
this.save();
}
public void setManager(BattlePassManager manager) {
this.manager = manager;
}
public Player getPlayer() {
return manager.getPlayer();
}
public boolean isPremium() {
return this.mode > 0;
}
private BattlePassRewardDef getRewardData(int level) {
return GameData.getBattlePassRewardDataTable().get((this.getBattlePassId() << 16) + level);
}
public int getMaxExp() {
var data = GameData.getBattlePassLevelDataTable().get(this.getLevel() + 1);
return data != null ? data.getExp() : 0;
}
public void addExp(int amount) {
// Setup
int expRequired = this.getMaxExp();
// Add exp
this.exp += amount;
// Check for level ups
while (this.exp >= expRequired && expRequired > 0) {
this.level += 1;
this.exp -= expRequired;
expRequired = this.getMaxExp();
}
}
public synchronized void resetDailyQuests() {
// Reset daily quests
for (var data : GameData.getBattlePassQuestDataTable()) {
// Get quest
var quest = getQuests().computeIfAbsent(data.getId(), i -> new GameQuest(data));
// Don't reset weekly quests
if (!data.isDaily()) {
continue;
}
// Reset progress
quest.resetProgress();
// Sync quest with player client
this.syncQuest(quest);
}
// Persist to database
this.save();
}
public synchronized void trigger(QuestCondType condition, int progress, int param) {
for (var quest : getQuests().values()) {
// Try to trigger quest
boolean result = quest.trigger(condition, progress, param);
// Skip if quest progress wasn't changed
if (!result) {
continue;
}
// Sync quest with player client
this.syncQuest(quest);
// Update in database
Nebula.getGameDatabase().update(this, this.getUid(), "quests." + quest.getId(), quest);
}
}
/**
* Update this quest on the player client
*/
private void syncQuest(GameQuest quest) {
if (!getPlayer().hasSession()) {
return;
}
getPlayer().addNextPackage(
NetMsgId.quest_change_notify,
quest.toProto()
);
}
public BattlePass receiveQuestReward(int questId) {
// Get received quests
var claimList = new ArrayList<GameQuest>();
if (questId > 0) {
// Claim specific quest
var quest = this.getQuests().get(questId);
if (quest != null && !quest.isClaimed()) {
claimList.add(quest);
}
} else {
// Claim all
for (var quest : this.getQuests().values()) {
if (!quest.isComplete() || quest.isClaimed()) {
continue;
}
claimList.add(quest);
}
}
// Sanity check
if (claimList.isEmpty()) {
return null;
}
// Init exp
int exp = 0;
int expWeek = 0;
// Claim
for (var quest : claimList) {
// Get data
var data = GameData.getBattlePassQuestDataTable().get(quest.getId());
if (data == null) {
continue;
}
// Set claimed
quest.setClaimed(true);
// Add exp
exp += data.getExp();
// Check if quest is weekly
if (quest.getType() == QuestType.BattlePassWeekly) {
expWeek += data.getExp();
}
}
// Add exp
if (exp > 0) {
this.addExp(exp);
}
if (expWeek > 0) {
this.expWeek += expWeek;
}
// Save to database
this.save();
// Success
return this;
}
public PlayerChangeInfo receiveReward(boolean premium, int levelId) {
// Get bitset
Bitset rewards = null;
if (premium) {
rewards = this.getPremiumReward();
} else {
rewards = this.getBasicReward();
}
// Make sure we haven't already claimed the reward
if (rewards.isSet(levelId)) {
return null;
}
// Set claimed
rewards.setBit(levelId);
// Save to database
this.save();
// Get reward data
var data = this.getRewardData(levelId);
if (data == null) {
return new PlayerChangeInfo();
}
// Add items
if (premium) {
return getPlayer().getInventory().addItems(data.getPremiumRewards());
} else {
return getPlayer().getInventory().addItems(data.getBasicRewards());
}
}
public PlayerChangeInfo receiveReward() {
// Init rewards
var rewards = new ItemParamMap();
// Get unclaimed rewards
for (int i = 1; i <= this.getLevel(); i++) {
// Cache reward data
BattlePassRewardDef data = null;
// Basic reward
if (!this.getBasicReward().isSet(i)) {
// Set flag
this.getBasicReward().setBit(i);
// Get reward data if we havent already
if (data == null) {
data = this.getRewardData(i);
}
// Add basic rewards
if (data != null) {
rewards.add(data.getBasicRewards());
}
}
// Premium reward
if (this.isPremium() && !this.getPremiumReward().isSet(i)) {
// Set flag
this.getPremiumReward().setBit(i);
// Get reward data if we havent already
if (data == null) {
data = this.getRewardData(i);
}
// Add basic rewards
if (data != null) {
rewards.add(data.getPremiumRewards());
}
}
}
// Save if we have any rewards to add
if (rewards.size() > 0) {
this.save();
} else {
return null;
}
// Add rewards
return getPlayer().getInventory().addItems(rewards);
}
// Proto
public BattlePassInfo toProto() {
var proto = BattlePassInfo.newInstance()
.setId(this.getBattlePassId())
.setLevel(this.getLevel())
.setMode(this.getMode())
.setExp(this.getExp())
.setExpThisWeek(this.getExpWeek())
.setDeadline(Long.MAX_VALUE)
.setBasicReward(this.getBasicReward().toByteArray())
.setPremiumReward(this.getPremiumReward().toByteArray());
var daily = proto.getMutableDailyQuests();
var weekly = proto.getMutableWeeklyQuests();
for (var quest : this.getQuests().values()) {
if (quest.getType() == QuestType.BattlePassDaily) {
daily.addList(quest.toProto());
} else if (quest.getType() == QuestType.BattlePassWeekly) {
weekly.addList(quest.toProto());
}
}
return proto;
}
}

View File

@@ -0,0 +1,27 @@
package emu.nebula.game.battlepass;
import emu.nebula.Nebula;
import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerManager;
import lombok.Getter;
@Getter
public class BattlePassManager extends PlayerManager {
private BattlePass battlePass;
public BattlePassManager(Player player) {
super(player);
}
// Database
public void loadFromDatabase() {
this.battlePass = Nebula.getGameDatabase().getObjectByUid(BattlePass.class, getPlayer().getUid());
if (this.battlePass == null) {
this.battlePass = new BattlePass(this);
} else {
this.battlePass.setManager(this);
}
}
}

View File

@@ -153,7 +153,7 @@ public class Character implements GameDatabaseObject {
// Check if we leveled up
if (this.level > oldLevel) {
// Trigger quest
this.getPlayer().getQuestManager().triggerQuest(QuestCondType.CharacterUpTotal, this.level - oldLevel);
this.getPlayer().triggerQuest(QuestCondType.CharacterUpTotal, this.level - oldLevel);
}
// Save to database

View File

@@ -133,7 +133,7 @@ public class GameDisc implements GameDatabaseObject {
// Check if we leveled up
if (this.level > oldLevel) {
// Trigger quest
this.getPlayer().getQuestManager().triggerQuest(QuestCondType.DiscStrengthenTotal, this.level - oldLevel);
this.getPlayer().triggerQuest(QuestCondType.DiscStrengthenTotal, this.level - oldLevel);
}
// Save to database

View File

@@ -63,8 +63,8 @@ public class InstanceManager extends PlayerManager {
this.getProgress().saveInstanceLog(log, logName, data.getId(), star);
// Quest triggers
this.getPlayer().getQuestManager().triggerQuest(questCondition, 1);
this.getPlayer().getQuestManager().triggerQuest(QuestCondType.BattleTotal, 1);
this.getPlayer().triggerQuest(questCondition, 1);
this.getPlayer().triggerQuest(QuestCondType.BattleTotal, 1);
}
// Set extra data
@@ -131,8 +131,8 @@ public class InstanceManager extends PlayerManager {
change.setExtraData(list);
// Quest triggers
this.getPlayer().getQuestManager().triggerQuest(questCondition, count);
this.getPlayer().getQuestManager().triggerQuest(QuestCondType.BattleTotal, count);
this.getPlayer().triggerQuest(questCondition, count);
this.getPlayer().triggerQuest(QuestCondType.BattleTotal, count);
// Success
return change.setSuccess(true);

View File

@@ -9,6 +9,7 @@ import emu.nebula.Nebula;
import emu.nebula.data.GameData;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.player.PlayerManager;
import emu.nebula.game.quest.QuestCondType;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.Notify.Skin;
import emu.nebula.proto.Public.Honor;
@@ -444,6 +445,13 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
}
}
// Trigger quest
if (amount > 0) {
this.getPlayer().triggerQuest(QuestCondType.ItemsAdd, amount, id);
} else {
this.getPlayer().triggerQuest(QuestCondType.ItemsDeplete, Math.abs(amount), id);
}
//
return change;
}

View File

@@ -12,6 +12,7 @@ import emu.nebula.data.GameData;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.account.Account;
import emu.nebula.game.agent.AgentManager;
import emu.nebula.game.battlepass.BattlePassManager;
import emu.nebula.game.character.CharacterStorage;
import emu.nebula.game.formation.FormationManager;
import emu.nebula.game.gacha.GachaManager;
@@ -77,6 +78,7 @@ public class Player implements GameDatabaseObject {
// Managers
private final transient CharacterStorage characters;
private final transient GachaManager gachaManager;
private final transient BattlePassManager battlePassManager;
private final transient StarTowerManager starTowerManager;
private final transient InstanceManager instanceManager;
private final transient InfinityTowerManager infinityTowerManager;
@@ -100,6 +102,7 @@ public class Player implements GameDatabaseObject {
// Init player managers
this.characters = new CharacterStorage(this);
this.gachaManager = new GachaManager(this);
this.battlePassManager = new BattlePassManager(this);
this.starTowerManager = new StarTowerManager(this);
this.instanceManager = new InstanceManager(this);
this.infinityTowerManager = new InfinityTowerManager(this);
@@ -475,7 +478,7 @@ public class Player implements GameDatabaseObject {
change = modifyEnergy(-amount, change);
// Trigger quest
this.getQuestManager().triggerQuest(QuestCondType.EnergyDeplete, amount);
this.triggerQuest(QuestCondType.EnergyDeplete, amount);
// Complete
return change;
@@ -535,6 +538,18 @@ public class Player implements GameDatabaseObject {
public void resetDailies() {
// Reset daily quests
this.getQuestManager().resetDailyQuests();
this.getBattlePassManager().getBattlePass().resetDailyQuests();
}
// Trigger quests
public void triggerQuest(QuestCondType condition, int progress) {
this.triggerQuest(condition, progress, 0);
}
public void triggerQuest(QuestCondType condition, int progress, int param) {
this.getQuestManager().trigger(condition, progress, param);
this.getBattlePassManager().getBattlePass().trigger(condition, progress, param);
}
// Login
@@ -562,6 +577,7 @@ public class Player implements GameDatabaseObject {
// Load from database
this.getCharacters().loadFromDatabase();
this.getStarTowerManager().loadFromDatabase();
this.getBattlePassManager().loadFromDatabase();
// Load inventory before referenced classes
if (this.inventory == null) {
@@ -589,7 +605,7 @@ public class Player implements GameDatabaseObject {
this.checkResetDailies();
// Trigger quest login
this.getQuestManager().triggerQuest(QuestCondType.LoginTotal, 1);
this.triggerQuest(QuestCondType.LoginTotal, 1);
}
// Next packages
@@ -684,7 +700,9 @@ public class Player implements GameDatabaseObject {
state.getMutableMail()
.setNew(this.getMailbox().hasNewMail());
state.getMutableBattlePass();
state.getMutableBattlePass()
.setState(1);
state.getMutableFriendEnergy();
state.getMutableMallPackage();
state.getMutableAchievement();
@@ -732,17 +750,7 @@ public class Player implements GameDatabaseObject {
}
// Quests
var quests = proto.getMutableQuests();
for (var quest : this.getQuestManager().getQuests().values()) {
quests.addList(quest.toProto());
}
for (int id : this.getQuestManager().getClaimedActiveIds()) {
proto.addDailyActiveIds(id);
}
state.getMutableWorldClassReward()
.setFlag(this.getQuestManager().getLevelRewards().toBigEndianByteArray());
this.getQuestManager().encodeProto(proto);
// Add dictionary tabs
for (var dictionaryData : GameData.getDictionaryTabDataTable()) {
@@ -761,7 +769,7 @@ public class Player implements GameDatabaseObject {
}
// Add progress
this.getProgress().toProto(proto);
this.getProgress().encodeProto(proto);
// Handbook
proto.addHandbook(this.getCharacters().getCharacterHandbook());

View File

@@ -108,7 +108,7 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
// Proto
public void toProto(PlayerInfo proto) {
public void encodeProto(PlayerInfo proto) {
// Check if we want to unlock all instances
boolean unlockAll = Nebula.getConfig().getServerOptions().unlockInstances;

View File

@@ -1,7 +1,6 @@
package emu.nebula.game.quest;
import dev.morphia.annotations.Entity;
import emu.nebula.data.resources.DailyQuestDef;
import emu.nebula.proto.Public.Quest;
import emu.nebula.proto.Public.QuestProgress;
import lombok.Getter;
@@ -13,6 +12,7 @@ public class GameQuest {
private int id;
private int type;
private int cond;
private int param;
private int curProgress;
private int maxProgress;
@@ -25,11 +25,15 @@ public class GameQuest {
}
public GameQuest(DailyQuestDef data) {
public GameQuest(QuestData data) {
this.id = data.getId();
this.type = QuestType.Daily;
this.type = data.getQuestType();
this.cond = data.getCompleteCond();
this.maxProgress = data.getCompleteCondParams()[0];
if (data.getCompleteCondParams().length >= 2) {
this.param = data.getCompleteCondParams()[1];
}
}
public void resetProgress() {
@@ -51,7 +55,7 @@ public class GameQuest {
return 0;
}
public boolean trigger(QuestCondType condition, int param) {
public boolean trigger(QuestCondType condition, int progress, int param) {
// Sanity check
if (this.isComplete()) {
return false;
@@ -62,8 +66,13 @@ public class GameQuest {
return false;
}
// Check quest param
if (this.param != 0 && param != this.param) {
return false;
}
// Get new progress
int newProgress = Math.min(this.curProgress + param, this.maxProgress);
int newProgress = Math.min(this.curProgress + progress, this.maxProgress);
// Set
if (this.curProgress != newProgress) {

View File

@@ -0,0 +1,13 @@
package emu.nebula.game.quest;
public interface QuestData {
public int getId();
public int getQuestType();
public int getCompleteCond();
public int[] getCompleteCondParams();
}

View File

@@ -0,0 +1,47 @@
package emu.nebula.game.quest;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
// Because some quests in the data files do not have condition params, we will hardcode them here
public class QuestHelper {
public static final QuestParams DEFAULT = new QuestParams(0, new int[1]);
@Getter
private static final Int2ObjectMap<QuestParams> battlePassQuestParams = new Int2ObjectOpenHashMap<>();
static {
battlePassQuestParams.put(1001, new QuestParams(QuestCondType.LoginTotal, 1));
battlePassQuestParams.put(1002, new QuestParams(QuestCondType.EnergyDeplete, 160));
battlePassQuestParams.put(1003, new QuestParams(QuestCondType.BattleTotal, 6));
battlePassQuestParams.put(1004, new QuestParams(QuestCondType.QuestWithSpecificType, 5, QuestType.Daily));
battlePassQuestParams.put(2001, new QuestParams(QuestCondType.TowerEnterFloor, 1));
battlePassQuestParams.put(2002, new QuestParams(QuestCondType.WeekBoosClearSpecificDifficultyAndTotal, 3));
battlePassQuestParams.put(2003, new QuestParams(QuestCondType.BattleTotal, 20));
battlePassQuestParams.put(2004, new QuestParams(QuestCondType.LoginTotal, 5));
battlePassQuestParams.put(2005, new QuestParams(QuestCondType.AgentFinishTotal, 3));
battlePassQuestParams.put(2006, new QuestParams(QuestCondType.ItemsDeplete, 100000, 1));
battlePassQuestParams.put(2007, new QuestParams(QuestCondType.GiftGiveTotal, 5));
battlePassQuestParams.put(2008, new QuestParams(QuestCondType.EnergyDeplete, 1200));
}
@Getter
public static class QuestParams {
public int completeCond;
public int[] completeCondParams;
public QuestParams(int cond, int[] params) {
this.completeCond = cond;
this.completeCondParams = params;
}
public QuestParams(int cond, int param) {
this(cond, new int[] {param});
}
public QuestParams(QuestCondType cond, int... params) {
this(cond.getValue(), params);
}
}
}

View File

@@ -6,6 +6,7 @@ import java.util.Map;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.nebula.Nebula;
import emu.nebula.data.GameData;
import emu.nebula.data.resources.WorldClassDef;
@@ -15,10 +16,13 @@ import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerChangeInfo;
import emu.nebula.game.player.PlayerManager;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.PlayerData.PlayerInfo;
import emu.nebula.util.Bitset;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.AccessLevel;
import lombok.Getter;
@@ -90,10 +94,10 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
this.save();
}
public synchronized void triggerQuest(QuestCondType condition, int param) {
public synchronized void trigger(QuestCondType condition, int progress, int param) {
for (var quest : getQuests().values()) {
// Try to trigger quest
boolean result = quest.trigger(condition, param);
boolean result = quest.trigger(condition, progress, param);
// Skip if quest progress wasn't changed
if (!result) {
@@ -108,6 +112,21 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
}
}
/**
* Update this quest on the player client
*/
private void syncQuest(GameQuest quest) {
if (!getPlayer().hasSession()) {
return;
}
getPlayer().addNextPackage(
NetMsgId.quest_change_notify,
quest.toProto()
);
}
public PlayerChangeInfo receiveQuestReward(int questId) {
// Get received quests
var claimList = new ArrayList<GameQuest>();
@@ -160,6 +179,9 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
// Update in database
Nebula.getGameDatabase().update(this, this.getUid(), "activity", this.getActivity());
// Trigger quest
this.getPlayer().triggerQuest(QuestCondType.QuestWithSpecificType, claimList.size(), QuestType.Daily);
// Success
return change.setSuccess(true);
}
@@ -250,20 +272,6 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
return change.setSuccess(true);
}
/**
* Update this quest on the player client
*/
private void syncQuest(GameQuest quest) {
if (!getPlayer().hasSession()) {
return;
}
getPlayer().addNextPackage(
NetMsgId.quest_change_notify,
quest.toProto()
);
}
// Daily reward
public PlayerChangeInfo claimDailyReward() {
@@ -281,9 +289,29 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
Nebula.getGameDatabase().update(this, this.getUid(), "hasDailyReward", this.hasDailyReward);
// Trigger quest
this.triggerQuest(QuestCondType.DailyShopReceiveShopTotal, 1);
this.getPlayer().triggerQuest(QuestCondType.DailyShopReceiveShopTotal, 1);
// Success
return change.setSuccess(true);
}
// Serialization
public void encodeProto(PlayerInfo proto) {
var quests = proto.getMutableQuests();
for (var quest : this.getQuests().values()) {
quests.addList(quest.toProto());
}
for (int id : this.getClaimedActiveIds()) {
proto.addDailyActiveIds(id);
}
proto.getMutableState()
.getMutableWorldClassReward()
.setFlag(this.getLevelRewards().toBigEndianByteArray());
proto.setTourGuideQuestGroup(1);
}
}

View File

@@ -60,7 +60,7 @@ public class StarTowerManager extends PlayerManager {
this.game = new StarTowerGame(this, data, formation, req);
// Trigger quest
this.getPlayer().getQuestManager().triggerQuest(QuestCondType.TowerEnterFloor, 1);
this.getPlayer().triggerQuest(QuestCondType.TowerEnterFloor, 1);
// Success
return this.game;

View File

@@ -10,7 +10,11 @@ public class HandlerBattlePassInfoReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
return session.encodeMsg(NetMsgId.battle_pass_info_failed_ack);
// Get battle pass proto
var info = session.getPlayer().getBattlePassManager().getBattlePass().toProto();
// Encode and send
return session.encodeMsg(NetMsgId.battle_pass_info_succeed_ack, info);
}
}

View File

@@ -0,0 +1,35 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.BattlePassQuestRewardReceive.BattlePassQuestRewardResp;
import emu.nebula.proto.Public.UI32;
import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.battle_pass_quest_reward_receive_req)
public class HandlerBattlePassQuestRewardReceiveReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
// Parse req
var req = UI32.parseFrom(message);
// Recieve reward
var battlePass = session.getPlayer().getBattlePassManager().getBattlePass().receiveQuestReward(req.getValue());
if (battlePass == null) {
return session.encodeMsg(NetMsgId.battle_pass_quest_reward_receive_failed_ack);
}
// Build response
var rsp = BattlePassQuestRewardResp.newInstance()
.setLevel(battlePass.getLevel())
.setExp(battlePass.getExp())
.setExpThisWeek(battlePass.getExpWeek());
// Encode and send
return session.encodeMsg(NetMsgId.battle_pass_quest_reward_receive_succeed_ack, rsp);
}
}

View File

@@ -0,0 +1,47 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.BattlePassRewardReceive.BattlePassRewardReceiveReq;
import emu.nebula.proto.BattlePassRewardReceive.BattlePassRewardReceiveResp;
import emu.nebula.net.HandlerId;
import emu.nebula.game.player.PlayerChangeInfo;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.battle_pass_reward_receive_req)
public class HandlerBattlePassRewardReceiveReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
// Parse request
var req = BattlePassRewardReceiveReq.parseFrom(message);
// Setup variables
PlayerChangeInfo change = null;
var battlePass = session.getPlayer().getBattlePassManager().getBattlePass();
// Claim
if (req.getPremium() > 0) {
change = session.getPlayer().getBattlePassManager().getBattlePass().receiveReward(true, req.getPremium());
} else if (req.getBasic() > 0) {
change = session.getPlayer().getBattlePassManager().getBattlePass().receiveReward(false, req.getBasic());
} else if (req.hasAll()) {
change = session.getPlayer().getBattlePassManager().getBattlePass().receiveReward();
}
// Check
if (change == null) {
return session.encodeMsg(NetMsgId.battle_pass_reward_receive_failed_ack);
}
// Build response
var rsp = BattlePassRewardReceiveResp.newInstance()
.setBasicReward(battlePass.getBasicReward().toByteArray())
.setPremiumReward(battlePass.getPremiumReward().toByteArray())
.setChange(change.toProto());
// Encode and send
return session.encodeMsg(NetMsgId.battle_pass_reward_receive_succeed_ack, rsp);
}
}

View File

@@ -13,7 +13,7 @@ public class HandlerClientEventReportReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
// Interact
session.getPlayer().getQuestManager().triggerQuest(QuestCondType.ClientReport, 1);
session.getPlayer().triggerQuest(QuestCondType.ClientReport, 1);
// Encode response
return session.encodeMsg(NetMsgId.client_event_report_succeed_ack, Nil.newInstance());