Compare commits

..

71 Commits

Author SHA1 Message Date
YouHaveTrouble ae89704380 fix a type error 2025-05-11 17:03:28 +02:00
YouHaveTrouble 972b9251c3 bump version 2025-05-11 17:00:18 +02:00
YouHaveTrouble 2f10f940d8 bump version 2025-05-11 16:52:02 +02:00
YouHaveTrouble d962c85629 better quest adventurer choice ux 2025-05-11 16:37:58 +02:00
YouHaveTrouble e71326d89b update busy status dynamically 2025-05-11 14:36:10 +02:00
YouHaveTrouble 20567be96d new experimental quest view 2025-05-08 21:08:03 +02:00
YouHaveTrouble 3c79074c4c create GDD for the next iteration of the game 2024-11-19 20:35:55 +01:00
YouHaveTrouble 571dee6cc9 update news 2024-08-23 23:55:24 +02:00
YouHaveTrouble b49a30bb6e actually bearable mobile view for quests board 2023-09-23 15:29:47 +02:00
YouHaveTrouble 2a38461a47 move icon svgs to components, so they can load along everything else 2023-09-16 15:58:54 +02:00
YouHaveTrouble 52c340be26 bump version 2023-09-16 15:39:23 +02:00
YouHaveTrouble f2c5304643 don't load paths dynamically to allow offline play 2023-09-16 15:26:36 +02:00
YouHaveTrouble b6aefde9da add / to the path 2023-09-16 14:29:29 +02:00
YouHaveTrouble f31a0013a9 Merge pull request #11 from xYundy/fix-quest-button-on-ios
Fix select button color for iOS Safari
2023-09-15 12:04:09 +02:00
Wojciech 'xYundy' Zięciak e4af877358 Add missing empty line at EOF 2023-09-15 12:03:48 +02:00
YouHaveTrouble d162771749 Merge pull request #10 from xYundy/public-testing
Add script which allow to test app on external devices
2023-09-15 12:03:15 +02:00
YouHaveTrouble f53644119a Merge pull request #9 from xYundy/pwa-mobile-icon
Add PWA icons and manifest
2023-09-15 12:02:21 +02:00
Wojciech 'xYundy' Zięciak 34999a27b7 add PWA icons and manifest 2023-09-15 11:59:11 +02:00
Wojciech 'xYundy' Zięciak 6e23f9a7b4 Fix select button color for iOS Safar 2023-09-15 11:55:27 +02:00
Wojciech 'xYundy' Zięciak d852f6fd96 Add script which allow to test app on ext. devices 2023-09-15 11:47:43 +02:00
YouHaveTrouble 0e0c133f6f swap meta image 2023-09-07 19:06:29 +02:00
YouHaveTrouble 8a6b581981 bump version 2023-09-07 18:39:53 +02:00
YouHaveTrouble 5497717605 have enough portraits for now 2023-09-07 18:38:11 +02:00
YouHaveTrouble 85fa53b16e add gradient background for adventurer portraits 2023-09-07 18:23:36 +02:00
YouHaveTrouble 9feab73fd2 small tool to automatically merge data and portraits into one file 2023-09-07 18:23:17 +02:00
YouHaveTrouble ed7c9e66ec add new adventurers and their assets 2023-09-07 18:22:22 +02:00
YouHaveTrouble 82350c6c42 new loading screen 2023-08-28 19:33:44 +02:00
YouHaveTrouble 0b7489b21f add new assets 2023-08-28 19:33:29 +02:00
YouHaveTrouble 21fb2c5f72 update depends 2023-08-20 17:20:29 +02:00
YouHaveTrouble 20e7af41ae bump version 2023-07-26 00:24:44 +02:00
YouHaveTrouble 17a091bec8 stop changelog from bumping the page when loading 2023-07-26 00:18:57 +02:00
YouHaveTrouble 13cb2c5a55 don't save possibly heavy adventurer portraits and load them from global data 2023-07-26 00:01:24 +02:00
YouHaveTrouble 28fe055f32 cleanup 2023-07-23 22:38:59 +02:00
YouHaveTrouble b35de64cea move most assets to components and use base64 versions of them 2023-07-23 22:24:43 +02:00
YouHaveTrouble 1e7eb5c750 move adventurer portraits to the data to make sure they load along with everything else 2023-07-23 17:27:20 +02:00
YouHaveTrouble 1b1695bf1f bump version 2023-07-03 23:52:34 +02:00
YouHaveTrouble f524ce5e2e make a and s rank quests appear more often 2023-07-03 23:51:25 +02:00
YouHaveTrouble d14ef20fb1 rebalance adventurer capacity cost 2023-07-03 23:25:27 +02:00
YouHaveTrouble 83070b632b update depends 2023-07-03 23:25:16 +02:00
YouHaveTrouble 2723801c9c update depends 2023-07-03 22:51:40 +02:00
YouHaveTrouble 5d1ce0a262 make gold update actually work 2023-07-03 22:38:50 +02:00
YouHaveTrouble 74ebac9862 rebalance upgrades 2023-07-03 22:38:38 +02:00
YouHaveTrouble 2a5b162605 bump version 2023-06-26 19:44:57 +02:00
YouHaveTrouble d6e897c289 save data export and import 2023-06-26 19:44:32 +02:00
YouHaveTrouble a2b8b40464 download news once per hour 2023-06-26 18:02:38 +02:00
YouHaveTrouble bdaa3bc029 news 2023-06-26 17:43:12 +02:00
YouHaveTrouble 816c1fb0e7 make adventurer choice closeable by clicking on the slot again 2023-06-26 17:12:46 +02:00
YouHaveTrouble acdd8f6964 prevent phones from turning off display when the game's running 2023-06-26 16:03:39 +02:00
YouHaveTrouble 5fa8f2cf15 tooltip on technical menu 2023-06-26 15:54:14 +02:00
YouHaveTrouble 5a818fb142 prototype tooltips 2023-06-22 14:18:08 +02:00
YouHaveTrouble 6625a5b9de move title styles to global file 2023-06-20 20:06:54 +02:00
YouHaveTrouble cf69b92934 bump version 2023-06-20 19:58:31 +02:00
YouHaveTrouble 005cde4a01 remove debug 2023-06-20 19:57:34 +02:00
YouHaveTrouble 1b94d676e7 minor fixes 2023-06-20 19:56:36 +02:00
YouHaveTrouble 7cda820a99 add metadata 2023-06-20 19:56:28 +02:00
YouHaveTrouble 7281fdab60 dynamically load home view 2023-06-20 19:39:13 +02:00
YouHaveTrouble 3b9441b555 improve loading process 2023-06-20 19:37:27 +02:00
YouHaveTrouble c2abfd6dfd add loading screen to hide loading assets and not yet loaded gamestate 2023-06-20 19:18:50 +02:00
YouHaveTrouble f05c393600 new favicon 2023-06-20 19:18:09 +02:00
YouHaveTrouble 23b6f4b4f8 update packages and fix breaking changes 2023-06-20 18:12:20 +02:00
YouHaveTrouble d5195aa157 bump version 2023-05-12 21:52:06 +02:00
YouHaveTrouble 15072fe2c8 fix up ts errors 2023-05-12 21:52:00 +02:00
YouHaveTrouble ba4a6cb2a2 add quest auto-finish upgrade 2023-05-12 21:47:23 +02:00
YouHaveTrouble 4847d22f7e cancel update and save tasks on main component unload 2023-05-12 19:58:47 +02:00
YouHaveTrouble 2c053cc3eb increased cost of adventurer capacity upgrade 2023-05-12 19:13:48 +02:00
YouHaveTrouble c55e0c8bb6 added quest gold modifier upgrade 2023-05-12 18:41:40 +02:00
YouHaveTrouble 29aafedcfa fix max guild level being displayed on level 7 when upgraded to it 2023-05-12 18:25:45 +02:00
YouHaveTrouble 8070c855a0 drastically reduce price and scaling of exp modifier upgrade 2023-05-12 18:24:59 +02:00
YouHaveTrouble aaf0eb6546 bump version 2023-04-23 15:24:59 +02:00
YouHaveTrouble c14a135c94 display release date of an update next to its title 2023-04-23 15:22:59 +02:00
YouHaveTrouble 40d5a876b8 add quest exp modifier upgrade 2023-04-23 15:22:40 +02:00
106 changed files with 2374 additions and 1289 deletions
+23
View File
@@ -0,0 +1,23 @@
# Game Design Document
## 1. Introduction
Guild Master is a game simulating being a fantasy guild master. The player will be able to recruit adventurers,
send them on quests, and manage the guild's resources.
## 2. Gameplay
Player will recruit adventurers and assign then to quests. Adventurers will have different statistics and a passive
ability that will make each character unique. Player will have to manage guild's resources to complete more and more
resource intensive quests and assignments.
## 3. Mechanics
Menus. Lots of menus. Possibly with fancy animations.
## 4. Characters
Set amount of available adventurers. Each with their own inventory and passive ability. Items in the inventory will
boost specific statistics of the character. They will be scaled based on level and the xp curve will be exponential.
## 5. Quests
There will always be a minimum set amount of quests available. Adventurers will have to be assigned to a quest that is
within their level range. Quests will come in different difficulties within the level range.
+21
View File
@@ -0,0 +1,21 @@
/**
* This file is used to import the character portraits to base64 from raw assets
*/
const fs = require('fs');
const characterData = require('./rawAssets/data/adventurers.json');
for (const character of characterData) {
try {
const base64 = base64_encode(`./rawAssets/img/portraits/${character.id}.png`);
character.portrait = "data:image/png;base64,"+base64;
} catch (e) {
console.error(`Error: Didn't find portrait for ${character.id}`);
}
}
fs.writeFileSync('./public/data/adventurers.json', JSON.stringify(characterData, null, 2), "utf-8");
function base64_encode(file) {
return fs.readFileSync(file, "base64");
}
+20 -8
View File
@@ -1,18 +1,30 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guild Master - Adventurer's guild management game</title> <title>Guild Master - Adventurer's guild management game</title>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=EB+Garamond&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=EB+Garamond&display=swap" rel="stylesheet">
<link rel="manifest" href="/manifest.json" />
<meta name="description"
content="Guild Master is a browser game where you manage your own adventurer's guild!"/>
<meta property="twitter:title" content="Guild Master - Adventurer's guild management game"/>
<meta property="twitter:image" content="https://guildmaster.yht.one/img/app-icons/icon.png"/>
<meta property="twitter:description"
content="Guild Master is a browser game where you manage your own adventurer's guild!"/>
<meta property="og:title" content="Guild Master - Adventurer's guild management game"/>
<meta property="og:url" content="https://guildmaster.yht.one/"/>
<meta property="og:description"
content="Guild Master is a browser game where you manage your own adventurer's guild!"/>
<meta property="og:image" content="https://guildmaster.yht.one/img/app-icons/icon.png"/>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<noscript> <noscript>
This is a javascript game. You need to enable javascript for it to work. This is a javascript game. You need to enable javascript for it to work.
</noscript> </noscript>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
+1
View File
@@ -0,0 +1 @@
I'm working on it...
+844 -873
View File
File diff suppressed because it is too large Load Diff
+15 -13
View File
@@ -1,29 +1,31 @@
{ {
"name": "adventurers-guild", "name": "adventurers-guild",
"version": "0.5.0", "version": "0.14.2",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"dev-public": "vite --host",
"build": "run-p type-check build-only && cp -r CNAME dist/CNAME", "build": "run-p type-check build-only && cp -r CNAME dist/CNAME",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --noEmit" "type-check": "vue-tsc --noEmit",
"gen-character-data": "node characterDataGenerator.js"
}, },
"dependencies": { "dependencies": {
"@vueuse/components": "^9.13.0", "@vueuse/components": "^9.13.0",
"sass": "^1.59.3", "sass": "^1.66.1",
"vue": "^3.2.45", "vue": "^3.3.4",
"vue-router": "^4.1.6" "vue-router": "^4.2.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.11.12", "@types/node": "^18.17.6",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.3.1",
"@vue/tsconfig": "^0.1.3", "@vue/tsconfig": "^0.4.0",
"eslint": "^8.36.0", "eslint": "^8.47.0",
"eslint-plugin-vue": "^9.9.0", "eslint-plugin-vue": "^9.17.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"typescript": "~4.7.4", "typescript": "~5.1.6",
"vite": "^4.0.0", "vite": "4.4.9",
"vue-tsc": "^1.0.12" "vue-tsc": "^1.8.3"
} }
} }
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

