diff --git a/src/main/java/emu/nebula/GameConstants.java b/src/main/java/emu/nebula/GameConstants.java index 6b8c41b..9b5080c 100644 --- a/src/main/java/emu/nebula/GameConstants.java +++ b/src/main/java/emu/nebula/GameConstants.java @@ -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; } diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index a7bb956..ea5eed6 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -53,6 +53,12 @@ public class GameData { @Getter private static DataTable ResidentShopDataTable = new DataTable<>(); @Getter private static DataTable ResidentGoodsDataTable = new DataTable<>(); + // Battle Pass + @Getter private static DataTable BattlePassDataTable = new DataTable<>(); + @Getter private static DataTable BattlePassLevelDataTable = new DataTable<>(); + @Getter private static DataTable BattlePassQuestDataTable = new DataTable<>(); + @Getter private static DataTable BattlePassRewardDataTable = new DataTable<>(); + // Commissions @Getter private static DataTable AgentDataTable = new DataTable<>(); diff --git a/src/main/java/emu/nebula/data/resources/BattlePassDef.java b/src/main/java/emu/nebula/data/resources/BattlePassDef.java new file mode 100644 index 0000000..5f11b4d --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/BattlePassDef.java @@ -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; + } +} diff --git a/src/main/java/emu/nebula/data/resources/BattlePassLevelDef.java b/src/main/java/emu/nebula/data/resources/BattlePassLevelDef.java new file mode 100644 index 0000000..bd99d98 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/BattlePassLevelDef.java @@ -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; + } +} diff --git a/src/main/java/emu/nebula/data/resources/BattlePassQuestDef.java b/src/main/java/emu/nebula/data/resources/BattlePassQuestDef.java new file mode 100644 index 0000000..5ed4175 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/BattlePassQuestDef.java @@ -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); + } +} diff --git a/src/main/java/emu/nebula/data/resources/BattlePassRewardDef.java b/src/main/java/emu/nebula/data/resources/BattlePassRewardDef.java new file mode 100644 index 0000000..0966578 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/BattlePassRewardDef.java @@ -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); + } + } +} diff --git a/src/main/java/emu/nebula/data/resources/DailyQuestDef.java b/src/main/java/emu/nebula/data/resources/DailyQuestDef.java index 1924748..bd553e7 100644 --- a/src/main/java/emu/nebula/data/resources/DailyQuestDef.java +++ b/src/main/java/emu/nebula/data/resources/DailyQuestDef.java @@ -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; } diff --git a/src/main/java/emu/nebula/game/agent/AgentManager.java b/src/main/java/emu/nebula/game/agent/AgentManager.java index 6f0c550..04b941f 100644 --- a/src/main/java/emu/nebula/game/agent/AgentManager.java +++ b/src/main/java/emu/nebula/game/agent/AgentManager.java @@ -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); } diff --git a/src/main/java/emu/nebula/game/battlepass/BattlePass.java b/src/main/java/emu/nebula/game/battlepass/BattlePass.java new file mode 100644 index 0000000..627112a --- /dev/null +++ b/src/main/java/emu/nebula/game/battlepass/BattlePass.java @@ -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 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(); + + 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; + } +} diff --git a/src/main/java/emu/nebula/game/battlepass/BattlePassManager.java b/src/main/java/emu/nebula/game/battlepass/BattlePassManager.java new file mode 100644 index 0000000..0e92ed8 --- /dev/null +++ b/src/main/java/emu/nebula/game/battlepass/BattlePassManager.java @@ -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); + } + } +} diff --git a/src/main/java/emu/nebula/game/character/Character.java b/src/main/java/emu/nebula/game/character/Character.java index f32a48a..b22ae42 100644 --- a/src/main/java/emu/nebula/game/character/Character.java +++ b/src/main/java/emu/nebula/game/character/Character.java @@ -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 diff --git a/src/main/java/emu/nebula/game/character/GameDisc.java b/src/main/java/emu/nebula/game/character/GameDisc.java index ec6935f..82ef840 100644 --- a/src/main/java/emu/nebula/game/character/GameDisc.java +++ b/src/main/java/emu/nebula/game/character/GameDisc.java @@ -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 diff --git a/src/main/java/emu/nebula/game/instance/InstanceManager.java b/src/main/java/emu/nebula/game/instance/InstanceManager.java index 8360dd9..bdcc5f2 100644 --- a/src/main/java/emu/nebula/game/instance/InstanceManager.java +++ b/src/main/java/emu/nebula/game/instance/InstanceManager.java @@ -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); diff --git a/src/main/java/emu/nebula/game/inventory/Inventory.java b/src/main/java/emu/nebula/game/inventory/Inventory.java index 18f7208..3e11866 100644 --- a/src/main/java/emu/nebula/game/inventory/Inventory.java +++ b/src/main/java/emu/nebula/game/inventory/Inventory.java @@ -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; } diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index 8d9b658..b6249b1 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -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()); diff --git a/src/main/java/emu/nebula/game/player/PlayerProgress.java b/src/main/java/emu/nebula/game/player/PlayerProgress.java index fc43e15..d8b765b 100644 --- a/src/main/java/emu/nebula/game/player/PlayerProgress.java +++ b/src/main/java/emu/nebula/game/player/PlayerProgress.java @@ -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; diff --git a/src/main/java/emu/nebula/game/quest/GameQuest.java b/src/main/java/emu/nebula/game/quest/GameQuest.java index 5e89520..545dfcc 100644 --- a/src/main/java/emu/nebula/game/quest/GameQuest.java +++ b/src/main/java/emu/nebula/game/quest/GameQuest.java @@ -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) { diff --git a/src/main/java/emu/nebula/game/quest/QuestData.java b/src/main/java/emu/nebula/game/quest/QuestData.java new file mode 100644 index 0000000..d59e013 --- /dev/null +++ b/src/main/java/emu/nebula/game/quest/QuestData.java @@ -0,0 +1,13 @@ +package emu.nebula.game.quest; + +public interface QuestData { + + public int getId(); + + public int getQuestType(); + + public int getCompleteCond(); + + public int[] getCompleteCondParams(); + +} diff --git a/src/main/java/emu/nebula/game/quest/QuestHelper.java b/src/main/java/emu/nebula/game/quest/QuestHelper.java new file mode 100644 index 0000000..77e5886 --- /dev/null +++ b/src/main/java/emu/nebula/game/quest/QuestHelper.java @@ -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 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); + } + } +} diff --git a/src/main/java/emu/nebula/game/quest/QuestManager.java b/src/main/java/emu/nebula/game/quest/QuestManager.java index f0354a0..5fb8719 100644 --- a/src/main/java/emu/nebula/game/quest/QuestManager.java +++ b/src/main/java/emu/nebula/game/quest/QuestManager.java @@ -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(); @@ -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); + } } diff --git a/src/main/java/emu/nebula/game/tower/StarTowerManager.java b/src/main/java/emu/nebula/game/tower/StarTowerManager.java index a99ab2b..6607148 100644 --- a/src/main/java/emu/nebula/game/tower/StarTowerManager.java +++ b/src/main/java/emu/nebula/game/tower/StarTowerManager.java @@ -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; diff --git a/src/main/java/emu/nebula/server/handlers/HandlerBattlePassInfoReq.java b/src/main/java/emu/nebula/server/handlers/HandlerBattlePassInfoReq.java index 95f87f4..8dfde27 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerBattlePassInfoReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerBattlePassInfoReq.java @@ -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); } } diff --git a/src/main/java/emu/nebula/server/handlers/HandlerBattlePassQuestRewardReceiveReq.java b/src/main/java/emu/nebula/server/handlers/HandlerBattlePassQuestRewardReceiveReq.java new file mode 100644 index 0000000..46b072b --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerBattlePassQuestRewardReceiveReq.java @@ -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); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerBattlePassRewardReceiveReq.java b/src/main/java/emu/nebula/server/handlers/HandlerBattlePassRewardReceiveReq.java new file mode 100644 index 0000000..a84a17c --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerBattlePassRewardReceiveReq.java @@ -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); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerClientEventReportReq.java b/src/main/java/emu/nebula/server/handlers/HandlerClientEventReportReq.java index 45970d3..9dbe008 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerClientEventReportReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerClientEventReportReq.java @@ -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());