Implement daily activity missions

This commit is contained in:
Melledy
2025-11-07 05:29:57 -08:00
parent 67029b7094
commit ad538d59ed
26 changed files with 513 additions and 18 deletions

View File

@@ -1,20 +1,25 @@
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;
import lombok.Setter;
@Getter
@Entity(useDiscriminator = false)
public class GameQuest {
private int id;
private int type;
private int cond;
private int curProgress;
private int maxProgress;
@Setter
private boolean claimed;
@Deprecated
public GameQuest() {
@@ -23,11 +28,50 @@ public class GameQuest {
public GameQuest(DailyQuestDef data) {
this.id = data.getId();
this.type = QuestType.Daily;
this.cond = data.getCompleteCond();
this.maxProgress = data.getCompleteCondParams()[0];
}
public void resetProgress() {
this.curProgress = 0;
this.claimed = false;
}
public boolean isComplete() {
return this.curProgress >= this.maxProgress;
}
private int getStatus() {
if (this.isClaimed()) {
return 2;
} else if (this.isComplete()) {
return 1;
}
return 0;
}
public boolean trigger(QuestCondType condition, int param) {
// Sanity check
if (this.isComplete()) {
return false;
}
// Skip if not the correct condition
if (this.cond != condition.getValue()) {
return false;
}
// Get new progress
int newProgress = Math.min(this.curProgress + param, this.maxProgress);
// Set
if (this.curProgress != newProgress) {
this.curProgress = newProgress;
return true;
}
return false;
}
// Proto
@@ -40,6 +84,7 @@ public class GameQuest {
var proto = Quest.newInstance()
.setId(this.getId())
.setTypeValue(this.getType())
.setStatus(this.getStatus())
.addProgress(progress);
return proto;

View File

@@ -0,0 +1,144 @@
package emu.nebula.game.quest;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
public enum QuestCondType {
BattleTotal (3),
BattlesTotalWithPartner (4),
CharacterAcquireQuantityRarityAndAdvancement (6),
CharacterAdvanceTotal (7),
CharacterSkillUpTotal (8),
CharacterSkillWithSpecificUpTotal (9),
CharacterUpTotal (12),
CharacterWithSpecificAdvance (13),
CharacterWithSpecificUpLevel (15),
CharactersWithSpecificNumberLevelAndAttributes (17),
CharactersWithSpecificQuantityAdvancementCountAndAttribute (19),
CharactersWithSpecificQuantityRarityAndLevel (22),
ChatTotal (23),
DailyInstanceClearSpecificDifficultyAndTotal (24),
DailyInstanceClearSpecificTypeAndTotal (25),
DailyInstanceClearTotal (26),
DiscAcquireQuantityLevelAndRarity (30),
DiscAcquireQuantityPhaseAndRarity (31),
DiscAcquireQuantityStarAndRarity (32),
DiscLimitBreakTotal (33),
DiscPromoteTotal (34),
DiscStrengthenTotal (35),
DiscWithSpecificQuantityLevelAndRarity (36),
DiscWithSpecificQuantityPhaseAndRarity (37),
DiscWithSpecificQuantityStarAndRarity (38),
EnergyDeplete (39),
GachaTotal (44),
GiftGiveTotal (45),
InfinityTowerClearSpecificFloor (46),
InfinityTowerClearTotal (47),
ItemsAdd (48),
ItemsDeplete (49),
ItemsProductTotal (50),
LoginTotal (51),
QuestTravelerDuelChallengeTotal (52),
QuestTourGuideSpecific (53),
QuestTravelerDuelSpecific (54),
QuestWithSpecificType (55),
RegionBossClearSpecificFullStarWithBossIdAndDifficulty (56),
RegionBossClearSpecificLevelWithDifficultyAndTotal (57),
RegionBossClearSpecificTotal (58),
RegionBossClearTotal (59),
SkillsWithSpecificQuantityAndLevel (60),
StageClearSpecificStars (62),
StoryClear (63),
TravelerDuelChallengeSpecificBoosLevelWithDifficultyAndTotal (64),
TravelerDuelClearBossTotal (65),
TravelerDuelClearSpecificBossIdAndDifficulty (66),
TravelerDuelChallengeClearSpecificBossLevelAndAffix (67),
TravelerDuelClearSpecificBossLevelWithDifficultyAndTotal (68),
TravelerDuelClearSpecificBossTotal (69),
TravelerDuelChallengeRankUploadTotal (70),
WorldClassSpecific (71),
RegionBossClearSpecificTypeWithTotal (72),
CharactersWithSpecificDatingCount (73),
CharactersDatingTotal (74),
VampireSurvivorPassedSpecificLevel (77),
CharacterParticipateTowerNumber (78),
CharacterAllSkillReachSpecificLevel (79),
TravelerDuelPlayTotal (80),
VampireClearTotal (81),
VampireWithSpecificClearTotal (82),
AgentFinishTotal (83),
AgentWithSpecificFinishTotal (84),
ActivityMiningEnterLayer (86),
ActivityMiningDestroyGrid (87),
BossRushTotalStars (88),
InfinityTowerClearSpecificDifficultyAndTotal (89),
SkillInstanceClearTotal (90),
VampireSurvivorSpecificPassedLevel (91),
WeekBoosClearSpecificDifficultyAndTotal (92),
NpcAffinityWithSpecificLevel (93),
CharacterPassedWithSpecificTowerAndCount (94),
ActivityCookieLevelAccPackage (96),
ActivityCookieLevelScore (97),
ActivityCookieTypeAccPackage (98),
ActivityCookieTypeAccPackCookie (99),
ActivityCookieTypeAccRhythm (100),
ActivityCookieTypeChallenge (101),
CharGemInstanceClearTotal (104),
DailyShopReceiveShopTotal (105),
AgentApplyTotal (106),
ActivityScore (107),
ActivityTypeAvgReadWithSpecificIdAndLevelId (108),
ActivityTypeLevelPassedWithSpecificIdAndLevelId (109),
ActivityTypeLevel3StarPassedWithSpecificIdAndLevelId (110),
ActivityTypeLevelStarWithSpecificIdAndLevelTypeTotal (111),
ActivityTypeLevelPassedWithSpecificIdAndLevelIdAndSpecificPositionAndCharElem (112),
ActivityTypeLevelPassedSpecificIdTotal (113),
ClientReport (200),
TowerBuildSpecificScoreWithTotal (504),
TowerClearSpecificLevelWithDifficultyAndTotal (507),
TowerEnterTotal (510),
TowerSpecificDifficultyShopBuyTimes (514),
TowerGrowthSpecificNote (515),
TowerClearSpecificLevelWithDifficultyAndTotalHistory (516),
TowerBookWithSpecificEvent (517),
TowerBookWithSpecificFateCard (518),
TowerBookWithSpecificPotential (520),
TowerBuildSpecificDifficultyAndScoreWithTotal (521),
TowerSpecificDifficultyStrengthenMachineTotal (522),
TowerSpecificDifficultyKillBossTotal (524),
TowerBookSpecificCharWithPotentialTotal (525),
TowerBuildSpecificCharSpecificScoreWithTotal (526),
TowerGrowthWithSpecificNote (527),
TowerSpecificFateCardReRollTotal (528),
TowerSpecificPotentialReRollTotal (529),
TowerSpecificShopReRollTotal (530),
TowerSpecificNoteActivateTotal (531),
TowerSpecificNoteLevelTotal (532),
TowerSpecificPotentialBonusTotal (533),
TowerSpecificPotentialLuckyTotal (534),
TowerSpecificShopBuyDiscountTotal (535),
TowerSpecificSecondarySkillActivateTotal (536),
TowerSpecificGetExtraNoteLvTotal (537),
TowerEnterFloor (538),
TowerSweepTimes (539),
TowerSweepTotal (540);
@Getter
private final int value;
private final static Int2ObjectMap<QuestCondType> map = new Int2ObjectOpenHashMap<>();
static {
for (QuestCondType type : QuestCondType.values()) {
map.put(type.getValue(), type);
}
}
private QuestCondType(int value) {
this.value = value;
}
public static QuestCondType getByValue(int value) {
return map.get(value);
}
}

View File

@@ -1,16 +1,22 @@
package emu.nebula.game.quest;
import java.util.HashMap;
import java.util.HashSet;
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.database.GameDatabaseObject;
import emu.nebula.game.inventory.ItemParamMap;
import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerChangeInfo;
import emu.nebula.game.player.PlayerManager;
import emu.nebula.net.NetMsgId;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.Getter;
@Getter
@@ -19,6 +25,11 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
@Id
private int uid;
// Daily activity missions
private int activity;
private IntSet claimedActiveIds;
// Quests
private Map<Integer, GameQuest> quests;
@Deprecated // Morphia only
@@ -29,6 +40,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
public QuestManager(Player player) {
super(player);
this.uid = player.getUid();
this.claimedActiveIds = new IntOpenHashSet();
this.quests = new HashMap<>();
this.resetDailyQuests();
@@ -36,7 +48,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
this.save();
}
public void resetDailyQuests() {
public synchronized void resetDailyQuests() {
// Reset daily quests
for (var data : GameData.getDailyQuestDataTable()) {
// Get quest
@@ -45,17 +57,142 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
// Reset progress
quest.resetProgress();
// Update to player
if (getPlayer().hasSession()) {
getPlayer().addNextPackage(
NetMsgId.quest_change_notify,
quest.toProto()
);
}
// Sync quest with player client
this.syncQuest(quest);
}
// Reset activity
this.activity = 0;
this.claimedActiveIds.clear();
// Persist to database
this.save();
}
public synchronized void triggerQuest(QuestCondType condition, int param) {
for (var quest : getQuests().values()) {
// Try to trigger quest
boolean result = quest.trigger(condition, 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);
}
}
public PlayerChangeInfo receiveReward(int questId) {
// Get received quests
var claimList = new HashSet<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;
}
// Create change info
var change = new PlayerChangeInfo();
// Claim
for (var quest : claimList) {
// Get data
var data = GameData.getDailyQuestDataTable().get(quest.getId());
if (data != null) {
// Add reward data
this.getPlayer().getInventory().addItem(data.getItemTid(), data.getItemQty(), change);
// Add activity
this.activity += data.getActive();
}
// Set claimed
quest.setClaimed(true);
// Update in database
Nebula.getGameDatabase().update(this, this.getUid(), "quests." + quest.getId(), quest);
}
// Update in database
Nebula.getGameDatabase().update(this, this.getUid(), "activity", this.getActivity());
// Success
return change;
}
public PlayerChangeInfo claimActiveRewards() {
// Init
var claimList = new IntArrayList();
var rewards = new ItemParamMap();
// Get claimable
for (var data : GameData.getDailyQuestActiveDataTable()) {
if (this.getClaimedActiveIds().contains(data.getId())) {
continue;
}
if (this.getActivity() >= data.getActive()) {
// Add rewards
rewards.add(data.getRewards());
// Add to claimed activity list
claimList.add(data.getId());
}
}
// Sanity check
if (claimList.isEmpty()) {
return null;
}
// Add rewards
var change = this.getPlayer().getInventory().addItems(rewards);
// Set claimed list
change.setExtraData(claimList);
// Update in database
this.getClaimedActiveIds().addAll(claimList);
Nebula.getGameDatabase().update(this, this.getUid(), "claimedActiveIds", this.getClaimedActiveIds());
// Success
return change;
}
/**
* Update this quest on the player client
*/
private void syncQuest(GameQuest quest) {
if (!getPlayer().hasSession()) {
return;
}
getPlayer().addNextPackage(
NetMsgId.quest_change_notify,
quest.toProto()
);
}
}