+59
View File
@@ -0,0 +1,59 @@
{
"name": "Guild Master - Adventurer's guild management game",
"short_name": "Guild Master",
"theme_color": "#3C2114",
"background_color": "#d9c8b3",
"display": "standalone",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "img/app-icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "img/app-icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "img/app-icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "img/app-icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "img/app-icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "img/app-icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "img/app-icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "img/app-icons/icon.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}
+102
View File
@@ -0,0 +1,102 @@
[
{
"id": "aldek",
"name": "Aldek",
"attackExponent": 1.1
},
{
"id": "aria",
"name": "Aria",
"attackExponent": 1.1
},
{
"id": "burnett",
"name": "Burnett",
"attackExponent": 1.1
},
{
"id": "charlotte",
"name": "Charlotte",
"attackExponent": 1.1
},
{
"id": "ella",
"name": "Ella",
"attackExponent": 1.1
},
{
"id": "elyza",
"name": "Elyza",
"attackExponent": 1.1
},
{
"id": "emille",
"name": "Emille",
"attackExponent": 1.1
},
{
"id": "garret",
"name": "Garret",
"attackExponent": 1.1
},
{
"id": "gryza",
"name": "Gryza",
"attackExponent": 1.1
},
{
"id": "lestat",
"name": "Lestat",
"attackExponent": 1.1
},
{
"id": "lydia",
"name": "Lydia",
"attackExponent": 1.1
},
{
"id": "noor",
"name": "Noor",
"attackExponent": 1.1
},
{
"id": "noron",
"name": "Noron",
"attackExponent": 1.1
},
{
"id": "oola",
"name": "Oola",
"attackExponent": 1.1
},
{
"id": "owen",
"name": "Owen",
"attackExponent": 1.1
},
{
"id": "ryslette",
"name": "Ryslette",
"attackExponent": 1.1
},
{
"id": "sally",
"name": "Sally",
"attackExponent": 1.1
},
{
"id": "tovu",
"name": "Tovu",
"attackExponent": 1.1
},
{
"id": "wrydio",
"name": "Wrydio",
"attackExponent": 1.1
},
{
"id": "xarya",
"name": "Xarya",
"attackExponent": 1.1
}
]

