Skip to content

Commit

Permalink
Implement quest pool unlocking 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSzabo committed Aug 6, 2024
1 parent 6f03bb7 commit 4aeec61
Show file tree
Hide file tree
Showing 16 changed files with 162 additions and 13 deletions.
33 changes: 32 additions & 1 deletion src/main/java/gg/auroramc/quests/AuroraQuests.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
import gg.auroramc.quests.hooks.HookManager;
import gg.auroramc.quests.listener.*;
import gg.auroramc.quests.placeholder.QuestPlaceholderHandler;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

import java.util.concurrent.TimeUnit;

@Getter
public class AuroraQuests extends JavaPlugin {
@Getter
Expand All @@ -28,6 +31,7 @@ public static AuroraLogger logger() {
private ConfigManager configManager;
private CommandManager commandManager;
private QuestManager questManager;
private ScheduledTask unlockTask;

@Override
public void onLoad() {
Expand Down Expand Up @@ -70,27 +74,35 @@ public void onEnable() {
questManager.reload();

Bukkit.getPluginManager().registerEvents(new PlayerListener(this), this);
reloadUnlockTask();
}

public void reload() {
configManager.reload();
commandManager.reload();
questManager.reload();
reloadUnlockTask();
Bukkit.getOnlinePlayers().forEach(player -> {
questManager.rollQuestsIfNecessary(player);
questManager.tryUnlockQuestPools(player);
questManager.tryStartGlobalQuests(player);
questManager.rollQuestsIfNecessary(player);
});
}

@Override
public void onDisable() {
commandManager.unregisterCommands();

try {
l.info("Shutting down scheduler...");
StdSchedulerFactory.getDefaultScheduler().shutdown(true);
} catch (SchedulerException e) {
l.severe("Failed to shutdown scheduler: " + e.getMessage());
}

if (unlockTask != null && !unlockTask.isCancelled()) {
unlockTask.cancel();
}
}

private void registerListeners() {
Expand All @@ -115,4 +127,23 @@ private void registerListeners() {
pm.registerEvents(new SmeltingListener(), this);
pm.registerEvents(new TamingListener(), this);
}

private void reloadUnlockTask() {
var cf = configManager.getConfig().getUnlockTask();

if (!cf.getEnabled()) {
if (unlockTask != null && !unlockTask.isCancelled()) {
unlockTask.cancel();
unlockTask = null;
}
return;
}

unlockTask = Bukkit.getAsyncScheduler().runAtFixedRate(this, (task) -> {
Bukkit.getOnlinePlayers().forEach(player -> {
questManager.tryUnlockQuestPools(player);
questManager.tryStartGlobalQuests(player);
});
}, cf.getInterval(), cf.getInterval(), TimeUnit.SECONDS);
}
}
17 changes: 16 additions & 1 deletion src/main/java/gg/auroramc/quests/api/data/QuestData.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,21 @@ public class QuestData extends UserDataHolder {
private final Map<String, Set<String>> completedQuests = Maps.newConcurrentMap();
private final Map<String, Long> completedCount = Maps.newConcurrentMap();
private final Map<String, Set<String>> questUnlocks = Maps.newConcurrentMap();
private final Set<String> poolUnlocks = Sets.newConcurrentHashSet();

public PoolRollData getPoolRollData(String poolId) {
return rolledQuests.get(poolId);
}

public void unlockPool(String poolId) {
poolUnlocks.add(poolId);
dirty.set(true);
}

public boolean isPoolUnlocked(String poolId) {
return poolUnlocks.contains(poolId);
}

public void setRolledQuests(String poolId, List<String> quests) {
rolledQuests.put(poolId, new PoolRollData(System.currentTimeMillis(), quests));
completedQuests.computeIfAbsent(poolId, k -> Sets.newConcurrentHashSet()).clear();
Expand Down Expand Up @@ -104,7 +114,7 @@ public void serializeInto(ConfigurationSection data) {
for (var questEntry : poolEntry.getValue().entrySet()) {
var questSection = poolSection.createSection(questEntry.getKey());
for (var taskEntry : questEntry.getValue().entrySet()) {
if(taskEntry.getValue() > 0) {
if (taskEntry.getValue() > 0) {
questSection.set(taskEntry.getKey(), taskEntry.getValue());
}
}
Expand All @@ -118,6 +128,9 @@ public void serializeInto(ConfigurationSection data) {
}
}

// Pool unlocks
data.set("pool_unlocks", poolUnlocks.stream().toList());

// Completed quests
for (var poolEntry : completedQuests.entrySet()) {
for (var questEntry : poolEntry.getValue()) {
Expand Down Expand Up @@ -174,5 +187,7 @@ public void initFrom(@Nullable ConfigurationSection data) {
completedCount.put(key, completedCountSection.getLong(key));
}
}

poolUnlocks.addAll(data.getStringList("pool_unlocks"));
}
}
11 changes: 6 additions & 5 deletions src/main/java/gg/auroramc/quests/api/quest/Quest.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ public String getId() {
public void progress(Player player, String taskType, double count, Map<String, Object> params) {
if (!taskTypes.contains(taskType)) return;
if (isCompleted(player)) return;
if (!canStart(player)) return;
if (!holder.isUnlocked(player)) return;
if (!isUnlocked(player)) return;

for (var task : tasks.values()) {
if (task.getTaskType().equals(taskType)) {
task.progress(player, count, params);
Expand Down Expand Up @@ -111,16 +113,15 @@ private boolean hasStartRequirements() {

public boolean isUnlocked(Player player) {
var data = AuroraAPI.getUserManager().getUser(player).getData(QuestData.class);
return config.getStartRequirements() == null || data.isQuestStartUnlocked(holder.getId(), getId());
return data.isQuestStartUnlocked(holder.getId(), getId()) || !hasStartRequirements();
}

public void tryStart(Player player) {
if (!holder.isGlobal()) return;
if (!hasStartRequirements()) return;
if (isUnlocked(player)) return;
var data = AuroraAPI.getUserManager().getUser(player).getData(QuestData.class);
if (data.isQuestStartUnlocked(holder.getId(), getId())) return;

if (canStart(player) && config.getStartRequirements() != null) {
if (config.getStartRequirements() != null && canStart(player)) {
data.setQuestStartUnlock(holder.getId(), getId());
var msg = AuroraQuests.getInstance().getConfigManager().getMessageConfig().getGlobalQuestUnlocked();
Chat.sendMessage(player, msg, Placeholder.of("{quest}", config.getName()), Placeholder.of("{pool}", holder.getConfig().getName()));
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/gg/auroramc/quests/api/quest/QuestManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ private void actuallyProgress(Player player, String taskType, double amount, Map
synchronized (getPlayerLock(player)) {
for (var pool : pools.values()) {
if (!pool.hasTaskType(taskType)) continue;
if (!pool.isUnlocked(player)) continue;
for (var quest : pool.getNotCompletedPlayerQuests(player)) {
quest.progress(player, taskType, amount, params);
}
Expand Down Expand Up @@ -113,4 +114,10 @@ public void tryStartGlobalQuests(Player player) {
}
}
}

public void tryUnlockQuestPools(Player player) {
for (var pool : pools.values()) {
pool.tryUnlock(player);
}
}
}
52 changes: 52 additions & 0 deletions src/main/java/gg/auroramc/quests/api/quest/QuestPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import gg.auroramc.aurora.api.AuroraAPI;
import gg.auroramc.aurora.api.item.TypeId;
import gg.auroramc.aurora.api.levels.MatcherManager;
import gg.auroramc.aurora.api.message.Chat;
import gg.auroramc.aurora.api.message.Placeholder;
Expand Down Expand Up @@ -54,6 +55,55 @@ public QuestPool(PoolConfig config, RewardFactory rewardFactory) {
AuroraQuests.logger().debug("Loaded difficulties for pool " + config.getId() + ": " + String.join(", ", config.getDifficulties().keySet()));
}

private boolean canUnlock(Player player) {
var data = AuroraAPI.getUserManager().getUser(player).getData(QuestData.class);

if (config.getUnlockRequirements() == null) return true;

if (config.getUnlockRequirements().getQuests() != null) {
for (var questId : config.getUnlockRequirements().getQuests()) {
var typeId = TypeId.fromString(questId);
var pool = typeId.namespace().equals("minecraft") ? getId() : typeId.namespace();
if (!data.hasCompletedQuest(pool, typeId.id())) {
return false;
}
}
}

if (config.getUnlockRequirements().getPermissions() != null) {
for (var perm : config.getUnlockRequirements().getPermissions()) {
if (!player.hasPermission(perm)) {
return false;
}
}
}

return true;
}

private boolean hasStartRequirements() {
return config.getUnlockRequirements() != null && ((config.getUnlockRequirements().getQuests() != null && !config.getUnlockRequirements().getQuests().isEmpty()) ||
(config.getUnlockRequirements().getPermissions() != null && !config.getUnlockRequirements().getPermissions().isEmpty()));
}

public boolean isUnlocked(Player player) {
var data = AuroraAPI.getUserManager().getUser(player).getData(QuestData.class);
return !hasStartRequirements() || data.isPoolUnlocked(getId());
}

public void tryUnlock(Player player) {
if (isUnlocked(player)) return;
var data = getQuestData(player);

if (canUnlock(player)) {
data.unlockPool(getId());
var msg = AuroraQuests.getInstance().getConfigManager().getMessageConfig().getPoolUnlocked();
Chat.sendMessage(player, msg, Placeholder.of("{pool}", config.getName()));
tryStartGlobalQuests(player);
reRollQuests(player, false);
}
}

public boolean hasTaskType(String taskType) {
return taskTypes.contains(taskType);
}
Expand Down Expand Up @@ -177,6 +227,7 @@ public Collection<Quest> getQuests() {

public void tryStartGlobalQuests(Player player) {
if (!isGlobal()) return;
if (!isUnlocked(player)) return;
for (var quest : quests.values()) {
quest.tryStart(player);
}
Expand All @@ -186,6 +237,7 @@ public boolean rollIfNecessary(Player player, boolean sendNotification) {
AuroraQuests.logger().debug("Checking if player " + player.getName() + " needs to reroll quests for pool " + config.getId());
if (!isTimedRandom()) return false;
if (!questRoller.isValid()) return false;
if (!isUnlocked(player)) return false;
AuroraQuests.logger().debug("Pool is timed random and quest roller is valid");

try {
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/gg/auroramc/quests/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class Config extends AuroraConfig {
private LevelUpMessage questCompleteMessage;
private CommandAliasConfig commandAliases;
private List<String> sortOrder;
private UnlockTaskConfig unlockTask = new UnlockTaskConfig();

@IgnoreField
private Map<String, Integer> sortOderMap;
Expand Down Expand Up @@ -55,6 +56,12 @@ public static final class DisplayComponent {
private String line;
}

@Getter
public static final class UnlockTaskConfig {
private Boolean enabled = false;
private Integer interval = 5;
}

@Getter
public static final class LevelUpMessage {
private Boolean enabled;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/gg/auroramc/quests/config/MessageConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class MessageConfig extends AuroraConfig {
private String reRolledSource = "&aQuests for {player} for pool {pool} have been re-rolled!";
private String globalQuestUnlocked = "&aYou have unlocked the {quest} quest in {pool}!";
private String poolNotFound = "&cThere isn't any quest line with this id: {pool}!";
private String poolUnlocked = "&aYou have unlocked a new quest pool: {pool}!";

public MessageConfig(AuroraQuests plugin, String language) {
super(getFile(plugin, language));
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/gg/auroramc/quests/config/quest/PoolConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class PoolConfig extends AuroraConfig {
private PoolMenuItem menuItem;
private PoolMenu menu;
private Leveling leveling;
private StartRequirementConfig unlockRequirements;

@Setter
@IgnoreField
Expand All @@ -37,6 +38,7 @@ public static class PoolMenuItem {
private Boolean showInMainMenu;
private Integer page;
private ItemConfig item;
private List<String> lockedLore;
}

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ public class LuckPermsHook implements Hook {
public void hook(AuroraQuests plugin) {
plugin.getQuestManager().getRewardFactory()
.registerRewardType(NamespacedId.fromDefault("permission"), PermissionReward.class);

plugin.getQuestManager().getRewardAutoCorrector()
.registerCorrector(NamespacedId.fromDefault("permission"), new PermissionCorrector());

var lp = LuckPermsProvider.get();

lp.getEventBus().subscribe(UserDataRecalculateEvent.class, (event) -> {
var player = Bukkit.getPlayer(event.getUser().getUniqueId());
if (player != null) plugin.getQuestManager().tryStartGlobalQuests(player);
if (player != null) {
plugin.getQuestManager().tryUnlockQuestPools(player);
plugin.getQuestManager().tryStartGlobalQuests(player);
}
});

AuroraQuests.logger().info("Hooked into LuckPerms for permission rewards and for permission start requirements");
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/gg/auroramc/quests/listener/PlayerListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ public void onPlayerQuit(PlayerQuitEvent event) {
}

private void roll(Player player) {
plugin.getQuestManager().tryUnlockQuestPools(player);
plugin.getQuestManager().tryStartGlobalQuests(player);

var pools = plugin.getQuestManager().rollQuestsIfNecessary(player);

if (pools.isEmpty()) return;
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/gg/auroramc/quests/menu/MainMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ private AuroraMenu createMenu() {
});

var pools = AuroraQuests.getInstance().getQuestManager().getQuestPools();
var maybeInt = pools.stream().filter(pool -> pool.getConfig().getMenuItem().getShowInMainMenu())
var maybeInt = pools.stream()
.filter(pool -> pool.getConfig().getMenuItem().getShowInMainMenu())
.filter(pool -> pool.isUnlocked(player) || pool.getConfig().getUnlockRequirements().isAlwaysShowInMenu())
.mapToInt(pool -> pool.getConfig().getMenuItem().getPage()).max();

if (maybeInt.isPresent()) {
Expand All @@ -61,6 +63,7 @@ private AuroraMenu createMenu() {


for (var pool : pools) {
if (!pool.isUnlocked(player) && !pool.getConfig().getUnlockRequirements().isAlwaysShowInMenu()) continue;
var mi = pool.getConfig().getMenuItem();
if (!mi.getShowInMainMenu()) continue;
if (mi.getPage() != page) continue;
Expand All @@ -85,12 +88,21 @@ private AuroraMenu createMenu() {
AuroraAPI.formatNumber(Math.max(Bukkit.getOnlinePlayers().size(), AuroraAPI.getLeaderboards().getBoardSize(boardName)))));
}

var lore = new ArrayList<>(mi.getItem().getLore());

if (!pool.isUnlocked(player)) {
lore.addAll(mi.getLockedLore());
}

menu.addItem(ItemBuilder.of(mi.getItem())
.setLore(lore)
.placeholder(Placeholder.of("{name}", pool.getConfig().getName()))
.placeholder(Placeholder.of("{total_completed}", AuroraAPI.formatNumber(pool.getCompletedQuestCount(player))))
.placeholder(placeholders)
.build(player), (e) -> {
new PoolMenu(player, pool).open();
if (pool.isUnlocked(player)) {
new PoolMenu(player, pool).open();
}
});
}

Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ debug: false
language: en
prevent-creative-mode: true

# Timer to try to unlock global quests and quest pools if for some reason the event driven method doesn't work
unlock-task:
enabled: false
# Interval in seconds
interval: 5

command-aliases:
quests: [ "quests", "quest" ]

Expand Down
Loading

0 comments on commit 4aeec61

Please sign in to comment.