164 lines
4.3 KiB
JavaScript
164 lines
4.3 KiB
JavaScript
const DatapackLoader = require("./DatapackLoader");
|
|
const { PlayerQuest, Player, Inventory, sequelize } = require("../models");
|
|
|
|
class QuestsManager {
|
|
async onPlayerLogin(playerId, socket = null) {
|
|
try {
|
|
await this.checkAutoQuests(playerId, socket);
|
|
await this.trackProgress(playerId, "LOGIN", null, 1, socket);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
async checkAutoQuests(playerId, socket = null) {
|
|
const allQuests = Array.from(DatapackLoader.registry.quests);
|
|
const player = await Player.findByPk(playerId);
|
|
|
|
for (const quest of allQuests) {
|
|
if (quest.meta?.autoAccept && player.level >= (quest.minLevel || 0)) {
|
|
const [pq, created] = await PlayerQuest.findOrCreate({
|
|
where: { playerId, questId: quest.id },
|
|
defaults: {
|
|
status: "active",
|
|
progress: quest.objectives.map((obj) => ({
|
|
...obj,
|
|
currentAmount: 0,
|
|
})),
|
|
},
|
|
});
|
|
|
|
if (created && socket) {
|
|
socket.emit("quest:new", {
|
|
id: pq.questId,
|
|
status: pq.status,
|
|
objectives: pq.progress,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async trackProgress(playerId, type, targetId, amount = 1, socket = null) {
|
|
try {
|
|
const activeQuests = await PlayerQuest.findAll({
|
|
where: { playerId, status: "active" },
|
|
});
|
|
|
|
for (const pq of activeQuests) {
|
|
const staticData = DatapackLoader.getQuest(pq.questId);
|
|
if (!staticData) continue;
|
|
|
|
let isChanged = false;
|
|
const currentProgress = pq.progress;
|
|
|
|
const updatedProgress = currentProgress.map((obj) => {
|
|
if (
|
|
obj.type === type &&
|
|
(obj.targetId === targetId || type === "LOGIN")
|
|
) {
|
|
if (obj.currentAmount < obj.requiredAmount) {
|
|
obj.currentAmount = Math.min(
|
|
obj.currentAmount + amount,
|
|
obj.requiredAmount,
|
|
);
|
|
isChanged = true;
|
|
}
|
|
}
|
|
return obj;
|
|
});
|
|
|
|
if (isChanged) {
|
|
const isReady = updatedProgress.every(
|
|
(obj) => obj.currentAmount >= obj.requiredAmount,
|
|
);
|
|
|
|
await pq.update({
|
|
progress: updatedProgress,
|
|
status: isReady ? "ready" : "active",
|
|
});
|
|
|
|
if (socket) {
|
|
socket.emit("quest:update", {
|
|
id: pq.questId,
|
|
status: isReady ? "ready" : "active",
|
|
objectives: updatedProgress,
|
|
});
|
|
}
|
|
|
|
if (isReady && staticData.meta?.autoComplete) {
|
|
await this.claimRewards(playerId, pq.questId);
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
async claimRewards(playerId, questId) {
|
|
const t = await sequelize.transaction();
|
|
|
|
try {
|
|
const pq = await PlayerQuest.findOne({
|
|
where: { playerId, questId, status: "ready" },
|
|
transaction: t,
|
|
lock: t.LOCK.UPDATE,
|
|
});
|
|
|
|
if (!pq) {
|
|
await t.rollback();
|
|
throw new Error("QUEST_NOT_READY_OR_CLAIMED");
|
|
}
|
|
|
|
const staticData = DatapackLoader.getQuest(questId);
|
|
const player = await Player.findByPk(playerId, { transaction: t });
|
|
const rewards = staticData.rewards;
|
|
|
|
await pq.update({ status: "completed" }, { transaction: t });
|
|
|
|
if (rewards.credits) {
|
|
await player.increment("credits", {
|
|
by: rewards.credits,
|
|
transaction: t,
|
|
});
|
|
}
|
|
|
|
if (rewards.xp) {
|
|
await player.increment("experience", {
|
|
by: rewards.xp,
|
|
transaction: t,
|
|
});
|
|
}
|
|
|
|
if (rewards.items?.length > 0) {
|
|
for (const item of rewards.items) {
|
|
const [invItem] = await Inventory.findOrCreate({
|
|
where: { playerId, itemId: item.id },
|
|
defaults: { quantity: 0 },
|
|
transaction: t,
|
|
});
|
|
await invItem.increment("quantity", {
|
|
by: item.count,
|
|
transaction: t,
|
|
});
|
|
}
|
|
}
|
|
|
|
await t.commit();
|
|
const updatedPlayer = await player.reload();
|
|
|
|
return {
|
|
success: true,
|
|
rewards,
|
|
newTotalCredits: updatedPlayer.credits,
|
|
};
|
|
} catch (err) {
|
|
if (t) await t.rollback();
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new QuestsManager();
|