Compare commits

...

5 Commits

10 changed files with 186 additions and 153 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "adventurers-guild", "name": "adventurers-guild",
"version": "0.3.0", "version": "0.4.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+50 -4
View File
@@ -14,10 +14,10 @@ import {RouterLink, RouterView} from 'vue-router'</script>
:guild="guild" :guild="guild"
:adventurers="adventurers" :adventurers="adventurers"
:quests="missives" :quests="missives"
:lastRecruitTime="lastRecruitHandled" :adventurerForHire="adventurerForHire"
@finalizeQuest="finalizeQuest($event)" @finalizeQuest="finalizeQuest($event)"
@wipeSave="resetSave()" @wipeSave="resetSave()"
@recruitActionTaken="lastRecruitHandled = Number(new Date())" @recruitActionTaken="recruitAction($event)"
/> />
</template> </template>
@@ -27,9 +27,10 @@ import {Adventurer} from "@/classes/Adventurer";
import {getQuestWithRewards, Quest} from "@/classes/Quest"; import {getQuestWithRewards, Quest} from "@/classes/Quest";
import {Guild} from "@/classes/Guild"; import {Guild} from "@/classes/Guild";
import {getFromString, QuestRank} from "@/classes/QuestRank"; import {getFromString, QuestRank} from "@/classes/QuestRank";
import {GameData, loadAvailableQuests, loadGame, saveGame} from "@/GameData"; import {GameData, loadAdventurersForHire, loadAvailableQuests, loadGame, saveGame} from "@/GameData";
import type {GuildUpgrade} from "@/classes/GuildUpgrade"; import type {GuildUpgrade} from "@/classes/GuildUpgrade";
import {AdventurerCapacityUpgrade} from "@/classes/guildUpgrades/AdventurerCapacityUpgrade"; import {AdventurerCapacityUpgrade} from "@/classes/guildUpgrades/AdventurerCapacityUpgrade";
import {getNewAdventurerForHire} from "@/classes/Recruitment";
export default defineComponent({ export default defineComponent({
name: "GuildView", name: "GuildView",
@@ -45,6 +46,8 @@ export default defineComponent({
F: null as null|number, F: null as null|number,
} as { [key: string]: null|number }, } as { [key: string]: null|number },
lastRecruitHandled: null as null|number, lastRecruitHandled: null as null|number,
adventurerForHire: null as Adventurer|null,
adventurersDatabase: {} as Array<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: {
@@ -77,6 +80,28 @@ export default defineComponent({
} }
}, },
async checkForNewRecruit(currentTimestamp: number) {
if (this.lastRecruitHandled === null) {
this.lastRecruitHandled = 0;
}
if (currentTimestamp - this.lastRecruitHandled >= 1000 * 60 * 30 && this.adventurerForHire === null) {
this.adventurerForHire = getNewAdventurerForHire(this.adventurersDatabase);
}
},
recruitAction(adventurer: Adventurer|null): void {
this.lastRecruitHandled = Number(new Date());
this.adventurerForHire = null;
if (adventurer === null) return;
this.adventurers[adventurer.id] = adventurer;
for (const id in this.adventurersDatabase) {
const databaseAdventurer = this.adventurersDatabase[id];
if (databaseAdventurer.id === adventurer.id) {
this.adventurersDatabase.splice(Number(id), 1);
break;
}
}
},
finalizeQuest(missive: Quest) { finalizeQuest(missive: Quest) {
missive.progressPoints = 0; missive.progressPoints = 0;
this.guild.gold += missive.goldReward; this.guild.gold += missive.goldReward;
@@ -134,6 +159,8 @@ export default defineComponent({
} }
this.adventurers = adventurers; this.adventurers = adventurers;
const missives = {} as { [key: string]: { [key: string]: Quest } }; const missives = {} as { [key: string]: { [key: string]: Quest } };
for (const id in saveData.missives) { for (const id in saveData.missives) {
@@ -153,6 +180,21 @@ export default defineComponent({
this.missives = missives; this.missives = missives;
this.lastRecruitHandled = saveData.lastRecruitAction ?? 0; this.lastRecruitHandled = saveData.lastRecruitAction ?? 0;
if (Object.keys(this.adventurers).length <= 0) {
this.adventurerForHire = this.adventurersDatabase[0];
return;
}
if (saveData.currentlyForHireId !== null) {
for (const id in this.adventurersDatabase) {
const adventurer = this.adventurersDatabase[id];
if (adventurer.id === saveData.currentlyForHireId) {
this.adventurerForHire = adventurer;
return;
}
}
}
}, },
resetSave() { resetSave() {
if (!confirm("You are about to wipe your save file. Are you sure?")) return; if (!confirm("You are about to wipe your save file. Are you sure?")) return;
@@ -163,6 +205,7 @@ export default defineComponent({
async mounted() { async mounted() {
this.loadGame(); this.loadGame();
this.quests = await loadAvailableQuests(); this.quests = await loadAvailableQuests();
this.adventurersDatabase = await loadAdventurersForHire(Object.keys(this.adventurers));
setInterval(() => { setInterval(() => {
saveGame(new GameData( saveGame(new GameData(
@@ -170,7 +213,8 @@ export default defineComponent({
this.adventurers, this.adventurers,
this.missives, this.missives,
this.lastQuestGot, this.lastQuestGot,
this.lastRecruitHandled this.lastRecruitHandled,
this.adventurerForHire?.id ?? null
) )
); );
}, 10*1000) }, 10*1000)
@@ -180,6 +224,8 @@ export default defineComponent({
const now = Number(new Date()); const now = Number(new Date());
this.checkForNewRecruit(now);
for (const id in this.lastQuestGot) { for (const id in this.lastQuestGot) {
const lastTime = this.lastQuestGot[getFromString(id as QuestRank)]; const lastTime = this.lastQuestGot[getFromString(id as QuestRank)];
if (lastTime === null) this.lastQuestGot[getFromString(id as QuestRank)] = 0; if (lastTime === null) this.lastQuestGot[getFromString(id as QuestRank)] = 0;
+10 -1
View File
@@ -9,13 +9,21 @@ export class GameData {
missives: { [key: string]: { [key: string]: Quest } }; missives: { [key: string]: { [key: string]: Quest } };
lastQuestGot: { [key: string]: null | number }; lastQuestGot: { [key: string]: null | number };
lastRecruitAction: null | number; lastRecruitAction: null | number;
currentlyForHireId: string|null;
constructor(guild: Guild, adventurers: { [key: string]: Adventurer }, missives: { [key: string]: { [key: string]: Quest } }, lastQuestGot: { [key: string]: null | number }, lastRecruitAction: null | number) { constructor(
guild: Guild, adventurers: { [key: string]: Adventurer },
missives: { [key: string]: { [key: string]: Quest } },
lastQuestGot: { [key: string]: null | number },
lastRecruitAction: null | number,
currentlyForHireId: string|null = null
) {
this.guild = guild; this.guild = guild;
this.adventurers = adventurers; this.adventurers = adventurers;
this.missives = missives; this.missives = missives;
this.lastQuestGot = lastQuestGot; this.lastQuestGot = lastQuestGot;
this.lastRecruitAction = lastRecruitAction; this.lastRecruitAction = lastRecruitAction;
this.currentlyForHireId = currentlyForHireId;
} }
} }
@@ -33,6 +41,7 @@ export function saveGame(
missives: data.missives, missives: data.missives,
lastQuestGot: data.lastQuestGot, lastQuestGot: data.lastQuestGot,
lastRecruitAction: data.lastRecruitAction, lastRecruitAction: data.lastRecruitAction,
adventurerForHireId: data.currentlyForHireId,
})); }));
} }
+2 -1
View File
@@ -1,5 +1,6 @@
import type {GuildUpgrade} from "@/classes/GuildUpgrade"; import type {GuildUpgrade} from "@/classes/GuildUpgrade";
import {AdventurerCapacityUpgrade} from "@/classes/guildUpgrades/AdventurerCapacityUpgrade"; import {AdventurerCapacityUpgrade} from "@/classes/guildUpgrades/AdventurerCapacityUpgrade";
import formatGold from "@/classes/NumberMagic";
export class Guild { export class Guild {
gold: number; gold: number;
@@ -30,7 +31,7 @@ export class Guild {
} else { } else {
const newCost = this.getUpgradeCost(); const newCost = this.getUpgradeCost();
if (newCost === null) return; if (newCost === null) return;
this.displayUpgradeCost = newCost; this.displayUpgradeCost = formatGold(newCost);
this.upgradeCost = newCost; this.upgradeCost = newCost;
} }
} }
+10
View File
@@ -0,0 +1,10 @@
const formatter = new Intl.NumberFormat('en-US', {
maximumFractionDigits: 3,
// @ts-ignore - typescript doesn't know about this option for some godforsaken reason
notation: "compact",
useGrouping: true,
});
export default function formatGold(number: number): string {
return formatter.format(number);
}
+13
View File
@@ -0,0 +1,13 @@
import type {Adventurer} from "@/classes/Adventurer";
/**
* Get a random adventurer from the pool
* @param adventurerPool
* @returns {Adventurer|null} null if the pool is empty
*/
export function getNewAdventurerForHire(adventurerPool: Array<Adventurer>): Adventurer|null {
if (adventurerPool.length <= 0) return null;
const randomId = adventurerPool.length * Math.random() << 0;
return adventurerPool[randomId];
}
+3 -1
View File
@@ -8,7 +8,7 @@
:disabled="guild.gold < guild.adventurerCapacity.nextLevelCost" :disabled="guild.gold < guild.adventurerCapacity.nextLevelCost"
@click="upgradeAdventurerCapacity()" @click="upgradeAdventurerCapacity()"
> >
Upgrade ({{ guild.adventurerCapacity.nextLevelCost.toFixed(0) }} gold) Upgrade ({{ formatGold(guild.adventurerCapacity.nextLevelCost) }} gold)
</button> </button>
</div> </div>
</section> </section>
@@ -17,6 +17,7 @@
<script lang="ts"> <script lang="ts">
import {Guild} from "@/classes/Guild"; import {Guild} from "@/classes/Guild";
import {defineComponent, type PropType} from "vue"; import {defineComponent, type PropType} from "vue";
import formatGold from "../classes/NumberMagic";
export default defineComponent({ export default defineComponent({
name: "UpgradesList", name: "UpgradesList",
@@ -30,6 +31,7 @@ export default defineComponent({
} }
}, },
methods: { methods: {
formatGold,
upgradeAdventurerCapacity(): void { upgradeAdventurerCapacity(): void {
if (!this.guild.adventurerCapacity) return; if (!this.guild.adventurerCapacity) return;
if (!this.guild.adventurerCapacity.nextLevelCost) return; if (!this.guild.adventurerCapacity.nextLevelCost) return;
+8 -59
View File
@@ -3,12 +3,12 @@
<section class="recruit panel pinned-paper"> <section class="recruit panel pinned-paper">
<h1>Applying adventurers</h1> <h1>Applying adventurers</h1>
<div class="adventurers"> <div class="adventurers">
<div v-if="currentlyForHire"> <div v-if="adventurerForHire">
<adventurer-tile class="hire-tile" :adventurer="currentlyForHire"/> <adventurer-tile class="hire-tile" :adventurer="adventurerForHire"/>
<div class="decision"> <div class="decision">
<span <span
title="Hire" title="Hire"
@click="hireAdventurer(currentlyForHire)" @click="hireAdventurer()"
:class="{disabled: Object.keys(adventurers).length >= guild.adventurerCapacity.getAdventurerCapacity()}" :class="{disabled: Object.keys(adventurers).length >= guild.adventurerCapacity.getAdventurerCapacity()}"
> >
@@ -73,76 +73,25 @@ export default defineComponent({
}, },
required: true, required: true,
}, },
lastRecruitTime: { adventurerForHire: {
type: Number as PropType<number>, type: Object as PropType<Adventurer|null>,
default() { default() {
return 0; return null;
} }
}, },
}, },
methods: { methods: {
getRandomAdventurer(): Adventurer | null { hireAdventurer(): void {
if (this.adventurersForHire.length <= 0) return null;
const randomId = this.adventurersForHire.length * Math.random() << 0;
return this.adventurersForHire[randomId];
},
getNewAdventurerForHire(): void {
const adventurer = this.getRandomAdventurer();
if (adventurer === null) {
this.currentlyForHire = null;
return;
}
if (this.adventurers[adventurer.id] != null) {
this.currentlyForHire = null;
return;
}
this.currentlyForHire = adventurer;
window.localStorage.setItem("currentlyForHire", adventurer.id);
},
hireAdventurer(adventurer: Adventurer | any): void {
if (Object.keys(this.adventurers).length >= this.guild.adventurerCapacity.getAdventurerCapacity()) return; if (Object.keys(this.adventurers).length >= this.guild.adventurerCapacity.getAdventurerCapacity()) return;
this.adventurers[adventurer.id] = adventurer; this.$emit("recruitActionTaken", this.adventurerForHire);
this.currentlyForHire = null;
window.localStorage.removeItem("currentlyForHire");
this.$emit("recruitActionTaken", adventurer);
}, },
dismissAdventurer() { dismissAdventurer() {
if (Object.keys(this.adventurers).length <= 0) return; if (Object.keys(this.adventurers).length <= 0) return;
this.currentlyForHire = null;
this.$emit("recruitActionTaken", null); this.$emit("recruitActionTaken", null);
window.localStorage.removeItem("currentlyForHire");
} }
}, },
async mounted() { async mounted() {
this.adventurersForHire = await loadAdventurersForHire(Object.keys(this.adventurers));
if (Object.keys(this.adventurers).length <= 0) {
this.currentlyForHire = this.adventurersForHire[0];
window.localStorage.setItem("currentlyForHire", this.adventurersForHire[0].id);
return;
}
const savedCurrentForHire = window.localStorage.getItem("currentlyForHire");
if (savedCurrentForHire !== null) {
for (const id in this.adventurersForHire) {
const adventurer = this.adventurersForHire[id];
if (adventurer.id === savedCurrentForHire) {
this.currentlyForHire = adventurer;
return;
}
}
}
const now = Number(new Date());
if (now - this.lastRecruitTime >= 1000 * 60 * 30 && this.currentlyForHire === null) {
this.getNewAdventurerForHire();
}
}, },
emits: ["recruitActionTaken"] emits: ["recruitActionTaken"]
+3 -1
View File
@@ -13,7 +13,7 @@
<img src="/img/quests/overlays/nail.png" alt="" draggable="false"/> <img src="/img/quests/overlays/nail.png" alt="" draggable="false"/>
</div> </div>
<section class="coffer"> <section class="coffer">
<p>Coffer: {{ guild.gold }} gold</p> <p>Coffer: {{ formatGold(guild.gold) }} gold</p>
</section> </section>
<section class="upgrade"> <section class="upgrade">
<p>Guild level: {{ guild.level }}</p> <p>Guild level: {{ guild.level }}</p>
@@ -40,9 +40,11 @@ import {Guild} from "@/classes/Guild";
import {version} from "../../package.json" import {version} from "../../package.json"
import UpgradesList from "@/components/UpgradesList.vue"; import UpgradesList from "@/components/UpgradesList.vue";
import formatGold from "../classes/NumberMagic";
export default defineComponent({ export default defineComponent({
name: "GuildView", name: "GuildView",
methods: {formatGold},
components: {UpgradesList}, components: {UpgradesList},
data: () => { data: () => {
return { return {
+86 -85
View File
@@ -1,89 +1,90 @@
<template> <template>
<div class="guild" v-if="guild.level >= 7 && Object.keys(quests.S).length > 0"> <section>
<h1>Rank S Quests</h1> <div class="guild" v-if="guild.level >= 7 && Object.keys(quests.S).length > 0">
<section class="missives"> <h1>Rank S Quests</h1>
<QuestMissive <section class="missives">
v-for="(missive, key, index) in quests.S" <QuestMissive
:key="key" v-for="(missive, key, index) in quests.S"
:adventurers="adventurers" :key="key"
:missive="missive" :adventurers="adventurers"
@click="finalizeQuest(missive)" :missive="missive"
/> @click="finalizeQuest(missive)"
</section> />
</div> </section>
<div class="guild" v-if="guild.level >= 6 && Object.keys(quests.A).length > 0"> </div>
<h1>Rank A Quests</h1> <div class="guild" v-if="guild.level >= 6 && Object.keys(quests.A).length > 0">
<section class="missives"> <h1>Rank A Quests</h1>
<QuestMissive <section class="missives">
v-for="(missive, key, index) in quests.A" <QuestMissive
:key="key" v-for="(missive, key, index) in quests.A"
:adventurers="adventurers" :key="key"
:missive="missive" :adventurers="adventurers"
@click="finalizeQuest(missive)" :missive="missive"
/> @click="finalizeQuest(missive)"
</section> />
</div> </section>
<div class="guild" v-if="guild.level >= 5 && Object.keys(quests.B).length > 0"> </div>
<h1>Rank B Quests</h1> <div class="guild" v-if="guild.level >= 5 && Object.keys(quests.B).length > 0">
<section class="missives"> <h1>Rank B Quests</h1>
<QuestMissive <section class="missives">
v-for="(missive, key, index) in quests.B" <QuestMissive
:key="key" v-for="(missive, key, index) in quests.B"
:adventurers="adventurers" :key="key"
:missive="missive" :adventurers="adventurers"
@click="finalizeQuest(missive)" :missive="missive"
/> @click="finalizeQuest(missive)"
</section> />
</div> </section>
<div class="guild" v-if="guild.level >= 4 && Object.keys(quests.C).length > 0"> </div>
<h1>Rank C Quests</h1> <div class="guild" v-if="guild.level >= 4 && Object.keys(quests.C).length > 0">
<section class="missives"> <h1>Rank C Quests</h1>
<QuestMissive <section class="missives">
v-for="(missive, key, index) in quests.C" <QuestMissive
:key="key" v-for="(missive, key, index) in quests.C"
:adventurers="adventurers" :key="key"
:missive="missive" :adventurers="adventurers"
@click="finalizeQuest(missive)" :missive="missive"
/> @click="finalizeQuest(missive)"
</section> />
</div> </section>
<div class="guild" v-if="guild.level >= 3 && Object.keys(quests.D).length > 0"> </div>
<h1>Rank D Quests</h1> <div class="guild" v-if="guild.level >= 3 && Object.keys(quests.D).length > 0">
<section class="missives"> <h1>Rank D Quests</h1>
<QuestMissive <section class="missives">
v-for="(missive, key, index) in quests.D" <QuestMissive
:key="key" v-for="(missive, key, index) in quests.D"
:adventurers="adventurers" :key="key"
:missive="missive" :adventurers="adventurers"
@click="finalizeQuest((missive))" :missive="missive"
/> @click="finalizeQuest((missive))"
</section> />
</div> </section>
<div class="guild" v-if="guild.level >= 2 && Object.keys(quests.E).length > 0"> </div>
<h1>Rank E Quests</h1> <div class="guild" v-if="guild.level >= 2 && Object.keys(quests.E).length > 0">
<section class="missives"> <h1>Rank E Quests</h1>
<QuestMissive <section class="missives">
v-for="(missive, key, index) in quests.E" <QuestMissive
:key="key" v-for="(missive, key, index) in quests.E"
:adventurers="adventurers" :key="key"
:missive="missive" :adventurers="adventurers"
@click="finalizeQuest(missive)" :missive="missive"
/> @click="finalizeQuest(missive)"
</section> />
</div> </section>
<div class="guild" v-if="Object.keys(quests.F).length > 0"> </div>
<h1>Rank F Quests</h1> <div class="guild" v-if="Object.keys(quests.F).length > 0">
<section class="missives"> <h1>Rank F Quests</h1>
<QuestMissive <section class="missives">
v-for="(missive, key, index) in quests.F" <QuestMissive
:key="key" v-for="(missive, key, index) in quests.F"
:adventurers="adventurers" :key="key"
:missive="missive" :adventurers="adventurers"
@click="finalizeQuest(missive)" :missive="missive"
/> @click="finalizeQuest(missive)"
</section> />
</div> </section>
</div>
</section>
</template> </template>
<script lang="ts"> <script lang="ts">