Before

Width:  |  Height:  |  Size: 4.9 MiB

After

Width:  |  Height:  |  Size: 4.9 MiB

Before

Width:  |  Height:  |  Size: 5.6 MiB

After

Width:  |  Height:  |  Size: 5.6 MiB

Before

Width:  |  Height:  |  Size: 5.3 MiB

After

Width:  |  Height:  |  Size: 5.3 MiB

Before

Width:  |  Height:  |  Size: 4.9 MiB

After

Width:  |  Height:  |  Size: 4.9 MiB

Before

Width:  |  Height:  |  Size: 406 KiB

After

Width:  |  Height:  |  Size: 406 KiB

Before

Width:  |  Height:  |  Size: 470 KiB

After

Width:  |  Height:  |  Size: 470 KiB

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="800px" viewBox="0 0 569.613 569.614"
xml:space="preserve">
<g>
<g>
<path d="M371.49,563.638l113.052-65.854c5.26-3.063,9.088-8.094,10.64-13.975c1.555-5.888,0.701-12.148-2.359-17.405
l-30.769-52.807c4.789-6.524,9.083-13.115,12.972-19.918c3.893-6.799,7.405-13.84,10.606-21.275l61.114-0.221
c6.086-0.021,11.915-2.464,16.202-6.781c4.287-4.32,6.687-10.165,6.665-16.255l-0.48-130.833
c-0.024-6.089-2.464-11.919-6.784-16.206c-4.299-4.269-10.113-6.662-16.166-6.662c-0.03,0-0.062,0-0.089,0l-61.182,0.242
c-6.444-14.462-14.428-28.14-23.871-40.913l30.417-53.143c6.294-11.001,2.481-25.025-8.52-31.316L369.403,5.335
c-5.281-3.023-11.545-3.822-17.424-2.231c-5.872,1.598-10.872,5.462-13.892,10.747L307.665,67
c-15.766-1.662-31.653-1.613-47.363,0.144l-30.796-52.892c-3.063-5.263-8.094-9.091-13.975-10.646
c-5.884-1.551-12.148-0.704-17.408,2.359L85.068,71.823c-10.949,6.38-14.657,20.429-8.28,31.38l30.765,52.831
c-4.761,6.484-9.048,13.076-12.953,19.899c-3.904,6.824-7.417,13.855-10.6,21.255l-61.139,0.235
C10.187,197.472-0.046,207.785,0,220.456L0.48,351.29c0.024,6.086,2.463,11.919,6.784,16.206
c4.299,4.269,10.11,6.661,16.166,6.661c0.028,0,0.058,0,0.086,0l61.203-0.229c6.432,14.452,14.413,28.131,23.868,40.915
l-30.413,53.141c-3.023,5.284-3.825,11.548-2.231,17.423c1.597,5.872,5.462,10.872,10.747,13.896l113.535,64.977
c3.596,2.056,7.513,3.032,11.38,3.032c7.962,0,15.701-4.146,19.942-11.552l30.417-53.149c15.799,1.671,31.684,1.619,47.348-0.144
l30.799,52.89c3.066,5.26,8.094,9.088,13.978,10.643C359.967,567.552,366.23,566.705,371.49,563.638z M341.129,465.911
c-4.902-8.418-14.599-12.815-24.137-10.994c-20.588,3.935-42.174,3.999-63.128,0.202c-9.572-1.735-19.184,2.741-24.015,11.181
l-26.748,46.745l-73.694-42.18l26.75-46.741c4.832-8.439,3.819-19.006-2.521-26.371c-13.978-16.239-24.685-34.594-31.818-54.554
c-3.265-9.131-11.918-15.227-21.61-15.227c-0.027,0-0.058,0-0.085,0l-53.825,0.199l-0.315-84.937l53.819-0.205
c9.722-0.04,18.366-6.197,21.576-15.374c3.69-10.557,7.962-20.019,13.06-28.917c5.101-8.914,11.089-17.387,18.311-25.897
c6.294-7.417,7.225-17.993,2.334-26.396l-27.081-46.509l73.385-42.754l27.078,46.497c4.893,8.4,14.544,12.821,24.095,11.004
c20.716-3.911,42.317-3.978,63.189-0.19c9.557,1.753,19.189-2.742,24.019-11.178l26.753-46.744l73.697,42.179l-26.753,46.742
c-4.826,8.437-3.816,19,2.521,26.368c13.956,16.221,24.669,34.587,31.842,54.59c3.271,9.119,11.919,15.202,21.604,15.202
c0.031,0,0.062,0,0.092,0l53.789-0.214l0.315,84.927l-53.783,0.192c-9.712,0.037-18.351,6.182-21.569,15.347
c-3.746,10.654-8.023,20.131-13.082,28.975c-5.064,8.847-11.067,17.338-18.356,25.958c-6.271,7.418-7.194,17.978-2.305,26.368
l27.078,46.472l-73.391,42.749L341.129,465.911z"/>
<path d="M392.531,346.458c16.472-28.773,20.746-62.24,12.047-94.232s-29.342-58.685-58.115-75.151
c-18.761-10.74-40.05-16.417-61.562-16.417c-44.446,0-85.762,23.944-107.822,62.485c-33.994,59.404-13.327,135.39,46.071,169.386
c18.764,10.737,40.052,16.411,61.564,16.411C329.158,408.943,370.475,385.001,392.531,346.458z M352.696,323.658
c-13.902,24.293-39.955,39.385-67.985,39.385c-13.528,0-26.934-3.58-38.764-10.349c-37.433-21.426-50.456-69.312-29.033-106.751
c13.905-24.291,39.958-39.385,67.987-39.385c13.528,0,26.932,3.58,38.762,10.355c18.136,10.379,31.142,27.197,36.628,47.359
C365.771,284.435,363.075,305.524,352.696,323.658z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Before

