diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index 13bf2c8..ca85e08 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -123,6 +123,7 @@ public class GameData { @Getter private static DataTable StarTowerQuestDataTable = new DataTable<>(); @Getter private static DataTable StarTowerBuildRankDataTable = new DataTable<>(); @Getter private static DataTable StarTowerBookPotentialRewardDataTable = new DataTable<>(); + @Getter private static DataTable StarTowerBookEventRewardDataTable = new DataTable<>(); @Getter private static DataTable TowerVitaOptionDataTable = new DataTable<>(); @Getter private static DataTable SubNoteSkillPromoteGroupDataTable = new DataTable<>(); @@ -133,6 +134,11 @@ public class GameData { @Getter private static DataTable StarTowerBookFateCardQuestDataTable = new DataTable<>(); @Getter private static DataTable StarTowerBookFateCardDataTable = new DataTable<>(); @Getter private static DataTable FateCardDataTable = new DataTable<>(); + + // Star Tower: NPC affinity + @Getter private static DataTable StarTowerNpcDataTable = new DataTable<>(); + @Getter private static DataTable NpcAffinityGroupDataTable = new DataTable<>(); + @Getter private static DataTable NpcAffinityPlotDataTable = new DataTable<>(); // ===== Infinity Tower ===== @Getter private static DataTable InfinityTowerLevelDataTable = new DataTable<>(); diff --git a/src/main/java/emu/nebula/data/resources/NpcAffinityGroupDef.java b/src/main/java/emu/nebula/data/resources/NpcAffinityGroupDef.java new file mode 100644 index 0000000..7be5af0 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/NpcAffinityGroupDef.java @@ -0,0 +1,19 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import lombok.Getter; + +@Getter +@ResourceType(name = "NPCAffinityGroup.json") +public class NpcAffinityGroupDef extends BaseDef { + private int Id; + private int Level; + private int AffinityValue; + private int AffinityGroupId; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/data/resources/NpcAffinityPlotDef.java b/src/main/java/emu/nebula/data/resources/NpcAffinityPlotDef.java new file mode 100644 index 0000000..c5f08c0 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/NpcAffinityPlotDef.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 = "NPCAffinityPlot.json") +public class NpcAffinityPlotDef extends BaseDef { + private int Id; + private int NPCId; + private int AffinityLevel; + private int ItemId; + private int ItemQty; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/data/resources/StarTowerBookEventRewardDef.java b/src/main/java/emu/nebula/data/resources/StarTowerBookEventRewardDef.java new file mode 100644 index 0000000..5817373 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/StarTowerBookEventRewardDef.java @@ -0,0 +1,73 @@ +package emu.nebula.data.resources; + +import java.util.Arrays; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import emu.nebula.data.ResourceType.LoadPriority; +import emu.nebula.game.quest.QuestData; +import emu.nebula.game.quest.QuestType; + +import lombok.Getter; + +@Getter +@ResourceType(name = "StarTowerBookEventReward.json", loadPriority = LoadPriority.LOW) +public class StarTowerBookEventRewardDef extends BaseDef implements QuestData { + private int Id; + private int Type; // 1 = Character clear count, 2 = NPC affinity level + private int NPCId; + private int Sort; + private int CompleteCond; + private String CompleteCondParams; + private int ItemId; + private int ItemQty; + + // Cached data + private transient int[] condParams; + + @Override + public int getId() { + return this.Id; + } + + @Override + public int getQuestType() { + return QuestType.TowerEvent; + } + + @Override + public int getCompleteCond() { + return this.CompleteCond; + } + + @Override + public int[] getCompleteCondParams() { + if (this.condParams == null) { + this.condParams = new int[] { 1, 0 }; + } + + return this.condParams; + } + + @Override + public void onLoad() { + // Flip params + // The data stores params as [target id, required amount] + // while GameQuest expects [required amount, target id] + if (this.CompleteCondParams != null && !this.CompleteCondParams.isBlank()) { + var params = Arrays.stream(this.CompleteCondParams.replaceAll("[\\[\\] ]", "") + .split(",")) + .filter(value -> !value.isEmpty()) + .mapToInt(Integer::parseInt) + .toArray(); + + if (params.length >= 2) { + this.condParams = new int[] { params[1], params[0] }; + } + } + + if (this.condParams == null) { + this.condParams = new int[] { 1, 0 }; + } + } +} diff --git a/src/main/java/emu/nebula/data/resources/StarTowerNpcDef.java b/src/main/java/emu/nebula/data/resources/StarTowerNpcDef.java new file mode 100644 index 0000000..fbb519d --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/StarTowerNpcDef.java @@ -0,0 +1,17 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import lombok.Getter; + +@Getter +@ResourceType(name = "StarTowerNPC.json") +public class StarTowerNpcDef extends BaseDef { + private int Id; + private int AffinityGroupId; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/game/player/PlayerProgress.java b/src/main/java/emu/nebula/game/player/PlayerProgress.java index 26eb189..6501c61 100644 --- a/src/main/java/emu/nebula/game/player/PlayerProgress.java +++ b/src/main/java/emu/nebula/game/player/PlayerProgress.java @@ -6,10 +6,12 @@ import java.util.Map; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Id; import dev.morphia.annotations.PostLoad; + import emu.nebula.Nebula; import emu.nebula.data.GameData; import emu.nebula.data.resources.InfinityTowerLevelDef; import emu.nebula.database.GameDatabaseObject; +import emu.nebula.game.tower.StarTowerNpcData; import emu.nebula.game.tower.StarTowerVitaData; import emu.nebula.game.tutorial.TutorialLevelLog; import emu.nebula.game.vampire.VampireSurvivorLog; @@ -21,10 +23,12 @@ import emu.nebula.proto.Public.SkillInstance; import emu.nebula.proto.Public.VampireSurvivorLevel; import emu.nebula.proto.Public.WeekBossLevel; import emu.nebula.util.Bitset; + import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; + import lombok.Getter; @Getter @@ -38,6 +42,7 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject private int[] starTowerGrowth; private int towerTickets; private StarTowerVitaData towerVitaData; + private Map towerNpcs; // Instances private Int2IntMap dailyInstanceLog; @@ -96,6 +101,9 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject // Star Tower Vita this.towerVitaData = new StarTowerVitaData(this); + // Star Tower NPCs + this.towerNpcs = new HashMap<>(); + // Save to database this.save(); } @@ -129,6 +137,17 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject // Success return true; } + + public StarTowerNpcData getStarTowerNpcById(int npcId) { + var npc = this.getTowerNpcs().get(npcId); + + if (npc == null) { + npc = new StarTowerNpcData(this.getPlayer(), npcId); + this.getTowerNpcs().put(npcId, npc); + } + + return npc; + } /** * Returns the maximum amount of weekly tickets that a player can receive without hitting the limit @@ -341,6 +360,12 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject this.towerVitaData.setPlayerProgress(this); } + // Set star tower npc data + if (this.towerNpcs == null) { + this.towerNpcs = new HashMap<>(); + shouldSave = true; + } + // Carry over infinity tower progress if (this.infinityArenaLog != null) { for (int levelId : this.infinityArenaLog.values()) { @@ -366,4 +391,13 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject this.save(); } } + + @Override + public void onLoad() { + // Set player for all npcs + // This needs to be in onLoad because that is when PlayerProgress's player field is set + for (var npc : this.towerNpcs.values()) { + npc.setPlayer(this.getPlayer()); + } + } } diff --git a/src/main/java/emu/nebula/game/quest/QuestManager.java b/src/main/java/emu/nebula/game/quest/QuestManager.java index 56db770..06635f3 100644 --- a/src/main/java/emu/nebula/game/quest/QuestManager.java +++ b/src/main/java/emu/nebula/game/quest/QuestManager.java @@ -10,6 +10,7 @@ import dev.morphia.annotations.Id; import emu.nebula.GameConstants; import emu.nebula.Nebula; import emu.nebula.data.GameData; +import emu.nebula.data.resources.StarTowerBookEventRewardDef; import emu.nebula.data.resources.WorldClassDef; import emu.nebula.database.GameDatabaseObject; import emu.nebula.game.inventory.ItemParamMap; @@ -61,6 +62,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { this.levelRewards = new Bitset(); this.initStarTowerQuests(); + this.initStarTowerBookEventQuests(); this.resetDailyQuests(true); @@ -97,6 +99,8 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { if (progress > 0) { quest.setProgress(progress); } + } else if (cond == QuestCondition.TowerBookWithSpecificEvent.getValue()) { + this.syncStarTowerBookEventCountQuest(quest); } } @@ -249,11 +253,16 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { var quest = this.getList().get(data.getQuestKey()); if (quest == null) { - this.addQuest(new GameQuest(data)); + quest = this.addQuest(new GameQuest(data)); changed = true; } else if (quest.refreshData(data)) { changed = true; } + + // Book collection quests can complete retroactively + if (this.syncStarTowerBookEventCountQuest(quest)) { + changed = true; + } } return changed; @@ -292,6 +301,155 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { } } + // Star Tower book events (anecdotes) + + /** + * Returns the total number of anecdotes the player has collected. + */ + public synchronized int getClaimedStarTowerBookEventCount() { + int count = 0; + + for (var quest : this.getQuests()) { + if (quest.getType() == QuestType.TowerEvent && quest.isClaimed()) { + count++; + } + } + + return count; + } + + /** + * Syncs the progress of Star Tower book collection quests. + */ + private boolean syncStarTowerBookEventCountQuest(GameQuest quest) { + if (quest.getCond() != QuestCondition.TowerBookWithSpecificEvent.getValue()) { + return false; + } + + int progress = Math.min(this.getClaimedStarTowerBookEventCount(), quest.getMaxProgress()); + + if (progress <= quest.getCurProgress()) { + return false; + } + + return quest.setProgress(progress); + } + + /** + * Adds all Star Tower book event rewards as quests. + * Returns true if any quest was added or refreshed. + */ + public synchronized boolean initStarTowerBookEventQuests() { + boolean changed = false; + + for (var data : GameData.getStarTowerBookEventRewardDataTable()) { + // Get existing quest + var quest = this.getList().get(data.getQuestKey()); + + if (quest == null) { + quest = this.addQuest(new GameQuest(data)); + changed = true; + } else if (quest.refreshData(data)) { + changed = true; + } + + // NPC affinity quests can complete retroactively + if (this.syncStarTowerBookEventQuest(quest, data)) { + changed = true; + } + } + + return changed; + } + + /** + * Syncs progress for book event quests that can be completed retroactively. + */ + private boolean syncStarTowerBookEventQuest(GameQuest quest, StarTowerBookEventRewardDef data) { + if (data.getCompleteCond() != QuestCondition.NpcAffinityWithSpecificLevel.getValue()) { + return false; + } + + var npc = this.getPlayer().getProgress().getTowerNpcs().get(quest.getParam()); + + if (npc == null) { + return false; + } + + int progress = Math.min(npc.getLevel(), quest.getMaxProgress()); + + if (progress <= quest.getCurProgress()) { + return false; + } + + return quest.setProgress(progress); + } + + public synchronized PlayerChangeInfo receiveStarTowerBookEventReward(int questId) { + // Get received quests + var claimList = new ArrayList(); + + if (questId > 0) { + // Claim specific quest + var quest = this.getQuest(QuestType.TowerEvent, questId); + + if (quest != null && quest.canClaim()) { + claimList.add(quest); + } + } else { + // Claim all + for (var quest : this.getQuests()) { + if (quest.getType() != QuestType.TowerEvent) { + continue; + } + + if (!quest.canClaim()) { + continue; + } + + claimList.add(quest); + } + } + + // Sanity check + if (claimList.isEmpty()) { + return null; + } + + // Create change info + var change = new PlayerChangeInfo(); + var received = new IntArrayList(); + + // Claim + for (var quest : claimList) { + // Get data + var data = GameData.getStarTowerBookEventRewardDataTable().get(quest.getId()); + + if (data != null) { + // Add reward + this.getPlayer().getInventory().addItem(data.getItemId(), data.getItemQty(), change); + } + + // Set claimed + quest.setClaimed(true); + + // Add received id + received.add(quest.getId()); + + // Update in database + this.saveQuest(quest); + } + + // Update Star Tower book collection quests + this.trigger(QuestCondition.TowerBookWithSpecificEvent.getValue(), claimList.size(), 0, 0); + + // Set received ids as extra data + change.setExtraData(received); + + // Success + return change.setSuccess(true); + } + public synchronized PlayerChangeInfo receiveStarTowerQuestReward(int questId) { // Get received quests var claimList = new ArrayList(); @@ -340,7 +498,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { quest.setClaimed(true); // Update in database - Nebula.getGameDatabase().update(this, this.getUid(), "list." + quest.getKey(), quest); + this.saveQuest(quest); } // Unlock newly available quests @@ -420,7 +578,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { quest.setClaimed(true); // Update in database - Nebula.getGameDatabase().update(this, this.getUid(), "list." + quest.getKey(), quest); + this.saveQuest(quest); } // Trigger quest @@ -541,7 +699,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { quest.setClaimed(true); // Update in database - Nebula.getGameDatabase().update(this, this.getUid(), "list." + quest.getKey(), quest); + this.saveQuest(quest); } // Trigger quest @@ -705,6 +863,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { // Add missing Star Tower quests and refresh their condition data // We do not need to save here because QuestManager::resetDailyQuests will save to the database this.initStarTowerQuests(); + this.initStarTowerBookEventQuests(); // Reset daily quests this.resetDailyQuests(true); @@ -713,9 +872,15 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject { shouldSave = false; } else { // Add missing Star Tower quests and refresh their condition data - if (this.initStarTowerQuests()) { + boolean changed = this.initStarTowerQuests(); + + if (this.initStarTowerBookEventQuests()) { + changed = true; + } + + if (changed) { shouldSave = true; - } + } } // Save to database diff --git a/src/main/java/emu/nebula/game/tower/StarTowerGame.java b/src/main/java/emu/nebula/game/tower/StarTowerGame.java index 02760a4..e59aa19 100644 --- a/src/main/java/emu/nebula/game/tower/StarTowerGame.java +++ b/src/main/java/emu/nebula/game/tower/StarTowerGame.java @@ -71,6 +71,7 @@ public class StarTowerGame { private int teamExp; private int nextLevelExp; private int charHp; + private int npcInteractions; private int battleTime; private int[] charIds; private int[] discIds; @@ -85,6 +86,9 @@ public class StarTowerGame { private ItemParamMap potentials; private IntSet secondarySkills; + // NPC + private ItemParamMap npcAffinity; + // Modifiers private StarTowerModifiers modifiers; @@ -167,6 +171,9 @@ public class StarTowerGame { // Temp data to cache for rare potential count this.rarePotentialCount = new ItemParamMap(); + // Cached npc affinity map + this.npcAffinity = new ItemParamMap(); + // Enter first room this.enterNextRoom(); this.getRoom().setMapInfo(req); @@ -258,6 +265,15 @@ public class StarTowerGame { return Utils.randomElement(this.getCharIds()); } + public StarTowerNpcData getNpc(int npcId) { + return getPlayer().getProgress().getStarTowerNpcById(npcId); + } + + public void onNpcAffinityIncrease(int npcId, int increase) { + this.npcInteractions++; + this.npcAffinity.add(npcId, increase); + } + public void setHp(int hp) { this.charHp = hp; } @@ -855,7 +871,8 @@ public class StarTowerGame { // Settle info var settle = rsp.getMutableSettle() .setTotalTime(this.getBattleTime()) - .setBuild(this.getBuild().toProto()); + .setBuild(this.getBuild().toProto()) + .addAllReward(this.getNpcAffinityRewards()); // Set empty change info settle.getMutableChange(); @@ -933,7 +950,7 @@ public class StarTowerGame { .setId(this.getId()) .setCharHp(this.getCharHp()) .setTeamLevel(this.getTeamLevel()) - .setNPCInteractions(0) + .setNPCInteractions(this.getNpcInteractions()) .setTotalTime(this.getBattleTime()) .setBuildId(this.getBuildId()); @@ -994,5 +1011,26 @@ public class StarTowerGame { return proto; } + + public NPCAffinityLevelReward[] getNpcAffinityRewards() { + var list = new ArrayList(); + + for (var entry : this.npcAffinity) { + int npcId = entry.getIntKey(); + int increase = entry.getIntValue(); + var npc = this.getNpc(npcId); + + var reward = NPCAffinityLevelReward.newInstance(); + + reward.getMutableChange() + .setNPCId(npcId) + .setAffinity(npc.getAffinity()) + .setIncrease(increase); + + list.add(reward); + } + + return list.toArray(new NPCAffinityLevelReward[list.size()]); + } } diff --git a/src/main/java/emu/nebula/game/tower/StarTowerManager.java b/src/main/java/emu/nebula/game/tower/StarTowerManager.java index 8d40a30..3ff2018 100644 --- a/src/main/java/emu/nebula/game/tower/StarTowerManager.java +++ b/src/main/java/emu/nebula/game/tower/StarTowerManager.java @@ -1,5 +1,6 @@ package emu.nebula.game.tower; +import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -171,6 +172,42 @@ public class StarTowerManager extends PlayerManager { return change; } + // NPC affinities + + public Collection getNpcAffinities() { + return this.getProgress().getTowerNpcs().values(); + } + + public PlayerChangeInfo receiveNpcAffinityPlotReward(int plotId) { + // Get plot data + var plot = GameData.getNpcAffinityPlotDataTable().get(plotId); + if (plot == null) { + return null; + } + + // Get npc + var npc = this.getProgress().getStarTowerNpcById(plot.getNPCId()); + + // Check if the plot can be claimed + if (npc.getLevel() < plot.getAffinityLevel() || npc.hasPlot(plotId)) { + return null; + } + + // Sanity check item + if (GameData.getItemDataTable().get(plot.getItemId()) == null) { + return null; + } + + // Add reward + var change = this.getPlayer().getInventory().addItem(plot.getItemId(), plot.getItemQty()); + + // Mark plot as received + npc.addPlot(plotId); + + // Success + return change; + } + // Builds public Long2ObjectMap getBuilds() { @@ -568,6 +605,11 @@ public class StarTowerManager extends PlayerManager { game.getDifficulty() ); + // Star Tower book event quests (anecdotes) + for (int charId : game.getCharIds()) { + this.getPlayer().trigger(QuestCondition.CharacterPassedWithSpecificTowerAndCount, 1, charId); + } + // Achievement conditions var achievements = this.getPlayer().getAchievementManager(); diff --git a/src/main/java/emu/nebula/game/tower/StarTowerNpcData.java b/src/main/java/emu/nebula/game/tower/StarTowerNpcData.java new file mode 100644 index 0000000..0f987d1 --- /dev/null +++ b/src/main/java/emu/nebula/game/tower/StarTowerNpcData.java @@ -0,0 +1,183 @@ +package emu.nebula.game.tower; + +import dev.morphia.annotations.Entity; +import emu.nebula.Nebula; +import emu.nebula.data.GameData; +import emu.nebula.data.resources.NpcAffinityGroupDef; +import emu.nebula.game.player.Player; +import emu.nebula.game.quest.QuestCondition; +import emu.nebula.proto.Public.NPCAffinity; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import lombok.Getter; + +@Getter +@Entity(useDiscriminator = false) +public class StarTowerNpcData { + private int id; + private int level; + private int affinity; + private IntSet plotIds; + + // Cached reference to the player + private transient Player player; + + @Deprecated // Morphia only + public StarTowerNpcData() { + + } + + public StarTowerNpcData(Player player, int id) { + this.id = id; + this.plotIds = new IntOpenHashSet(); + this.setPlayer(player); + this.save(); + } + + public void setPlayer(Player player) { + this.player = player; + + // Recalculate the level in case the npc was created before levels were stored + this.refreshLevel(); + } + + public IntSet getPlotIds() { + return this.plotIds; + } + + /** + * Gets the affinity data for this NPC at the specified level. + */ + private NpcAffinityGroupDef getAffinityGroupData(int level) { + var data = GameData.getStarTowerNpcDataTable().get(this.getId()); + if (data == null) { + return null; + } + + return GameData.getNpcAffinityGroupDataTable().get((data.getAffinityGroupId() * 100) + level); + } + + /** + * Calculates the NPC's affinity level from its current affinity. + */ + private int calculateLevel() { + int level = this.level; + var nextLevel = this.getAffinityGroupData(level + 1); + + // Level up while the player has enough affinity for the next level + while (nextLevel != null && nextLevel.getAffinityValue() > 0 && this.affinity >= nextLevel.getAffinityValue()) { + level++; + nextLevel = this.getAffinityGroupData(level + 1); + } + + return level; + } + + /** + * Updates the cached level from the NPC's current affinity. + * @return True if the level changed + */ + public boolean refreshLevel() { + int level = this.calculateLevel(); + + if (this.level == level) { + return false; + } + + this.level = level; + + return true; + } + + /** + * Returns the maximum amount of affinity this NPC can have. + */ + public int getMaxAffinity() { + int maxAffinity = 0; + int level = 0; + var data = this.getAffinityGroupData(level); + + // Only iterate the affinity levels for this NPC's group + while (data != null) { + maxAffinity = Math.max(maxAffinity, data.getAffinityValue()); + + level++; + data = this.getAffinityGroupData(level); + } + + return maxAffinity; + } + + public int addAffinity(int amount) { + if (amount <= 0) { + return 0; + } + + // Save old affinity + int oldAffinity = this.affinity; + int oldLevel = this.level; + + // Cap affinity at the maximum level for this npc + int newAffinity = this.affinity + amount; + int maxAffinity = this.getMaxAffinity(); + + if (maxAffinity > 0) { + newAffinity = Math.min(newAffinity, maxAffinity); + } + + this.affinity = newAffinity; + this.refreshLevel(); + + // Return changed affinity amount + int change = this.affinity - oldAffinity; + + if (change > 0 || this.level != oldLevel) { + this.save(); + } + + // Trigger affinity level quests for each level gained + if (this.level > oldLevel && this.player != null) { + this.player.trigger(QuestCondition.NpcAffinityWithSpecificLevel, this.level - oldLevel, this.getId()); + } + + return change; + } + + public boolean hasPlot(int plotId) { + return this.getPlotIds().contains(plotId); + } + + public boolean addPlot(int plotId) { + if (!this.getPlotIds().add(plotId)) { + return false; + } + + this.save(); + + return true; + } + + // Proto + + public NPCAffinity toProto() { + var proto = NPCAffinity.newInstance() + .setNPCId(this.getId()) + .setAffinity(this.getAffinity()); + + for (int plotId : this.getPlotIds()) { + proto.addPlotIds(plotId); + } + + return proto; + } + + // Database + + /** + * Saves this npc data to the database + */ + public void save() { + Nebula.getGameDatabase().update(player.getProgress(), player.getUid(), "towerNpcs." + this.getId(), this, true); + } + +} diff --git a/src/main/java/emu/nebula/game/tower/cases/StarTowerNpcEventCase.java b/src/main/java/emu/nebula/game/tower/cases/StarTowerNpcEventCase.java index 030205c..976ab91 100644 --- a/src/main/java/emu/nebula/game/tower/cases/StarTowerNpcEventCase.java +++ b/src/main/java/emu/nebula/game/tower/cases/StarTowerNpcEventCase.java @@ -6,6 +6,7 @@ import emu.nebula.GameConstants; import emu.nebula.data.resources.StarTowerEventDef; import emu.nebula.game.achievement.AchievementCondition; import emu.nebula.game.player.PlayerChangeInfo; +import emu.nebula.proto.PublicStarTower.NPCAffinityChange; import emu.nebula.proto.PublicStarTower.NPCAffinityInfo; import emu.nebula.proto.PublicStarTower.StarTowerRoomCase; import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq; @@ -268,9 +269,25 @@ public class StarTowerNpcEventCase extends StarTowerBaseCase { success.setOptionsResult(completed); this.completed = completed; - // Achievment if (completed) { + // Trigger achievement this.getGame().getAchievementManager().trigger(AchievementCondition.TowerEventTimes, 1); + + // Add npc affinity if the option succeeded + // We skip the weekly limit on this server + var npc = this.getGame().getNpc(npcId); + int affinityIncrease = npc.addAffinity(100); + + // Increment star tower's tracked npc interactions + this.getGame().onNpcAffinityIncrease(npcId, affinityIncrease); + + // Create affinity proto + var affinityInfo = NPCAffinityChange.newInstance() + .setNPCId(npc.getId()) + .setAffinity(npc.getAffinity()) + .setIncrease(affinityIncrease); + + success.addAffinityChange(affinityInfo); } // Complete @@ -331,9 +348,11 @@ public class StarTowerNpcEventCase extends StarTowerBaseCase { @Override public void encodeProto(StarTowerRoomCase proto) { + var npc = this.getGame().getNpc(npcId); + var info = NPCAffinityInfo.newInstance() .setNPCId(this.getNpcId()) - .setAffinity(0); + .setAffinity(npc.getAffinity()); proto.getMutableSelectOptionsEventCase() .setEvtId(this.getEventId()) diff --git a/src/main/java/emu/nebula/server/handlers/HandlerNpcAffinityBookGetReq.java b/src/main/java/emu/nebula/server/handlers/HandlerNpcAffinityBookGetReq.java index bd5c191..cd37248 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerNpcAffinityBookGetReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerNpcAffinityBookGetReq.java @@ -11,9 +11,15 @@ public class HandlerNpcAffinityBookGetReq extends NetHandler { @Override public byte[] handle(GameSession session, byte[] message) throws Exception { + var player = session.getPlayer(); + // Build response var rsp = NPCAffinityBookGetResp.newInstance(); + for (var npc : player.getStarTowerManager().getNpcAffinities()) { + rsp.addInfos(npc.toProto()); + } + // Encode and send return session.encodeMsg(NetMsgId.npc_affinity_book_get_succeed_ack, rsp); } diff --git a/src/main/java/emu/nebula/server/handlers/HandlerNpcAffinityPlotRewardReceiveReq.java b/src/main/java/emu/nebula/server/handlers/HandlerNpcAffinityPlotRewardReceiveReq.java new file mode 100644 index 0000000..26abe8e --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerNpcAffinityPlotRewardReceiveReq.java @@ -0,0 +1,45 @@ +package emu.nebula.server.handlers; + +import emu.nebula.data.GameData; +import emu.nebula.net.GameSession; +import emu.nebula.net.HandlerId; +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.NpcAffinityPlotRewardReceive.NPCAffinityPlotRewardReceiveResp; +import emu.nebula.proto.Public.ItemTpl; +import emu.nebula.proto.Public.UI32; + +@HandlerId(NetMsgId.npc_affinity_plot_reward_receive_req) +public class HandlerNpcAffinityPlotRewardReceiveReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = UI32.parseFrom(message); + int plotId = req.getValue(); + + // Get plot data + var plot = GameData.getNpcAffinityPlotDataTable().get(plotId); + if (plot == null) { + return session.encodeMsg(NetMsgId.npc_affinity_plot_reward_receive_failed_ack); + } + + // Claim reward + var change = session.getPlayer().getStarTowerManager().receiveNpcAffinityPlotReward(plotId); + + if (change == null) { + return session.encodeMsg(NetMsgId.npc_affinity_plot_reward_receive_failed_ack); + } + + // Build response + var rsp = NPCAffinityPlotRewardReceiveResp.newInstance() + .setChange(change.toProto()) + .addShow(ItemTpl.newInstance() + .setTid(plot.getItemId()) + .setQty(plot.getItemQty())); + + // Encode and send + return session.encodeMsg(NetMsgId.npc_affinity_plot_reward_receive_succeed_ack, rsp); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerStarTowerBookEventRewardReceiveReq.java b/src/main/java/emu/nebula/server/handlers/HandlerStarTowerBookEventRewardReceiveReq.java new file mode 100644 index 0000000..fa572d8 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerStarTowerBookEventRewardReceiveReq.java @@ -0,0 +1,38 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.GameSession; +import emu.nebula.net.HandlerId; +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.Public.UI32; +import emu.nebula.proto.StarTowerBookEventRewardReceive.StarTowerBookEventRewardReceiveResp; +import it.unimi.dsi.fastutil.ints.IntList; + +@HandlerId(NetMsgId.star_tower_book_event_reward_receive_req) +public class HandlerStarTowerBookEventRewardReceiveReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = UI32.parseFrom(message); + + // Claim reward(s) + var change = session.getPlayer().getQuestManager().receiveStarTowerBookEventReward(req.getValue()); + + if (change == null) { + return session.encodeMsg(NetMsgId.star_tower_book_event_reward_receive_failed_ack); + } + + // Build response + var rsp = StarTowerBookEventRewardReceiveResp.newInstance() + .setChange(change.toProto()); + + // Add list of received reward ids to response + var received = (IntList) change.getExtraData(); + received.forEach(rsp::addReceivedIds); + + // Encode and send + return session.encodeMsg(NetMsgId.star_tower_book_event_reward_receive_succeed_ack, rsp); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerStarTowerGiveUpReq.java b/src/main/java/emu/nebula/server/handlers/HandlerStarTowerGiveUpReq.java index cd003fd..bb91cd7 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerStarTowerGiveUpReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerStarTowerGiveUpReq.java @@ -19,9 +19,11 @@ public class HandlerStarTowerGiveUpReq extends NetHandler { // Build response var rsp = StarTowerGiveUpResp.newInstance() + .setNpcInteraction(game.getNpcInteractions()) .setBuild(game.getBuild().toProto()) .setPotentialCnt(game.getTotalPotentialCount()) - .setFloor(game.getFloorCount()); + .setFloor(game.getFloorCount()) + .addAllReward(game.getNpcAffinityRewards()); rsp.getMutableChange();