Implemented settings and info modals

This commit is contained in:
thepaperpilot 2021-05-22 15:29:06 -05:00
parent 82adac7317
commit 829eac7068
21 changed files with 27557 additions and 26956 deletions

54
package-lock.json generated
View file

@ -10,9 +10,11 @@
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-select": "^3.11.2",
"vuex": "^3.4.0" "vuex": "^3.4.0"
}, },
"devDependencies": { "devDependencies": {
"@types/vue-select": "^3.11.1",
"@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0", "@vue/cli-plugin-vuex": "~4.5.0",
@ -1634,6 +1636,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@types/vue-select": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/@types/vue-select/-/vue-select-3.11.1.tgz",
"integrity": "sha512-7Ho92vg5smiwCQLF6KXp50TNCYEaotXTloxw3OB1cinRb8WKIEYVU7Xiu95ObdQDZyz1ghL8Pzx39blNoIl4wg==",
"dev": true,
"dependencies": {
"vue": "^2.0.0"
}
},
"node_modules/@types/webpack": { "node_modules/@types/webpack": {
"version": "4.41.28", "version": "4.41.28",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.28.tgz", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.28.tgz",
@ -13588,6 +13599,14 @@
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
"dev": true "dev": true
}, },
"node_modules/vue-select": {
"version": "3.11.2",
"resolved": "https://registry.npmjs.org/vue-select/-/vue-select-3.11.2.tgz",
"integrity": "sha512-pIOcY8ajWNSwg8Ns4eHVr5ZWwqKCSZeQRymTnlUI8i+3QiQXF6JIM4lylK6mVfbccs4S6vOyxB7zmJBpp7tDUg==",
"peerDependencies": {
"vue": "2.x"
}
},
"node_modules/vue-style-loader": { "node_modules/vue-style-loader": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@ -16165,6 +16184,15 @@
} }
} }
}, },
"@types/vue-select": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/@types/vue-select/-/vue-select-3.11.1.tgz",
"integrity": "sha512-7Ho92vg5smiwCQLF6KXp50TNCYEaotXTloxw3OB1cinRb8WKIEYVU7Xiu95ObdQDZyz1ghL8Pzx39blNoIl4wg==",
"dev": true,
"requires": {
"vue": "^2.0.0"
}
},
"@types/webpack": { "@types/webpack": {
"version": "4.41.28", "version": "4.41.28",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.28.tgz", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.28.tgz",
@ -16276,7 +16304,6 @@
"integrity": "sha512-pM7CR3yXB6L8Gfn6EmX7FLNE3+V/15I3o33GkSNsWvgsMp6HVGXKkXgojrcfUUauyL1LZOdvTmu4enU2RePGHw==", "integrity": "sha512-pM7CR3yXB6L8Gfn6EmX7FLNE3+V/15I3o33GkSNsWvgsMp6HVGXKkXgojrcfUUauyL1LZOdvTmu4enU2RePGHw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/core": "^7.11.0",
"@babel/helper-compilation-targets": "^7.9.6", "@babel/helper-compilation-targets": "^7.9.6",
"@babel/helper-module-imports": "^7.8.3", "@babel/helper-module-imports": "^7.8.3",
"@babel/plugin-proposal-class-properties": "^7.8.3", "@babel/plugin-proposal-class-properties": "^7.8.3",
@ -16289,7 +16316,6 @@
"@vue/babel-plugin-jsx": "^1.0.3", "@vue/babel-plugin-jsx": "^1.0.3",
"@vue/babel-preset-jsx": "^1.2.4", "@vue/babel-preset-jsx": "^1.2.4",
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"core-js": "^3.6.5",
"core-js-compat": "^3.6.5", "core-js-compat": "^3.6.5",
"semver": "^6.1.0" "semver": "^6.1.0"
} }
@ -16441,7 +16467,8 @@
"version": "4.5.13", "version": "4.5.13",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.13.tgz", "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.13.tgz",
"integrity": "sha512-I1S9wZC7iI0Wn8kw8Zh+A2Qkf6s1M6vTGBkx8boXjuzfwEEyEHRxadsVCecZc8Mkpydo0nykj+MyYF96TKFuVA==", "integrity": "sha512-I1S9wZC7iI0Wn8kw8Zh+A2Qkf6s1M6vTGBkx8boXjuzfwEEyEHRxadsVCecZc8Mkpydo0nykj+MyYF96TKFuVA==",
"dev": true "dev": true,
"requires": {}
}, },
"@vue/cli-service": { "@vue/cli-service": {
"version": "4.5.13", "version": "4.5.13",
@ -16595,7 +16622,8 @@
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
"integrity": "sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==", "integrity": "sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==",
"dev": true "dev": true,
"requires": {}
}, },
"@vue/web-component-wrapper": { "@vue/web-component-wrapper": {
"version": "1.3.0", "version": "1.3.0",
@ -16810,7 +16838,8 @@
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
"dev": true "dev": true,
"requires": {}
}, },
"acorn-walk": { "acorn-walk": {
"version": "7.2.0", "version": "7.2.0",
@ -16840,13 +16869,15 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
"dev": true "dev": true,
"requires": {}
}, },
"ajv-keywords": { "ajv-keywords": {
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true "dev": true,
"requires": {}
}, },
"alphanum-sort": { "alphanum-sort": {
"version": "1.0.2", "version": "1.0.2",
@ -25878,6 +25909,12 @@
} }
} }
}, },
"vue-select": {
"version": "3.11.2",
"resolved": "https://registry.npmjs.org/vue-select/-/vue-select-3.11.2.tgz",
"integrity": "sha512-pIOcY8ajWNSwg8Ns4eHVr5ZWwqKCSZeQRymTnlUI8i+3QiQXF6JIM4lylK6mVfbccs4S6vOyxB7zmJBpp7tDUg==",
"requires": {}
},
"vue-style-loader": { "vue-style-loader": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@ -25915,7 +25952,8 @@
"vuex": { "vuex": {
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
"integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==" "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==",
"requires": {}
}, },
"watchpack": { "watchpack": {
"version": "1.7.5", "version": "1.7.5",

View file

@ -10,9 +10,11 @@
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-select": "^3.11.2",
"vuex": "^3.4.0" "vuex": "^3.4.0"
}, },
"devDependencies": { "devDependencies": {
"@types/vue-select": "^3.11.1",
"@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0", "@vue/cli-plugin-vuex": "~4.5.0",

View file

@ -35,24 +35,21 @@ export default {
<style> <style>
* { * {
transition-duration: 0.5s; transition-duration: 0.5s;
text-align: center;
font-family: "Roboto Mono", monospace; font-family: "Roboto Mono", monospace;
font-weight: bold; font-weight: bold;
table-align: center;
margin: auto; margin: auto;
-webkit-text-size-adjust: none;
text-size-adjust: none; text-size-adjust: none;
} }
*:focus { *:focus {
outline: none; outline: none;
webkit-outline: none;
} }
body { body {
overflow: hidden; overflow: hidden;
min-width: 640px; min-width: 640px;
transition: none; transition: none;
text-align: center;
} }
html, body, #app { html, body, #app {
@ -65,19 +62,21 @@ h1, h2, h3, b, input {
} }
a, a,
button,
.link { .link {
display: block; display: block;
font-size: 20px; color: var(--link);
color: #02f2f2; background: none;
border: none;
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
} }
a:hover, a:hover,
button:hover,
.link:hover { .link:hover {
transform: scale(1.2, 1.2); text-shadow: 5px 0 10px var(--link),
text-shadow: 5px 0 10px #02f2f2, -3px 0 12px var(--link);
-3px 0 12px #02f2f2;
} }
ul { ul {

View file

@ -0,0 +1,120 @@
<template>
<Modal :show="show" @close="$emit('closeDialog', 'Info')">
<div slot="header" class="info-modal-header">
<img class="info-modal-logo" v-if="logo" :src="logo" :alt="title" />
<div class="info-modal-title">
<h2>{{ title }}</h2>
<h4>v{{ versionNumber}}: {{ versionTitle }}</h4>
</div>
</div>
<div slot="body">
<div v-if="author">
By {{ author }}
</div>
<div>
Made in TMT-X, by thepaperpilot with inspiration from Acameada, Jacorb, and Aarex
</div>
<br/>
<div class="link" @click="$emit('openDialog', 'Changelog')">Changelog</div>
<br>
<div>
<a :href="discordLink" v-if="discordLink !== 'https://discord.gg/WzejVAx'">
<img src="images/discord.png" class="info-modal-discord" />
{{ discordLink }}
</a>
</div>
<div>
<a href="https://discord.gg/WzejVAx" class="info-modal-discord-link">
<img src="images/discord.png" class="info-modal-discord" />
The Paper Pilot Community
</a>
</div>
<div>
<a href="https://discord.gg/F3xveHV" class="info-modal-discord-link">
<img src="images/discord.png" class="info-modal-discord" />
The Modding Tree
</a>
</div>
<div>
<a href="https://discord.gg/wwQfgPa" class="info-modal-discord-link">
<img src="images/discord.png" class="info-modal-discord" />
Jacorb's Games
</a>
</div>
<br>
<div>Time Played: {{ timePlayed }}</div>
<div v-if="hotkeys.length > 0">
<h4>Hotkeys</h4>
<div v-for="key in hotkeys" :key="key.key">
{{ key.key }}: {{ key.description }}
</div>
</div>
</div>
</Modal>
</template>
<script>
import Modal from './Modal.vue';
import modInfo from '../../data/mod.js';
import { formatTime } from '../../util/bignum.js';
export default {
name: 'Info',
data() {
const { title, logo, author, discordName, discordLink, versionNumber, versionTitle } = modInfo;
return { title, logo, author, discordName, discordLink, versionNumber, versionTitle };
},
props: {
show: Boolean
},
components: {
Modal
},
computed: {
timePlayed() {
return formatTime(this.$store.state.timePlayed);
},
hotkeys() {
// TODO check layer is unlocked and hotkey is unlocked
return this.$root.hotkeys.filter(hotkey => hotkey || true);
}
}
};
</script>
<style scoped>
.info-modal-header {
display: flex;
margin: -20px;
margin-bottom: 0;
background: var(--secondary-background);
align-items: center;
}
.info-modal-header * {
margin: 0;
}
.info-modal-logo {
height: 4em;
width: 4em;
}
.info-modal-title {
display: flex;
flex-direction: column;
padding: 10px 0;
margin-left: 10px;
}
.info-modal-discord-link {
display: flex;
align-items: center;
}
.info-modal-discord {
height: 2em;
margin: 0;
margin-right: 4px;
}
</style>

View file

@ -0,0 +1,99 @@
<template>
<transition name="modal">
<div class="modal-mask" v-show="show" v-on:pointerdown.self="$emit('close')">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<div class="modal-default-footer">
<div class="modal-default-flex-grow"></div>
<button class="modal-default-button" @click="$emit('close')">
Close
</button>
</div>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
name: 'Modal',
props: {
show: Boolean
}
}
</script>
<style scoped>
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease;
}
.modal-wrapper {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.modal-container {
width: 400px;
max-width: calc(95vw - 40px);
background-color: var(--background);
padding: 20px;
border-radius: 5px;
transition: all 0.3s ease;
text-align: left;
border: var(--modal-border);
}
.modal-body {
margin: 20px 0;
}
.modal-default-footer {
display: flex;
}
.modal-default-flex-grow {
flex-grow: 1;
}
.modal-enter {
opacity: 0;
}
.modal-leave-active {
opacity: 0;
}
.modal-enter .modal-container,
.modal-leave-active .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
</style>

View file

@ -1,8 +1,9 @@
<template> <template>
<div>
<div class="nav" v-if="useHeader"> <div class="nav" v-if="useHeader">
<img v-if="banner" :src="banner" height="100%" :alt="title" /> <img v-if="banner" :src="banner" height="100%" :alt="title" />
<div v-else class="title">{{ title }}</div> <div v-else class="title">{{ title }}</div>
<div class="version" @click="showTab('changelog-tab')">v{{ version }}</div> <div class="version" @click="openDialog('Changelog')">v{{ version }}</div>
<div style="flex-grow: 1"></div> <div style="flex-grow: 1"></div>
<div class="discord"> <div class="discord">
<img src="images/discord.png" @click="window.open(discordLink, 'mywindow')" /> <img src="images/discord.png" @click="window.open(discordLink, 'mywindow')" />
@ -10,14 +11,13 @@
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'"> <li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
<a :href="discordLink" target="_blank">{{ discordName }}</a> <a :href="discordLink" target="_blank">{{ discordName }}</a>
</li> </li>
<li><a href="https://discord.gg/WzejVAx" target="_blank">TMT-X Server</a></li> <li><a href="https://discord.gg/WzejVAx" target="_blank">The Paper Pilot Community</a></li>
<li><a href="https://discord.gg/F3xveHV" target="_blank">TMT Server</a></li> <li><a href="https://discord.gg/F3xveHV" target="_blank">The Modding Tree</a></li>
<li><a href="http://discord.gg/wwQfgPa" target="_blank">TPT Server</a></li> <li><a href="http://discord.gg/wwQfgPa" target="_blank">Jacorb's Games</a></li>
</ul> </ul>
</div> </div>
<div v-if="tab !== 'info-tab'" class="info" @click="showTab('info-tab')"><br/>i</div> <div class="info" @click="openDialog('Info')"><br/>i</div>
<img v-if="tab !== 'options-tab'" class="options" src="images/options_wheel.png" <img class="options" src="images/options_wheel.png" @click="openDialog('Options')" />
@click="showTab('options-tab')" />
</div> </div>
<div v-else> <div v-else>
<div class="discord overlay"> <div class="discord overlay">
@ -26,21 +26,24 @@
<li v-if="discordLink !== 'https://discord.gg/WzejVAx'"> <li v-if="discordLink !== 'https://discord.gg/WzejVAx'">
<a :href="discordLink" target="_blank">{{ discordName }}</a> <a :href="discordLink" target="_blank">{{ discordName }}</a>
</li> </li>
<li><a href="https://discord.gg/WzejVAx" target="_blank">TMT-X Server</a></li> <li><a href="https://discord.gg/WzejVAx" target="_blank">The Paper Pilot Community</a></li>
<li><a href="https://discord.gg/F3xveHV" target="_blank">TMT Server</a></li> <li><a href="https://discord.gg/F3xveHV" target="_blank">The Modding Tree</a></li>
<li><a href="http://discord.gg/wwQfgPa" target="_blank">TPT Server</a></li> <li><a href="http://discord.gg/wwQfgPa" target="_blank">Jacorb's Games</a></li>
</ul> </ul>
</div> </div>
<div v-if="tab !== 'info-tab'" class="info overlay" @click="showTab('info-tab')"><br/>i</div> <div class="info overlay" @click="openDialog('Info')"><br/>i</div>
<img v-if="tab !== 'options-tab'" class="options overlay" src="images/options_wheel.png" <img class="options overlay" src="images/options_wheel.png" @click="openDialog('Options')" />
@click="showTab('options-tab')" /> <div class="version overlay" @click="openDialog('Changelog')">v{{ version }}</div>
<div class="version overlay" @click="showTab('changelog-tab')">v{{ version }}</div> </div>
<Info :show="showInfo" @openDialog="openDialog" @closeDialog="closeDialog" />
<Options :show="showOptions" @closeDialog="closeDialog" />
</div> </div>
</template> </template>
<script> <script>
import { mapState } from 'vuex';
import modInfo from '../../data/mod.js'; import modInfo from '../../data/mod.js';
import Info from './Info';
import Options from './Options';
export default { export default {
name: 'Nav', name: 'Nav',
@ -51,17 +54,24 @@ export default {
title: modInfo.title, title: modInfo.title,
discordName: modInfo.discordName, discordName: modInfo.discordName,
discordLink: modInfo.discordLink, discordLink: modInfo.discordLink,
version: modInfo.versionNumber version: modInfo.versionNumber,
showInfo: false,
showOptions: false,
showChangelog: false
} }
}, },
computed: mapState([ 'tab' ]), components: {
Info, Options
},
methods: { methods: {
openDiscord() { openDiscord() {
window.open(this.discordLink, 'mywindow'); window.open(this.discordLink, 'mywindow');
}, },
showTab(tab) { openDialog(dialog) {
console.log("TODO show tab", tab); this[`show${dialog}`] = true;
// tabs.splice(1, tabs.length, tab); },
closeDialog(dialog) {
this[`show${dialog}`] = false;
} }
} }
}; };
@ -69,13 +79,12 @@ export default {
<style scoped> <style scoped>
.nav { .nav {
background-color: var(--background_nav); background-color: var(--secondary-background);
display: flex; display: flex;
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
height: 50px; height: 50px;
z-index: 9999999;
width: 100%; width: 100%;
} }
@ -85,6 +94,10 @@ export default {
margin-left: 12px; margin-left: 12px;
} }
.overlay {
z-index: 100;
}
.discord { .discord {
width: 40px; width: 40px;
height: 40px; height: 40px;
@ -105,11 +118,11 @@ export default {
.discord-links { .discord-links {
position: fixed; position: fixed;
top: 45px; top: 45px;
padding: 30px; padding: 20px;
right: -280px; right: -280px;
width: 200px; width: 200px;
transition: right .25s ease; transition: right .25s ease;
background: var(--background_nav); background: var(--secondary-background);
} }
.discord.overlay .discord-links { .discord.overlay .discord-links {
@ -119,6 +132,10 @@ export default {
transition: left .25s ease; transition: left .25s ease;
} }
.discord-links li {
margin-bottom: 4px;
}
.discord:not(.overlay):hover .discord-links { .discord:not(.overlay):hover .discord-links {
right: 0; right: 0;
} }

View file

@ -0,0 +1,91 @@
<template>
<Modal :show="show" @close="$emit('closeDialog', 'Options')">
<div slot="header">
<h2>Options</h2>
</div>
<div slot="body">
<div class="actions">
<button @click="save">Manually Save</button>
<button @click="exportSave">Export</button>
<button @click="importSave" class="danger">Import</button>
<button @click="hardReset" class="danger">Hard Reset</button>
</div>
<Toggle title="Autosave" :value="autosave" @change="toggleOption('autosave')" />
<Toggle title="Offline Production" :value="offlineProd" @change="toggleOption('offlineProd')" />
<Toggle title="Show TPS" :value="showTPS" @change="toggleOption('showTPS')" />
<Select title="Theme" :options="themes" :value="theme" @change="setTheme" default="default" />
</div>
</Modal>
</template>
<script>
import Modal from './Modal.vue';
import Toggle from './fields/Toggle.vue';
import Select from './fields/Select.vue';
import themes from '../../data/themes.js';
import { camelToTitle } from '../../util/common.js';
import { mapState } from 'vuex';
import { SET_SETTING } from '../../store/mutations.js';
export default {
name: 'Options',
props: {
show: Boolean
},
data() {
return {
themes: Object.keys(themes).map(theme => ({ label: camelToTitle(theme), value: theme }))
}
},
components: {
Modal, Toggle, Select
},
computed: mapState([ "autosave", "offlineProd", "showTPS", "theme" ]),
methods: {
toggleOption(option) {
this.$store.commit(SET_SETTING, { setting: option });
},
setTheme(theme) {
this.$store.commit(SET_SETTING, { setting: "theme", value: theme });
},
save() {
},
hardReset() {
},
exportSave() {
},
importSave() {
}
}
};
</script>
<style scoped>
.actions {
display: flex;
justify-content: space-between;
padding-bottom: 10px;
}
.actions * {
margin: 0;
}
.danger {
border: solid 2px var(--danger);
padding-right: 0;
}
.danger::after {
content: "!";
color: white;
background: var(--danger);
padding: 2px;
margin-left: 6px;
height: 100%;
}
</style>

View file

@ -23,6 +23,6 @@ export default {
position: absolute; position: absolute;
left: 10px; left: 10px;
bottom: 10px; bottom: 10px;
z-index: 10000000; z-index: 100;
} }
</style> </style>

View file

@ -2,7 +2,9 @@
<div class="tabs"> <div class="tabs">
<div v-for="(tab, index) in tabs" class="tab" :key="index"> <div v-for="(tab, index) in tabs" class="tab" :key="index">
<button v-if="index > 0" class="goBack" @click="goBack(index)"></button> <button v-if="index > 0" class="goBack" @click="goBack(index)"></button>
{{ tab }} <component :is="layers[tab].component" :index="index" v-if="tab in layers && layers[tab].component" />
<tab-layer :layer="tab" :index="index" v-else-if="tab in layers" />
<component :is="tab" :index="index" v-else />
<div class="separator" v-if="index !== tabs.length - 1"></div> <div class="separator" v-if="index !== tabs.length - 1"></div>
</div> </div>
</div> </div>
@ -10,13 +12,19 @@
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { SET_TABS } from '../../store/mutations.js';
export default { export default {
name: 'Tabs', name: 'Tabs',
data() {
return {
layers: this.$root.layers
};
},
computed: mapState([ 'tabs' ]), computed: mapState([ 'tabs' ]),
methods: { methods: {
goBack(/* index */) { goBack(index) {
// TODO tabs.splice(index) this.$store.commit(SET_TABS, this.$store.state.tabs.slice(0, index));
} }
} }
}; };
@ -56,4 +64,9 @@ export default {
cursor: pointer; cursor: pointer;
line-height: 40px; line-height: 40px;
} }
.goBack:hover {
transform: scale(1.1, 1.1);
text-shadow: 0 0 7px var(--color);
}
</style> </style>

View file

@ -0,0 +1,67 @@
<template>
<div class="field">
<span class="field-title">{{ title }}</span>
<v-select :options="options" :value="value" @input="setSelected" />
</div>
</template>
<script>
import vSelect from 'vue-select';
import './fields.css';
import 'vue-select/dist/vue-select.css';
export default {
name: 'Select',
props: {
title: String,
options: Array, // https://vue-select.org/guide/options.html#options-prop
value: String,
default: String
},
components: {
vSelect
},
methods: {
setSelected(option) {
const value = option?.value || this.default;
this.$emit('change', value);
}
}
};
</script>
<style scoped>
span {
margin: 0;
}
</style>
<style>
.v-select {
width: 50%;
margin: 0;
}
.v-select .vs__dropdown-toggle {
border-color: rgba(var(--color), .26);
}
.v-select .vs__selected {
color: var(--color);
}
.v-select .vs__clear,
.v-select .vs__open-indicator {
fill: var(--color);
opacity: .5;
}
.v-select .vs__dropdown-menu {
background: var(--background);
border-color: rgba(var(--color), .26);
}
.v-select .vs__dropdown-option {
color: var(--color);
}
</style>

View file

@ -0,0 +1,99 @@
<template>
<label class="field">
<input type="checkbox" class="toggle" :checked="value" @input="handleInput" />
<span>{{ title }}</span>
</label>
</template>
<script>
import './fields.css';
// Reference: https://codepen.io/finnhvman/pen/pOeyjE
export default {
name: 'Toggle',
props: {
title: String,
value: Boolean
},
methods: {
handleInput(e) {
this.$emit('change', e.target.value);
}
}
};
</script>
<style scoped>
.field {
cursor: pointer;
}
input {
appearance: none;
pointer-events: none;
}
span {
width: 100%;
}
/* track */
span::before {
content: "";
float: right;
margin: 5px 0 5px 10px;
border-radius: 7px;
width: 36px;
height: 14px;
background-color: rgba(0, 0, 0, 0.38);
vertical-align: top;
transition: background-color 0.2s, opacity 0.2s;
}
/* thumb */
span::after {
content: "";
position: absolute;
top: 6px;
right: 16px;
border-radius: 50%;
width: 20px;
height: 20px;
background-color: white;
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
transition: background-color 0.2s, transform 0.2s;
}
input:checked + span::before {
background-color: rgba(33, 150, 243, 0.6);
}
input:checked + span::after {
background-color: rgb(33, 150, 243);
transform: translateX(16px);
}
/* active */
input:active + span::before {
background-color: rgba(33, 150, 243, 0.6);
}
input:checked:active + span::before {
background-color: rgba(0, 0, 0, 0.38);
}
/* disabled */
input:disabled + span {
color: black;
opacity: 0.38;
cursor: default;
}
input:disabled + span::before {
background-color: rgba(0, 0, 0, 0.38);
}
input:checked:disabled + span::before {
background-color: rgba(33, 150, 243, 0.6);
}
</style>

View file

@ -0,0 +1,8 @@
.field {
display: flex;
position: relative;
height: 2em;
margin: 10px 0;
user-select: none;
justify-content: space-between;
}

9
src/data/layers.js Normal file
View file

@ -0,0 +1,9 @@
// TODO create example layer and import it
export const layers = {
};
export const hotkeys = [
];

View file

@ -1,14 +1,12 @@
// import TreeTab from '../components/system/TreeTab.vue';
// Import Decimal and numberUtils from a different file to globally change which big num library gets used // Import Decimal and numberUtils from a different file to globally change which big num library gets used
import Decimal, * as numberUtils from '../util/break_eternity.js'; import Decimal, * as numberUtils from '../util/break_eternity.js';
export default { const modInfo = {
// General Info // General Info
title: "The Modding Tree X", title: "The Modding Tree X",
banner: null,
id: "tmt-x", id: "tmt-x",
author: "thepaperpilot", author: "thepaperpilot",
discordName: "TMT-X Server", discordName: "TMT-X",
discordLink: "https://discord.gg/WzejVAx", discordLink: "https://discord.gg/WzejVAx",
// Gameplay Options // Gameplay Options
@ -17,10 +15,14 @@ export default {
points: new Decimal(10), points: new Decimal(10),
} }
}, },
// TODO getPointGen or some abstract version?
hasWon() { hasWon() {
return false; return false;
}, },
update(delta) {
let gain = new Decimal(1);
// TODO add gain to player.deltas
gain.times(delta);
},
// Version // Version
versionNumber: "0.0", versionNumber: "0.0",
@ -28,8 +30,11 @@ export default {
// UI options // UI options
allowSmall: false, allowSmall: false,
useHeader: true, defaultDecimalsShown: 2,
//defaultTab: TreeTab useHeader: false,
banner: null,
logo: null,
initialTabs: ["tree-tab", "info-tab", "dummy"],
// Advanced Options // Advanced Options
/* eslint-disable-next-line no-unused-vars */ /* eslint-disable-next-line no-unused-vars */
@ -38,3 +43,7 @@ export default {
bigNum: { Decimal, ...numberUtils }, bigNum: { Decimal, ...numberUtils },
maxTickLength: 3600 maxTickLength: 3600
}; };
document.title = modInfo.title;
export default modInfo;

View file

@ -1,12 +1,15 @@
const defaultTheme = { const defaultTheme = {
"--background": "#0f0f0f", "--background": "#0f0f0f",
"--background_tooltip": "rgba(0, 0, 0, 0.75)", "--background-tooltip": "rgba(0, 0, 0, 0.75)",
"--background_nav": "#0f0f0f", "--secondary-background": "#0f0f0f",
"--color": "#dfdfdf", "--color": "#dfdfdf",
"--points": "#ffffff", "--points": "#ffffff",
"--locked": "#bf8f8f", "--locked": "#bf8f8f",
"--link": "#02f2f2", "--link": "#02f2f2",
"--separator": "#dfdfdf" "--separator": "#dfdfdf",
"--border-radius": "25%",
"--danger": "rgb(220, 53, 69)",
"--modal-border": "solid 2px var(--color)"
}; };
export default { export default {
@ -14,14 +17,16 @@ export default {
paper: { paper: {
...defaultTheme, ...defaultTheme,
"--background": "#2a323d", "--background": "#2a323d",
"--background_nav": "#333c4a", "--secondary-background": "#333c4a",
"--separator": "#333c4a" "--separator": "#333c4a",
"--border-radius": "4px",
"--modal-border": ""
}, },
aquad: { aqua: {
...defaultTheme, ...defaultTheme,
"--background": "#001f3f", "--background": "#001f3f",
"--background_tooltip": "rgba(0, 15, 31, 0.75)", "--background-tooltip": "rgba(0, 15, 31, 0.75)",
"--background_nav": "#001f3f", "--secondary-background": "#001f3f",
"--color": "#bfdfff", "--color": "#bfdfff",
"--points": "#dfefff", "--points": "#dfefff",
"--locked": "#c4a7b3", "--locked": "#c4a7b3",

View file

@ -1,12 +1,17 @@
import Vue from 'vue'; import Vue from 'vue';
import App from './App.vue'; import App from './App.vue';
import store from './store'; import store from './store';
import { layers, hotkeys } from './data/layers.js';
// Setup
Vue.config.productionTip = false; Vue.config.productionTip = false;
window.player = store.state; window.player = store.state;
new Vue({ // Create Vue
window.vue = new Vue({
store, store,
render: h => h(App) render: h => h(App),
data: { layers, hotkeys }
}).$mount('#app'); }).$mount('#app');
// Start game loop

View file

@ -1,15 +1,11 @@
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import { getInitialStore } from '../util/load.js'; import { getInitialStore } from '../util/load.js';
import mutations from './mutations';
Vue.use(Vuex) Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
state: getInitialStore(), state: getInitialStore(),
mutations: { mutations
},
actions: {
},
modules: {
}
}) })

16
src/store/mutations.js Normal file
View file

@ -0,0 +1,16 @@
import Vue from 'vue';
export const SET_TABS = 'SET_TABS';
export const SET_SETTING = 'SET_SETTING';
export default {
[SET_TABS](state, tabs) {
Vue.set(state, 'tabs', tabs);
},
[SET_SETTING](state, { setting, value }) {
if (value == null) {
value = !state[setting];
}
state[setting] = value;
}
};

View file

@ -12,7 +12,7 @@ export function exponentialFormat(num, precision, mantissa = true) {
m = decimalOne; m = decimalOne;
e = e.add(1); e = e.add(1);
} }
e = (e.gte(1e9) ? format(e, 3) : (e.gte(10000) ? commaFormat(e, 0) : e.toStringWithDecimalPlaces(0))) e = (e.gte(1e9) ? format(e, Math.max(Math.max(precision, 3), modInfo.defaultDecimalsShown)) : (e.gte(10000) ? commaFormat(e, 0) : e.toStringWithDecimalPlaces(0)))
if (mantissa) { if (mantissa) {
return m.toStringWithDecimalPlaces(precision)+"e"+e; return m.toStringWithDecimalPlaces(precision)+"e"+e;
} else { } else {
@ -42,12 +42,13 @@ export function regularFormat(num, precision) {
return (0).toFixed(precision); return (0).toFixed(precision);
} }
if (num.mag < 0.1 && precision !== 0) { if (num.mag < 0.1 && precision !== 0) {
precision = Math.max(precision, 4); precision = Math.max(Math.max(precision, 4), modInfo.defaultDecimalsShown);
} }
return num.toStringWithDecimalPlaces(precision); return num.toStringWithDecimalPlaces(precision);
} }
export function format(decimal, precision=2, small) { export function format(decimal, precision = null, small) {
if (precision == null) precision = modInfo.defaultDecimalsShown;
small = small || modInfo.allowSmall; small = small || modInfo.allowSmall;
decimal = new Decimal(decimal); decimal = new Decimal(decimal);
if (isNaN(decimal.sign)||isNaN(decimal.layer)||isNaN(decimal.mag)) { if (isNaN(decimal.sign)||isNaN(decimal.layer)||isNaN(decimal.mag)) {
@ -96,10 +97,10 @@ export function formatWhole(decimal) {
return "-"+formatWhole(decimal.neg()); return "-"+formatWhole(decimal.neg());
} }
if (decimal.gte(1e9)) { if (decimal.gte(1e9)) {
return format(decimal, 2); return format(decimal);
} }
if (decimal.lte(0.98) && !decimal.eq(0)) { if (decimal.lte(0.98) && !decimal.eq(0)) {
return format(decimal, 2); return format(decimal);
} }
return format(decimal, 0); return format(decimal, 0);
} }
@ -128,7 +129,7 @@ export function toPlaces(x, precision, maxAccepted) {
} }
// Will also display very small numbers // Will also display very small numbers
export function formatSmall(x, precision=2) { export function formatSmall(x, precision = null) {
return format(x, precision, true); return format(x, precision, true);
} }

6
src/util/common.js Normal file
View file

@ -0,0 +1,6 @@
// Reference: https://stackoverflow.com/questions/7225407/convert-camelcasetext-to-sentence-case-text
export function camelToTitle(camel) {
let title = camel.replace(/([A-Z])/g, " $1");
title = title.charAt(0).toUpperCase() + title.slice(1);
return title;
}

View file

@ -2,7 +2,7 @@ import modInfo from '../data/mod.js';
export function getInitialStore() { export function getInitialStore() {
return { return {
tabs: ["tree-tab", "info-tab", "dummy"], tabs: modInfo.initialTabs.slice(),
time: Date.now(), time: Date.now(),
autosave: true, autosave: true,
offlineProd: true, offlineProd: true,
@ -11,6 +11,7 @@ export function getInitialStore() {
hasNaN: false, hasNaN: false,
lastTenTicks: [], lastTenTicks: [],
showTPS: true, showTPS: true,
theme: "paper",
...modInfo.getStartingData() ...modInfo.getStartingData()
} }
} }