Width:  |  Height:  |  Size: 1018 KiB

After

Width:  |  Height:  |  Size: 1018 KiB

Before

Width:  |  Height:  |  Size: 947 KiB

After

Width:  |  Height:  |  Size: 947 KiB

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before

Width:  |  Height:  |  Size: 474 KiB

After

Width:  |  Height:  |  Size: 474 KiB

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

+259 -113
View File
File diff suppressed because one or more lines are too long
+11 -3
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;
@@ -31,9 +31,17 @@ export function saveGame(
data: GameData data: GameData
): void { ): void {
console.debug("Saving game..."); console.debug("Saving game...");
const adventurers = {} as { [key: string]: any };
for (const adventurerId in data.adventurers) {
const adventurer: {[key: string]: any} = JSON.parse(JSON.stringify(data.adventurers[adventurerId]));
delete adventurer.portrait;
adventurers[adventurerId] = adventurer;
}
window.localStorage.setItem("savedGame", JSON.stringify({ window.localStorage.setItem("savedGame", JSON.stringify({
guild: data.guild, guild: data.guild,
adventurers: data.adventurers, adventurers: adventurers,
missives: data.missives, missives: data.missives,
lastQuestGot: data.lastQuestGot, lastQuestGot: data.lastQuestGot,
lastRecruitAction: data.lastRecruitAction, lastRecruitAction: data.lastRecruitAction,
+96 -7
View File
File diff suppressed because one or more lines are too long
+15 -4
View File
@@ -1,7 +1,11 @@
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"; import {formatGold} from "@/classes/NumberMagic";
import QuestExpUpgrade from "@/classes/guildUpgrades/QuestExpUpgrade";
import QuestGoldUpgrade from "@/classes/guildUpgrades/QuestGoldUpgrade";
import AutoFinishQuestsUpgrade from "@/classes/guildUpgrades/AutoFinishQuestsUpgrade";
const MAX_LEVEL: number = 8;
export class Guild { export class Guild {
gold: number; gold: number;
@@ -9,6 +13,9 @@ export class Guild {
displayUpgradeCost: number|string; displayUpgradeCost: number|string;
upgradeCost: number|null = null; upgradeCost: number|null = null;
adventurerCapacity: AdventurerCapacityUpgrade; adventurerCapacity: AdventurerCapacityUpgrade;
expModifier: QuestExpUpgrade;
goldModifier: QuestGoldUpgrade;
autoFinishQuestsUpgrade: AutoFinishQuestsUpgrade;
constructor(level: number, gold: number, upgrades: {[index:string]: GuildUpgrade} = {}) { constructor(level: number, gold: number, upgrades: {[index:string]: GuildUpgrade} = {}) {
this.gold = gold; this.gold = gold;
@@ -18,6 +25,9 @@ export class Guild {
this.upgradeCost = this.getUpgradeCost(); this.upgradeCost = this.getUpgradeCost();
this.adventurerCapacity = upgrades.adventurerCapacity as AdventurerCapacityUpgrade ?? new AdventurerCapacityUpgrade(); this.adventurerCapacity = upgrades.adventurerCapacity as AdventurerCapacityUpgrade ?? new AdventurerCapacityUpgrade();
this.expModifier = upgrades.expModifier as QuestExpUpgrade ?? new QuestExpUpgrade();
this.goldModifier = upgrades.goldModifier as QuestGoldUpgrade ?? new QuestGoldUpgrade();
this.autoFinishQuestsUpgrade = upgrades.autoFinishQuestsUpgrade as AutoFinishQuestsUpgrade ?? new AutoFinishQuestsUpgrade();
} }
@@ -27,7 +37,7 @@ export class Guild {
if (this.gold < cost) return; if (this.gold < cost) return;
this.gold -= cost; this.gold -= cost;
this.level += 1; this.level += 1;
if (this.level >= 7) { if (this.level >= MAX_LEVEL) {
this.displayUpgradeCost = "Max level"; this.displayUpgradeCost = "Max level";
this.upgradeCost = null; this.upgradeCost = null;
} else { } else {
@@ -43,7 +53,7 @@ export class Guild {
} }
isMaxLevel(): boolean { isMaxLevel(): boolean {
return this.level >= 7; return this.level >= MAX_LEVEL;
} }
} }
@@ -53,5 +63,6 @@ const upgradeCosts = {
"3": 5000, "3": 5000,
"4": 10000, "4": 10000,
"5": 25000, "5": 25000,
"6": 50000, "6": 100000,
"7": 750000,
} as {[index:string]: number} } as {[index:string]: number}
+7
View File
@@ -0,0 +1,7 @@
export default interface MaxLevellable {
maxLevel: number;
isMaxLevel(): boolean;
}
+2 -1
View File
@@ -11,7 +11,8 @@ const damageFormatter = new Intl.NumberFormat('en-US', {
notation: "compact", notation: "compact",
}); });
export function formatGold(number: number): string { export function formatGold(number: number | null): string {
if (number === null) return "";
return goldFormatter.format(number); return goldFormatter.format(number);
} }
+18 -5
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 {
@@ -33,8 +44,10 @@ export class Quest {
/** /**
* Generate rewards for a quest and return it * Generate rewards for a quest and return it
* @param quest * @param quest
* @param expModifier - multiplification modifier for the exp reward
* @param goldModifier - multiplification modifier for the gold reward
*/ */
export function getQuestWithRewards(quest: Quest) { export function getQuestWithRewards(quest: Quest, expModifier: number = 1, goldModifier: number = 1) {
let maxProgress = 1; let maxProgress = 1;
@@ -69,8 +82,8 @@ export function getQuestWithRewards(quest: Quest) {
break; break;
} }
let goldReward = Math.floor(maxProgress/6); let goldReward = Math.floor(maxProgress/6 * goldModifier);
let expReward = Math.floor(Math.floor(maxProgress/120) - maxProgress/1000); let expReward = Math.floor((Math.floor(maxProgress/120) - maxProgress/1000) * expModifier);
// add some randomness to the rewards // add some randomness to the rewards
goldReward = Math.floor(randomNumberBetween(goldReward * 0.95, goldReward * 1.1)); goldReward = Math.floor(randomNumberBetween(goldReward * 0.95, goldReward * 1.1));
@@ -81,4 +94,4 @@ export function getQuestWithRewards(quest: Quest) {
function randomNumberBetween(min: number, max: number) { function randomNumberBetween(min: number, max: number) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
@@ -1,6 +1,6 @@
import {GuildUpgrade} from "@/classes/GuildUpgrade"; import {GuildUpgrade} from "@/classes/GuildUpgrade";
export class AdventurerCapacityUpgrade extends GuildUpgrade { export default class AdventurerCapacityUpgrade extends GuildUpgrade {
constructor(level: number = 1) { constructor(level: number = 1) {
super(); super();
this.level = level; this.level = level;
@@ -14,8 +14,8 @@ export class AdventurerCapacityUpgrade extends GuildUpgrade {
} }
getCostForLevel(level: number): number { getCostForLevel(level: number): number {
const scalingFactor = Math.pow(1.25, level - 1); if (level === 1) return 1500;
return Math.floor(1500 * scalingFactor * Math.pow(level, 1.25)); return Math.floor(1500 * (level * 4));
} }
/** /**
@@ -0,0 +1,70 @@
import {GuildUpgrade} from "@/classes/GuildUpgrade";
import type MaxLevellable from "@/classes/MaxLevellable";
import {QuestRank} from "@/classes/QuestRank";
export default class AutoFinishQuestsUpgrade extends GuildUpgrade implements MaxLevellable {
maxLevel: number;
constructor(level: number = 1) {
super();
this.level = level;
this.nextLevelCost = this.getCostForLevel(this.level);
this.guildLevelRequirement = 7;
this.maxLevel = 8;
}
upgrade(): void {
this.level += 1;
this.nextLevelCost = this.getCostForLevel(this.level);
}
getCostForLevel(level: number): number {
switch (level) {
case 1:
return 25000;
case 2:
return 50000;
case 3:
return 75000;
case 4:
return 150000;
case 5:
return 275000;
case 6:
return 750000;
case 7:
return 1500000;
case 8:
return 2500000;
default:
return 0;
}
}
isMaxLevel(): boolean {
return this.level >= this.maxLevel;
}
getRanksToAutoFinishQuestsIn(): Array<QuestRank> {
switch (this.level) {
case 1:
default:
return [];
case 2:
return [QuestRank.F];
case 3:
return [QuestRank.F, QuestRank.E];
case 4:
return [QuestRank.F, QuestRank.E, QuestRank.D];
case 5:
return [QuestRank.F, QuestRank.E, QuestRank.D, QuestRank.C];
case 6:
return [QuestRank.F, QuestRank.E, QuestRank.D, QuestRank.C, QuestRank.B];
case 7:
return [QuestRank.F, QuestRank.E, QuestRank.D, QuestRank.C, QuestRank.B, QuestRank.A];
case 8:
return [QuestRank.F, QuestRank.E, QuestRank.D, QuestRank.C, QuestRank.B, QuestRank.A, QuestRank.S];
}
}
}
@@ -0,0 +1,24 @@
import {GuildUpgrade} from "@/classes/GuildUpgrade";
export default class QuestExpUpgrade extends GuildUpgrade {
constructor(level: number = 1) {
super();
this.level = level;
this.nextLevelCost = this.getCostForLevel(this.level);
this.guildLevelRequirement = 8;
}
upgrade(): void {
this.level += 1;
this.nextLevelCost = this.getCostForLevel(this.level);
}
getCostForLevel(level: number): number {
if (level === 1) return 1000000;
return Math.floor(1000000 * (level * 1.05));
}
getModifier(): number {
return 1 + (this.level * 0.1);
}
}
@@ -0,0 +1,24 @@
import {GuildUpgrade} from "@/classes/GuildUpgrade";
export default class QuestGoldUpgrade extends GuildUpgrade {
constructor(level: number = 1) {
super();
this.level = level;
this.nextLevelCost = this.getCostForLevel(this.level);
this.guildLevelRequirement = 8;
}
upgrade(): void {
this.level += 1;
this.nextLevelCost = this.getCostForLevel(this.level);
}
getCostForLevel(level: number): number {
if (level === 1) return 1000000;
return Math.floor(1000000 * (level * 1.05));
}
getModifier(): number {
return 1 + (this.level * 0.1);
}
}
File diff suppressed because one or more lines are too long
+2 -3
View File
@@ -22,11 +22,10 @@ export default defineComponent({
}), }),
props: { props: {
adventurers: { adventurers: {
type: Object as PropType<{ [key: string]: Adventurer }>, type: Object as PropType<Array<Adventurer>>,
default() { default() {
return {} as { [key: string]: Adventurer }; return [] as Array<Adventurer>;
}, },
required: true,
}, },
}, },
+40 -19
View File
@@ -21,7 +21,9 @@ import { vOnClickOutside } from '@vueuse/components'
<span>+</span> <span>+</span>
</article> </article>
<div class="selection" v-if="selection" v-on-click-outside="closeSelect"> <div class="selection" v-if="selection" v-on-click-outside="closeSelect">
<button <span>Choose adventurer</span>
<div class="list">
<button
class="slot" class="slot"
v-for="adventurer in allAdventurers" v-for="adventurer in allAdventurers"
:key="adventurer.id" :key="adventurer.id"
@@ -31,11 +33,12 @@ import { vOnClickOutside } from '@vueuse/components'
$emit('hireAdventurer', adventurer.id); $emit('hireAdventurer', adventurer.id);
selection = false; selection = false;
}" }"
> >
<AdventurerTile <AdventurerTile
:adventurer="adventurer" :adventurer="adventurer"
/> />
</button> </button>
</div>
</div> </div>
</template> </template>
@@ -61,16 +64,17 @@ export default defineComponent({
}, },
}, },
allAdventurers: { allAdventurers: {
type: Object as PropType<{[key: string]: Adventurer}>, type: Object as PropType<Array<Adventurer>>,
default() { default() {
return {} as {[key: string]: Adventurer}; return [] as Array<Adventurer>;
}, },
required: true,
}, },
}, },
methods: { methods: {
closeSelect() { closeSelect() {
this.selection = false; setTimeout(() => {
this.selection = false;
}, 0);
} }
} }
}) })
@@ -81,22 +85,35 @@ export default defineComponent({
.selection { .selection {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 50%; left: 0;
width: max-content; width: 100%;
max-width: 17rem; max-width: 100%;
transform: translateX(-50%) translateY(104%);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-items: center; align-items: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5rem; gap: 0.5rem;
padding: 0.5rem; padding: 0.5rem;
background-color: rgba(0,0,0, 0.2); background-color: rgba(0,0,0, 0.6);
backdrop-filter: blur(4px);
z-index: 2; z-index: 2;
cursor: default; cursor: default;
max-height: 12rem; height: 100%;
overflow-y: auto; overflow-y: scroll;
scrollbar-gutter: stable;
.list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 1rem;
max-width: 100%;
}
span {
font-size: 1.5rem;
color: #fff;
}
.slot { .slot {
width: 5rem; width: 5rem;
height: 5rem; height: 5rem;
@@ -130,5 +147,9 @@ export default defineComponent({
width: 100%; width: 100%;
height: 100%; height: 100%;
font-size: 4.5rem; font-size: 4.5rem;
color: #000;
span {
transform: translateY(-0.5rem);
}
} }
</style> </style>
+2 -3
View File
@@ -20,9 +20,6 @@ export default defineComponent({
props: { props: {
adventurer: { adventurer: {
type: Object as PropType<Adventurer>, type: Object as PropType<Adventurer>,
default() {
return {} as Adventurer;
},
required: true, required: true,
} }
}, },
@@ -53,6 +50,8 @@ export default defineComponent({
line-height: 1; line-height: 1;
color: rgba(0, 0, 0, 0.75); color: rgba(0, 0, 0, 0.75);
position: relative; position: relative;
background: rgb(2,0,36);
background: radial-gradient(circle, rgba(2,0,36,1) 0%, rgb(69, 69, 84) 57%, rgb(85, 112, 117) 100%);
.level { .level {
position: absolute; position: absolute;
+89
View File
@@ -0,0 +1,89 @@
<template>
<div class="missives-wrapper">
<h1 v-if="label !== undefined">{{ label }}</h1>
<section class="missives">
<QuestMissive
v-for="(missive, key) in quests"
:key="key"
:adventurers="adventurers"
:missive="missive"
@click="finalizeQuest(missive)"
/>
</section>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
import QuestMissive from "@/components/QuestMissive.vue";
export default defineComponent({
name: "QuestGroup",
components: {QuestMissive},
props: {
quests: {
type: Object,
required: true,
},
adventurers: {
type: Object,
required: true,
},
finalizeQuest: {
type: Function,
required: true,
},
label: {
type: String,
default: undefined,
}
},
})
</script>
<style scoped lang="scss">
h1 {
text-align: center;
}
.missives-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.missives {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
gap: 1rem;
padding-block: 0.5rem;
padding-inline: 5rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
width: 100vw;
max-width: 100%;
}
@media(min-width: 800px) {
.missives-wrapper {
padding-inline: 1rem;
max-width: 100vw;
overflow-x: hidden;
}
.missives {
display: grid;
padding-inline: 0;
max-width: 1200px;
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
grid-auto-rows: auto;
gap: 1rem;
overflow-x: visible;
}
}
</style>
+37 -16
View File
@@ -4,21 +4,22 @@
:class="{done: missive.maxProgress <= missive.progressPoints}" :class="{done: missive.maxProgress <= missive.progressPoints}"
> >
<div class="parchment"> <div class="parchment">
<img src="/img/quests/backgrounds/dirty_paper.png" alt="parchment"> <Parchment/>
</div> </div>
<div class="stain" v-if="stain"> <div class="stain" v-if="stain">
<img src="/img/quests/overlays/water_stain.png" alt="stain"> <WaterStain/>
</div> </div>
<div class="drink-stain" v-if="drinkStain.exists"> <div class="drink-stain" v-if="drinkStain.exists">
<img src="/img/quests/overlays/drink_stain.png" alt="stain"> <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">
<button class="slot"> <button class="slot">
<AdventurerComponent <AdventurerComponent
:adventurer="missive.adventurers[0]" :adventurer="missive.adventurers[0]"
:all-adventurers="adventurers" :all-adventurers="notBusyAdventurers"
@hire-adventurer="(id) => { @hire-adventurer="(id) => {
adventurers[id].busy = true; adventurers[id].busy = true;
missive.adventurers[0] = adventurers[id]; missive.adventurers[0] = adventurers[id];
@@ -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">
@@ -51,16 +52,24 @@ import type {Quest} from "@/classes/Quest";
import AdventurerComponent from "@/components/AdventurerMiniComponent.vue"; import AdventurerComponent from "@/components/AdventurerMiniComponent.vue";
import type {Adventurer} from "@/classes/Adventurer"; import type {Adventurer} from "@/classes/Adventurer";
import {defineComponent, type PropType} from "vue"; import {defineComponent, type PropType} from "vue";
import DrinkStain from "@/components/misc/DrinkStain.vue";
import WaterStain from "@/components/misc/WaterStain.vue";
import Parchment from "@/components/misc/Parchment.vue";
export default defineComponent({ export default defineComponent({
name: "QuestMissive", name: "QuestMissive",
components: {AdventurerComponent}, components: {Parchment, WaterStain, DrinkStain, AdventurerComponent},
computed: {
progressPercentageValue(): string {
return `${this.missive.progressPoints / this.missive.maxProgress * 100}%`;
},
notBusyAdventurers(): Adventurer[] {
return Object.values(this.adventurers).filter(adventurer => !adventurer.busy);
},
},
props: { props: {
missive: { missive: {
type: Object as PropType<Quest | any>, type: Object as PropType<Quest | any>,
default() {
return {} as Quest;
},
required: true, required: true,
}, },
adventurers: { adventurers: {
@@ -73,7 +82,7 @@ export default defineComponent({
}, },
data: () => { data: () => {
return { return {
progressPercentage: "0%", progressPercentage: 0,
stain: false, stain: false,
drinkStain: { drinkStain: {
exists: false, exists: false,
@@ -85,8 +94,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 +110,7 @@ export default defineComponent({
} }
}, },
watch: { watch: {
missive: { "missive.progressPoints": {
handler() { handler() {
this.updateProgress(); this.updateProgress();
}, },
@@ -114,11 +122,14 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.missive { .missive {
width: min(100%, 14rem); width: 14rem;
min-width: 14rem;
text-align: center; text-align: center;
border: 2px solid #000; border: 2px solid #000;
padding: 0.5rem; padding: 0.5rem;
position: relative; position: relative;
scroll-snap-align: center;
margin: 0 auto;
.parchment { .parchment {
position: absolute; position: absolute;
@@ -159,7 +170,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;
} }
@@ -176,6 +187,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;
@@ -214,7 +235,6 @@ export default defineComponent({
background-color: rgba(0, 0, 0, 0.2); background-color: rgba(0, 0, 0, 0.2);
cursor: pointer; cursor: pointer;
border-radius: 0.2rem; border-radius: 0.2rem;
position: relative;
} }
} }
@@ -257,4 +277,5 @@ export default defineComponent({
} }
} }
} }
</style> </style>
+66 -6
View File
@@ -3,6 +3,7 @@
<h2>Upgrades</h2> <h2>Upgrades</h2>
<div class="upgrade"> <div class="upgrade">
<span>Adventurer capacity (level {{ guild.adventurerCapacity.level }})</span> <span>Adventurer capacity (level {{ guild.adventurerCapacity.level }})</span>
<small>Increases the maximum amount of recruited adventurers</small>
<button <button
class="button metal" class="button metal"
v-if="guild.adventurerCapacity.nextLevelCost" v-if="guild.adventurerCapacity.nextLevelCost"
@@ -12,6 +13,43 @@
Upgrade ({{ formatGold(guild.adventurerCapacity.nextLevelCost) }} gold) Upgrade ({{ formatGold(guild.adventurerCapacity.nextLevelCost) }} gold)
</button> </button>
</div> </div>
<div class="upgrade" v-if="guild.level >= guild.autoFinishQuestsUpgrade.guildLevelRequirement">
<span>Auto-finish quests (level {{ guild.autoFinishQuestsUpgrade.level - 1 }})</span>
<small>Automatically finish quests when they are completed.</small>
<button
class="button metal"
v-if="guild.autoFinishQuestsUpgrade.nextLevelCost"
:disabled="guild.gold < guild.autoFinishQuestsUpgrade.nextLevelCost || guild.autoFinishQuestsUpgrade.isMaxLevel()"
@click="upgradeAutoFinishQuests()"
>
<span v-if="!guild.autoFinishQuestsUpgrade.isMaxLevel()">Upgrade ({{ formatGold(guild.autoFinishQuestsUpgrade.nextLevelCost) }} gold)</span>
<span v-else>Max level</span>
</button>
</div>
<div class="upgrade" v-if="guild.level >= guild.expModifier.guildLevelRequirement">
<span>Quest exp modifier (level {{ guild.expModifier.level - 1 }})</span>
<small>Increases exp from newly offered quests by 10% per level</small>
<button
class="button metal"
v-if="guild.expModifier.nextLevelCost"
:disabled="guild.gold < guild.expModifier.nextLevelCost"
@click="upgradeQuestExpModifier()"
>
Upgrade ({{ formatGold(guild.expModifier.nextLevelCost) }} gold)
</button>
</div>
<div class="upgrade" v-if="guild.level >= guild.goldModifier.guildLevelRequirement">
<span>Quest gold modifier (level {{ guild.goldModifier.level - 1 }})</span>
<small>Increases gold from newly offered quests by 10% per level</small>
<button
class="button metal"
v-if="guild.goldModifier.nextLevelCost"
:disabled="guild.gold < guild.goldModifier.nextLevelCost"
@click="upgradeQuestGoldModifier()"
>
Upgrade ({{ formatGold(guild.goldModifier.nextLevelCost) }} gold)
</button>
</div>
</section> </section>
</template> </template>
@@ -25,9 +63,6 @@ export default defineComponent({
props: { props: {
guild: { guild: {
type: Object as PropType<Guild>, type: Object as PropType<Guild>,
default() {
return new Guild(1, 0) as Guild;
},
required: true, required: true,
} }
}, },
@@ -39,7 +74,29 @@ export default defineComponent({
if (this.guild.gold < this.guild.adventurerCapacity.nextLevelCost) return; if (this.guild.gold < this.guild.adventurerCapacity.nextLevelCost) return;
this.guild.gold -= this.guild.adventurerCapacity.nextLevelCost; this.guild.gold -= this.guild.adventurerCapacity.nextLevelCost;
this.guild.adventurerCapacity.upgrade(); this.guild.adventurerCapacity.upgrade();
} },
upgradeAutoFinishQuests(): void {
if (!this.guild.autoFinishQuestsUpgrade) return;
if (this.guild.autoFinishQuestsUpgrade.isMaxLevel()) return;
if (!this.guild.autoFinishQuestsUpgrade.nextLevelCost) return;
if (this.guild.gold < this.guild.autoFinishQuestsUpgrade.nextLevelCost) return;
this.guild.gold -= this.guild.autoFinishQuestsUpgrade.nextLevelCost;
this.guild.autoFinishQuestsUpgrade.upgrade();
},
upgradeQuestExpModifier(): void {
if (!this.guild.expModifier) return;
if (!this.guild.expModifier.nextLevelCost) return;
if (this.guild.gold < this.guild.expModifier.nextLevelCost) return;
this.guild.gold -= this.guild.expModifier.nextLevelCost;
this.guild.expModifier.upgrade();
},
upgradeQuestGoldModifier(): void {
if (!this.guild.goldModifier) return;
if (!this.guild.goldModifier.nextLevelCost) return;
if (this.guild.gold < this.guild.goldModifier.nextLevelCost) return;
this.guild.gold -= this.guild.goldModifier.nextLevelCost;
this.guild.goldModifier.upgrade();
},
} }
}); });
</script> </script>
@@ -63,12 +120,15 @@ export default defineComponent({
font-size: 1.25rem; font-size: 1.25rem;
font-weight: bold; font-weight: bold;
display: flex; display: flex;
flex-direction: row; flex-direction: column;
flex-wrap: wrap;
width: min(25rem, 100%); width: min(25rem, 100%);
justify-content: center; justify-content: center;
align-items: center; align-items: center;
gap: 0.2rem; gap: 0.2rem;
small {
font-weight: normal;
line-height: 1;
}
} }
} }
</style> </style>
+35
View File
@@ -0,0 +1,35 @@
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
name: "DiscordLogo",
props: {
width: {
type: String,
default: "2rem",
},
height: {
type: String,
default: "2rem",
},
},
})
</script>
<template>
<svg
:width="width"
:height="height"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>Discord</title>
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
</svg>
</template>
<style scoped lang="scss">
svg {
}
</style>
File diff suppressed because one or more lines are too long
+33
View File
@@ -0,0 +1,33 @@
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
name: "GithubLogo",
props: {
width: {
type: String,
default: "2rem",
},
height: {
type: String,
default: "2rem",
},
},
})
</script>
<template>
<svg
:width="width"
:height="height"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="24" height="24" fill="none"/>
<path d="M12,2A10,10,0,0,0,8.84,21.5c.5.08.66-.23.66-.5V19.31C6.73,19.91,6.14,18,6.14,18A2.69,2.69,0,0,0,5,16.5c-.91-.62.07-.6.07-.6a2.1,2.1,0,0,1,1.53,1,2.15,2.15,0,0,0,2.91.83,2.16,2.16,0,0,1,.63-1.34C8,16.17,5.62,15.31,5.62,11.5a3.87,3.87,0,0,1,1-2.71,3.58,3.58,0,0,1,.1-2.64s.84-.27,2.75,1a9.63,9.63,0,0,1,5,0c1.91-1.29,2.75-1,2.75-1a3.58,3.58,0,0,1,.1,2.64,3.87,3.87,0,0,1,1,2.71c0,3.82-2.34,4.66-4.57,4.91a2.39,2.39,0,0,1,.69,1.85V21c0,.27.16.59.67.5A10,10,0,0,0,12,2Z"/>
</svg>
</template>
<style scoped lang="scss">
</style>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,28 +1,37 @@
<template> <template>
<div class="changelog panel pinned-paper"> <div class="changelog panel pinned-paper">
<div class="nail top-left"> <div class="nail top-left">
<img src="/img/quests/overlays/nail.png" alt="" draggable="false"/> <Nail/>
</div> </div>
<div class="nail top-right"> <div class="nail top-right">
<img src="/img/quests/overlays/nail.png" alt="" draggable="false"/> <Nail/>
</div> </div>
<h1>Changelog</h1> <h1>Changelog</h1>
<div class="changelog-entry" v-for="release in releases"> <div class="changelog-list">
<hr> <div class="changelog-entry" v-for="release in releases">
<h2>Version {{ release.name }}</h2> <hr>
<pre>{{ release.body }}</pre> <h2><span>Version {{ release.name }}</span><small class="date">{{ timeFormat.format(release.createdAt) }}</small></h2>
<pre>{{ release.body }}</pre>
</div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent} from "vue"; import {defineComponent} from "vue";
import Nail from "@/components/misc/Nail.vue";
export default defineComponent({ export default defineComponent({
name: "ChangelogComponent", name: "ChangelogComponent",
components: {Nail},
data: () => ({ data: () => ({
releases: [] as Array<any>, timeFormat: Intl.DateTimeFormat(Intl.DateTimeFormat().resolvedOptions().locale, {
year: "numeric",
month: "numeric",
day: "numeric",
}),
releases: [] as Array<{body: string, name: string, createdAt: Date}>,
lastPage: 1, lastPage: 1,
}), }),
methods: { methods: {
@@ -35,11 +44,11 @@ export default defineComponent({
if (result === null) return; if (result === null) return;
const json = await result.json(); const json = await result.json();
for (const release of json) { for (const release of json) {
const version = {} as any; const version = {} as any;
version.body = release.body.trim(); version.body = release.body.trim();
version.name = release.name; version.name = release.name;
version.createdAt = new Date(release.published_at);
if (release.body.length === 0) continue; if (release.body.length === 0) continue;
this.releases.push(version); this.releases.push(version);
} }
@@ -47,7 +56,6 @@ export default defineComponent({
}, },
async mounted() { async mounted() {
this.getReleases(1); this.getReleases(1);
} }
}); });
</script> </script>
@@ -57,11 +65,14 @@ export default defineComponent({
padding-block: 3rem; padding-block: 3rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: flex-start;
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
max-width: 45rem; max-width: 45rem;
min-height: 30rem;
max-height: 30rem;
width: 100%; width: 100%;
overflow-y: auto;
h1 { h1 {
font-size: 3rem; font-size: 3rem;
@@ -70,12 +81,33 @@ export default defineComponent({
text-align: center; text-align: center;
} }
.changelog-list {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
gap: 1rem;
width: 100%;
overflow-y: auto;
}
.changelog-entry { .changelog-entry {
width: 100%; width: 100%;
h2 { h2 {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: flex-end;
margin: 0; margin: 0;
padding-inline: 1rem; padding-inline: 1rem;
gap: 0.5rem;
}
.date {
color: rgba(0,0,0, 0.6);
font-size: 1rem;
} }
hr { hr {
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -2,7 +2,7 @@ import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import './assets/main.scss' import '@/assets/main.scss'
const app = createApp(App) const app = createApp(App)
+10 -6
View File
@@ -1,5 +1,9 @@
import {createRouter, createWebHashHistory} from 'vue-router' import {createRouter, createWebHashHistory} from 'vue-router'
import HomeView from '../views/HomeView.vue'
import HomeView from '@/views/HomeView.vue';
import QuestView from "@/views/QuestView.vue";
import AdventurerView from "@/views/AdventurerView.vue";
import TechnicalView from "@/views/TechnicalView.vue";
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL), history: createWebHashHistory(import.meta.env.BASE_URL),
@@ -7,23 +11,23 @@ const router = createRouter({
{ {
path: '/', path: '/',
name: 'guild', name: 'guild',
component: HomeView component: HomeView,
}, },
{ {
path: '/quests', path: '/quests',
name: 'quests', name: 'quests',
component: () => import('../views/QuestView.vue') component: QuestView,
}, },
{ {
path: '/adventurers', path: '/adventurers',
name: 'adventurers', name: 'adventurers',
component: () => import('../views/AdventurerView.vue') component: AdventurerView,
}, },
{ {
path: '/technical', path: '/technical',
name: 'technical', name: 'technical',
component: () => import('../views/TechnicalView.vue') component: TechnicalView,
} },
] ]
}) })

Some files were not shown because too many files have changed in this diff Show More