new experimental quest view

This commit is contained in:
2025-05-08 21:08:03 +02:00
parent 3c79074c4c
commit 20567be96d
6 changed files with 147 additions and 169 deletions
+52 -74
View File
@@ -100,41 +100,29 @@ export default defineComponent({
allAdventurers: {} as { [key: string]: Adventurer }, allAdventurers: {} as { [key: string]: Adventurer },
adventurers: {} as { [key: string]: Adventurer }, adventurers: {} as { [key: string]: Adventurer },
quests: {} as { [key: string]: { [key: string]: Quest } }, quests: {} as { [key: string]: { [key: string]: Quest } },
missives: { missives: [] as Array<Quest>,
F: {} as { [key: string]: Quest },
E: {} as { [key: string]: Quest },
D: {} as { [key: string]: Quest },
C: {} as { [key: string]: Quest },
B: {} as { [key: string]: Quest },
A: {} as { [key: string]: Quest },
S: {} as { [key: string]: Quest },
} as { [key: string]: { [key: string]: Quest } },
}), }),
methods: { methods: {
async updateMissives() { async updateMissives() {
for (const missiveRank in this.missives) { for (const missive of this.missives) {
const rank = getFromString(missiveRank as QuestRank);
for (const missiveId in this.missives[rank.toString() as QuestRank]) {
const missive = this.missives[rank.toString()][missiveId];
if (missive.adventurers.length <= 0) { if (missive.adventurers.length <= 0) {
missive.progressPoints = 0; missive.progressPoints = 0;
continue; continue;
} }
if (missive.progressPoints >= missive.maxProgress) {
if (this.guild.autoFinishQuestsUpgrade.getRanksToAutoFinishQuestsIn().includes(rank)) {
this.finalizeQuest(missive);
continue;
}
continue;
}
for (const adventurerId in missive.adventurers) { for (const adventurerId in missive.adventurers) {
const adventurer = missive.adventurers[adventurerId]; const adventurer = missive.adventurers[adventurerId];
const attack = adventurer.getAttack(); const attack = adventurer.getAttack();
missive.progressPoints = Math.min(missive.progressPoints + attack, missive.maxProgress); missive.progressPoints = Math.min(missive.progressPoints + attack, missive.maxProgress);
} }
if (
missive.progressPoints >= missive.maxProgress
&& this.guild.autoFinishQuestsUpgrade.getRanksToAutoFinishQuestsIn().includes(missive.rank)
) {
this.finalizeQuest(missive);
} }
} }
}, },
async checkForNewRecruit(currentTimestamp: number) { async checkForNewRecruit(currentTimestamp: number) {
@@ -173,7 +161,10 @@ export default defineComponent({
adventurer.busy = false; adventurer.busy = false;
} }
missive.adventurers = []; missive.adventurers = [];
delete this.missives[missive.rank.toString() as QuestRank][missive.id]; const missiveIndex = this.missives.indexOf(missive);
if (missiveIndex > -1) {
this.missives.splice(missiveIndex, 1);
}
}, },
getRandomQuest(rank: QuestRank): Quest | null { getRandomQuest(rank: QuestRank): Quest | null {
const keys = Object.keys(this.quests[rank]); const keys = Object.keys(this.quests[rank]);
@@ -183,11 +174,10 @@ export default defineComponent({
const randomIdString = keys[randomId] as string; const randomIdString = keys[randomId] as string;
return getQuestWithRewards(questsForRank[randomIdString], this.guild.expModifier.getModifier(), this.guild.goldModifier.getModifier()); return getQuestWithRewards(questsForRank[randomIdString], this.guild.expModifier.getModifier(), this.guild.goldModifier.getModifier());
}, },
createMissive(questToAdd: Quest, rank: QuestRank) { createMissive(questToAdd: Quest) {
const quest = JSON.parse(JSON.stringify(questToAdd)); const quest = JSON.parse(JSON.stringify(questToAdd));
const newId = Math.random().toString(16).slice(2).toString(); quest.id = Math.random().toString(16).slice(2).toString();
quest.id = newId; this.missives.push(quest);
this.missives[rank][newId] = quest;
}, },
loadGame() { loadGame() {
const saveData = loadGame(); const saveData = loadGame();
@@ -239,23 +229,20 @@ export default defineComponent({
} }
this.adventurers = adventurers; this.adventurers = adventurers;
const missives = {} as { [key: string]: { [key: string]: Quest } }; if (Array.isArray(saveData.missives)) {
for (const data of saveData.missives) {
for (const id in saveData.missives) { const quest = new Quest(data.id, getFromString(data.rank), data.title, data.text, data.maxProgress, data.expReward, data.goldReward);
const missiveRank = {} as { [key: string]: Quest }
for (const questId in saveData.missives[id]) {
const data = saveData.missives[id][questId];
const quest = new Quest(questId, getFromString(data.rank), data.title, data.text, data.maxProgress, data.expReward, data.goldReward);
quest.progressPoints = data.progressPoints; quest.progressPoints = data.progressPoints;
if (data.adventurers.length > 0) { if (data.adventurers.length > 0) {
quest.adventurers.push(this.adventurers[data.adventurers[0].id]) for (const adventurer of data.adventurers) {
const adventurerId = adventurer.id;
if (this.adventurers[adventurerId] == null) continue;
quest.adventurers.push(this.adventurers[adventurerId]);
} }
missiveRank[questId] = quest;
} }
missives[id] = missiveRank; this.missives.push(quest)
}
} }
this.missives = missives;
this.lastRecruitHandled = saveData.lastRecruitAction ?? 0; this.lastRecruitHandled = saveData.lastRecruitAction ?? 0;
@@ -284,7 +271,8 @@ export default defineComponent({
try { try {
this.screenWakeLock = await navigator.wakeLock.request("screen"); this.screenWakeLock = await navigator.wakeLock.request("screen");
console.debug("Screen wake lock acquired"); console.debug("Screen wake lock acquired");
} catch (e) {} } catch (e) {
}
}, 1000); }, 1000);
console.debug("Loading game data") console.debug("Loading game data")
@@ -326,7 +314,6 @@ export default defineComponent({
})); }));
}, 10 * 1000)); }, 10 * 1000));
this.gameTickTask = Number(setInterval(() => { this.gameTickTask = Number(setInterval(() => {
this.updateMissives(); this.updateMissives();
@@ -341,78 +328,64 @@ export default defineComponent({
if (Number(now) - Number(this.lastQuestGot.F) >= 12 * 1000) { if (Number(now) - Number(this.lastQuestGot.F) >= 12 * 1000) {
this.lastQuestGot.F = now; this.lastQuestGot.F = now;
const keys = Object.keys(this.missives[QuestRank.F]); const currentQuestAmount = this.missives.filter(quest => quest.rank === QuestRank.F).length;
if (keys.length >= 5) return; if (currentQuestAmount >= 5) return;
const quest = this.getRandomQuest(QuestRank.F); const quest = this.getRandomQuest(QuestRank.F);
if (quest !== null) { if (quest !== null) this.createMissive(quest);
this.createMissive(quest, QuestRank.F);
}
} }
if (this.guild.level < 2) return; if (this.guild.level < 2) return;
if (Number(now) - Number(this.lastQuestGot.E) >= 20 * 1000) { if (Number(now) - Number(this.lastQuestGot.E) >= 20 * 1000) {
this.lastQuestGot.E = now; this.lastQuestGot.E = now;
const keys = Object.keys(this.missives[QuestRank.E]); const currentQuestAmount = this.missives.filter(quest => quest.rank === QuestRank.E).length;
if (keys.length >= 5) return; if (currentQuestAmount >= 5) return;
const quest = this.getRandomQuest(QuestRank.E); const quest = this.getRandomQuest(QuestRank.E);
if (quest !== null) { if (quest !== null) this.createMissive(quest);
this.createMissive(quest, QuestRank.E);
}
} }
if (this.guild.level < 3) return; if (this.guild.level < 3) return;
if (Number(now) - Number(this.lastQuestGot.D) >= 50 * 1000) { if (Number(now) - Number(this.lastQuestGot.D) >= 50 * 1000) {
this.lastQuestGot.D = now; this.lastQuestGot.D = now;
const keys = Object.keys(this.missives[QuestRank.D]); const currentQuestAmount = this.missives.filter(quest => quest.rank === QuestRank.D).length;
if (keys.length >= 5) return; if (currentQuestAmount >= 5) return;
const quest = this.getRandomQuest(QuestRank.D); const quest = this.getRandomQuest(QuestRank.D);
if (quest !== null) { if (quest !== null) this.createMissive(quest);
this.createMissive(quest, QuestRank.D);
}
} }
if (this.guild.level < 4) return; if (this.guild.level < 4) return;
if (Number(now) - Number(this.lastQuestGot.C) >= 2 * 60 * 1000) { if (Number(now) - Number(this.lastQuestGot.C) >= 2 * 60 * 1000) {
this.lastQuestGot.C = now; this.lastQuestGot.C = now;
const keys = Object.keys(this.missives[QuestRank.C]); const currentQuestAmount = this.missives.filter(quest => quest.rank === QuestRank.C).length;
if (keys.length >= 5) return; if (currentQuestAmount >= 5) return;
const quest = this.getRandomQuest(QuestRank.C); const quest = this.getRandomQuest(QuestRank.C);
if (quest !== null) { if (quest !== null) this.createMissive(quest);
this.createMissive(quest, QuestRank.C);
}
} }
if (this.guild.level < 5) return; if (this.guild.level < 5) return;
if (Number(now) - Number(this.lastQuestGot.B) >= 2 * 60 * 1000) { if (Number(now) - Number(this.lastQuestGot.B) >= 2 * 60 * 1000) {
this.lastQuestGot.B = now; this.lastQuestGot.B = now;
const keys = Object.keys(this.missives[QuestRank.B]); const currentQuestAmount = this.missives.filter(quest => quest.rank === QuestRank.B).length;
if (keys.length >= 5) return; if (currentQuestAmount >= 5) return;
const quest = this.getRandomQuest(QuestRank.B); const quest = this.getRandomQuest(QuestRank.B);
if (quest !== null) { if (quest !== null) this.createMissive(quest);
this.createMissive(quest, QuestRank.B);
}
} }
if (this.guild.level < 6) return; if (this.guild.level < 6) return;
if (Number(now) - Number(this.lastQuestGot.A) >= 3 * 60 * 1000) { if (Number(now) - Number(this.lastQuestGot.A) >= 3 * 60 * 1000) {
this.lastQuestGot.A = now; this.lastQuestGot.A = now;
const keys = Object.keys(this.missives[QuestRank.A]); const currentQuestAmount = this.missives.filter(quest => quest.rank === QuestRank.A).length;
if (keys.length >= 5) return; if (currentQuestAmount >= 5) return;
const quest = this.getRandomQuest(QuestRank.A); const quest = this.getRandomQuest(QuestRank.A);
if (quest !== null) { if (quest !== null) this.createMissive(quest);
this.createMissive(quest, QuestRank.A);
}
} }
if (this.guild.level < 6) return; if (this.guild.level < 6) return;
if (Number(now) - Number(this.lastQuestGot.S) >= 5 * 60 * 1000) { if (Number(now) - Number(this.lastQuestGot.S) >= 5 * 60 * 1000) {
this.lastQuestGot.S = now; this.lastQuestGot.S = now;
const keys = Object.keys(this.missives[QuestRank.S]); const currentQuestAmount = this.missives.filter(quest => quest.rank === QuestRank.S).length;
if (keys.length >= 5) return; if (currentQuestAmount >= 5) return;
const quest = this.getRandomQuest(QuestRank.S); const quest = this.getRandomQuest(QuestRank.S);
if (quest !== null) { if (quest !== null) this.createMissive(quest);
this.createMissive(quest, QuestRank.S);
}
} }
}, 250)); }, 250));
@@ -504,6 +477,7 @@ nav {
width: 80px; width: 80px;
height: 80px; height: 80px;
} }
.lds-ring div { .lds-ring div {
box-sizing: border-box; box-sizing: border-box;
display: block; display: block;
@@ -516,15 +490,19 @@ nav {
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #000 transparent transparent transparent; border-color: #000 transparent transparent transparent;
} }
.lds-ring div:nth-child(1) { .lds-ring div:nth-child(1) {
animation-delay: -0.45s; animation-delay: -0.45s;
} }
.lds-ring div:nth-child(2) { .lds-ring div:nth-child(2) {
animation-delay: -0.3s; animation-delay: -0.3s;
} }
.lds-ring div:nth-child(3) { .lds-ring div:nth-child(3) {
animation-delay: -0.15s; animation-delay: -0.15s;
} }
@keyframes lds-ring { @keyframes lds-ring {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);
+2 -2
View File
@@ -6,7 +6,7 @@ import {getFromString, QuestRank} from "@/classes/QuestRank";
export class GameData { export class GameData {
guild: Guild; guild: Guild;
adventurers: { [key: string]: Adventurer }; adventurers: { [key: string]: Adventurer };
missives: { [key: string]: { [key: string]: Quest } }; missives: Array<Quest>;
lastQuestGot: { [key: string]: null | number }; lastQuestGot: { [key: string]: null | number };
lastRecruitAction: null | number; lastRecruitAction: null | number;
adventurerForHireId: string | null; adventurerForHireId: string | null;
@@ -16,7 +16,7 @@ export class GameData {
) { ) {
this.guild = data.guild ?? new Guild(1, 0); this.guild = data.guild ?? new Guild(1, 0);
this.adventurers = data.adventurers ?? {} as { [key: string]: Adventurer }; this.adventurers = data.adventurers ?? {} as { [key: string]: Adventurer };
this.missives = data.missives ?? {} as { [key: string]: { [key: string]: Quest } }; this.missives = data.missives ?? [] as Array<Quest>;
this.lastQuestGot = data.lastQuestGot ?? {} as { [key: string]: null | number }; this.lastQuestGot = data.lastQuestGot ?? {} as { [key: string]: null | number };
this.lastRecruitAction = data.lastRecruitAction ?? null; this.lastRecruitAction = data.lastRecruitAction ?? null;
this.adventurerForHireId = data.adventurerForHireId ?? null; this.adventurerForHireId = data.adventurerForHireId ?? null;
+12 -1
View File
@@ -7,12 +7,22 @@ export class Quest {
title: string; title: string;
text: string; text: string;
adventurers: Array<Adventurer>; adventurers: Array<Adventurer>;
maxAdventurers: number;
progressPoints: number; progressPoints: number;
maxProgress: number; maxProgress: number;
expReward: number; expReward: number;
goldReward: number; goldReward: number;
constructor(id: string, rank: QuestRank, title: string, text: string, maxProgress: number, expReward: number, goldReward: number) { constructor(
id: string,
rank: QuestRank,
title: string,
text: string,
maxProgress: number,
expReward: number,
goldReward: number,
maxAdventurers: number = 1
) {
this.id = id; this.id = id;
this.rank = rank; this.rank = rank;
this.title = title; this.title = title;
@@ -22,6 +32,7 @@ export class Quest {
this.goldReward = goldReward; this.goldReward = goldReward;
this.progressPoints = 0; this.progressPoints = 0;
this.adventurers = []; this.adventurers = [];
this.maxAdventurers = maxAdventurers;
} }
getPercentProgress(): number { getPercentProgress(): number {
+11 -7
View File
@@ -59,27 +59,31 @@ h1 {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: start; justify-content: flex-start;
align-items: stretch;
gap: 1rem; gap: 1rem;
padding-block: 0.5rem; padding-block: 0.5rem;
padding-inline: 40%; padding-inline: 5rem;
overflow-x: auto; overflow-x: auto;
scroll-snap-type: x mandatory; scroll-snap-type: x mandatory;
width: max-content; width: 100vw;
max-width: 100%; max-width: 100%;
} }
@media(min-width: 800px) { @media(min-width: 800px) {
.missives-wrapper { .missives-wrapper {
padding-inline: 1rem; padding-inline: 1rem;
max-width: 100vw;
overflow-x: hidden;
} }
.missives { .missives {
justify-content: center; display: grid;
flex-wrap: wrap;
overflow-x: inherit;
padding-inline: 0; padding-inline: 0;
max-width: 1200px;
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
grid-auto-rows: auto;
gap: 1rem;
} }
} }
</style> </style>
+22 -6
View File
@@ -12,6 +12,7 @@
<div class="drink-stain" v-if="drinkStain.exists"> <div class="drink-stain" v-if="drinkStain.exists">
<DrinkStain/> <DrinkStain/>
</div> </div>
<div class="rank">{{missive.rank}}</div>
<h2>{{ missive.title }}</h2> <h2>{{ missive.title }}</h2>
<p>{{ missive.text }}</p> <p>{{ missive.text }}</p>
<div class="slots"> <div class="slots">
@@ -36,7 +37,7 @@
</div> </div>
<div class="progressWrap"> <div class="progressWrap">
<span class="progress"></span> <span class="progress"></span>
<span class="percentage">{{ progressPercentage }}</span> <span class="percentage">{{ `${progressPercentage.toFixed(2)}%` }}</span>
</div> </div>
<h3>Rewards</h3> <h3>Rewards</h3>
<div class="rewards"> <div class="rewards">
@@ -58,6 +59,11 @@ import Parchment from "@/components/misc/Parchment.vue";
export default defineComponent({ export default defineComponent({
name: "QuestMissive", name: "QuestMissive",
components: {Parchment, WaterStain, DrinkStain, AdventurerComponent}, components: {Parchment, WaterStain, DrinkStain, AdventurerComponent},
computed: {
progressPercentageValue(): string {
return `${this.missive.progressPoints / this.missive.maxProgress * 100}%`;
},
},
props: { props: {
missive: { missive: {
type: Object as PropType<Quest | any>, type: Object as PropType<Quest | any>,
@@ -73,7 +79,7 @@ export default defineComponent({
}, },
data: () => { data: () => {
return { return {
progressPercentage: "0%", progressPercentage: 0,
stain: false, stain: false,
drinkStain: { drinkStain: {
exists: false, exists: false,
@@ -85,8 +91,7 @@ export default defineComponent({
methods: { methods: {
updateProgress() { updateProgress() {
if (this.missive === undefined) return; if (this.missive === undefined) return;
const progress = (this.missive.progressPoints / this.missive.maxProgress * 100).toFixed(2); this.progressPercentage = this.missive.progressPoints / this.missive.maxProgress * 100;
this.progressPercentage = `${progress}%`;
}, },
randomNumber(min: number, max: number) { randomNumber(min: number, max: number) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
@@ -102,7 +107,7 @@ export default defineComponent({
} }
}, },
watch: { watch: {
missive: { "missive.progressPoints": {
handler() { handler() {
this.updateProgress(); this.updateProgress();
}, },
@@ -121,6 +126,7 @@ export default defineComponent({
padding: 0.5rem; padding: 0.5rem;
position: relative; position: relative;
scroll-snap-align: center; scroll-snap-align: center;
margin: 0 auto;
.parchment { .parchment {
position: absolute; position: absolute;
@@ -161,7 +167,7 @@ export default defineComponent({
left: 0; left: 0;
height: 100%; height: 100%;
display: block; display: block;
width: v-bind(progressPercentage); width: v-bind(progressPercentageValue);
background-color: rgba(0, 128, 0, 0.65); background-color: rgba(0, 128, 0, 0.65);
transition: width 250ms linear; transition: width 250ms linear;
} }
@@ -178,6 +184,16 @@ export default defineComponent({
} }
} }
.rank {
position: absolute;
top: -0.5rem;
left: 0.25rem;
font-size: 3rem;
font-weight: bold;
color: #ab0707;
z-index: -1;
}
.rewards { .rewards {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
+16 -47
View File
@@ -1,55 +1,18 @@
<template> <template>
<section> <section>
<QuestGroup <QuestGroup
v-if="guild.level >= 7 && Object.keys(quests.S).length > 0"
:adventurers="adventurers" :adventurers="adventurers"
:quests="quests.S" :quests="quests.filter(quest => quest.progressPoints < quest.maxProgress)"
:finalizeQuest="finalizeQuest" :finalizeQuest="finalizeQuest"
label="Rank S Quests" label="Quests"
v-show="quests.filter(quest => quest.progressPoints < quest.maxProgress).length > 0"
/> />
<QuestGroup <QuestGroup
v-if="guild.level >= 6 && Object.keys(quests.A).length > 0" :finalize-quest="finalizeQuest"
:adventurers="adventurers" :adventurers="adventurers"
:quests="quests.A" :quests="quests.filter(quest => quest.progressPoints >= quest.maxProgress)"
:finalizeQuest="finalizeQuest" label="Completed Quests"
label="Rank A Quests" v-show="quests.filter(quest => quest.progressPoints >= quest.maxProgress).length > 0"
/>
<QuestGroup
v-if="guild.level >= 5 && Object.keys(quests.B).length > 0"
:adventurers="adventurers"
:quests="quests.B"
:finalizeQuest="finalizeQuest"
label="Rank B Quests"
/>
<QuestGroup
v-if="guild.level >= 4 && Object.keys(quests.C).length > 0"
:adventurers="adventurers"
:quests="quests.C"
:finalizeQuest="finalizeQuest"
label="Rank C Quests"
/>
<QuestGroup
v-if="guild.level >= 3 && Object.keys(quests.D).length > 0"
:adventurers="adventurers"
:quests="quests.D"
:finalizeQuest="finalizeQuest"
label="Rank D Quests"
/>
<QuestGroup
v-if="guild.level >= 2 && Object.keys(quests.E).length > 0"
:adventurers="adventurers"
:quests="quests.E"
:finalizeQuest="finalizeQuest"
label="Rank E Quests"
/>
<QuestGroup
v-if="Object.keys(quests.F).length > 0"
:adventurers="adventurers"
:quests="quests.F"
:finalizeQuest="finalizeQuest"
label="Rank F Quests"
/> />
</section> </section>
</template> </template>
@@ -76,7 +39,7 @@ export default defineComponent({
required: true, required: true,
}, },
quests: { quests: {
type: Object as PropType<{ [key: string]: Quest }>, type: Object as PropType<Array<Quest>>,
required: true, required: true,
}, },
lastRecruitTime: { lastRecruitTime: {
@@ -98,13 +61,19 @@ export default defineComponent({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.guild { section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: flex-start;
align-items: center; align-items: center;
width: 100%; width: 100%;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
@media(min-width: 800px) {
section {
flex-direction: column-reverse;
}
}
</style> </style>